diff --git a/imgui.h b/imgui.h index 3268688d0..987d7e7fb 100644 --- a/imgui.h +++ b/imgui.h @@ -2754,6 +2754,7 @@ enum ImGuiMultiSelectFlags_ // you may as well not bother with clipping, as the cost should be negligible (as least on Dear ImGui side). // If you are not sure, always start without clipping and you can work your way to the more optimized version afterwards. // - The void* RangeSrc/RangeDst value represent a selectable object. They are the values you pass to SetNextItemSelectionUserData(). +// Most likely you will want to store an index here. // 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). @@ -2761,35 +2762,36 @@ enum ImGuiMultiSelectFlags_ // 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: -// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. -// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. -// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). -// 2) Honor Clear/SelectAll requests by updating your selection data. [Only required if you are using a clipper in step 4] -// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] -// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. -// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } -// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. -// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). -// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. -// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) -// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) +// Begin +// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrc and its selection state. +// It is because you need to pass its selection state (and you own selection) that we don't store this value in Dear ImGui. +// (For the initial frame or when resetting your selection state: you may use the value for your first item or a "null" value that matches the type stored in your void*). +// 2) Honor Clear/SelectAll requests by updating your selection data. Only required if you are using a clipper in step 4: but you can use same code as step 6 anyway. +// Loop +// 3) Set RangeSrcPassedBy=true if the RangeSrc item is part of the items clipped before the first submitted/visible item. [Only required if you are using a clipper in step 4] +// This is because for range-selection we need to know if we are currently "inside" or "outside" the range. +// If you are using integer indices everywhere, this is easy to compute: if (clipper.DisplayStart > (int)data->RangeSrc) { data->RangeSrcPassedBy = true; } +// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. +// Call IsItemToggledSelection() to query if the selection state has been toggled, if you need the info immediately for your display (before EndMultiSelect()). +// When cannot return a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggle" event instead. +// End +// 5) Call EndMultiSelect(). Save the value of ->RangeSrc for the next frame (you may convert the value in a format that is safe for persistance) +// 6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Always process them in this order (as you will receive Clear+SetRange request simultaneously) // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable() on a per-item basis. struct ImGuiMultiSelectData { - bool IsFocused; // Begin // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection. - bool RequestClear; // Begin, End // Request user to clear selection - bool RequestSelectAll; // Begin, End // Request user to select all - bool RequestSetRange; // End // Request user to set or clear selection in the [RangeSrc..RangeDst] range - bool RangeSrcPassedBy; // In loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. - bool RangeValue; // End // End: parameter from RequestSetRange request. True = Select Range, False = Unselect range. - void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() - void* RangeDst; // End // End: parameter from RequestSetRange request. - int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. + bool RequestClear; // Begin, End // 1. Request user to clear selection + bool RequestSelectAll; // Begin, End // 2. Request user to select all + bool RequestSetRange; // End // 3. Request user to set or clear selection in the [RangeSrc..RangeDst] range + bool RangeSrcPassedBy; // Loop // (If clipping) Need to be set by user if RangeSrc was part of the clipped set before submitting the visible items. Ignore if not clipping. + bool RangeValue; // End // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. + void* RangeSrc; // Begin, End // End: parameter from RequestSetRange request + you need to save this value so you can pass it again next frame. / Begin: this is the value you passed to BeginMultiSelect() + void* RangeDst; // End // End: parameter from RequestSetRange request. + int RangeDirection; // End // End: parameter from RequestSetRange request. +1 if RangeSrc came before RangeDst, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. ImGuiMultiSelectData() { Clear(); } void Clear() { - IsFocused = false; RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false; RangeSrc = RangeDst = NULL; RangeDirection = 0; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b683c495b..c8fc424dc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -608,6 +608,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static void ShowDemoWindowWidgets() { IMGUI_DEMO_MARKER("Widgets"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (!ImGui::CollapsingHeader("Widgets")) return; @@ -1353,6 +1354,7 @@ static void ShowDemoWindowWidgets() } IMGUI_DEMO_MARKER("Widgets/Selectables"); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Selectables")) { // Selectable() has 2 overloads: @@ -2759,26 +2761,28 @@ static void ShowDemoWindowWidgets() } } -// [Advanced] Helper class to simulate storage of a multi-selection state, used by the advanced multi-selection demos. +// [Advanced] Helper class to simulate storage of a multi-selection state, used by the BeginMultiSelect() demos. // We use ImGuiStorage (simple key->value storage) to avoid external dependencies but it's probably not optimal. // To store a single-selection: // - You only need a single variable and don't need any of this! // To store a multi-selection, in your real application you could: // - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object). This is by far the simplest // way to store your selection data, but it means you cannot have multiple simultaneous views over your objects. -// This is what may of the simpler demos in this file are using (so they are not using this class). -// - Otherwise, any externally stored unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) +// This is what many of the simpler demos in this file are using (so they are not using this class). +// - Use external storage: e.g. unordered_set/set/hash/map/interval trees (storing indices, objects id, etc.) // are generally appropriate. Even a large array of bool might work for you... // - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in -// your storage, so "Select All" becomes "Negative=1, Clear" and then sparse unselect can add to the storage. +// your storage, so "Select All" becomes "Negative=1 + Clear" and then sparse unselect can add to the storage. // About RefItem: -// - The MultiSelect API requires you to store information about the reference/pivot item (generally the last clicked item). +// - The BeginMultiSelect() API requires you to store information about the reference/pivot item (generally the last clicked item). struct ExampleSelection { - ImGuiStorage Storage; + // Data + ImGuiStorage Storage; // Selection set int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class) int RangeRef; // Reference/pivot item (generally last clicked item) + // Functions ExampleSelection() { RangeRef = 0; Clear(); } void Clear() { Storage.Clear(); SelectionSize = 0; } bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; } @@ -2791,11 +2795,20 @@ struct ExampleSelection // you will need a way to iterate from one object to another given the ID you use. // You are likely to need some kind of data structure to convert 'view index' <> 'object ID'. // FIXME-MULTISELECT: Would be worth providing a demo of doing this. - // FIXME-MULTISELECT: SetRange() is currently very inefficient since it doesn't take advantage of the fact that ImGuiStorage stores sorted key. - void SetRange(int n1, int n2, bool v) { if (n2 < n1) { int tmp = n2; n2 = n1; n1 = tmp; } for (int n = n1; n <= n2; n++) SetSelected(n, v); } - void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. + // FIXME-MULTISELECT: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key. + void SetRange(int a, int b, bool v) { if (b < a) { int tmp = b; b = a; a = tmp; } for (int n = a; n <= b; n++) SetSelected(n, v); } + void SelectAll(int count) { Storage.Data.resize(count); for (int idx = 0; idx < count; idx++) Storage.Data[idx] = ImGuiStoragePair((ImGuiID)idx, 1); SelectionSize = count; } // This could be using SetRange(), but it this way is faster. + + // Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Order->SelectAll->SetRange. + void ApplyRequests(ImGuiMultiSelectData* ms_data, int items_count) + { + if (ms_data->RequestClear) { Clear(); } + if (ms_data->RequestSelectAll) { SelectAll(items_count); } + if (ms_data->RequestSetRange) { SetRange((int)(intptr_t)ms_data->RangeSrc, (int)(intptr_t)ms_data->RangeDst, ms_data->RangeValue ? 1 : 0); } + } }; + static void ShowDemoWindowMultiSelect() { IMGUI_DEMO_MARKER("Widgets/Selection State"); @@ -2864,8 +2877,7 @@ static void ShowDemoWindowMultiSelect() { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; 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); } + selection.ApplyRequests(multi_select_data, ITEMS_COUNT); for (int n = 0; n < ITEMS_COUNT; n++) { @@ -2886,9 +2898,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection.RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection.Clear(); } - if (multi_select_data->RequestSelectAll) { selection.SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection.SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + selection.ApplyRequests(multi_select_data, ITEMS_COUNT); ImGui::EndListBox(); } @@ -2903,7 +2913,7 @@ static void ShowDemoWindowMultiSelect() // - 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); + //ImGui::SetNextItemOpen(true, ImGuiCond_Once); if (ImGui::TreeNode("Multiple Selection (full, advanced)")) { // Options @@ -2952,8 +2962,7 @@ static void ShowDemoWindowMultiSelect() 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); } + selection->ApplyRequests(multi_select_data, ITEMS_COUNT); if (multiple_selection_scopes) ImGui::Text("Selection size: %d", selection->GetSelectionSize()); // Draw counter below Separator and after BeginMultiSelect() @@ -2971,8 +2980,8 @@ static void ShowDemoWindowMultiSelect() clipper.Begin(ITEMS_COUNT); while (clipper.Step()) { - // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeRef was passed over. - if (clipper.DisplayStart > selection->RangeRef) + // IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over. + if ((int)(intptr_t)multi_select_data->RangeSrc <= clipper.DisplayStart) multi_select_data->RangeSrcPassedBy = true; for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) @@ -3052,9 +3061,7 @@ static void ShowDemoWindowMultiSelect() // Apply multi-select requests multi_select_data = ImGui::EndMultiSelect(); selection->RangeRef = (int)(intptr_t)multi_select_data->RangeSrc; - if (multi_select_data->RequestClear) { selection->Clear(); } - if (multi_select_data->RequestSelectAll) { selection->SelectAll(ITEMS_COUNT); } - if (multi_select_data->RequestSetRange) { selection->SetRange((int)(intptr_t)multi_select_data->RangeSrc, (int)(intptr_t)multi_select_data->RangeDst, multi_select_data->RangeValue ? 1 : 0); } + selection->ApplyRequests(multi_select_data, ITEMS_COUNT); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index 757204567..82f67dc60 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1715,15 +1715,19 @@ struct ImGuiOldColumns struct IMGUI_API ImGuiMultiSelectState { - ImGuiID FocusScopeId; // Same as g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually) + ImGuiMultiSelectFlags Flags; + ImGuiKeyChord KeyMods; + ImGuiWindow* Window; ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() ImGuiMultiSelectData Out; // The Out requests are finalized and returned by EndMultiSelect() + 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 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; } + void Clear() { FocusScopeId = 0; Flags = ImGuiMultiSelectFlags_None; KeyMods = ImGuiMod_None; Window = NULL; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2121,10 +2125,7 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaSize; // Range-Select/Multi-Select - ImGuiWindow* MultiSelectEnabledWindow; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select - ImGuiMultiSelectFlags MultiSelectFlags; - ImGuiMultiSelectState MultiSelectState; - ImGuiKeyChord MultiSelectKeyMods; + ImGuiMultiSelectState MultiSelectState; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -2389,10 +2390,6 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; - MultiSelectEnabledWindow = NULL; - MultiSelectFlags = ImGuiMultiSelectFlags_None; - MultiSelectKeyMods = ImGuiMod_None; - DimBgRatio = 0.0f; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index aa067502e..5da30ea2e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6464,7 +6464,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectEnabledWindow == window); + const bool is_multi_select = (g.MultiSelectState.Window == window); if (is_multi_select) { MultiSelectItemHeader(id, &selected); @@ -6816,7 +6816,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectEnabledWindow == window); + const bool is_multi_select = (g.MultiSelectState.Window == window); const bool was_selected = selected; if (is_multi_select) { @@ -7126,23 +7126,19 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.MultiSelectEnabledWindow == NULL); // No recursion allowed yet (we could allow it if we deem it useful) - IM_ASSERT(g.MultiSelectFlags == 0); - IM_ASSERT(g.MultiSelectState.FocusScopeId == 0); + 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) // FIXME: BeginFocusScope() - ImGuiMultiSelectState* ms = &g.MultiSelectState; ms->Clear(); ms->FocusScopeId = window->IDStack.back(); + ms->Flags = flags; + ms->Window = window; + ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); PushFocusScope(ms->FocusScopeId); - g.MultiSelectEnabledWindow = window; - g.MultiSelectFlags = flags; - - // Report focus - ms->In.IsFocused = ms->Out.IsFocused = (ms->FocusScopeId == g.NavFocusScopeId); // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame. - g.MultiSelectKeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; + ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods; if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { @@ -7154,14 +7150,14 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* // FIXME: Polling key mods after the fact (frame following the move request) is incorrect, but latching it would requires non-trivial change in MultiSelectItemFooter() if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { - if (g.MultiSelectKeyMods & ImGuiMod_Shift) + if (ms->KeyMods & ImGuiMod_Shift) ms->InRequestSetRangeNav = true; - if ((g.MultiSelectKeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) + if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0) ms->In.RequestClear = true; } // Shortcuts - if (ms->In.IsFocused) + if (ms->IsFocused) { // Select All helper shortcut (CTRL+A) // Note: we are comparing FocusScope so we don't need to be testing for IsWindowFocused() @@ -7174,10 +7170,8 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ms->In.RequestClear = true; } -#ifdef IMGUI_DEBUG_MULTISELECT - if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); - if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); -#endif + //if (ms->In.RequestClear) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestClear\n"); + //if (ms->In.RequestSelectAll) IMGUI_DEBUG_LOG("BeginMultiSelect: RequestSelectAll\n"); return &ms->In; } @@ -7190,7 +7184,7 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() // Clear selection when clicking void? // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection! - if (g.MultiSelectFlags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) + if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickWindowVoid) if (IsWindowHovered() && g.HoveredId == 0) if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None) { @@ -7199,18 +7193,16 @@ ImGuiMultiSelectData* ImGui::EndMultiSelect() } // Unwind - if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) + if (ms->Flags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; - g.MultiSelectState.FocusScopeId = 0; + ms->FocusScopeId = 0; + ms->Window = NULL; + ms->Flags = ImGuiMultiSelectFlags_None; PopFocusScope(); - g.MultiSelectEnabledWindow = NULL; - g.MultiSelectFlags = ImGuiMultiSelectFlags_None; -#ifdef IMGUI_DEBUG_MULTISELECT - if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); - if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); - if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); -#endif + //if (ms->Out.RequestClear) IMGUI_DEBUG_LOG("EndMultiSelect: RequestClear\n"); + //if (ms->Out.RequestSelectAll) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSelectAll\n"); + //if (ms->Out.RequestSetRange) IMGUI_DEBUG_LOG("EndMultiSelect: RequestSetRange %p..%p = %d\n", ms->Out.RangeSrc, ms->Out.RangeDst, ms->Out.RangeValue); return &ms->Out; } @@ -7257,13 +7249,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) if (ms->InRequestSetRangeNav) { IM_ASSERT(id != 0); - IM_ASSERT((g.MultiSelectKeyMods & ImGuiMod_Shift) != 0); + IM_ASSERT((ms->KeyMods & ImGuiMod_Shift) != 0); const bool is_range_dst = !ms->InRangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped. if (is_range_dst) ms->InRangeDstPassedBy = true; if (is_range_src || is_range_dst || ms->In.RangeSrcPassedBy != ms->InRangeDstPassedBy) selected = ms->In.RangeValue; - else if ((g.MultiSelectKeyMods & ImGuiMod_Ctrl) == 0) + else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0) selected = false; } @@ -7278,11 +7270,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) void* item_data = (void*)g.NextItemData.SelectionUserData; + const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; bool selected = *p_selected; bool pressed = *p_pressed; - const bool is_multiselect = (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoMultiSelect) == 0; - bool is_ctrl = (g.MultiSelectKeyMods & ImGuiMod_Ctrl) != 0; - bool is_shift = (g.MultiSelectKeyMods & ImGuiMod_Shift) != 0; + bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; + bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7334,7 +7326,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } else { - selected = (!is_ctrl || (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; + selected = (!is_ctrl || (ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ? true : !selected; ms->Out.RangeSrc = ms->Out.RangeDst = item_data; ms->Out.RangeValue = selected; } @@ -7371,7 +7363,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) } // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect) - if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect)) + if (ms->Out.RangeSrc == item_data && is_ctrl && is_shift && is_multiselect && !(ms->Flags & ImGuiMultiSelectFlags_NoUnselect)) ms->Out.RangeValue = selected; *p_selected = selected;