From 33fc61a091e0c631594e7354491ce91c233e69ff Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 Sep 2023 15:34:25 +0200 Subject: [PATCH] MultiSelect: use a single ImGuiMultiSelectIO buffer. + using local storage var in EndMultiSelect(), should be no-op. --- imgui_internal.h | 6 +++--- imgui_widgets.cpp | 52 +++++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index 9df3b01e8..63772203d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1720,10 +1720,10 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; ImGuiKeyChord KeyMods; - ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop. - ImGuiMultiSelectIO EndIO; // Requests are set during the loop and returned by EndMultiSelect(). + ImGuiMultiSelectIO IO; // Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop. bool LoopRequestClear; bool LoopRequestSelectAll; + bool IsEndIO; // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state. bool IsFocused; // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. bool IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation. bool NavIdPassedBy; @@ -1732,7 +1732,7 @@ struct IMGUI_API ImGuiMultiSelectTempData //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } - void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; BeginIO.Clear(); EndIO.Clear(); LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } + void Clear() { Storage = NULL; FocusScopeId = 0; Flags = 0; KeyMods = 0; IO.Clear(); IsEndIO = LoopRequestClear = LoopRequestSelectAll = IsFocused = IsSetRange = NavIdPassedBy = RangeSrcPassedBy = RangeDstPassedBy = false; } }; // Persistent storage for multi-select (as long as selection is alive) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a3f06f3ad..edf48aa68 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7149,10 +7149,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) storage->Window = window; ms->Storage = storage; - // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() - ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; - ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; - ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + ms->IO.RangeSrcItem = storage->RangeSrcItem; + ms->IO.NavIdItem = storage->NavIdItem; + ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; + ms->IO.Requests.resize(0); bool request_clear = false; bool request_select_all = false; @@ -7191,14 +7191,14 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) } if (request_clear || request_select_all) - ms->BeginIO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.push_back(ImGuiSelectionRequest(request_select_all ? ImGuiSelectionRequestType_SelectAll : ImGuiSelectionRequestType_Clear)); ms->LoopRequestClear = request_clear; ms->LoopRequestSelectAll = request_select_all; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests("BeginMultiSelect", &ms->BeginIO); + DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO); - return &ms->BeginIO; + return &ms->IO; } // Return updated ImGuiMultiSelectIO structure. Lifetime: until EndFrame() or next BeginMultiSelect() call. @@ -7206,46 +7206,49 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; + ImGuiMultiSelectState* storage = ms->Storage; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); - IM_ASSERT(g.CurrentMultiSelect != NULL && ms->Storage->Window == g.CurrentWindow); + IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); if (ms->IsFocused) { // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here. - if (ms->BeginIO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) + if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at begining of the scope (see tests for easy failure) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. - ms->Storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; + storage->RangeSrcItem = ImGuiSelectionUserData_Invalid; } - if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != ImGuiSelectionUserData_Invalid) + if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid) { IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); - ms->Storage->NavIdItem = ImGuiSelectionUserData_Invalid; - ms->Storage->NavIdSelected = -1; + storage->NavIdItem = ImGuiSelectionUserData_Invalid; + storage->NavIdSelected = -1; } } + if (ms->IsEndIO == false) + ms->IO.Requests.resize(0); + // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { - ms->EndIO.Requests.resize(0); - ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.resize(0); + ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } // Unwind ms->FocusScopeId = 0; ms->Flags = ImGuiMultiSelectFlags_None; - ms->BeginIO.Clear(); // Invalidate contents of BeginMultiSelect() to enforce scope. PopFocusScope(); g.CurrentMultiSelect = NULL; if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) - DebugLogMultiSelectRequests("EndMultiSelect", &ms->EndIO); + DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO); - return &ms->EndIO; + return &ms->IO; } void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) @@ -7260,7 +7263,7 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d { // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; - if (ms->BeginIO.RangeSrcItem == selection_user_data) + if (ms->IO.RangeSrcItem == selection_user_data) ms->RangeSrcPassedBy = true; } else @@ -7357,6 +7360,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } + if (ms->IsEndIO == false) + { + ms->IO.Requests.resize(0); + ms->IsEndIO = true; + } // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7416,8 +7424,8 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again. if (request_clear) { - ms->EndIO.Requests.resize(0); - ms->EndIO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); + ms->IO.Requests.resize(0); + ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } int range_direction; @@ -7443,7 +7451,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiSelectionUserData range_dst_item = item_data; req.RangeFirstItem = (range_direction > 0) ? storage->RangeSrcItem : range_dst_item; req.RangeLastItem = (range_direction > 0) ? range_dst_item : storage->RangeSrcItem; - ms->EndIO.Requests.push_back(req); + ms->IO.Requests.push_back(req); } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect)