Ver código fonte

MultiSelect: move demo's ExampleSelection to main api as a convenient ImGuiSelectionBasicStorage for basic users.

ocornut 1 ano atrás
pai
commit
0f633c1d99
3 arquivos alterados com 109 adições e 102 exclusões
  1. 51 2
      imgui.h
  2. 19 100
      imgui_demo.cpp
  3. 39 0
      imgui_widgets.cpp

+ 51 - 2
imgui.h

@@ -44,7 +44,7 @@ Index of this file:
 // [SECTION] ImGuiIO
 // [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
 // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
-// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO)
+// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage)
 // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
 // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
 // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)
@@ -179,6 +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 ImGuiSizeCallbackData;       // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
 struct ImGuiStorage;                // Helper for key->value storage (container sorted by key)
 struct ImGuiStoragePair;            // Helper for key->value storage (pair)
@@ -2720,7 +2721,7 @@ struct ImColor
 };
 
 //-----------------------------------------------------------------------------
-// [SECTION] Multi-Select API flags & structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO)
+// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage)
 //-----------------------------------------------------------------------------
 
 #define IMGUI_HAS_MULTI_SELECT      // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts.
@@ -2820,6 +2821,54 @@ struct ImGuiMultiSelectIO
     bool                        RangeSrcReset;  //        app:w     /  ms:r          // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection).
 };
 
+// Helper struct to store multi-selection state + apply multi-selection requests.
+// - Used by our demos and provided as a convenience if you want to quickly implement multi-selection.
+// - Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data.
+// - USING THIS IS NOT MANDATORY. This is only a helper and is not part of the main API. Advanced users are likely to implement their own.
+// To store a single-selection, you only need a single variable and don't need any of this!
+// To store a multi-selection, in your real application you could:
+// - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set<ImGuiID> replacement.
+// - B) Use your own external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, std::set<index>, interval trees, etc.
+//      are generally appropriate. Even a large array of bool might work for you...
+// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). Not recommended because:
+//      - it means you cannot have multiple simultaneous views over your objects.
+//      - some of our features requires you to provide the selection _size_, which with this specific strategy require additional work.
+// Our BeginMultiSelect() api/system doesn't make assumption about:
+// - how you want to identify items in multi-selection API?     Indices(*) / Custom Identifiers    / Pointers ?
+// - how you want to store persistent selection data?           Indices    / Custom Identifiers(*) / Pointers ?
+// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data.
+// In ImGuiSelectionBasicStorage we:
+// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO)
+// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index.
+//   - in some cases we use Index as custom identifier (default, not ideal)
+//   - in some cases we read an ID from some custom item data structure (better, and closer to what you would do in your codebase)
+// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection.
+// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC.
+// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well,
+// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify.
+struct ImGuiSelectionBasicStorage
+{
+    ImGuiStorage    Storage;                // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>
+    int             Size;                   // Number of selected items (== number of 1 in the Storage, maintained by this class).
+
+    // Adapter to convert item index to item identifier
+    void*           AdapterData;                                                            // e.g. selection.AdapterData = (void*)my_items;
+    ImGuiID         (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx);  // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->AdapterData)[idx]->ID; };
+
+    // Selection storage
+    ImGuiSelectionBasicStorage()            { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; }
+    void Clear()                            { Storage.Data.resize(0); Size = 0; }
+    void Swap(ImGuiSelectionBasicStorage& r){ Storage.Data.swap(r.Storage.Data); }
+    bool Contains(ImGuiID key) const        { return Storage.GetInt(key, 0) != 0; }
+    void AddItem(ImGuiID key)               { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; }
+    void RemoveItem(ImGuiID key)            { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; }
+    void UpdateItem(ImGuiID key, bool v)    { if (v) { AddItem(key); } else { RemoveItem(key); } }
+    int  GetSize() const                    { return Size; }
+
+    // Request handling (apply requests coming from BeginMultiSelect() and EndMultiSelect() functions)
+    IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count);
+};
+
 //-----------------------------------------------------------------------------
 // [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData)
 // Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.

+ 19 - 100
imgui_demo.cpp

@@ -2773,92 +2773,9 @@ static const char* ExampleNames[] =
     "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
 };
 
-// [Advanced] Helper class to store multi-selection state, used by the BeginMultiSelect() demos.
-// Provide an abstraction layer for the purpose of the demo showcasing different forms of underlying selection data.
-// To store a single-selection:
-// - You only need a single variable and don't need any of this!
-// To store a multi-selection, in your real application you could:
-// - A) Use external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, std::set<index>, interval trees, etc.
-//   are generally appropriate. Even a large array of bool might work for you...
-//   This code here use ImGuiStorage (a simple key->value storage) as a std::set replacement to avoid external dependencies.
-// - B) Or use intrusively stored selection (e.g. 'bool IsSelected' inside your object).
-//   - That means you cannot have multiple simultaneous views over your objects.
-//   - Some of our features requires you to provide the selection _size_, which with this specific strategy require additional work.
-//   - So we suggest using intrusive selection for multi-select is not really adequate.
-// Our multi-selection system doesn't make assumption about:
-// - how you want to identify items in multi-selection API?     Indices(*) / Custom Identifiers    / Pointers ?
-// - how you want to store persistent selection data?           Indices    / Custom Identifiers(*) / Pointers ?
-// (*) This is the suggested solution: pass indices to API (because easy to iterate/interpolate) + persist your custom identifiers inside selection data.
-// In this demo we:
-// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO)
-// - use a little extra indirection layer in order to abstract how persistent selection data is derived from an index.
-//   - in some cases we use Index as custom identifier
-//   - in some cases we read an ID from some custom item data structure (this is closer to what you would do in your codebase)
-// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection.
-// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THE UNNECESSARY 'AdapterIndexToStorageId()' INDIRECTION LOGIC.
-// In theory, for maximum abstraction, this class could contains AdapterIndexToUserData() and AdapterUserDataToIndex() functions as well,
-// but because we always use indices in SetNextItemSelectionUserData() in the demo, we omit that for clarify.
-struct ExampleSelection
+struct ExampleSelectionStorageWithDeletion : ImGuiSelectionBasicStorage
 {
-    // Data
-    ImGuiStorage                        Storage;        // Selection set (think of this as similar to e.g. std::set<ImGuiID>)
-    int                                 Size;           // Number of selected items (== number of 1 in the Storage, maintained by this class).
-    bool                                QueueDeletion;  // Request deleting selected items
-
-    // Adapter to convert item index to item identifier
-    // e.g.
-    //   selection.AdapterData = (void*)my_items;
-    //   selection.AdapterIndexToStorageId = [](ExampleSelection* s, int idx) { return ((MyItems**)s->AdapterData)[idx]->ID; };
-    void*                               AdapterData;
-    ImGuiID                             (*AdapterIndexToStorageId)(ExampleSelection* self, int idx);
-
-    // Functions
-    ExampleSelection()                  { Clear(); AdapterData = NULL; AdapterIndexToStorageId = [](ExampleSelection*, int idx) { return (ImGuiID)idx; };}
-    void Clear()                        { Storage.Data.resize(0); Size = 0; QueueDeletion = false; }
-    void Swap(ExampleSelection& rhs)    { Storage.Data.swap(rhs.Storage.Data); }
-    bool Contains(ImGuiID key) const    { return Storage.GetInt(key, 0) != 0; }
-	void AddItem(ImGuiID key)           { int* p_int = Storage.GetIntRef(key, 0); if (*p_int != 0) return; *p_int = 1; Size++; }
-	void RemoveItem(ImGuiID key)        { int* p_int = Storage.GetIntRef(key, 0); if (*p_int == 0) return; *p_int = 0; Size--; }
-    void UpdateItem(ImGuiID key, bool v){ if (v) AddItem(key); else RemoveItem(key); }
-    int  GetSize() const                { return Size; }
-    void DebugTooltip()                 { if (ImGui::BeginTooltip()) { for (auto& pair : Storage.Data) if (pair.val_i) ImGui::Text("0x%03X (%d)", pair.key, pair.key); ImGui::EndTooltip(); } }
-
-    // Apply requests coming from BeginMultiSelect() and EndMultiSelect().
-    // - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
-    // - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
-    //   - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.
-    //   - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform
-    //     a lookup in order to have some way to iterate/interpolate between two items.
-    // - A full-featured application is likely to allow search/filtering which is likely to lead to using indices
-    //   and constructing a view index <> object id/ptr data structure anyway.
-    // WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ExampleSelectionAdapter' INDIRECTION LOGIC.
-    // Notice that with the simplest adapter (using indices everywhere), all functions return their parameters.
-    // The most simple implementation (using indices everywhere) would look like:
-    //   for (ImGuiSelectionRequest& req : ms_io->Requests)
-    //   {
-    //      if (req.Type == ImGuiSelectionRequestType_Clear)     { Clear(); }
-    //      if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); 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->RangeSelected); } }
-    //   }
-    void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count)
-    {
-        IM_ASSERT(AdapterIndexToStorageId != NULL);
-        for (ImGuiSelectionRequest& req : ms_io->Requests)
-        {
-            if (req.Type == ImGuiSelectionRequestType_Clear)
-                Clear();
-            if (req.Type == ImGuiSelectionRequestType_SelectAll)
-            {
-                Storage.Data.resize(0);
-                Storage.Data.reserve(items_count);
-                for (int idx = 0; idx < items_count; idx++)
-                    AddItem(AdapterIndexToStorageId(this, idx));
-            }
-            if (req.Type == ImGuiSelectionRequestType_SetRange)
-                for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
-                    UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected);
-        }
-    }
+    bool QueueDeletion = false; // Track request deleting selected items
 
     // Find which item should be Focused after deletion.
     // Call _before_ item submission. Retunr an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
@@ -2870,6 +2787,8 @@ struct ExampleSelection
     int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
     {
         QueueDeletion = false;
+        if (Size == 0)
+            return -1;
 
         // If focused item is not selected...
         const int focused_idx = (int)ms_io->NavIdItem;  // Index of currently focused item
@@ -2922,9 +2841,9 @@ struct ExampleSelection
 // Example: Implement dual list box storage and interface
 struct ExampleDualListBox
 {
-    ImVector<ImGuiID>   Items[2];               // ID is index into ExampleName[]
-    ExampleSelection    Selections[2];          // Store ExampleItemId into selection
-    bool                OptKeepSorted = true;
+    ImVector<ImGuiID>           Items[2];               // ID is index into ExampleName[]
+    ImGuiSelectionBasicStorage  Selections[2];          // Store ExampleItemId into selection
+    bool                        OptKeepSorted = true;
 
     void MoveAll(int src, int dst)
     {
@@ -2956,7 +2875,7 @@ struct ExampleDualListBox
     {
         // In this example we store item id in selection (instead of item index)
         Selections[side].AdapterData = Items[side].Data;
-        Selections[side].AdapterIndexToStorageId = [](ExampleSelection* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; };
+        Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->AdapterData; return items[idx]; };
         Selections[side].ApplyRequests(ms_io, Items[side].Size);
     }
     static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
@@ -2986,7 +2905,7 @@ struct ExampleDualListBox
                 // FIXME-MULTISELECT: Dual List Box: Add context menus
                 // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
                 ImVector<ImGuiID>& items = Items[side];
-                ExampleSelection& selection = Selections[side];
+                ImGuiSelectionBasicStorage& selection = Selections[side];
 
                 ImGui::TableSetColumnIndex((side == 0) ? 0 : 2);
                 ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
@@ -3107,7 +3026,7 @@ static void ShowDemoWindowMultiSelect()
         if (ImGui::TreeNode("Multi-Select"))
         {
             // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
-            static ExampleSelection selection;
+            static ImGuiSelectionBasicStorage selection;
 
             ImGui::Text("Tips: Use 'Debug Log->Selection' to see selection requests as they happen.");
 
@@ -3148,7 +3067,7 @@ static void ShowDemoWindowMultiSelect()
         if (ImGui::TreeNode("Multi-Select (with clipper)"))
         {
             // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
-            static ExampleSelection selection;
+            static ImGuiSelectionBasicStorage selection;
 
             ImGui::Text("Added features:");
             ImGui::BulletText("Using ImGuiListClipper.");
@@ -3200,13 +3119,13 @@ static void ShowDemoWindowMultiSelect()
             // But you may decide to store selection data inside your item (aka intrusive storage).
             // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
             static ImVector<int> items;
-            static ExampleSelection selection;
+            static ExampleSelectionStorageWithDeletion selection;
 
             ImGui::Text("Adding features:");
             ImGui::BulletText("Dynamic list with Delete key support.");
             ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size);
-            if (ImGui::IsItemHovered() && selection.GetSize() > 0)
-                selection.DebugTooltip();
+            //if (ImGui::IsItemHovered() && selection.GetSize() > 0)
+            //    selection.DebugTooltip();
 
             // Initialize default list with 50 items + button to add/remove items.
             static int items_next_id = 0;
@@ -3282,7 +3201,7 @@ static void ShowDemoWindowMultiSelect()
             // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
             const int SCOPES_COUNT = 3;
             const int ITEMS_COUNT = 8; // Per scope
-            static ExampleSelection selections_data[SCOPES_COUNT];
+            static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
 
             // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
             static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
@@ -3295,7 +3214,7 @@ static void ShowDemoWindowMultiSelect()
             for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
             {
                 ImGui::PushID(selection_scope_n);
-                ExampleSelection* selection = &selections_data[selection_scope_n];
+                ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
                 ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags);
                 selection->ApplyRequests(ms_io, ITEMS_COUNT);
 
@@ -3375,7 +3294,7 @@ static void ShowDemoWindowMultiSelect()
             static ImVector<int> items;
             static int items_next_id = 0;
             if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } }
-            static ExampleSelection selection;
+            static ExampleSelectionStorageWithDeletion selection;
 
             ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size);
 
@@ -9712,7 +9631,7 @@ struct ExampleAssetsBrowser
 
     // State
     ImVector<ExampleAsset>  Items;
-    ExampleSelection        Selection;
+    ImGuiSelectionBasicStorage Selection;
     ImGuiID                 NextItemId = 0;
     bool                    SortDirty = false;
     float                   ZoomWheelAccum = 0.0f;
@@ -9846,7 +9765,7 @@ struct ExampleAssetsBrowser
 
             // Use custom selection adapter: store ID in selection (recommended)
             Selection.AdapterData = this;
-            Selection.AdapterIndexToStorageId = [](ExampleSelection* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; };
+            Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->AdapterData; return self->Items[idx].ID; };
             Selection.ApplyRequests(ms_io, Items.Size);
 
             // Altering ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...

+ 39 - 0
imgui_widgets.cpp

@@ -7112,6 +7112,7 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
 // - MultiSelectItemHeader() [Internal]
 // - MultiSelectItemFooter() [Internal]
 // - DebugNodeMultiSelectState() [Internal]
+// - ImGuiSelectionBasicStorage
 //-------------------------------------------------------------------------
 
 static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io)
@@ -7612,6 +7613,44 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
 #endif
 }
 
+// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
+// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
+// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
+//   - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.
+//   - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform
+//     a lookup in order to have some way to iterate/interpolate between two items.
+// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices
+//   and constructing a view index <> object id/ptr data structure anyway.
+// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC.
+// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters.
+// The most simple implementation (using indices everywhere) would look like:
+//   for (ImGuiSelectionRequest& req : ms_io->Requests)
+//   {
+//      if (req.Type == ImGuiSelectionRequestType_Clear)     { Clear(); }
+//      if (req.Type == ImGuiSelectionRequestType_SelectAll) { Clear(); 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->RangeSelected); } }
+//   }
+void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count)
+{
+    IM_ASSERT(AdapterIndexToStorageId != NULL);
+    for (ImGuiSelectionRequest& req : ms_io->Requests)
+    {
+        if (req.Type == ImGuiSelectionRequestType_Clear)
+            Clear();
+        if (req.Type == ImGuiSelectionRequestType_SelectAll)
+        {
+            Storage.Data.resize(0);
+            Storage.Data.reserve(items_count);
+            for (int idx = 0; idx < items_count; idx++)
+                AddItem(AdapterIndexToStorageId(this, idx));
+        }
+        if (req.Type == ImGuiSelectionRequestType_SetRange)
+            for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
+                UpdateItem(AdapterIndexToStorageId(this, idx), req.RangeSelected);
+    }
+}
+
+
 //-------------------------------------------------------------------------
 // [SECTION] Widgets: ListBox
 //-------------------------------------------------------------------------