mirror of
https://github.com/ocornut/imgui.git
synced 2024-11-27 16:29:02 +08:00
MultiSelect: (Breaking) BeginMultiSelect() doesn't need two last params maintained by users. Moving some storage from user to core. Proper deletion demo.
This commit is contained in:
parent
564dde0ee3
commit
9223ffc255
@ -14957,6 +14957,15 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
||||
TreePop();
|
||||
}
|
||||
|
||||
// Details for MultiSelect
|
||||
if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
|
||||
{
|
||||
for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
|
||||
if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
|
||||
DebugNodeMultiSelectState(state);
|
||||
TreePop();
|
||||
}
|
||||
|
||||
// Details for Docking
|
||||
#ifdef IMGUI_HAS_DOCK
|
||||
if (TreeNode("Docking"))
|
||||
|
57
imgui.h
57
imgui.h
@ -669,10 +669,9 @@ namespace ImGui
|
||||
IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
|
||||
|
||||
// Multi-selection system for Selectable() and TreeNode() functions.
|
||||
// This enables standard multi-selection/range-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow items to be fully clipped (= not submitted at all) when not visible.
|
||||
// Read comments near ImGuiMultiSelectIO for details.
|
||||
// When enabled, Selectable() and TreeNode() functions will return true when selection needs toggling.
|
||||
IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected);
|
||||
// - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.
|
||||
// - Read comments near ImGuiMultiSelectIO for details.
|
||||
IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags);
|
||||
IMGUI_API ImGuiMultiSelectIO* EndMultiSelect();
|
||||
IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
|
||||
|
||||
@ -908,7 +907,7 @@ namespace ImGui
|
||||
IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing.
|
||||
IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item).
|
||||
IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode().
|
||||
IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly)
|
||||
IMGUI_API bool IsItemToggledSelection(); // was the last item selection state toggled? (after Selectable(), TreeNode() etc.) We only returns toggle _event_ in order to handle clipping correctly.
|
||||
IMGUI_API bool IsAnyItemHovered(); // is any item hovered?
|
||||
IMGUI_API bool IsAnyItemActive(); // is any item active?
|
||||
IMGUI_API bool IsAnyItemFocused(); // is any item focused?
|
||||
@ -2725,10 +2724,8 @@ struct ImColor
|
||||
#define IMGUI_HAS_MULTI_SELECT // Multi-Select/Range-Select WIP branch // <-- This is currently _not_ in the top of imgui.h to prevent merge conflicts.
|
||||
|
||||
// Flags for BeginMultiSelect().
|
||||
// This system is designed to allow mouse/keyboard multi-selection, including support for range-selection (SHIFT+click and SHIFT+keyboard),
|
||||
// which is difficult to re-implement manually. If you disable multi-selection with ImGuiMultiSelectFlags_NoMultiSelect
|
||||
// (which is provided for consistency and flexibility), the whole BeginMultiSelect() system becomes largely overkill as
|
||||
// you can handle single-selection in a simpler manner by just calling Selectable() and reacting on clicks yourself.
|
||||
// (we provide 'ImGuiMultiSelectFlags_NoMultiSelect' for consistency and flexiblity, but it essentially disable the main purpose of BeginMultiSelect().
|
||||
// If you use 'ImGuiMultiSelectFlags_NoMultiSelect' you can handle single-selection in a simpler way by just calling Selectable()/TreeNode() and reacting on clicks).
|
||||
enum ImGuiMultiSelectFlags_
|
||||
{
|
||||
ImGuiMultiSelectFlags_None = 0,
|
||||
@ -2739,12 +2736,11 @@ enum ImGuiMultiSelectFlags_
|
||||
//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)
|
||||
};
|
||||
|
||||
// Abstract:
|
||||
// - This system helps you implements standard multi-selection idioms (CTRL+Click/Arrow, SHIFT+Click/Arrow, etc) in a way that allow
|
||||
// selectable items to be fully clipped (= not submitted at all) when not visible. Clipping is typically provided by ImGuiListClipper.
|
||||
// Handling all of this in a single pass imgui is a little tricky, and this is why we provide those functionalities.
|
||||
// Note however that if you don't need SHIFT+Click/Arrow range-select + clipping, you can handle a simpler form of multi-selection
|
||||
// yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers.
|
||||
// Multi-selection system
|
||||
// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc) in a way that
|
||||
// allow a clipper to be used (so most non-visible items won't be submitted). Handling this correctly is tricky, this is why
|
||||
// we provide the functionality. Note however that if you don't need SHIFT+Mouse/Keyboard range-select + clipping, you could use
|
||||
// a simpler form of multi-selection yourself, by reacting to click/presses on Selectable() items and checking keyboard modifiers.
|
||||
// The unusual complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements.
|
||||
// - In the spirit of Dear ImGui design, your code owns the selection data.
|
||||
// So this is designed to handle all kind of selection data: e.g. instructive selection (store a bool inside each object),
|
||||
@ -2762,36 +2758,33 @@ enum ImGuiMultiSelectFlags_
|
||||
// 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 RangeSrcItem to RangeDstItem).
|
||||
// Usage flow:
|
||||
// Begin
|
||||
// 1) Call BeginMultiSelect() with the last saved value of ->RangeSrcItem 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/SetRange 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 RangeSrcItem 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->RangeSrcItem) { data->RangeSrcPassedBy = true; }
|
||||
// 4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls.
|
||||
// (You may optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given item, if you need that info immediately for your display (before EndMultiSelect()).)
|
||||
// (When cannot provide a "IsItemSelected()" value because we need to consider clipped/unprocessed items, this is why we return a "Toggled" event instead.)
|
||||
// End
|
||||
// 5) Call EndMultiSelect(). Save the value of ->RangeSrcItem 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.
|
||||
// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
|
||||
// - (2) [If using a clipper] Honor Clear/SelectAll/SetRange requests by updating your selection data. Can use same code as Step 6.
|
||||
// LOOP - (3) [If using a clipper] Set RangeSrcPassedBy=true if the RangeSrcItem item is part of the items clipped before the first submitted/visible item.
|
||||
// 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->RangeSrcItem) { data->RangeSrcPassedBy = true; }
|
||||
// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls.
|
||||
// (optionally call IsItemToggledSelection() to query if the selection state has been toggled for a given visible item, if you need that info immediately for your display, before EndMultiSelect())
|
||||
// END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
|
||||
// - (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). Can use 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.
|
||||
struct ImGuiMultiSelectIO
|
||||
{
|
||||
// - Always process requests in this order: Clear, SelectAll, SetRange.
|
||||
// - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is exhibited in the demo)
|
||||
// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code, 'BEGIN'=BeginMultiSelect() and after, 'END'=EndMultiSelect() and after.
|
||||
// REQUESTS ----------------// BEGIN / LOOP / END
|
||||
bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection.
|
||||
bool RequestSelectAll; // ms:w, app:r / / ms:w, app:r // 2. Request app/user to select all.
|
||||
bool RequestSetRange; // / / ms:w, app:r // 3. Request app/user to select/unselect [RangeSrcItem..RangeDstItem] items, based on RangeSelected. In practice, only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false.
|
||||
// STATE/ARGUMENTS ---------// BEGIN / LOOP / END
|
||||
void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionData() value for RangeSrcItem. End: parameter from RequestSetRange request.
|
||||
void* RangeSrcItem; // ms:w / app:r / ms:w, app:r // Begin: Last known SetNextItemSelectionUserData() value for RangeSrcItem. End: parameter from RequestSetRange request.
|
||||
void* RangeDstItem; // / / ms:w, app:r // End: parameter from RequestSetRange request.
|
||||
ImS8 RangeDirection; // / / ms:w, app:r // End: parameter from RequestSetRange request. +1 if RangeSrcItem came before RangeDstItem, -1 otherwise. Available as an indicator in case you cannot infer order from the void* values. If your void* values are storing indices you will never need this.
|
||||
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).
|
||||
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(); }
|
||||
void Clear() { memset(this, 0, sizeof(*this)); }
|
||||
|
114
imgui_demo.cpp
114
imgui_demo.cpp
@ -2773,17 +2773,14 @@ static void ShowDemoWindowWidgets()
|
||||
// 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.
|
||||
// About RefItem:
|
||||
// - The BeginMultiSelect() API requires you to store information about the reference/pivot item (generally the last clicked item).
|
||||
struct ExampleSelection
|
||||
{
|
||||
// Data
|
||||
ImGuiStorage Storage; // Selection set
|
||||
int SelectionSize; // Number of selected items (== number of 1 in the Storage, maintained by this class). // FIXME-MULTISELECT: Imply more difficult to track with intrusive selection schemes?
|
||||
int RangeRef; // Reference/pivot item (generally last clicked item)
|
||||
|
||||
// Functions
|
||||
ExampleSelection() { RangeRef = 0; Clear(); }
|
||||
ExampleSelection() { Clear(); }
|
||||
void Clear() { Storage.Clear(); SelectionSize = 0; }
|
||||
bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; }
|
||||
void SetSelected(int n, bool v) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == (int)v) return; if (v) SelectionSize++; else SelectionSize--; *p_int = (bool)v; }
|
||||
@ -2806,6 +2803,17 @@ struct ExampleSelection
|
||||
if (ms_io->RequestSetRange) { SetRange((int)(intptr_t)ms_io->RangeSrcItem, (int)(intptr_t)ms_io->RangeDstItem, ms_io->RangeSelected ? 1 : 0); }
|
||||
}
|
||||
|
||||
void DebugTooltip()
|
||||
{
|
||||
if (ImGui::BeginTooltip())
|
||||
{
|
||||
for (auto& pair : Storage.Data)
|
||||
if (pair.val_i)
|
||||
ImGui::Text("0x%03X (%d)", pair.key, pair.key);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
// Call after BeginMultiSelect()
|
||||
// We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
|
||||
// Essentially this would be a ms_io->RequestNextFocusBeforeDeletion
|
||||
@ -2813,13 +2821,17 @@ struct ExampleSelection
|
||||
template<typename ITEM_TYPE>
|
||||
int CalcNextFocusIdxForBeforeDeletion(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& 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))
|
||||
return (int)(intptr_t)ms_io->NavIdItem;
|
||||
|
||||
// Return first unselected item after RangeSrcItem
|
||||
for (int n = (int)(intptr_t)ms_io->RangeSrcItem + 1; n < items.Size; n++)
|
||||
if (!GetSelected(n))
|
||||
return n;
|
||||
|
||||
// Otherwise return last unselected item
|
||||
for (int n = (int)(intptr_t)ms_io->RangeSrcItem - 1; n >= 0; n--)
|
||||
for (int n = IM_MIN((int)(intptr_t)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--)
|
||||
if (!GetSelected(n))
|
||||
return n;
|
||||
return -1;
|
||||
@ -2833,7 +2845,7 @@ struct ExampleSelection
|
||||
// This does two things:
|
||||
// - (1) Update Items List (delete items from it)
|
||||
// - (2) Convert the new focus index from old selection index (before deletion) to new selection index (after selection), and select it.
|
||||
// FIXME: (2.3) if NavId is not selected, stay on same item -> facilitate persisting focus if ID change? (if ID is index-based) -> by setting focus again
|
||||
// If NavId was not selected, next_focus_idx_in_old_selection == -1 and we stay on same item.
|
||||
// You are expected to handle both of those in user-space because Dear ImGui rightfully doesn't own items data nor selection data.
|
||||
// This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data.
|
||||
IM_UNUSED(ms_io);
|
||||
@ -2850,6 +2862,7 @@ struct ExampleSelection
|
||||
items.swap(new_items);
|
||||
|
||||
// Update selection
|
||||
//IMGUI_DEBUG_LOG("ApplyDeletion(): next_focus_idx_in_new_selection = %d\n", next_focus_idx_in_new_selection);
|
||||
Clear();
|
||||
if (next_focus_idx_in_new_selection != -1)
|
||||
SetSelected(next_focus_idx_in_new_selection, true);
|
||||
@ -2922,11 +2935,10 @@ 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("RangeRef: %d", selection.RangeRef);
|
||||
if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)))
|
||||
{
|
||||
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape;
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef));
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags);
|
||||
selection.ApplyRequests(ms_io, ITEMS_COUNT);
|
||||
|
||||
for (int n = 0; n < ITEMS_COUNT; n++)
|
||||
@ -2940,7 +2952,6 @@ static void ShowDemoWindowMultiSelect()
|
||||
|
||||
// Apply multi-select requests
|
||||
ms_io = ImGui::EndMultiSelect();
|
||||
selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem;
|
||||
selection.ApplyRequests(ms_io, ITEMS_COUNT);
|
||||
|
||||
ImGui::EndListBox();
|
||||
@ -2960,20 +2971,18 @@ static void ShowDemoWindowMultiSelect()
|
||||
|
||||
ImGui::Text("Adding features:");
|
||||
ImGui::BulletText("Dynamic list with Delete key support.");
|
||||
ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size);
|
||||
if (ImGui::IsItemHovered() && selection.GetSize() > 0)
|
||||
selection.DebugTooltip();
|
||||
|
||||
// Initialize default list with 50 items + button to add more.
|
||||
static int items_next_id = 0;
|
||||
if (items_next_id == 0)
|
||||
for (int n = 0; n < 50; n++)
|
||||
items.push_back(items_next_id++);
|
||||
ImGui::Text("Selection: %d/%d", selection.GetSize(), items.Size);
|
||||
if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Add 20 items"))
|
||||
for (int n = 0; n < 20; n++)
|
||||
items.push_back(items_next_id++);
|
||||
|
||||
ImGui::Text("Selection size: %d/%d", selection.GetSize(), items.Size);
|
||||
ImGui::Text("RangeRef: %d", selection.RangeRef);
|
||||
if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetSelected(items.Size - 1, false); items.pop_back(); } } // This is to test
|
||||
|
||||
// Extra to support deletion: Submit scrolling range to avoid glitches on deletion
|
||||
const float items_height = ImGui::GetTextLineHeightWithSpacing();
|
||||
@ -2981,7 +2990,7 @@ static void ShowDemoWindowMultiSelect()
|
||||
if (ImGui::BeginListBox("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20)))
|
||||
{
|
||||
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape;
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef));
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags);
|
||||
selection.ApplyRequests(ms_io, items.Size);
|
||||
|
||||
// FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal.
|
||||
@ -2989,6 +2998,7 @@ static void ShowDemoWindowMultiSelect()
|
||||
// FIXME-MULTISELECT: Test with intermediary modal dialog.
|
||||
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); }
|
||||
|
||||
for (int n = 0; n < items.Size; n++)
|
||||
{
|
||||
@ -2997,22 +3007,44 @@ static void ShowDemoWindowMultiSelect()
|
||||
sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]);
|
||||
|
||||
bool item_is_selected = selection.GetSelected(n);
|
||||
ImGui::SetNextItemSelectionData((void*)(intptr_t)n);
|
||||
ImGui::SetNextItemSelectionUserData(n);
|
||||
ImGui::Selectable(label, item_is_selected);
|
||||
if (ImGui::IsItemToggledSelection())
|
||||
selection.SetSelected(n, !item_is_selected);
|
||||
|
||||
// FIXME-MULTISELECT: turn into a ms_io->RequestFocusIdx
|
||||
if (next_focus_item_idx == n)
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
ImGui::SetKeyboardFocusHere(-1); // FIXME-MULTISELECT: Need to avoid selection.
|
||||
}
|
||||
|
||||
// Apply multi-select requests
|
||||
#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
|
||||
ms_io->RangeSrcReset = true;
|
||||
ms_io = ImGui::EndMultiSelect();
|
||||
selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem;
|
||||
selection.ApplyRequests(ms_io, items.Size);
|
||||
if (want_delete)
|
||||
selection.ApplyDeletion(ms_io, items, next_focus_item_idx);
|
||||
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
|
||||
|
||||
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
@ -3035,7 +3067,7 @@ static void ShowDemoWindowMultiSelect()
|
||||
ImGui::PushID(selection_scope_n);
|
||||
|
||||
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape; // | ImGuiMultiSelectFlags_ClearOnClickRectVoid
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection->RangeRef, selection->GetSelected(selection->RangeRef));
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags);
|
||||
selection->ApplyRequests(ms_io, ITEMS_COUNT);
|
||||
|
||||
for (int n = 0; n < ITEMS_COUNT; n++)
|
||||
@ -3049,7 +3081,6 @@ static void ShowDemoWindowMultiSelect()
|
||||
|
||||
// Apply multi-select requests
|
||||
ms_io = ImGui::EndMultiSelect();
|
||||
selection->RangeRef = (int)(intptr_t)ms_io->RangeSrcItem;
|
||||
selection->ApplyRequests(ms_io, ITEMS_COUNT);
|
||||
ImGui::PopID();
|
||||
}
|
||||
@ -3105,7 +3136,7 @@ static void ShowDemoWindowMultiSelect()
|
||||
if (widget_type == WidgetType_TreeNode)
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
|
||||
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, (void*)(intptr_t)selection.RangeRef, selection.GetSelected(selection.RangeRef));
|
||||
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags);
|
||||
selection.ApplyRequests(ms_io, items.Size);
|
||||
|
||||
// FIXME-MULTISELECT: Shortcut(). Hard to demo this? May be helpful to send a helper/optional "delete" signal.
|
||||
@ -3113,6 +3144,7 @@ static void ShowDemoWindowMultiSelect()
|
||||
// FIXME-MULTISELECT: Test with intermediary modal dialog.
|
||||
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); }
|
||||
|
||||
if (show_in_table)
|
||||
{
|
||||
@ -3126,14 +3158,18 @@ static void ShowDemoWindowMultiSelect()
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
if (use_clipper)
|
||||
{
|
||||
clipper.Begin(items.Size);
|
||||
if (next_focus_item_idx != -1)
|
||||
clipper.IncludeItemByIndex(next_focus_item_idx); // Ensure item to focus is not clipped
|
||||
}
|
||||
|
||||
while (!use_clipper || clipper.Step())
|
||||
{
|
||||
// IF clipping is used you need to set 'RangeSrcPassedBy = true' if RangeSrcItem was passed over.
|
||||
if (use_clipper)
|
||||
if ((int)(intptr_t)ms_io->RangeSrcItem <= clipper.DisplayStart)
|
||||
ms_io->RangeSrcPassedBy = true;
|
||||
// IF clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over.
|
||||
// If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData()
|
||||
if (use_clipper && clipper.DisplayStart > (int)(intptr_t)ms_io->RangeSrcItem)
|
||||
ms_io->RangeSrcPassedBy = true;
|
||||
|
||||
const int item_begin = use_clipper ? clipper.DisplayStart : 0;
|
||||
const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
|
||||
@ -3217,6 +3253,12 @@ static void ShowDemoWindowMultiSelect()
|
||||
break;
|
||||
}
|
||||
|
||||
// If clipping is used: you need to set 'RangeSrcPassedBy = true' if RangeSrc was passed over.
|
||||
// If you submit all items this is unnecessary as this is one by SetNextItemSelectionUserData()
|
||||
// Here we essentially notify before EndMultiSelect() that RangeSrc is still present in our data set.
|
||||
if (use_clipper && items.Size > (int)(intptr_t)ms_io->RangeSrcItem)
|
||||
ms_io->RangeSrcPassedBy = true;
|
||||
|
||||
if (show_in_table)
|
||||
{
|
||||
ImGui::EndTable();
|
||||
@ -3225,11 +3267,21 @@ 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)
|
||||
ms_io->RangeSrcReset = true;
|
||||
ms_io = ImGui::EndMultiSelect();
|
||||
selection.RangeRef = (int)(intptr_t)ms_io->RangeSrcItem;
|
||||
selection.ApplyRequests(ms_io, items.Size);
|
||||
if (want_delete)
|
||||
selection.ApplyDeletion(ms_io, items, next_focus_item_idx);
|
||||
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
|
||||
|
||||
if (widget_type == WidgetType_TreeNode)
|
||||
ImGui::PopStyleVar();
|
||||
|
@ -134,6 +134,7 @@ struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a de
|
||||
struct ImGuiLastItemData; // Status storage for last submitted items
|
||||
struct ImGuiLocEntry; // A localization entry.
|
||||
struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only
|
||||
struct ImGuiMultiSelectState; // Multi-selection persistent state (for focused selection).
|
||||
struct ImGuiMultiSelectTempData; // Multi-selection temporary state (while traversing).
|
||||
struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result
|
||||
struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
|
||||
@ -1716,19 +1717,34 @@ struct ImGuiOldColumns
|
||||
// Temporary storage for multi-select
|
||||
struct IMGUI_API ImGuiMultiSelectTempData
|
||||
{
|
||||
ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)
|
||||
ImGuiMultiSelectState* Storage;
|
||||
ImGuiID FocusScopeId; // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)
|
||||
ImGuiMultiSelectFlags Flags;
|
||||
ImGuiKeyChord KeyMods;
|
||||
ImGuiWindow* Window;
|
||||
ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop.
|
||||
ImGuiMultiSelectIO EndIO; // Requests are set during the loop 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 IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
|
||||
bool RangeDstPassedBy; // Set by the item that matches NavJustMovedToId when IsSetRange is set.
|
||||
//ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid.
|
||||
ImGuiMultiSelectIO BeginIO; // Requests are set and returned by BeginMultiSelect(), written to by user during the loop.
|
||||
ImGuiMultiSelectIO EndIO; // Requests are set during the loop 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 IsSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
|
||||
bool NavIdPassedBy;
|
||||
bool RangeDstPassedBy; // Set by the the item that matches NavJustMovedToId when IsSetRange is set.
|
||||
//ImRect Rect; // Extent of selection scope between BeginMultiSelect() / EndMultiSelect(), used by ImGuiMultiSelectFlags_ClearOnClickRectVoid.
|
||||
|
||||
ImGuiMultiSelectTempData() { Clear(); }
|
||||
void Clear() { memset(this, 0, sizeof(*this)); }
|
||||
void Clear() { memset(this, 0, sizeof(*this)); BeginIO.RangeSrcItem = EndIO.RangeSrcItem = BeginIO.RangeDstItem = EndIO.RangeDstItem = BeginIO.NavIdItem = EndIO.NavIdItem = (void*)-1; }
|
||||
};
|
||||
|
||||
// Persistent storage for multi-select (as long as selection is alive)
|
||||
struct IMGUI_API ImGuiMultiSelectState
|
||||
{
|
||||
ImGuiWindow* Window;
|
||||
ImGuiID ID;
|
||||
int LastFrameActive; // Last used frame-count, for GC.
|
||||
ImS8 RangeSelected; // -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; }
|
||||
};
|
||||
|
||||
#endif // #ifdef IMGUI_HAS_MULTI_SELECT
|
||||
@ -2170,6 +2186,7 @@ struct ImGuiContext
|
||||
// Multi-Select state
|
||||
ImGuiMultiSelectTempData* CurrentMultiSelect; // FIXME-MULTISELECT: We currently don't support recursing/stacking multi-select
|
||||
ImGuiMultiSelectTempData MultiSelectTempData[1];
|
||||
ImPool<ImGuiMultiSelectState> MultiSelectStorage;
|
||||
|
||||
// Hover Delay system
|
||||
ImGuiID HoverItemDelayId;
|
||||
@ -3568,6 +3585,7 @@ namespace ImGui
|
||||
IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings);
|
||||
IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state);
|
||||
IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
|
||||
IMGUI_API void DebugNodeMultiSelectState(ImGuiMultiSelectState* state);
|
||||
IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label);
|
||||
IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings);
|
||||
IMGUI_API void DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);
|
||||
|
@ -7132,7 +7132,7 @@ static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSe
|
||||
}
|
||||
|
||||
// Return ImGuiMultiSelectIO structure. Lifetime: valid until corresponding call to EndMultiSelect().
|
||||
ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* range_ref, bool range_ref_is_selected)
|
||||
ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
@ -7141,21 +7141,38 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r
|
||||
g.CurrentMultiSelect = ms;
|
||||
|
||||
// FIXME: BeginFocusScope()
|
||||
const ImGuiID id = window->IDStack.back();
|
||||
ms->Clear();
|
||||
ms->FocusScopeId = window->IDStack.back();
|
||||
ms->FocusScopeId = id;
|
||||
ms->Flags = flags;
|
||||
ms->Window = window;
|
||||
ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId);
|
||||
PushFocusScope(ms->FocusScopeId);
|
||||
|
||||
// Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame.
|
||||
ms->KeyMods = g.NavJustMovedToId ? g.NavJustMovedToKeyMods : g.IO.KeyMods;
|
||||
|
||||
// Bind storage
|
||||
ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id);
|
||||
storage->ID = id;
|
||||
storage->LastFrameActive = g.FrameCount;
|
||||
storage->Window = window;
|
||||
ms->Storage = storage;
|
||||
|
||||
// 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;
|
||||
|
||||
if (!ms->IsFocused)
|
||||
return &ms->BeginIO; // This is cleared at this point.
|
||||
|
||||
/*
|
||||
if ((flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0)
|
||||
{
|
||||
ms->BeginIO.RangeSrcItem = ms->EndIO.RangeSrcItem = range_ref;
|
||||
ms->BeginIO.RangeSelected = ms->EndIO.RangeSelected = range_ref_is_selected;
|
||||
}
|
||||
*/
|
||||
|
||||
// Auto clear when using Navigation to move within the selection
|
||||
// (we compare FocusScopeId so it possible to use multiple selections inside a same window)
|
||||
@ -7163,19 +7180,21 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, void* r
|
||||
{
|
||||
if (ms->KeyMods & ImGuiMod_Shift)
|
||||
ms->IsSetRange = true;
|
||||
if (ms->IsSetRange)
|
||||
IM_ASSERT(storage->RangeSrcItem != (void*)-1); // Not ready -> could clear?
|
||||
if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0)
|
||||
ms->BeginIO.RequestClear = true;
|
||||
}
|
||||
|
||||
// Shortcut: Select all (CTRL+A)
|
||||
if (ms->IsFocused && !(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll))
|
||||
if (!(flags & ImGuiMultiSelectFlags_NoMultiSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll))
|
||||
if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A))
|
||||
ms->BeginIO.RequestSelectAll = true;
|
||||
|
||||
// Shortcut: Clear selection (Escape)
|
||||
// FIXME-MULTISELECT: Only hog shortcut if selection is not null, meaning we need "has selection or "selection size" data here.
|
||||
// Otherwise may be done by caller but it means Shortcut() needs to be exposed.
|
||||
if (ms->IsFocused && (flags & ImGuiMultiSelectFlags_ClearOnEscape))
|
||||
if (flags & ImGuiMultiSelectFlags_ClearOnEscape)
|
||||
if (Shortcut(ImGuiKey_Escape))
|
||||
ms->BeginIO.RequestClear = true;
|
||||
|
||||
@ -7191,7 +7210,21 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
|
||||
IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId);
|
||||
IM_ASSERT(g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Window == g.CurrentWindow);
|
||||
IM_ASSERT(g.CurrentMultiSelect != NULL && ms->Storage->Window == g.CurrentWindow);
|
||||
|
||||
if (ms->IsFocused)
|
||||
{
|
||||
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.
|
||||
ms->Storage->RangeSrcItem = (void*)-1;
|
||||
}
|
||||
if (ms->NavIdPassedBy == false && ms->Storage->NavIdItem != (void*)-1)
|
||||
{
|
||||
IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdData.\n");
|
||||
ms->Storage->NavIdItem = (void*)-1;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear selection when clicking void?
|
||||
// We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection!
|
||||
@ -7205,7 +7238,6 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
|
||||
|
||||
// Unwind
|
||||
ms->FocusScopeId = 0;
|
||||
ms->Window = NULL;
|
||||
ms->Flags = ImGuiMultiSelectFlags_None;
|
||||
ms->BeginIO.Clear(); // Invalidate contents of BeginMultiSelect() to enforce scope.
|
||||
PopFocusScope();
|
||||
@ -7222,28 +7254,31 @@ 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.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;
|
||||
|
||||
// Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping)
|
||||
if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect)
|
||||
{
|
||||
// Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping)
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect;
|
||||
if (ms->BeginIO.RangeSrcItem == (void*)selection_user_data)
|
||||
ms->BeginIO.RangeSrcPassedBy = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
|
||||
if (!ms->IsFocused)
|
||||
return;
|
||||
ImGuiMultiSelectState* storage = ms->Storage;
|
||||
|
||||
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;
|
||||
|
||||
// Apply Clear/SelectAll requests requested by BeginMultiSelect().
|
||||
@ -7260,12 +7295,22 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected)
|
||||
if (ms->IsSetRange)
|
||||
{
|
||||
IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0);
|
||||
const bool is_range_src = (ms->BeginIO.RangeSrcItem == item_data);
|
||||
const bool is_range_dst = !ms->RangeDstPassedBy && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped.
|
||||
const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id; // Assume that g.NavJustMovedToId is not clipped.
|
||||
if (is_range_dst)
|
||||
{
|
||||
ms->RangeDstPassedBy = true;
|
||||
if (storage->RangeSrcItem == (void*)-1) // If we don't have RangeSrc, assign RangeSrc = RangeDst
|
||||
{
|
||||
storage->RangeSrcItem = item_data;
|
||||
storage->RangeSelected = selected ? 1 : 0;
|
||||
}
|
||||
}
|
||||
const bool is_range_src = storage->RangeSrcItem == item_data;
|
||||
if (is_range_src || is_range_dst || ms->BeginIO.RangeSrcPassedBy != ms->RangeDstPassedBy)
|
||||
selected = ms->BeginIO.RangeSelected;
|
||||
{
|
||||
IM_ASSERT(storage->RangeSrcItem != (void*)-1 && storage->RangeSelected != -1);
|
||||
selected = (storage->RangeSelected != 0);
|
||||
}
|
||||
else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0)
|
||||
selected = false;
|
||||
}
|
||||
@ -7277,16 +7322,36 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
bool selected = *p_selected;
|
||||
bool pressed = *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;
|
||||
|
||||
void* item_data = (void*)g.NextItemData.SelectionUserData;
|
||||
|
||||
const bool is_multiselect = (ms->Flags & ImGuiMultiSelectFlags_NoMultiSelect) == 0;
|
||||
bool selected = *p_selected;
|
||||
bool pressed = *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)
|
||||
{
|
||||
@ -7343,15 +7408,16 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
|
||||
{
|
||||
// Shift+Arrow always select
|
||||
// Ctrl+Shift+Arrow copy source selection state (alrady stored by BeginMultiSelect() in RangeSelected)
|
||||
if (!is_ctrl)
|
||||
ms->EndIO.RangeSelected = true;
|
||||
//IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue);
|
||||
ms->EndIO.RangeSrcItem = (storage->RangeSrcItem != (void*)-1) ? storage->RangeSrcItem : item_data;
|
||||
ms->EndIO.RangeSelected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true;
|
||||
ms->EndIO.RangeDirection = ms->BeginIO.RangeSrcPassedBy ? +1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ctrl inverts selection, otherwise always select
|
||||
selected = is_ctrl ? !selected : true;
|
||||
ms->EndIO.RangeSrcItem = item_data;
|
||||
ms->EndIO.RangeSrcItem = storage->RangeSrcItem = item_data;
|
||||
ms->EndIO.RangeSelected = selected;
|
||||
ms->EndIO.RangeDirection = +1;
|
||||
}
|
||||
@ -7371,13 +7437,37 @@ 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 (storage->RangeSrcItem == item_data)
|
||||
storage->RangeSelected = selected ? 1 : 0;
|
||||
if (ms->EndIO.RangeSrcItem == item_data && is_ctrl && is_shift && is_multiselect)
|
||||
{
|
||||
if (ms->EndIO.RequestSetRange)
|
||||
IM_ASSERT(storage->RangeSrcItem == ms->EndIO.RangeSrcItem);
|
||||
ms->EndIO.RangeSelected = selected;
|
||||
}
|
||||
|
||||
*p_selected = selected;
|
||||
*p_pressed = pressed;
|
||||
}
|
||||
|
||||
void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
|
||||
{
|
||||
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.
|
||||
if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
|
||||
bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X%s", storage->ID, is_active ? "" : " *Inactive*");
|
||||
if (!is_active) { PopStyleColor(); }
|
||||
if (!open)
|
||||
return;
|
||||
Text("ID = 0x%08X", storage->ID);
|
||||
Text("RangeSrcItem = %p, RangeSelected = %d", storage->RangeSrcItem, storage->RangeSelected);
|
||||
Text("NavIdItem = %p", storage->NavIdItem);
|
||||
TreePop();
|
||||
#else
|
||||
IM_UNUSED(storage);
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Widgets: ListBox
|
||||
//-------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user