From 90305c57e43d57338c2743ecc1644f4ca8e5e368 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 25 Sep 2023 19:53:02 +0200 Subject: [PATCH] MultiSelect: (breaking) renamed ImGuiMultiSelectFlags_ClearOnClickWindowVoid -> ImGuiMultiSelectFlags_ClearOnClickVoid. Added ImGuiMultiSelectFlags_ScopeWindow, ImGuiMultiSelectFlags_ScopeRect. --- imgui.h | 9 +++++---- imgui_demo.cpp | 30 +++++++++++++++++++++++------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 13 +++++++++++-- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/imgui.h b/imgui.h index 2ce1f1983..53a51c42a 100644 --- a/imgui.h +++ b/imgui.h @@ -2734,10 +2734,11 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_SingleSelect = 1 << 0, // Disable selecting more than one item. This is available to allow single-selection code to use same code/logic is desired, but may not be very useful. ImGuiMultiSelectFlags_NoSelectAll = 1 << 1, // Disable CTRL+A shortcut to set RequestSelectAll ImGuiMultiSelectFlags_ClearOnEscape = 1 << 2, // Clear selection when pressing Escape while scope is focused. - ImGuiMultiSelectFlags_ClearOnClickWindowVoid= 1 << 3, // Clear selection when clicking on empty location within host window (use if BeginMultiSelect() covers a whole window) - //ImGuiMultiSelectFlags_ClearOnClickRectVoid= 1 << 4, // Clear selection when clicking on empty location within rectangle covered by selection scope (use if multiple BeginMultiSelect() are used in the same host window) - ImGuiMultiSelectFlags_SelectOnClick = 1 << 5, // Apply selection on mouse down when clicking on unselected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 6, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 3, // Clear selection when clicking on empty location within scope. + ImGuiMultiSelectFlags_ScopeWindow = 1 << 4, // Scope for _ClearOnClickVoid and _BoxSelect is whole window (Default). Use if (use if BeginMultiSelect() covers a whole window. + ImGuiMultiSelectFlags_ScopeRect = 1 << 5, // Scope for _ClearOnClickVoid and _BoxSelect is rectangle covering submitted items. Use if multiple BeginMultiSelect() are used in the same host window. + ImGuiMultiSelectFlags_SelectOnClick = 1 << 7, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 8, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. }; // Multi-selection system diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9ae3c68cf..00b02eb08 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3289,17 +3289,25 @@ static void ShowDemoWindowMultiSelect() static ExampleSelection selections_data[SCOPES_COUNT]; ExampleSelectionAdapter selection_adapter; // Use default: Pass index to SetNextItemSelectionUserData(), store index in Selection + // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window. + static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) + flags &= ~ImGuiMultiSelectFlags_ScopeRect; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) + flags &= ~ImGuiMultiSelectFlags_ScopeWindow; + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++) { ExampleSelection* selection = &selections_data[selection_scope_n]; + + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); + ImGui::SeparatorText("Selection scope"); ImGui::Text("Selection size: %d/%d", selection->GetSize(), ITEMS_COUNT); ImGui::PushID(selection_scope_n); - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid - ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); - selection->ApplyRequests(ms_io, &selection_adapter, ITEMS_COUNT); - for (int n = 0; n < ITEMS_COUNT; n++) { char label[64]; @@ -3354,8 +3362,16 @@ static void ShowDemoWindowMultiSelect() ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickWindowVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickWindowVoid); - ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); + ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid); + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow)) + flags &= ~ImGuiMultiSelectFlags_ScopeRect; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) + flags &= ~ImGuiMultiSelectFlags_ScopeWindow; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) + flags &= ~ImGuiMultiSelectFlags_SelectOnClick; + ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); // Initialize default list with 1000 items. static ImVector items; @@ -9826,7 +9842,7 @@ struct ExampleAssetsBrowser ImGui::SetCursorScreenPos(start_pos); // Multi-select - ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid; if (AllowDragUnselected) ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // To allow dragging an unselected item without altering selection. diff --git a/imgui_internal.h b/imgui_internal.h index 9bb378f23..6f930aec4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1720,6 +1720,8 @@ struct IMGUI_API ImGuiMultiSelectTempData ImGuiMultiSelectState* Storage; ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) ImGuiMultiSelectFlags Flags; + ImVec2 ScopeRectMin; + ImVec2 BackupCursorMaxPos; ImGuiKeyChord KeyMods; bool LoopRequestClear; bool LoopRequestSelectAll; @@ -1729,7 +1731,6 @@ struct IMGUI_API ImGuiMultiSelectTempData bool NavIdPassedBy; bool RangeSrcPassedBy; // Set by the item that matches RangeSrcItem. bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set. - //ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid. ImGuiMultiSelectTempData() { Clear(); } void Clear() { size_t io_sz = sizeof(IO); IO.Clear(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 500be32f1..dade5fb38 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7138,6 +7138,8 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) ms->FocusScopeId = id; ms->Flags = flags; ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); + ms->BackupCursorMaxPos = window->DC.CursorMaxPos; + ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos; PushFocusScope(ms->FocusScopeId); // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. @@ -7208,6 +7210,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiContext& g = *GImGui; ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; + ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); @@ -7230,17 +7233,23 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() if (ms->IsEndIO == false) ms->IO.Requests.resize(0); + const ImRect scope_rect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin)); + const bool scope_hovered = (ms->Flags & ImGuiMultiSelectFlags_ScopeRect) ? IsMouseHoveringRect(scope_rect.Min, scope_rect.Max) : IsWindowHovered(); + // 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 (scope_hovered && g.HoveredId == 0) + { + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { ms->IO.Requests.resize(0); ms->IO.Requests.push_back(ImGuiSelectionRequest(ImGuiSelectionRequestType_Clear)); } + } // Unwind + window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos); ms->FocusScopeId = 0; ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope();