diff --git a/imgui.cpp b/imgui.cpp index 55c19ef7..7ca3764d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3778,7 +3778,6 @@ void ImGui::Shutdown() g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); g.InputTextDeactivatedState.ClearFreeMemory(); - g.MultiSelectScopeWindow = NULL; g.SettingsWindows.clear(); g.SettingsHandlers.clear(); @@ -12356,6 +12355,7 @@ void ImGui::NavInitRequestApplyResult() g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = 0; + g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) ? true : false; } // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) @@ -12612,6 +12612,7 @@ void ImGui::NavMoveRequestApplyResult() g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; + g.NavJustMovedToHasSelectionData = (result->InFlags & ImGuiItemFlags_HasSelectionUserData) ? true : false; } // Apply new NavID/Focus diff --git a/imgui_internal.h b/imgui_internal.h index edebee47..04ebb069 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1213,6 +1213,7 @@ enum ImGuiNextItemDataFlags_ ImGuiNextItemDataFlags_HasOpen = 1 << 1, ImGuiNextItemDataFlags_HasShortcut = 1 << 2, ImGuiNextItemDataFlags_HasRefVal = 1 << 3, + ImGuiNextItemDataFlags_HasSelectionData = 1 << 4, }; struct ImGuiNextItemData @@ -1220,6 +1221,7 @@ struct ImGuiNextItemData ImGuiNextItemDataFlags Flags; ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap. // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem() + ImGuiID FocusScopeId; // Set by SetNextItemSelectionUserData() (!= 0 signify value has been set) ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) float Width; // Set by SetNextItemWidth() ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() @@ -1714,13 +1716,14 @@ struct ImGuiOldColumns struct IMGUI_API ImGuiMultiSelectState { + ImGuiID FocusScopeId; // Same as CurrentWindow->DC.FocusScopeIdCurrent (unless another selection scope was pushed manually) ImGuiMultiSelectData In; // The In requests are set and returned by BeginMultiSelect() 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. ImGuiMultiSelectState() { Clear(); } - void Clear() { In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } + void Clear() { FocusScopeId = 0; In.Clear(); Out.Clear(); InRangeDstPassedBy = InRequestSetRangeNav = false; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -2061,6 +2064,7 @@ struct ImGuiContext ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyChord NavJustMovedToKeyMods; + bool NavJustMovedToHasSelectionData; // " (FIXME-NAV: We should maybe just store ImGuiNavMoveResult) ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse @@ -2109,10 +2113,9 @@ struct ImGuiContext ImVec2 NavWindowingAccumDeltaSize; // Range-Select/Multi-Select - ImGuiID MultiSelectScopeId; - ImGuiWindow* MultiSelectScopeWindow; + bool MultiSelectEnabled; ImGuiMultiSelectFlags MultiSelectFlags; - ImGuiMultiSelectState MultiSelectState; + ImGuiMultiSelectState MultiSelectState; // 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) @@ -2340,6 +2343,7 @@ struct ImGuiContext NavHighlightActivatedId = 0; NavHighlightActivatedTimer = 0.0f; NavJustMovedToKeyMods = ImGuiMod_None; + NavJustMovedToHasSelectionData = false; NavInputSource = ImGuiInputSource_Keyboard; NavLayer = ImGuiNavLayer_Main; NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; @@ -2370,9 +2374,8 @@ struct ImGuiContext NavWindowingToggleLayer = false; NavWindowingToggleKey = ImGuiKey_None; - MultiSelectScopeId = 0; - MultiSelectScopeWindow = NULL; - MultiSelectFlags = 0; + MultiSelectEnabled = false; + MultiSelectFlags = ImGuiMultiSelectFlags_None; DimBgRatio = 0.0f; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 67aa1d7a..4c6c84fa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6354,7 +6354,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l const bool was_selected = selected; // Multi-selection support (header) - const bool is_multi_select = (g.MultiSelectScopeWindow == window); + const bool is_multi_select = g.MultiSelectEnabled; if (is_multi_select) { flags |= ImGuiTreeNodeFlags_OpenOnArrow; @@ -6697,7 +6697,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.MultiSelectScopeWindow == window); + const bool is_multi_select = g.MultiSelectEnabled; const bool was_selected = selected; if (is_multi_select) { @@ -7003,17 +7003,20 @@ void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected) { - ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(g.MultiSelectScopeId == 0); // No recursion allowed yet (we could allow it if we deem it useful) + IM_ASSERT(g.MultiSelectEnabled == false); // No recursion allowed yet (we could allow it if we deem it useful) IM_ASSERT(g.MultiSelectFlags == 0); + IM_ASSERT(g.MultiSelectState.FocusScopeId == 0); + // FIXME: BeginFocusScope() ImGuiMultiSelectState* ms = &g.MultiSelectState; - g.MultiSelectScopeId = window->IDStack.back(); - g.MultiSelectScopeWindow = window; - g.MultiSelectFlags = flags; ms->Clear(); + ms->FocusScopeId = window->IDStack.back(); + PushFocusScope(ms->FocusScopeId); + g.MultiSelectEnabled = true; + g.MultiSelectFlags = flags; if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0) { @@ -7022,7 +7025,7 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* } // Auto clear when using Navigation to move within the selection (we compare SelectScopeId so it possible to use multiple lists inside a same window) - if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.MultiSelectScopeId) + if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData) { if (g.IO.KeyShift) ms->InRequestSetRangeNav = true; @@ -7045,14 +7048,16 @@ ImGuiMultiSelectData* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* ImGuiMultiSelectData* ImGui::EndMultiSelect() { - ImGuiContext& g = *ImGui::GetCurrentContext(); + ImGuiContext& g = *GImGui; ImGuiMultiSelectState* ms = &g.MultiSelectState; - IM_ASSERT(g.MultiSelectScopeId != 0); + IM_ASSERT(g.MultiSelectState.FocusScopeId == g.CurrentFocusScopeId); + if (g.MultiSelectFlags & ImGuiMultiSelectFlags_NoUnselect) ms->Out.RangeValue = true; - g.MultiSelectScopeId = 0; - g.MultiSelectScopeWindow = NULL; - g.MultiSelectFlags = 0; + g.MultiSelectState.FocusScopeId = 0; + PopFocusScope(); + g.MultiSelectEnabled = false; + g.MultiSelectFlags = ImGuiMultiSelectFlags_None; #ifdef IMGUI_DEBUG_MULTISELECT if (ms->Out.RequestClear) printf("[%05d] EndMultiSelect: RequestClear\n", g.FrameCount); @@ -7068,18 +7073,22 @@ void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_d // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. ImGuiContext& g = *GImGui; - if (g.MultiSelectScopeId != 0) + if (g.MultiSelectState.FocusScopeId != 0) g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect; else g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; g.NextItemData.SelectionUserData = selection_user_data; + g.NextItemData.FocusScopeId = g.CurrentFocusScopeId; } void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected) { ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; ImGuiMultiSelectState* ms = &g.MultiSelectState; + IM_UNUSED(window); + IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); IM_ASSERT((g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid) && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope"); void* item_data = (void*)g.NextItemData.SelectionUserData;