소스 검색

MultiSelect: Box-Select: Further refactor to extra mode code away from multi-select function into box-select funcitons.

ocornut 1 년 전
부모
커밋
f3d77d8e71
3개의 변경된 파일109개의 추가작업 그리고 80개의 파일을 삭제
  1. 2 2
      imgui.cpp
  2. 6 1
      imgui_internal.h
  3. 101 77
      imgui_widgets.cpp

+ 2 - 2
imgui.cpp

@@ -14997,8 +14997,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
     // Details for MultiSelect
     if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
     {
-        ImGuiBoxSelectState* ms = &g.BoxSelectState;
-        Text("BoxSelect ID=0x%08X, Starting = %d, Active %d", ms->ID, ms->IsStarting, ms->IsActive);
+        ImGuiBoxSelectState* bs = &g.BoxSelectState;
+        BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
         for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
             if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
                 DebugNodeMultiSelectState(state);

+ 6 - 1
imgui_internal.h

@@ -1718,6 +1718,7 @@ struct ImGuiBoxSelectState
     bool                    IsActive;
     bool                    IsStarting;
     bool                    IsStartedFromVoid;  // Starting click was not from an item.
+    bool                    RequestClear;
     ImGuiKeyChord           KeyMods : 16;       // Latched key-mods for box-select logic.
     ImVec2                  StartPosRel;        // Start position in window-contents relative space (to support scrolling)
     ImVec2                  EndPosRel;          // End position in window-contents relative space
@@ -1729,7 +1730,6 @@ struct ImGuiBoxSelectState
     ImRect                  UnclipRect;         // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.
     ImRect                  BoxSelectRectPrev;  // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)
     ImRect                  BoxSelectRectCurr;
-    ImGuiSelectionUserData  LastSubmittedItem;  // Copy of last submitted item data, used to merge output ranges.
 
     ImGuiBoxSelectState()   { memset(this, 0, sizeof(*this)); }
 };
@@ -1762,6 +1762,7 @@ struct IMGUI_API ImGuiMultiSelectTempData
     bool                    NavIdPassedBy;
     bool                    RangeSrcPassedBy;   // Set by the item that matches RangeSrcItem.
     bool                    RangeDstPassedBy;   // Set by the item that matches NavJustMovedToId when IsSetRange is set.
+    ImGuiSelectionUserData  BoxSelectLastitem;  // Copy of last submitted item data, used to merge output ranges.
 
     ImGuiMultiSelectTempData()  { Clear(); }
     void Clear()            { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation.
@@ -3403,6 +3404,10 @@ namespace ImGui
     IMGUI_API int           TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
     IMGUI_API int           TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);
 
+    // Box-Select API
+    IMGUI_API bool          BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags);
+    IMGUI_API void          EndBoxSelect(const ImRect& scope_rect, bool enable_scroll);
+
     // Multi-Select API
     IMGUI_API void          MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags);
     IMGUI_API void          MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed);

+ 101 - 77
imgui_widgets.cpp

@@ -7113,12 +7113,15 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
 
 //-------------------------------------------------------------------------
 // [SECTION] Widgets: Box-Select support
+// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet.
 //-------------------------------------------------------------------------
-// - BoxSelectStart() [Internal]
+// - BoxSelectStartDrag() [Internal]
 // - BoxSelectScrollWithMouseDrag() [Internal]
+// - BeginBoxSelect() [Internal]
+// - EndBoxSelect() [Internal]
 //-------------------------------------------------------------------------
 
-static void BoxSelectStart(ImGuiID id, ImGuiSelectionUserData clicked_item)
+static void BoxSelectStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item)
 {
     ImGuiContext& g = *GImGui;
     ImGuiBoxSelectState* bs = &g.BoxSelectState;
@@ -7159,6 +7162,91 @@ static void BoxSelectScrollWithMouseDrag(ImGuiWindow* window, const ImRect& inne
     }
 }
 
+bool ImGui::BeginBoxSelect(ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags)
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiBoxSelectState* bs = &g.BoxSelectState;
+    KeepAliveID(box_select_id);
+    if (bs->ID != box_select_id)
+        return false;
+
+    bs->UnclipMode = false;
+    bs->RequestClear = false;
+
+    // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry.
+    if (bs->IsStarting && IsMouseDragPastThreshold(0))
+    {
+        bs->IsActive = true;
+        bs->Window = window;
+        bs->IsStarting = false;
+        SetActiveID(bs->ID, window);
+        if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0)
+            bs->RequestClear = true;
+    }
+    else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false)
+    {
+        bs->IsActive = bs->IsStarting = false;
+        if (g.ActiveId == bs->ID)
+            ClearActiveID();
+        bs->ID = 0;
+    }
+    if (!bs->IsActive)
+        return false;
+
+    // Current frame absolute prev/current rectangles are used to toggle selection.
+    // They are derived from positions relative to scrolling space.
+    const ImRect scope_rect = window->InnerClipRect;
+    ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
+    ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already
+    ImVec2 curr_end_pos_abs = g.IO.MousePos;
+    if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow)  // Box-select scrolling only happens with ScopeWindow
+        curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);
+    bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);
+    bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
+    bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);
+    bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);
+
+    // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper)
+    // Storing an extra rect used by widgets supporting box-select.
+    if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)
+        if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
+        {
+            bs->UnclipRect = bs->BoxSelectRectPrev;
+            bs->UnclipRect.Add(bs->BoxSelectRectCurr);
+            bs->UnclipMode = true;
+        }
+
+    //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
+    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
+    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f);
+    return true;
+}
+
+void ImGui::EndBoxSelect(const ImRect& scope_rect, bool enable_scroll)
+{
+    ImGuiContext& g = *GImGui;
+    ImGuiWindow* window = g.CurrentWindow;
+    ImGuiBoxSelectState* bs = &g.BoxSelectState;
+    IM_ASSERT(bs->IsActive);
+    bs->UnclipMode = false;
+
+    // Render selection rectangle
+    bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view
+    ImRect box_select_r = bs->BoxSelectRectCurr;
+    box_select_r.ClipWith(scope_rect);
+    window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
+    window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling
+
+    // Scroll
+    if (enable_scroll)
+    {
+        ImRect scroll_r = scope_rect;
+        scroll_r.Expand(-g.FontSize);
+        //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255));
+        if (!scroll_r.Contains(g.IO.MousePos))
+            BoxSelectScrollWithMouseDrag(window, scroll_r);
+    }
+}
 
 //-------------------------------------------------------------------------
 // [SECTION] Widgets: Multi-Select support
@@ -7268,59 +7356,9 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags)
     if (flags & ImGuiMultiSelectFlags_BoxSelect)
     {
         ms->BoxSelectId = GetID("##BoxSelect");
-        KeepAliveID(ms->BoxSelectId);
-    }
-    if ((flags & ImGuiMultiSelectFlags_BoxSelect) && ms->BoxSelectId == bs->ID)
-    {
-        bs->UnclipMode = false;
-
-        // BoxSelectStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry.
-        if (bs->IsStarting && IsMouseDragPastThreshold(0))
-        {
-            bs->IsActive = true;
-            bs->Window = ms->Storage->Window;
-            bs->IsStarting = false;
-            SetActiveID(ms->BoxSelectId, window);
-            if (bs->IsStartedFromVoid && (bs->KeyMods & ImGuiMod_Shift) == 0)
-                request_clear = true;
-        }
-        else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false)
-        {
-            bs->ID = 0;
-            bs->IsActive = bs->IsStarting = false;
-            if (g.ActiveId == ms->BoxSelectId)
-                ClearActiveID();
-        }
-        if (bs->IsActive)
-        {
-            // Current frame absolute prev/current rectangles are used to toggle selection.
-            // They are derived from positions relative to scrolling space.
-            const ImRect scope_rect = window->InnerClipRect;
-            ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
-            ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already
-            ImVec2 curr_end_pos_abs = g.IO.MousePos;
-            if (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow)  // Box-select scrolling only happens with ScopeWindow
-                curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);
-            bs->LastSubmittedItem = ImGuiSelectionUserData_Invalid;
-            bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);
-            bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
-            bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);
-            bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);
-
-            // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper)
-            // Storing an extra rect used by widgets supporting box-select.
-            if (flags & ImGuiMultiSelectFlags_BoxSelect2d)
-                if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
-                {
-                    bs->UnclipRect = bs->BoxSelectRectPrev;
-                    bs->UnclipRect.Add(bs->BoxSelectRectCurr);
-                    bs->UnclipMode = true;
-                }
-
-            //GetForegroundDrawList()->AddRect(ms->BoxSelectNoClipRect.Min, ms->BoxSelectNoClipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
-            //GetForegroundDrawList()->AddRect(ms->BoxSelectRectPrev.Min, ms->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
-            //GetForegroundDrawList()->AddRect(ms->BoxSelectRectCurr.Min, ms->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f);
-        }
+        ms->BoxSelectLastitem = ImGuiSelectionUserData_Invalid;
+        if (BeginBoxSelect(window, ms->BoxSelectId, flags))
+            request_clear |= bs->RequestClear;
     }
 
     if (request_clear || request_select_all)
@@ -7364,24 +7402,10 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
             storage->NavIdSelected = -1;
         }
 
-        ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId);
-        if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && bs != NULL)
+        if ((ms->Flags & ImGuiMultiSelectFlags_BoxSelect) && GetBoxSelectState(ms->BoxSelectId))
         {
-            // Box-select: render selection rectangle
-            bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view
-            ImRect box_select_r = bs->BoxSelectRectCurr;
-            box_select_r.ClipWith(scope_rect);
-            window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
-            window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavHighlight)); // FIXME-MULTISELECT: Styling
-
-            // Box-select: scroll
-            ImRect scroll_r = scope_rect;
-            scroll_r.Expand(-g.FontSize);
-            //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255));
-            if ((ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0 && !scroll_r.Contains(g.IO.MousePos))
-                BoxSelectScrollWithMouseDrag(window, scroll_r);
-
-            bs->UnclipMode = false;
+            bool enable_scroll = (ms->Flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms->Flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
+            EndBoxSelect(scope_rect, enable_scroll);
         }
     }
 
@@ -7395,7 +7419,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
     {
         if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect)
             if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1)
-                BoxSelectStart(ms->BoxSelectId, ImGuiSelectionUserData_Invalid);
+                BoxSelectStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid);
 
         if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid)
             if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None)
@@ -7544,7 +7568,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
             selected = pressed = true;
     }
 
-    // Box-select handling
+    // Box-select toggle handling
     if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId))
     {
         const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect);
@@ -7554,12 +7578,12 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
             selected = !selected;
             ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, item_data, item_data };
             ImGuiSelectionRequest* prev_req = (ms->IO.Requests.Size > 0) ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL;
-            if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == bs->LastSubmittedItem && prev_req->RangeSelected == selected)
+            if (prev_req && prev_req->Type == ImGuiSelectionRequestType_SetRange && prev_req->RangeLastItem == ms->BoxSelectLastitem && prev_req->RangeSelected == selected)
                 prev_req->RangeLastItem = item_data; // Merge span into same request
             else
                 ms->IO.Requests.push_back(req);
         }
-        bs->LastSubmittedItem = item_data;
+        ms->BoxSelectLastitem = item_data;
     }
 
     // Right-click handling: this could be moved at the Selectable() level.
@@ -7588,7 +7612,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
         ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
         if (ms->Flags & ImGuiMultiSelectFlags_BoxSelect)
             if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1)
-                BoxSelectStart(ms->BoxSelectId, item_data);
+                BoxSelectStartDrag(ms->BoxSelectId, item_data);
 
         //----------------------------------------------------------------------------------------
         // ACTION                      | Begin  | Pressed/Activated  | End