From 0af6fbb51d5797b9245e97ed3d061917a296302b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Oct 2023 17:20:33 +0200 Subject: [PATCH] MultiSelect: added support for nested/stacked BeginMultiSelect(). Mimicking table logic, reusing amortized buffers. --- imgui.cpp | 3 +++ imgui.h | 3 +++ imgui_internal.h | 6 ++++-- imgui_widgets.cpp | 14 +++++++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ab9cc59b6..3128892f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3819,6 +3819,7 @@ void ImGui::Shutdown() g.DrawChannelsTempMergeBuffer.clear(); g.MultiSelectStorage.Clear(); + g.MultiSelectTempData.clear_destruct(); g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); @@ -3930,6 +3931,8 @@ void ImGui::GcCompactTransientMiscBuffers() ImGuiContext& g = *GImGui; g.ItemFlagsStack.clear(); g.GroupStack.clear(); + g.MultiSelectTempDataStacked = 0; + g.MultiSelectTempData.clear_destruct(); TableGcCompactSettings(); } diff --git a/imgui.h b/imgui.h index bb6ab85fc..31b6bcafa 100644 --- a/imgui.h +++ b/imgui.h @@ -2811,6 +2811,9 @@ struct ImGuiSelectionRequest ImGuiSelectionRequest(ImGuiSelectionRequestType type = ImGuiSelectionRequestType_None) { Type = type; RangeSelected = false; RangeFirstItem = RangeLastItem = (ImGuiSelectionUserData)-1; } }; +// Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). +// Read the large comments block above for details. +// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). struct ImGuiMultiSelectIO { ImVector Requests; // ms:w, app:r / ms:w app:r // Requests diff --git a/imgui_internal.h b/imgui_internal.h index 271946569..8062edad8 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2200,8 +2200,9 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Multi-Select state - ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select - ImGuiMultiSelectTempData MultiSelectTempData[1]; + ImGuiMultiSelectTempData* CurrentMultiSelect; + int MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size) + ImVector MultiSelectTempData; ImPool MultiSelectStorage; // Hover Delay system @@ -2445,6 +2446,7 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; CurrentMultiSelect = NULL; + MultiSelectTempDataStacked = 0; HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b8b7032aa..a20ca6b4b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7156,8 +7156,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0]; - IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) + + if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size) + g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData()); + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1]; IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that. g.CurrentMultiSelect = ms; if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0) @@ -7276,6 +7278,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); + IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); const ImRect scope_rect = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)) : window->InnerClipRect; if (ms->IsFocused) @@ -7332,14 +7335,15 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() // Unwind window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); - ms->FocusScopeId = 0; - ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); - g.CurrentMultiSelect = NULL; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); + ms->FocusScopeId = 0; + ms->Flags = ImGuiMultiSelectFlags_None; + g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL; + return &ms->IO; }