From 11bcae1ebd4a5098d09e322aeb6ace356e019e03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 23 May 2023 19:36:11 +0200 Subject: [PATCH] MultiSelect: refactor before introducing persistant state pool and to facilitate adding recursion + debug log calls. This is mostly the noisy/shallow stuff committed here, to get this out of the way. --- imgui.cpp | 2 +- imgui.h | 7 ++++--- imgui_internal.h | 10 ++++++---- imgui_widgets.cpp | 40 +++++++++++++++++++++++++--------------- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 733a5dfe1..8f34acf92 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15799,7 +15799,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO); ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); - //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) diff --git a/imgui.h b/imgui.h index 550e64361..349c77f3e 100644 --- a/imgui.h +++ b/imgui.h @@ -2747,6 +2747,10 @@ enum ImGuiMultiSelectFlags_ // Note however that if you don't need SHIFT+Click/Arrow range-select + clipping, you can handle a simpler form of multi-selection // yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers. // The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements. +// - In the spirit of Dear ImGui design, your code owns the selection data. +// So this is designed to handle all kind of selection data: e.g. instructive selection (store a bool inside each object), +// external array (store an array aside from your objects), hash/map/set (store only selected items in a hash/map/set), +// or other structures (store indices in an interval tree), etc. // - TreeNode() and Selectable() are supported. // - The work involved to deal with multi-selection differs whether you want to only submit visible items (and clip others) or submit all items // regardless of their visibility. Clipping items is more efficient and will allow you to deal with large lists (1k~100k items) with near zero @@ -2758,9 +2762,6 @@ enum ImGuiMultiSelectFlags_ // Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points and you will need to interpolate // between them to honor range selection. But the code never assume that sortable integers are used (you may store pointers to your object, // and then from the pointer have your own way of iterating from RangeSrc to RangeDst). -// - In the spirit of Dear ImGui design, your code own the selection data. So this is designed to handle all kind of selection data: -// e.g. instructive selection (store a bool inside each object), external array (store an array aside from your objects), -// hash/map/set (store only selected items in a hash/map/set), or other structures (store indices in an interval tree), etc. // Usage flow: // Begin // 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. diff --git a/imgui_internal.h b/imgui_internal.h index c51f28759..ad5366765 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -134,7 +134,7 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiMultiSelectState; // Multi-selection state +struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing). struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions @@ -1713,7 +1713,7 @@ struct ImGuiOldColumns #ifdef IMGUI_HAS_MULTI_SELECT -struct IMGUI_API ImGuiMultiSelectState +struct IMGUI_API ImGuiMultiSelectTempData { ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; @@ -1726,7 +1726,7 @@ struct IMGUI_API ImGuiMultiSelectState bool InRequestSetRangeNav; // (Internal) set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. - ImGuiMultiSelectState() { Clear(); } + ImGuiMultiSelectTempData() { Clear(); } void Clear() { FocusScopeId = 0; Flags = ImGuiMultiSelectFlags_None; KeyMods = ImGuiMod_None; Window = NULL; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; @@ -2167,7 +2167,8 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Multi-Select state - ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select + ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select + ImGuiMultiSelectTempData MultiSelectTempData[1]; // Hover Delay system ImGuiID HoverItemDelayId; @@ -2409,6 +2410,7 @@ struct ImGuiContext CurrentTable = NULL; TablesTempDataStacked = 0; CurrentTabBar = NULL; + CurrentMultiSelect = NULL; HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5fbe0821d..55453dd50 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7122,13 +7122,21 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) // - MultiSelectItemFooter() [Internal] //------------------------------------------------------------------------- +static void DebugLogMultiSelectRequests(const ImGuiMultiSelectData* data) +{ + ImGuiContext& g = *GImGui; + if (data->RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); + if (data->RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); + if (data->RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", data->RangeSrc, data->RangeDst, data->RangeValue, data->RangeDirection); +} + ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - - ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(ms->Window == NULL && ms->Flags == 0 && ms->FocusScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) + ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[0];; + IM_ASSERT(g.CurrentMultiSelect == NULL); // No recursion allowed yet (we could allow it if we deem it useful) + g.CurrentMultiSelect = ms; // FIXME: BeginFocusScope() ms->Clear(); @@ -7169,8 +7177,8 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* if (Shortcut(ImGuiKey_Escape)) ms->In.RequestClear = true; - //if (ms->In.RequestClear) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestClear\n"); - //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("BeginMultiSelect: RequestSelectAll\n"); + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests(&ms->In); return &ms->In; } @@ -7178,8 +7186,9 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiMultiSelectData* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; - ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); + IM_ASSERT(g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Window == g.CurrentWindow); // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! @@ -7198,10 +7207,10 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ms->Window = NULL; ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); + g.CurrentMultiSelect = NULL; - //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestClear\n"); - //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSelectAll\n"); - //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG_SELECTION("EndMultiSelect: RequestSetRange %p..%p = %d (dir %+d)\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue, ms->Out.RangeDirection); + if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) + DebugLogMultiSelectRequests(&ms->Out); return &ms->Out; } @@ -7218,16 +7227,17 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d g.NextItemData.SelectionUserData = selection_user_data; g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; - // Auto updating RangeSrcPassedBy for cases were clipper is not used. - if (g.MultiSelectState.In.RangeSrc == (void*)selection_user_data) - g.MultiSelectState.In.RangeSrcPassedBy = true; + // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) + if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect) + if (ms->In.RangeSrc == (void*)selection_user_data) + ms->In.RangeSrcPassedBy = true; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectState* ms = &g.MultiSelectState; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; IM_UNUSED(window); IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); @@ -7266,7 +7276,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMultiSelectState* ms = &g.MultiSelectState; + ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; void* item_data = (void*)g.NextItemData.SelectionUserData;