From db4898cb913552af14b37099ab06b60eb17a8149 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Jun 2024 19:00:13 +0200 Subject: [PATCH] MultiSelect: added ImGuiSelectionExternalStorage helper. Simplify bool demo. --- imgui.h | 16 +++++++++++++++- imgui_demo.cpp | 29 ++++++++--------------------- imgui_widgets.cpp | 16 ++++++++++++++++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/imgui.h b/imgui.h index c0c2d0b2c..050c8bb06 100644 --- a/imgui.h +++ b/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, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage) +// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage) // [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) @@ -180,6 +180,7 @@ struct ImGuiOnceUponAFrame; // Helper for running a block of code not mo struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. +struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. 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) @@ -2860,6 +2861,19 @@ struct ImGuiSelectionBasicStorage ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } }; +// Optional helper to apply multi-selection requests to existing randomly accessible storage. +// Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state. +struct ImGuiSelectionExternalStorage +{ + // Members + void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; } + void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; + + // Methods + ImGuiSelectionExternalStorage() { memset(this, 0, sizeof(*this)); } + IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected() +}; + //----------------------------------------------------------------------------- // [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. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b3dc5ef54..3cd2046a1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3212,41 +3212,28 @@ static void ShowDemoWindowMultiSelect() ImGui::BulletText("Shift+Keyboard to copy current value to other boxes."); // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper. - static bool values[20] = {}; + static bool items[20] = {}; static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape; ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect", &flags, ImGuiMultiSelectFlags_BoxSelect); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. - struct Funcs - { - static void ApplyMultiSelectRequestsToBoolArray(ImGuiMultiSelectIO* ms_io, bool items[], int items_count) - { - for (ImGuiSelectionRequest& req : ms_io->Requests) - { - if (req.Type == ImGuiSelectionRequestType_SetAll) - for (int n = 0; n < items_count; n++) - items[n] = req.Selected; - else if (req.Type == ImGuiSelectionRequestType_SetRange) - for (int n = (int)req.RangeFirstItem; n <= (int)req.RangeLastItem; n++) - items[n] = req.Selected; - } - } - }; - if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) { - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); //// By specs, it could be optional to apply requests from BeginMultiSelect() if not using a clipper. + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); + ImGuiSelectionExternalStorage storage_wrapper; + storage_wrapper.UserData = (void*)items; + storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; }; + storage_wrapper.ApplyRequests(ms_io); for (int n = 0; n < 20; n++) { char label[32]; sprintf(label, "Item %d", n); ImGui::SetNextItemSelectionUserData(n); - ImGui::Checkbox(label, &values[n]); + ImGui::Checkbox(label, &items[n]); } ms_io = ImGui::EndMultiSelect(); - Funcs::ApplyMultiSelectRequestsToBoolArray(ms_io, values, IM_ARRAYSIZE(values)); + storage_wrapper.ApplyRequests(ms_io); } ImGui::EndChild(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1ebabe4d1..c85b6ff9a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7854,6 +7854,22 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) } } +// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). +// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage +// This makes no assumption about underlying storage. +void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) +{ + IM_ASSERT(AdapterSetItemSelected); + for (ImGuiSelectionRequest& req : ms_io->Requests) + { + if (req.Type == ImGuiSelectionRequestType_SetAll) + for (int idx = 0; idx < ms_io->ItemsCount; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + if (req.Type == ImGuiSelectionRequestType_SetRange) + for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) + AdapterSetItemSelected(this, idx, req.Selected); + } +} //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox