diff --git a/imgui.h b/imgui.h index 3939b30b..1abfaf44 100644 --- a/imgui.h +++ b/imgui.h @@ -2668,11 +2668,13 @@ struct ImColor // you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself. enum ImGuiMultiSelectFlags_ { - ImGuiMultiSelectFlags_None = 0, - ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, - ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. - ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll - ImGuiMultiSelectFlags_ClearOnEscape = 1 << 3, // Enable ESC shortcut to clear selection + ImGuiMultiSelectFlags_None = 0, + ImGuiMultiSelectFlags_NoMultiSelect = 1 << 0, + ImGuiMultiSelectFlags_NoUnselect = 1 << 1, // Disable unselecting items with CTRL+Click, CTRL+Space etc. + ImGuiMultiSelectFlags_NoSelectAll = 1 << 2, // Disable CTRL+A shortcut to set RequestSelectAll + 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_ClearOnEscape = 1 << 5, // Clear selection when pressing Escape while scope is focused. }; // Abstract: diff --git a/imgui_demo.cpp b/imgui_demo.cpp index dcc21293..1c10136c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2880,42 +2880,64 @@ static void ShowDemoWindowMultiSelect() // - Showcase clipping. // - Showcase basic drag and drop. // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing). + // - Showcase having multiple multi-selection scopes in the same window. // - Showcase using inside a table. IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)"); ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { - // Demonstrate holding/updating multi-selection data and using the BeginMultiSelect/EndMultiSelect API to support range-selection and clipping. - static ExampleSelection selections_data[1]; - ExampleSelection* selection = &selections_data[0]; - // Options enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode }; static bool use_table = false; static bool use_drag_drop = true; + static bool multiple_selection_scopes = false; static WidgetType widget_type = WidgetType_TreeNode; if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; } ImGui::SameLine(); if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; } ImGui::Checkbox("Use table", &use_table); ImGui::Checkbox("Use drag & drop", &use_drag_drop); + ImGui::Checkbox("Distinct selection scopes in same window", &multiple_selection_scopes); - // (spare brace to add a loop here later) + // When 'multiple_selection_scopes' is set we show 3 selection scopes in the host window instead of 1 in a scrolling window. + static ExampleSelection selections_data[3]; + const int selection_scope_count = multiple_selection_scopes ? 3 : 1; + for (int selection_scope_n = 0; selection_scope_n < selection_scope_count; selection_scope_n++) { - ImGui::Text("Selection size: %d", selection->GetSelectionSize()); + ExampleSelection* selection = &selections_data[selection_scope_n]; + + const int ITEMS_COUNT = multiple_selection_scopes ? 12 : 1000; + ImGui::PushID(selection_scope_n); // Open a scrolling region - const int ITEMS_COUNT = 1000; - if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + bool draw_selection = true; + if (multiple_selection_scopes) + { + ImGui::SeparatorText("Selection scope"); + } + else + { + ImGui::Text("Selection size: %d", selection->GetSelectionSize()); + draw_selection = ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)); + } + if (draw_selection) { ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); if (widget_type == WidgetType_TreeNode) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); - ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_None, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; + if (multiple_selection_scopes) + ;// flags |= ImGuiMultiSelectFlags_ClearOnClickRectVoid; + else + flags |= ImGuiMultiSelectFlags_ClearOnClickWindowVoid; + ImGuiMultiSelectData* multi_select_data = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef)); if (multi_select_data->RequestClear) { selection->Clear(); } if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } + if (multiple_selection_scopes) + ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() + if (use_table) { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f)); @@ -3017,15 +3039,12 @@ static void ShowDemoWindowMultiSelect() if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); - ImGui::EndListBox(); + if (multiple_selection_scopes == false) + ImGui::EndListBox(); } + ImGui::PopID(); // ImGui::PushID(selection_scope_n); + } // for each selection scope (1 or 3) - if (use_table) - { - ImGui::EndTable(); - ImGui::PopStyleVar(); - } - } ImGui::TreePop(); } ImGui::TreePop(); diff --git a/imgui_internal.h b/imgui_internal.h index dfc2fc4c..694d186d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1721,6 +1721,7 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() bool InRangeDstPassedBy; // (Internal) set by the the item that match NavJustMovedToId when InRequestRangeSetNav is set. 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(); } void Clear() { FocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e8caf4e7..53f1488e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7071,6 +7071,17 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() ImGuiMultiSelectState* ms = &g.MultiSelectState; IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + // Clear selection when clicking void? + // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! + if (g.MultiSelectFlags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) + if (IsWindowHovered() && g.HoveredId == 0) + if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) + { + ms->Out.RequestClear = true; + ms->Out.RequestSelectAll = ms->Out.RequestSetRange = false; + } + + // Unwind if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; g.MultiSelectState.FocusScopeId = 0;