From df1eeb9a20d70fdd6b712f292ec23881809465f7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 2 Jun 2023 15:17:01 +0200 Subject: [PATCH] MultiSelect: Maintain NavIdSelected for user. Simplify deletion demo. --- imgui.h | 1 + imgui_demo.cpp | 44 +++++++------------------------------------- imgui_internal.h | 3 ++- imgui_widgets.cpp | 30 +++++++++++++++++------------- 4 files changed, 27 insertions(+), 51 deletions(-) diff --git a/imgui.h b/imgui.h index cb4e27832..df38b174c 100644 --- a/imgui.h +++ b/imgui.h @@ -2784,6 +2784,7 @@ struct ImGuiMultiSelectIO bool RangeSelected; // / / ms:w, app:r // End: parameter from RequestSetRange request. true = Select Range, false = Unselect Range. bool RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was part of the clipped set before submitting the visible items. Ignore if not clipping. bool RangeSrcReset; // / app:w / ms:r // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection). + bool NavIdSelected; // ms:w, app:r / / // (If using deletion) Last known selection state for NavId (if part of submitted items). void* NavIdItem; // ms:w, app:r / / ms:w app:r // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectIO() { Clear(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8681ae47d..b0cc60bd8 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2821,8 +2821,7 @@ struct ExampleSelection template int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector& items) { - // FIXME-MULTISELECT: Need to avoid auto-select, aka SetKeyboardFocusHere() into public facing FocusItem() that doesn't activate. - if (!GetSelected((int)(intptr_t)ms_io->NavIdItem)) + if (ms_io->NavIdSelected == false) return (int)(intptr_t)ms_io->NavIdItem; // Return first unselected item after RangeSrcItem @@ -2996,6 +2995,7 @@ static void ShowDemoWindowMultiSelect() // FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal. // FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed. // FIXME-MULTISELECT: Test with intermediary modal dialog. + // FIXME-MULTISELECT: If pressing Delete + another key we have slightly ambiguous behavior. const bool want_delete = (selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete); const int next_focus_item_idx = want_delete ? selection.CalcNextFocusIdxForBeforeDeletion(ms_io, items) : -1; //if (want_delete) { IMGUI_DEBUG_LOG("next_focus_item_idx = %d\n", next_focus_item_idx); } @@ -3017,34 +3017,13 @@ static void ShowDemoWindowMultiSelect() ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: Need to avoid selection. } -#if 0 - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdData); - if (want_delete && !nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc + // Apply multi-select requests + if (want_delete && ms_io->NavIdSelected == false) // FIXME: would work without '&& !NavIdSelected' just take an extra frame to recover RangeSrc ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#else - // Apply multi-select requests - if (want_delete) - { - // When deleting: this handle details for scrolling/focus/selection to be updated correctly without any glitches. - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); - if (!nav_id_was_selected) // FIXME: would work without '&& !nav_id_was_selected' just take an extra frame to recover RangeSrc - ms_io->RangeSrcReset = true; - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); - } - else - { - // Simple version - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - } -#endif - + selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); ImGui::EndListBox(); } @@ -3267,21 +3246,12 @@ static void ShowDemoWindowMultiSelect() } // Apply multi-select requests -#if 1 - // full correct - bool nav_id_was_selected = selection.GetSelected((int)(intptr_t)ms_io->NavIdItem); - if (want_delete && !nav_id_was_selected) + if (want_delete && ms_io->NavIdSelected == false) ms_io->RangeSrcReset = true; ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, items.Size); if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#else - ms_io = ImGui::EndMultiSelect(); - selection.ApplyRequests(ms_io, items.Size); - if (want_delete) - selection.ApplyDeletion(ms_io, items, nav_id_was_selected ? next_focus_item_idx : -1); -#endif + selection.ApplyDeletion(ms_io, items, ms_io->NavIdSelected ? next_focus_item_idx : -1); if (widget_type == WidgetType_TreeNode) ImGui::PopStyleVar(); diff --git a/imgui_internal.h b/imgui_internal.h index eaeca4ece..21d350c89 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1740,11 +1740,12 @@ struct IMGUI_API ImGuiMultiSelectState ImGuiID ID; int LastFrameActive; // Last used frame-count, for GC. ImS8 RangeSelected; // -1 (don't have) or true/false + ImS8 NavIdSelected; // -1 (don't have) or true/false void* RangeSrcItem; // void* NavIdItem; // SetNextItemSelectionUserData() value for NavId (if part of submitted items) ImGuiMultiSelectState() { Init(0); } - void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } + void Init(ImGuiID id) { Window = NULL; ID = id; LastFrameActive = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = (void*)-1; } }; #endif // #ifdef IMGUI_HAS_MULTI_SELECT diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 126727c23..f7b91dd47 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7160,8 +7160,10 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags) // FIXME-MULTISELECT: Set for the purpose of user calling RangeSrcPassedBy // FIXME-MULTISELECT: Index vs Pointers. - ms->BeginIO.RangeSrcItem = storage->RangeSrcItem; - ms->BeginIO.NavIdItem = storage->NavIdItem; + // We want EndIO's NavIdItem/NavIdSelected to match BeginIO's one, so the value never changes after EndMultiSelect() + ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = storage->RangeSrcItem; + ms->BeginIO.NavIdItem = ms->EndIO.NavIdItem = storage->NavIdItem; + ms->BeginIO.NavIdSelected = ms->EndIO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false; if (!ms->IsFocused) return &ms->BeginIO; // This is cleared at this point. @@ -7216,13 +7218,14 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() { if (ms->BeginIO.RangeSrcReset || (ms->BeginIO.RangeSrcPassedBy == false && ms->BeginIO.RangeSrcItem != (void*)-1)) { - IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrc.\n"); // Will set be to NavId. + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId. ms->Storage->RangeSrcItem = (void*)-1; } if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != (void*)-1) { - IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdData.\n"); + IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n"); ms->Storage->NavIdItem = (void*)-1; + ms->Storage->NavIdSelected = -1; } } @@ -7328,11 +7331,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; if (pressed) - { ms->IsFocused = true; - //if (storage->Id != ms->FocusScopeId) - // storage->Init(ms->FocusScopeId); - } if (!ms->IsFocused) return; @@ -7342,15 +7341,11 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0; bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0; - if (g.NavId == id) - storage->NavIdItem = item_data; if (g.NavId == id && storage->RangeSrcItem == (void*)-1) { storage->RangeSrcItem = item_data; storage->RangeSelected = selected; // Will be updated at the end of this function anyway. } - if (storage->NavIdItem == item_data) - ms->NavIdPassedBy = true; // Auto-select as you navigate a list if (g.NavJustMovedToId == id) @@ -7446,6 +7441,15 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) ms->EndIO.RangeSelected = selected; } + // Update/store the selection state of focused item + if (g.NavId == id) + { + storage->NavIdItem = item_data; + storage->NavIdSelected = selected ? 1 : 0; + } + if (storage->NavIdItem == item_data) + ms->NavIdPassedBy = true; + *p_selected = selected; *p_pressed = pressed; } @@ -7461,7 +7465,7 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage) return; Text("ID = 0x%08X", storage->ID); Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected); - Text("NavIdItem = %p", storage->NavIdItem); + Text("NavIdData = %p, NavIdSelected = %d", storage->NavIdItem, storage->NavIdSelected); TreePop(); #else IM_UNUSED(storage);