mirror of
https://github.com/ocornut/imgui.git
synced 2026-01-11 00:04:24 +00:00
MultiSelect: Demo: rework ExampleSelection names to map better to typical user code + variety of Comments tweaks.
This commit is contained in:
parent
ff95fdb668
commit
c3753809b1
2 changed files with 82 additions and 87 deletions
47
imgui.h
47
imgui.h
|
|
@ -671,7 +671,8 @@ namespace ImGui
|
||||||
|
|
||||||
// Multi-selection system for Selectable() and TreeNode() functions.
|
// Multi-selection system for Selectable() and TreeNode() functions.
|
||||||
// - 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.
|
// - 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.
|
||||||
// - ImGuiSelectionUserData is often used to store your item index. Read comments near ImGuiMultiSelectIO for details.
|
// - ImGuiSelectionUserData is often used to store your item index.
|
||||||
|
// - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State' for demo.
|
||||||
IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags);
|
IMGUI_API ImGuiMultiSelectIO* BeginMultiSelect(ImGuiMultiSelectFlags flags);
|
||||||
IMGUI_API ImGuiMultiSelectIO* EndMultiSelect();
|
IMGUI_API ImGuiMultiSelectIO* EndMultiSelect();
|
||||||
IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
|
IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
|
||||||
|
|
@ -2740,32 +2741,35 @@ enum ImGuiMultiSelectFlags_
|
||||||
// Multi-selection system
|
// Multi-selection system
|
||||||
// - Refer to 'Demo->Widgets->Selection State' for references using this.
|
// - Refer to 'Demo->Widgets->Selection State' for references using this.
|
||||||
// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc)
|
// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc)
|
||||||
// and supports a clipper being used. Handling this manually may be tricky, this is why we provide the functionality.
|
// and supports a clipper being used. Handling this manually and correctly i tricky, this is why we provide
|
||||||
// If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can use a simpler form of multi-selection yourself,
|
// the functionality. If you don't need SHIFT+Mouse/Keyboard range-select + clipping, you can implement a
|
||||||
// by reacting to click/presses on Selectable() items and checking keyboard modifiers.
|
// simple form of multi-selection yourself, by reacting to click/presses on Selectable() items.
|
||||||
// The complexity of this system is mostly caused by supporting SHIFT+Click/Arrow range-select with clipped elements.
|
|
||||||
// - TreeNode() and Selectable() are supported but custom widgets may use it as well.
|
// - TreeNode() and Selectable() are supported but custom widgets may use it as well.
|
||||||
// - In the spirit of Dear ImGui design, your code owns actual selection data.
|
// - In the spirit of Dear ImGui design, your code owns actual selection data.
|
||||||
// This is designed to allow all kinds of selection storage you may use in your application:
|
// This is designed to allow all kinds of selection storage you may use in your application:
|
||||||
// e.g. instructive selection (store a bool inside each object), external array (store an array in your view data, next
|
// e.g. set/map/hash (store only selected items), instructive selection (store a bool inside each object),
|
||||||
// to your objects), set/map/hash (store only selected items), or other structures (store indices in an interval tree), etc.
|
// external array (store an array in your view data, next to your objects), or other structures (store indices
|
||||||
// - The work involved to deal with multi-selection differs whether you want to only submit visible items and clip others,
|
// in an interval tree), etc.
|
||||||
// or submit all items regardless of their visibility. Clipping items is more efficient and will allow you to deal with
|
// - The work involved to deal with multi-selection differs whether you want to only submit visible items and
|
||||||
// large lists (1k~100k items) with no performance penalty, but requires a little more work on the code.
|
// clip others, or submit all items regardless of their visibility. Clipping items is more efficient and will
|
||||||
// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost you should
|
// allow you to deal with large lists (1k~100k items) with no performance penalty, but requires a little more
|
||||||
// be negligible (as least on Dear ImGui side).
|
// work on the code.
|
||||||
// If you are not sure, always start without clipping and you can work your way to the optimized version afterwards.
|
// For small selection set (<100 items), you might want to not bother with using the clipper, as the cost
|
||||||
|
// should be negligible (as least on Dear ImGui side).
|
||||||
|
// If you are not sure, always start without clipping! You can work your way to the optimized version afterwards.
|
||||||
// About ImGuiSelectionUserData:
|
// About ImGuiSelectionUserData:
|
||||||
// - This is your application-defined identifier in a selection set:
|
// - This is your application-defined identifier in a selection set:
|
||||||
// - For each item is submitted by your calls to SetNextItemSelectionUserData().
|
// - For each item is submitted by your calls to SetNextItemSelectionUserData().
|
||||||
// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO.
|
// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO.
|
||||||
// - Most applications will store an object INDEX, hence the chosen name and type.
|
// - Most applications will store an object INDEX, hence the chosen name and type.
|
||||||
// Storing an integer index is the easiest thing to do, as SetRange requests will give you two end points
|
// Storing an integer index is the easiest thing to do, as RequestSetRange requests will give you two end-points
|
||||||
// and you will need to interpolate between them to honor range selection.
|
// and you will need to iterate/interpolate between them to honor range selection.
|
||||||
// - However it is perfectly possible to store a POINTER inside this value! The multi-selection system never assume
|
// - However it is perfectly possible to store a POINTER inside this value! The multi-selection system never assume
|
||||||
// that you identify items by indices, and never attempt to interpolate between two ImGuiSelectionUserData values.
|
// that you identify items by indices. It never attempt to iterate/interpolate between 2 ImGuiSelectionUserData values.
|
||||||
// - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead
|
// - As most users will want to cast this to integer, for convenience and to reduce confusion we use ImS64 instead
|
||||||
// of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this.
|
// of void*, being syntactically easier to downcast. But feel free to reinterpret_cast a pointer into this.
|
||||||
|
// - You may store another type of (e.g. an data) but this may make your lookups and the interpolation between
|
||||||
|
// two values more cumbersome.
|
||||||
// - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler.
|
// - If you need to wrap this API for another language/framework, feel free to expose this as 'int' if simpler.
|
||||||
// Usage flow:
|
// Usage flow:
|
||||||
// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
|
// BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
|
||||||
|
|
@ -2777,12 +2781,15 @@ enum ImGuiMultiSelectFlags_
|
||||||
// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect())
|
// - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls. (optionally call IsItemToggledSelection() if you need that info immediately for displaying your item, before EndMultiSelect())
|
||||||
// END - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
|
// 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.
|
// - (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.
|
// If you submit all items (no clipper), Step 2 and 3 and will be handled by Selectable()/TreeNode on a per-item basis.
|
||||||
// However it is perfectly fine to honor all steps even if you don't use a clipper.
|
// However it is perfectly fine to honor all steps even if you don't use a clipper.
|
||||||
|
// Advanced:
|
||||||
|
// - Deletion: If you need to handle items deletion a little more work if needed for post-deletion focus and scrolling to be correct.
|
||||||
|
// refer to 'Demo->Widgets->Selection State' for demos supporting deletion.
|
||||||
struct ImGuiMultiSelectIO
|
struct ImGuiMultiSelectIO
|
||||||
{
|
{
|
||||||
// - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Debug Log->Selection' to see requests as they happen.
|
// - Always process requests in this order: Clear, SelectAll, SetRange. Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen.
|
||||||
// - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is exhibited in the demo)
|
// - Some fields are only necessary if your list is dynamic and allows deletion (getting "post-deletion" state right is shown 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.
|
// - 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
|
// REQUESTS --------------------------------// BEGIN / LOOP / END
|
||||||
bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection.
|
bool RequestClear; // ms:w, app:r / / ms:w, app:r // 1. Request app/user to clear selection.
|
||||||
|
|
|
||||||
122
imgui_demo.cpp
122
imgui_demo.cpp
|
|
@ -2766,54 +2766,45 @@ static void ShowDemoWindowWidgets()
|
||||||
// To store a single-selection:
|
// To store a single-selection:
|
||||||
// - You only need a single variable and don't need any of this!
|
// - You only need a single variable and don't need any of this!
|
||||||
// To store a multi-selection, in your real application you could:
|
// 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 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.)
|
// - 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...
|
// are generally appropriate. Even a large array of bool might work for you... This is what we are doing here.
|
||||||
// - If you need to handle extremely large selections, it might be advantageous to support a "negative" mode in
|
// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object).
|
||||||
// your storage, so "Select All" becomes "Negative=1 + Clear" and then sparse unselect can add to the storage.
|
// - This is simple, but it means you cannot have multiple simultaneous views over your objects.
|
||||||
|
// - This is what many of the simpler demos in other sections of this file are using (so they are not using this class).
|
||||||
|
// - Some of our features requires you to provide the selection size, which with this specific strategy require additional work:
|
||||||
|
// either you maintain it (which requires storage outside of objects) either you recompute (which may be costly for large sets).
|
||||||
|
// - So I would suggest that using intrusive selection for multi-select is not the most adequate.
|
||||||
struct ExampleSelection
|
struct ExampleSelection
|
||||||
{
|
{
|
||||||
// Data
|
// Data
|
||||||
ImGuiStorage Storage; // Selection set
|
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 Size; // 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?
|
||||||
bool QueueDeletion; // Request deleting selected items
|
bool QueueDeletion; // Request deleting selected items
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
ExampleSelection() { Clear(); }
|
ExampleSelection() { Clear(); }
|
||||||
void Clear() { Storage.Clear(); SelectionSize = 0; QueueDeletion = false; }
|
void Clear() { Storage.Clear(); Size = 0; QueueDeletion = false; }
|
||||||
bool GetSelected(int n) const { return Storage.GetInt((ImGuiID)n, 0) != 0; }
|
bool Contains(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; }
|
void AddItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int != 0) return; *p_int = 1; Size++; }
|
||||||
int GetSize() const { return SelectionSize; }
|
void RemoveItem(int n) { int* p_int = Storage.GetIntRef((ImGuiID)n, 0); if (*p_int == 0) return; *p_int = 0; Size--; }
|
||||||
|
void UpdateItem(int n, bool v) { if (v) AddItem(n); else RemoveItem(n); }
|
||||||
|
int GetSize() const { return Size; }
|
||||||
|
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(); } }
|
||||||
|
|
||||||
// When using SetRange() / SelectAll() we assume that our objects ID are indices.
|
// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
|
||||||
// In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers).
|
// - Must be done in this order! Clear->SelectAll->SetRange. Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
|
||||||
// If your selection system is storing selection using object ID and you want to support Shift+Click range-selection,
|
// - Honoring RequestSetRange requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
|
||||||
// you will need a way to iterate from one item to the other item given the ID you use.
|
// - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.
|
||||||
// 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).
|
// - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform
|
||||||
// Note: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key.
|
// a lookup and have some way to iterate between two values.
|
||||||
void SetRange(int a, int b, bool v) { for (int n = a; n <= b; n++) SetSelected(n, v); }
|
// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices and
|
||||||
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.
|
// constructing a view index <> object id/ptr data structure. (FIXME-MULTISELECT: Would be worth providing a demo of doing this).
|
||||||
|
// - (Our implementation is slightly inefficient because it doesn't take advantage of the fat that ImguiStorage stores sorted key)
|
||||||
// Apply requests coming from BeginMultiSelect() and EndMultiSelect(). Must be done in this order! Clear->SelectAll->SetRange.
|
|
||||||
// Enable 'Debug Log->Selection' to see selection requests as they happen.
|
|
||||||
void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count)
|
void ApplyRequests(ImGuiMultiSelectIO* ms_io, int items_count)
|
||||||
{
|
{
|
||||||
if (ms_io->RequestClear) { Clear(); }
|
if (ms_io->RequestClear) { Clear(); }
|
||||||
if (ms_io->RequestSelectAll) { SelectAll(items_count); }
|
if (ms_io->RequestSelectAll) { Clear(); for (int n = 0; n < items_count; n++) { AddItem(n); } }
|
||||||
if (ms_io->RequestSetRange) { SetRange((int)ms_io->RangeFirstItem, (int)ms_io->RangeLastItem, ms_io->RangeSelected ? 1 : 0); }
|
if (ms_io->RequestSetRange) { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { UpdateItem(n, ms_io->RangeSelected); } }
|
||||||
}
|
|
||||||
|
|
||||||
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().
|
// Call after BeginMultiSelect().
|
||||||
|
|
@ -2827,20 +2818,20 @@ struct ExampleSelection
|
||||||
QueueDeletion = false;
|
QueueDeletion = false;
|
||||||
|
|
||||||
// If current item is not selected.
|
// If current item is not selected.
|
||||||
if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)'
|
if (ms_io->NavIdSelected == false) // Here 'NavIdSelected' should be == to 'GetSelected(ms_io->NavIdData)'
|
||||||
{
|
{
|
||||||
ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item.
|
ms_io->RangeSrcReset = true; // Request to recover RangeSrc from NavId next frame. Would be ok to reset even without the !NavIdSelected test but it would take an extra frame to recover RangeSrc when deleting a selected item.
|
||||||
return (int)ms_io->NavIdItem; // Request to land on same item after deletion.
|
return (int)ms_io->NavIdItem; // Request to land on same item after deletion.
|
||||||
}
|
}
|
||||||
|
|
||||||
// If current item is selected: land on first unselected item after RangeSrc.
|
// If current item is selected: land on first unselected item after RangeSrc.
|
||||||
for (int n = (int)ms_io->RangeSrcItem + 1; n < items.Size; n++)
|
for (int n = (int)ms_io->RangeSrcItem + 1; n < items.Size; n++)
|
||||||
if (!GetSelected(n))
|
if (!Contains(n))
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
// If current item is selected: otherwise return last unselected item.
|
// If current item is selected: otherwise return last unselected item.
|
||||||
for (int n = IM_MIN((int)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--)
|
for (int n = IM_MIN((int)ms_io->RangeSrcItem, items.Size) - 1; n >= 0; n--)
|
||||||
if (!GetSelected(n))
|
if (!Contains(n))
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -2859,12 +2850,12 @@ struct ExampleSelection
|
||||||
// This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data.
|
// This particular ExampleSelection case is designed to showcase maintaining selection-state separated from items-data.
|
||||||
IM_UNUSED(ms_io);
|
IM_UNUSED(ms_io);
|
||||||
ImVector<ITEM_TYPE> new_items;
|
ImVector<ITEM_TYPE> new_items;
|
||||||
new_items.reserve(items.Size - SelectionSize);
|
new_items.reserve(items.Size - Size);
|
||||||
int next_focus_idx_in_old_selection = (int)ms_io->RequestFocusItem;
|
int next_focus_idx_in_old_selection = (int)ms_io->RequestFocusItem;
|
||||||
int next_focus_idx_in_new_selection = -1;
|
int next_focus_idx_in_new_selection = -1;
|
||||||
for (int n = 0; n < items.Size; n++)
|
for (int n = 0; n < items.Size; n++)
|
||||||
{
|
{
|
||||||
if (!GetSelected(n))
|
if (!Contains(n))
|
||||||
new_items.push_back(items[n]);
|
new_items.push_back(items[n]);
|
||||||
if (next_focus_idx_in_old_selection == n)
|
if (next_focus_idx_in_old_selection == n)
|
||||||
next_focus_idx_in_new_selection = new_items.Size - 1;
|
next_focus_idx_in_new_selection = new_items.Size - 1;
|
||||||
|
|
@ -2874,20 +2865,19 @@ struct ExampleSelection
|
||||||
// Update selection
|
// Update selection
|
||||||
Clear();
|
Clear();
|
||||||
if (next_focus_idx_in_new_selection != -1 && ms_io->NavIdSelected)
|
if (next_focus_idx_in_new_selection != -1 && ms_io->NavIdSelected)
|
||||||
SetSelected(next_focus_idx_in_new_selection, true);
|
AddItem(next_focus_idx_in_new_selection);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ShowDemoWindowMultiSelect()
|
static void ShowDemoWindowMultiSelect()
|
||||||
{
|
{
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State");
|
IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
|
||||||
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
if (ImGui::TreeNode("Selection State & Multi-Select"))
|
||||||
if (ImGui::TreeNode("Selection State"))
|
|
||||||
{
|
{
|
||||||
HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
|
HelpMarker("Selections can be built under Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
|
||||||
|
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Single Selection");
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
|
||||||
if (ImGui::TreeNode("Single Selection"))
|
if (ImGui::TreeNode("Single-Select"))
|
||||||
{
|
{
|
||||||
static int selected = -1;
|
static int selected = -1;
|
||||||
for (int n = 0; n < 5; n++)
|
for (int n = 0; n < 5; n++)
|
||||||
|
|
@ -2901,8 +2891,9 @@ static void ShowDemoWindowMultiSelect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demonstrate implementation a most-basic form of multi-selection manually
|
// Demonstrate implementation a most-basic form of multi-selection manually
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (simplfied, manual)");
|
// This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
|
||||||
if (ImGui::TreeNode("Multiple Selection (simplified, manual)"))
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
|
||||||
|
if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)"))
|
||||||
{
|
{
|
||||||
HelpMarker("Hold CTRL and click to select multiple items.");
|
HelpMarker("Hold CTRL and click to select multiple items.");
|
||||||
static bool selection[5] = { false, false, false, false, false };
|
static bool selection[5] = { false, false, false, false, false };
|
||||||
|
|
@ -2929,9 +2920,8 @@ static void ShowDemoWindowMultiSelect()
|
||||||
|
|
||||||
// Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API.
|
// Demonstrate holding/updating multi-selection data using the BeginMultiSelect/EndMultiSelect API.
|
||||||
// SHIFT+Click w/ CTRL and other standard features are supported.
|
// SHIFT+Click w/ CTRL and other standard features are supported.
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full)");
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
|
||||||
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
if (ImGui::TreeNode("Multi-Select"))
|
||||||
if (ImGui::TreeNode("Multiple Selection (full)"))
|
|
||||||
{
|
{
|
||||||
static ExampleSelection selection;
|
static ExampleSelection selection;
|
||||||
|
|
||||||
|
|
@ -2956,7 +2946,7 @@ static void ShowDemoWindowMultiSelect()
|
||||||
{
|
{
|
||||||
char label[64];
|
char label[64];
|
||||||
sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]);
|
sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]);
|
||||||
bool item_is_selected = selection.GetSelected(n);
|
bool item_is_selected = selection.Contains(n);
|
||||||
ImGui::SetNextItemSelectionUserData(n);
|
ImGui::SetNextItemSelectionUserData(n);
|
||||||
ImGui::Selectable(label, item_is_selected);
|
ImGui::Selectable(label, item_is_selected);
|
||||||
}
|
}
|
||||||
|
|
@ -2978,8 +2968,8 @@ static void ShowDemoWindowMultiSelect()
|
||||||
// - (3) BeginXXXX process
|
// - (3) BeginXXXX process
|
||||||
// - (4) Focus process
|
// - (4) Focus process
|
||||||
// - (5) EndXXXX process
|
// - (5) EndXXXX process
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, with deletion)");
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
|
||||||
if (ImGui::TreeNode("Multiple Selection (full, with deletion)"))
|
if (ImGui::TreeNode("Multi-Select (with deletion)"))
|
||||||
{
|
{
|
||||||
// Intentionally separating items data from selection data!
|
// Intentionally separating items data from selection data!
|
||||||
// But you may decide to store selection data inside your item (aka intrusive storage).
|
// But you may decide to store selection data inside your item (aka intrusive storage).
|
||||||
|
|
@ -2999,7 +2989,7 @@ static void ShowDemoWindowMultiSelect()
|
||||||
items.push_back(items_next_id++);
|
items.push_back(items_next_id++);
|
||||||
if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
|
if (ImGui::SmallButton("Add 20 items")) { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
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
|
if (ImGui::SmallButton("Remove 20 items")) { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.RemoveItem(items.Size - 1); items.pop_back(); } } // This is to test
|
||||||
|
|
||||||
// (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
|
// (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
|
||||||
const float items_height = ImGui::GetTextLineHeightWithSpacing();
|
const float items_height = ImGui::GetTextLineHeightWithSpacing();
|
||||||
|
|
@ -3025,11 +3015,9 @@ static void ShowDemoWindowMultiSelect()
|
||||||
char label[64];
|
char label[64];
|
||||||
sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]);
|
sprintf(label, "Object %05d: %s", item_id, random_names[item_id % IM_ARRAYSIZE(random_names)]);
|
||||||
|
|
||||||
bool item_is_selected = selection.GetSelected(n);
|
bool item_is_selected = selection.Contains(n);
|
||||||
ImGui::SetNextItemSelectionUserData(n);
|
ImGui::SetNextItemSelectionUserData(n);
|
||||||
ImGui::Selectable(label, item_is_selected);
|
ImGui::Selectable(label, item_is_selected);
|
||||||
if (ImGui::IsItemToggledSelection())
|
|
||||||
selection.SetSelected(n, !item_is_selected);
|
|
||||||
if (next_focus_item_idx == n)
|
if (next_focus_item_idx == n)
|
||||||
ImGui::SetKeyboardFocusHere(-1);
|
ImGui::SetKeyboardFocusHere(-1);
|
||||||
}
|
}
|
||||||
|
|
@ -3046,8 +3034,8 @@ static void ShowDemoWindowMultiSelect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demonstrate individual selection scopes in same window
|
// Demonstrate individual selection scopes in same window
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, multiple scopes)");
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
|
||||||
if (ImGui::TreeNode("Multiple Selection (full, multiple scopes)"))
|
if (ImGui::TreeNode("Multi-Select (multiple scopes)"))
|
||||||
{
|
{
|
||||||
const int SCOPES_COUNT = 3;
|
const int SCOPES_COUNT = 3;
|
||||||
const int ITEMS_COUNT = 8; // Per scope
|
const int ITEMS_COUNT = 8; // Per scope
|
||||||
|
|
@ -3068,7 +3056,7 @@ static void ShowDemoWindowMultiSelect()
|
||||||
{
|
{
|
||||||
char label[64];
|
char label[64];
|
||||||
sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]);
|
sprintf(label, "Object %05d: %s", n, random_names[n % IM_ARRAYSIZE(random_names)]);
|
||||||
bool item_is_selected = selection->GetSelected(n);
|
bool item_is_selected = selection->Contains(n);
|
||||||
ImGui::SetNextItemSelectionUserData(n);
|
ImGui::SetNextItemSelectionUserData(n);
|
||||||
ImGui::Selectable(label, item_is_selected);
|
ImGui::Selectable(label, item_is_selected);
|
||||||
}
|
}
|
||||||
|
|
@ -3087,9 +3075,9 @@ static void ShowDemoWindowMultiSelect()
|
||||||
// - Showcase basic drag and drop.
|
// - Showcase basic drag and drop.
|
||||||
// - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
|
// - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
|
||||||
// - Showcase using inside a table.
|
// - Showcase using inside a table.
|
||||||
IMGUI_DEMO_MARKER("Widgets/Selection State/Multiple Selection (full, advanced)");
|
IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
|
||||||
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
//ImGui::SetNextItemOpen(true, ImGuiCond_Once);
|
||||||
if (ImGui::TreeNode("Multiple Selection (full, advanced)"))
|
if (ImGui::TreeNode("Multi-Select (advanced)"))
|
||||||
{
|
{
|
||||||
// Options
|
// Options
|
||||||
enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
|
enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
|
||||||
|
|
@ -3137,7 +3125,7 @@ static void ShowDemoWindowMultiSelect()
|
||||||
// FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed.
|
// FIXME-MULTISELECT: may turn into 'ms_io->RequestDelete' -> need HasSelection passed.
|
||||||
const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete));
|
const bool want_delete = selection.QueueDeletion || ((selection.GetSize() > 0) && ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete));
|
||||||
if (want_delete)
|
if (want_delete)
|
||||||
selection.ApplyDeletionPreLoop(ms_io, items);
|
ms_io->RequestFocusItem = selection.ApplyDeletionPreLoop(ms_io, items);
|
||||||
const int next_focus_item_idx = (int)ms_io->RequestFocusItem;
|
const int next_focus_item_idx = (int)ms_io->RequestFocusItem;
|
||||||
|
|
||||||
if (show_in_table)
|
if (show_in_table)
|
||||||
|
|
@ -3192,7 +3180,7 @@ static void ShowDemoWindowMultiSelect()
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool item_is_selected = selection.GetSelected(n);
|
bool item_is_selected = selection.Contains(n);
|
||||||
ImGui::SetNextItemSelectionUserData(n);
|
ImGui::SetNextItemSelectionUserData(n);
|
||||||
if (widget_type == WidgetType_Selectable)
|
if (widget_type == WidgetType_Selectable)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue