From 78494c92a205cb75d8bde01a680096689d74fb45 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 19 Dec 2023 14:06:58 +0100 Subject: [PATCH] RangeSelect/MultiSelect: (Breaking) Added current_selection_size to BeginMultiSelect(). Required for shortcut routing so we can e.g. have Escape be used to clear selection THEN to exit child window. --- imgui.h | 2 +- imgui_demo.cpp | 14 +++++++------- imgui_widgets.cpp | 10 ++++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/imgui.h b/imgui.h index 3399394f..7dd69615 100644 --- a/imgui.h +++ b/imgui.h @@ -674,7 +674,7 @@ namespace ImGui // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used. // - ImGuiSelectionUserData is often used to store your item index. // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo. - IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags); + IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size = -1); IMGUI_API ImGuiMultiSelectIO* EndMultiSelect(); IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dda94841..e132b74a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2909,7 +2909,7 @@ struct ExampleDualListBox if (child_visible) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); ApplySelectionRequests(ms_io, side); for (int item_n = 0; item_n < items.Size; item_n++) @@ -3040,7 +3040,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) @@ -3074,7 +3074,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, ITEMS_COUNT); ImGuiListClipper clipper; @@ -3138,7 +3138,7 @@ static void ShowDemoWindowMultiSelect() if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY)) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, items.Size); const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0); @@ -3254,7 +3254,7 @@ static void ShowDemoWindowMultiSelect() { ImGui::PushID(selection_scope_n); ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n]; - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size); selection->ApplyRequests(ms_io, ITEMS_COUNT); ImGui::SeparatorText("Selection scope"); @@ -3353,7 +3353,7 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size); selection.ApplyRequests(ms_io, items.Size); const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu; @@ -9698,7 +9698,7 @@ struct ExampleAssetsBrowser ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. if (AllowBoxSelect) ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; // Enable box-select in 2D mode. - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags); + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size); // Use custom selection adapter: store ID in selection (recommended) Selection.AdapterData = this; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4cc4d0d9..38325798 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7176,7 +7176,10 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe // Return ImGuiMultiSelectIO structure. // Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect(). -ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) +// Passing 'current_selection_size' is currently optional: +// - it is useful for shortcut routing with ImGuiMultiSelectFlags_ClearOnEscape: so we can have Escape be used to clear selection THEN to exit child window. +// - if it is costly for you to compute, but can easily tell if your selection is empty or not, you may alter the ImGuiMultiSelectFlags_ClearOnEscape flag based on that. +ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int current_selection_size) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -7243,9 +7246,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) if (ms->IsFocused) { // Shortcut: Clear selection (Escape) - // FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here. - // Otherwise may be done by caller but it means Shortcut() needs to be exposed. - if (flags & ImGuiMultiSelectFlags_ClearOnEscape) + // Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window. + if ((flags & ImGuiMultiSelectFlags_ClearOnEscape) && (current_selection_size != 0)) if (Shortcut(ImGuiKey_Escape)) request_clear = true;