From 6ddc5f38afa990875a38b9e8b644f4770173da7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 23 Aug 2023 19:47:24 +0200 Subject: [PATCH] MultiSelect: Demo: added simpler demo using Clipper. Clarify RangeSrcPassedBy doc. --- imgui.h | 12 +++++++----- imgui_demo.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/imgui.h b/imgui.h index 730a53e2b..ea47d0fd8 100644 --- a/imgui.h +++ b/imgui.h @@ -2774,11 +2774,13 @@ enum ImGuiMultiSelectFlags_ // Usage flow: // BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (2) [If using clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 6. -// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item. +// LOOP - (3) [If using clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item was already passed by. // 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 in ImGuiSelectionUserData, this is easy to compute: if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } -// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing in each clipper step to tell if current DisplayStart comes after RangeSrcItem.. -// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect()) +// - If you are using integer indices in ImGuiSelectionUserData, this is easy to compute: +// if (clipper.DisplayStart > data->RangeSrcItem) { data->RangeSrcPassedBy = true; } +// - If you are using pointers in ImGuiSelectionUserData, you may need additional processing, e.g. find the index of RangeSrcItem before applying the above operation. +// - This also needs to be done at the end of the clipper loop, otherwise we can't tell if the item still exist. +// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. // END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result. // - (6) Honor Clear/SelectAll/SetRange requests by updating your selection data. Same code as Step 2. // If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis. @@ -2801,7 +2803,7 @@ struct ImGuiMultiSelectIO ImGuiSelectionUserData RangeFirstItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom) ImGuiSelectionUserData RangeLastItem; // / / ms:w, app:r // End: parameter for RequestSetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top) bool RangeSelected; // / / ms:w, app:r // End: parameter for 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 RangeSrcPassedBy; // / ms:rw app:w / ms:r // (If using clipper) Need to be set by app/user if RangeSrcItem was passed by. Ignore if not clipping. bool RangeSrcReset; // app:w / 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). ImGuiSelectionUserData NavIdItem; // ms:w, app:r / / // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3cd8bf6ad..3cf95e646 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2911,7 +2911,7 @@ static void ShowDemoWindowMultiSelect() ImGui::TreePop(); } - const char* random_names[] = + static const char* random_names[] = { "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper", "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava", @@ -2935,7 +2935,7 @@ static void ShowDemoWindowMultiSelect() // The BeginListBox() has no actual purpose for selection logic (other that offering a scrolling region). const int ITEMS_COUNT = 50; - ImGui::Text("Selection size: %d", selection.GetSize()); + ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) { ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; @@ -2951,7 +2951,49 @@ static void ShowDemoWindowMultiSelect() ImGui::Selectable(label, item_is_selected); } - // Apply multi-select requests + ms_io = ImGui::EndMultiSelect(); + selection.ApplyRequests(ms_io, ITEMS_COUNT); + + ImGui::EndListBox(); + } + ImGui::TreePop(); + } + + // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect() + IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)"); + if (ImGui::TreeNode("Multi-Select (with clipper)")) + { + static ExampleSelection selection; + + ImGui::Text("Added features:"); + ImGui::BulletText("Using ImGuiListClipper."); + + const int ITEMS_COUNT = 10000; + ImGui::Text("Selection: %d/%d", selection.GetSize(), ITEMS_COUNT); + if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20))) + { + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; + ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags); + selection.ApplyRequests(ms_io, ITEMS_COUNT); + + ImGuiListClipper clipper; + clipper.Begin(ITEMS_COUNT); + while (clipper.Step()) + { + if (!ms_io->RangeSrcPassedBy && clipper.DisplayStart > ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; + for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++) + { + char label[64]; + sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]); + bool item_is_selected = selection.Contains(n); + ImGui::SetNextItemSelectionUserData(n); + ImGui::Selectable(label, item_is_selected); + } + } + if (!ms_io->RangeSrcPassedBy && ITEMS_COUNT > ms_io->RangeSrcItem) + ms_io->RangeSrcPassedBy = true; + ms_io = ImGui::EndMultiSelect(); selection.ApplyRequests(ms_io, ITEMS_COUNT);