Prechádzať zdrojové kódy

MultiSelect: (breaking) Added 'items_count' parameter to BeginMultiSelect(). Will enable extra features, and remove equivalent param from ImGuiSelectionBasicStorage::ApplyRequests(.

ocornut 1 rok pred
rodič
commit
ab995d3d4f
3 zmenil súbory, kde vykonal 46 pridanie a 35 odobranie
  1. 7 5
      imgui.h
  2. 20 20
      imgui_demo.cpp
  3. 19 10
      imgui_widgets.cpp

+ 7 - 5
imgui.h

@@ -179,7 +179,7 @@ struct ImGuiMultiSelectIO;          // Structure to interact with a BeginMultiSe
 struct ImGuiOnceUponAFrame;         // Helper for running a block of code not more than once a frame
 struct ImGuiPayload;                // User data payload for drag and drop operations
 struct ImGuiPlatformImeData;        // Platform IME data for io.PlatformSetImeDataFn() function.
-struct ImGuiSelectionBasicStorage;  // Helper struct to store multi-selection state + apply multi-selection requests.
+struct ImGuiSelectionBasicStorage;  // Optional helper to store multi-selection state + apply multi-selection requests.
 struct ImGuiSelectionRequest;       // A selection request (stored in ImGuiMultiSelectIO)
 struct ImGuiSizeCallbackData;       // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
 struct ImGuiStorage;                // Helper for key->value storage (container sorted by key)
@@ -673,9 +673,10 @@ namespace ImGui
 
     // Multi-selection system for Selectable() and TreeNode() functions.
     // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.
-    // - ImGuiSelectionUserData is often used to store your item index.
+    // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else).
     // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo.
-    IMGUI_API ImGuiMultiSelectIO*   BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size = -1);
+    // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them.
+    IMGUI_API ImGuiMultiSelectIO*   BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1);
     IMGUI_API ImGuiMultiSelectIO*   EndMultiSelect();
     IMGUI_API void                  SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
     IMGUI_API bool                  IsItemToggledSelection();                                   // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly.
@@ -2799,6 +2800,7 @@ struct ImGuiMultiSelectIO
     ImGuiSelectionUserData      NavIdItem;      //  ms:w, app:r     /                // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items).
     bool                        NavIdSelected;  //  ms:w, app:r     /        app:r   // (If using deletion) Last known selection state for NavId (if part of submitted items).
     bool                        RangeSrcReset;  //        app:w     /  ms:r          // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection).
+    int                         ItemsCount;     //  ms:w, app:r     /        app:r   // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally.
 };
 
 // Selection request type
@@ -2846,8 +2848,8 @@ struct ImGuiSelectionBasicStorage
     ImGuiID         (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx);  // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; };
     void*           UserData;       // User data for use by adapter function                // e.g. selection.UserData = (void*)my_items;
 
-    // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions
-    IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count);
+    // Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' based to BeginMultiSelect()
+    IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io);
 
     // Methods: selection storage
     ImGuiSelectionBasicStorage()                { Clear(); UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; }

+ 20 - 20
imgui_demo.cpp

@@ -2874,7 +2874,7 @@ struct ExampleDualListBox
         // In this example we store item id in selection (instead of item index)
         Selections[side].UserData = Items[side].Data;
         Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
-        Selections[side].ApplyRequests(ms_io, Items[side].Size);
+        Selections[side].ApplyRequests(ms_io);
     }
     static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
     {
@@ -2929,7 +2929,7 @@ struct ExampleDualListBox
                 if (child_visible)
                 {
                     ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;
-                    ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size);
+                    ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
                     ApplySelectionRequests(ms_io, side);
 
                     for (int item_n = 0; item_n < items.Size; item_n++)
@@ -3060,8 +3060,8 @@ static void ShowDemoWindowMultiSelect()
             if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
             {
                 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
-                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size);
-                selection.ApplyRequests(ms_io, ITEMS_COUNT);
+                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
+                selection.ApplyRequests(ms_io);
 
                 for (int n = 0; n < ITEMS_COUNT; n++)
                 {
@@ -3073,7 +3073,7 @@ static void ShowDemoWindowMultiSelect()
                 }
 
                 ms_io = ImGui::EndMultiSelect();
-                selection.ApplyRequests(ms_io, ITEMS_COUNT);
+                selection.ApplyRequests(ms_io);
             }
             ImGui::EndChild();
             ImGui::TreePop();
@@ -3094,8 +3094,8 @@ static void ShowDemoWindowMultiSelect()
             if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
             {
                 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
-                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size);
-                selection.ApplyRequests(ms_io, ITEMS_COUNT);
+                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
+                selection.ApplyRequests(ms_io);
 
                 ImGuiListClipper clipper;
                 clipper.Begin(ITEMS_COUNT);
@@ -3114,7 +3114,7 @@ static void ShowDemoWindowMultiSelect()
                 }
 
                 ms_io = ImGui::EndMultiSelect();
-                selection.ApplyRequests(ms_io, ITEMS_COUNT);
+                selection.ApplyRequests(ms_io);
             }
             ImGui::EndChild();
             ImGui::TreePop();
@@ -3158,8 +3158,8 @@ static void ShowDemoWindowMultiSelect()
             if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
             {
                 ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
-                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size);
-                selection.ApplyRequests(ms_io, items.Size);
+                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
+                selection.ApplyRequests(ms_io);
 
                 const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0);
                 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
@@ -3179,7 +3179,7 @@ static void ShowDemoWindowMultiSelect()
 
                 // Apply multi-select requests
                 ms_io = ImGui::EndMultiSelect();
-                selection.ApplyRequests(ms_io, items.Size);
+                selection.ApplyRequests(ms_io);
                 if (want_delete)
                     selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
             }
@@ -3275,8 +3275,8 @@ static void ShowDemoWindowMultiSelect()
             {
                 ImGui::PushID(selection_scope_n);
                 ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
-                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size);
-                selection->ApplyRequests(ms_io, ITEMS_COUNT);
+                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT);
+                selection->ApplyRequests(ms_io);
 
                 ImGui::SeparatorText("Selection scope");
                 ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT);
@@ -3292,7 +3292,7 @@ static void ShowDemoWindowMultiSelect()
 
                 // Apply multi-select requests
                 ms_io = ImGui::EndMultiSelect();
-                selection->ApplyRequests(ms_io, ITEMS_COUNT);
+                selection->ApplyRequests(ms_io);
                 ImGui::PopID();
             }
             ImGui::TreePop();
@@ -3375,8 +3375,8 @@ static void ShowDemoWindowMultiSelect()
                 if (widget_type == WidgetType_TreeNode)
                     ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
 
-                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size);
-                selection.ApplyRequests(ms_io, items.Size);
+                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
+                selection.ApplyRequests(ms_io);
 
                 const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
                 const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
@@ -3520,7 +3520,7 @@ static void ShowDemoWindowMultiSelect()
 
                 // Apply multi-select requests
                 ms_io = ImGui::EndMultiSelect();
-                selection.ApplyRequests(ms_io, items.Size);
+                selection.ApplyRequests(ms_io);
                 if (want_delete)
                     selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
 
@@ -9846,12 +9846,12 @@ struct ExampleAssetsBrowser
                 ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection.
             if (AllowBoxSelect)
                 ms_flags |= ImGuiMultiSelectFlags_BoxSelect; // Enable box-select in 2D mode.
-            ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size);
+            ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size);
 
             // Use custom selection adapter: store ID in selection (recommended)
             Selection.UserData = this;
             Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
-            Selection.ApplyRequests(ms_io, Items.Size);
+            Selection.ApplyRequests(ms_io);
 
             const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
             const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1;
@@ -9959,7 +9959,7 @@ struct ExampleAssetsBrowser
             }
 
             ms_io = ImGui::EndMultiSelect();
-            Selection.ApplyRequests(ms_io, Items.Size);
+            Selection.ApplyRequests(ms_io);
             if (want_delete)
                 Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);
 

+ 19 - 10
imgui_widgets.cpp

@@ -7300,10 +7300,13 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
 
 // Return ImGuiMultiSelectIO structure.
 // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
-// Passing 'current_selection_size' is currently optional:
-// - it is useful for shortcut routing with ImGuiMultiSelectFlags_ClearOnEscape: so we can have Escape be used to clear selection THEN to exit child window.
-// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may pass 1, or use the ImGuiMultiSelectFlags_ClearOnEscape flag dynamically.
-ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size)
+// Passing 'selection_size' and 'items_count' parameters is currently optional.
+// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0,
+//    allowing a first press to clear selection THEN the second press to leave child window and return to parent.
+// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently).
+// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1.
+//   - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically.
+ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count)
 {
     ImGuiContext& g = *GImGui;
     ImGuiWindow* window = g.CurrentWindow;
@@ -7341,15 +7344,16 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur
     ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id);
     storage->ID = id;
     storage->LastFrameActive = g.FrameCount;
-    storage->LastSelectionSize = current_selection_size;
+    storage->LastSelectionSize = selection_size;
     storage->Window = window;
     ms->Storage = storage;
 
     // Output to user
+    ms->IO.Requests.resize(0);
     ms->IO.RangeSrcItem = storage->RangeSrcItem;
     ms->IO.NavIdItem = storage->NavIdItem;
     ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false;
-    ms->IO.Requests.resize(0);
+    ms->IO.ItemsCount = items_count;
 
     // Clear when using Navigation to move within the scope
     // (we compare FocusScopeId so it possible to use multiple selections inside a same window)
@@ -7375,7 +7379,7 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int cur
     {
         // Shortcut: Clear selection (Escape)
         // Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window.
-        if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (current_selection_size != 0))
+        if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (selection_size != 0))
             if (Shortcut(ImGuiKey_Escape))
                 request_clear = true;
 
@@ -7826,17 +7830,22 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
 //      if (req.Type == ImGuiSelectionRequestType_SetAll)    { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { AddItem(n); } }
 //      if (req.Type == ImGuiSelectionRequestType_SetRange)  { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->Selected); } }
 //   }
-void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count)
+void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
 {
+    // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional.
+    // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect().
+    // Other scheme may handle SetAll differently.
+    IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!");
     IM_ASSERT(AdapterIndexToStorageId != NULL);
+
     for (ImGuiSelectionRequest& req : ms_io->Requests)
     {
         if (req.Type == ImGuiSelectionRequestType_SetAll)
             Clear();
         if (req.Type == ImGuiSelectionRequestType_SetAll && req.Selected)
         {
-            Storage.Data.reserve(items_count);
-            for (int idx = 0; idx < items_count; idx++)
+            Storage.Data.reserve(ms_io->ItemsCount);
+            for (int idx = 0; idx < ms_io->ItemsCount; idx++)
                 SetItemSelected(AdapterIndexToStorageId(this, idx), true);
         }
         if (req.Type == ImGuiSelectionRequestType_SetRange)