// 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.
// - 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.
// - (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.
// - (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.
// However it is perfectly fine to honor all steps even if you don't use a clipper.
// 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.
// 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.
structImGuiMultiSelectIO
{
// - Always process requests in this order: Clear, SelectAll, SetRange. Use '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)
// - 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 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.
// REQUESTS --------------------------------// BEGIN / LOOP / END
// - 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 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.
// are generally appropriate. Even a large array of bool might work for you... This is what we are doing here.
// - Use intrusively stored selection (e.g. 'bool IsSelected' inside your object).
// - 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.
structExampleSelection
{
// Data
ImGuiStorageStorage;// Selection set
intSelectionSize;// 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?
intSize;// 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?
// When using SetRange() / SelectAll() we assume that our objects ID are indices.
// In this demo we always store selection using indices and never in another manner (e.g. object ID or pointers).
// If your selection system is storing selection using object ID and you want to support Shift+Click range-selection,
// you will need a way to iterate from one item to the other item 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).
// Note: This implementation of SetRange() is inefficient because it doesn't take advantage of the fact that ImGuiStorage stores sorted key.
voidSelectAll(intcount){Storage.Data.resize(count);for(intidx=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! Clear->SelectAll->SetRange.
// Enable 'Debug Log->Selection' to see selection requests as they happen.
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.
return(int)ms_io->NavIdItem;// Request to land on same item after deletion.
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.
}
// If current item is selected: land on first unselected item after RangeSrc.
if(ImGui::SmallButton("Remove 20 items")){for(intn=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(intn=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