MultiSelect: Shallow tweaks/refactors.

Including moving IsFocused back internally for now.
This commit is contained in:
ocornut 2023-04-12 19:48:58 +02:00
parent a05700e327
commit 78cb1661cb
4 changed files with 88 additions and 90 deletions

18
imgui.h
View File

@ -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,27 +2762,29 @@ 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:
// 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]
// 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.
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.
@ -2789,7 +2792,6 @@ struct ImGuiMultiSelectData
ImGuiMultiSelectData() { Clear(); }
void Clear()
{
IsFocused = false;
RequestClear = RequestSelectAll = RequestSetRange = RangeSrcPassedBy = RangeValue = false;
RangeSrc = RangeDst = NULL;
RangeDirection = 0;

View File

@ -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); }
// 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();

View File

@ -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;

View File

@ -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;