1
0
mirror of https://github.com/ocornut/imgui.git synced 2025-01-19 01:34:08 +01:00

MultiSelect: ImGuiSelectionBasicStorage: added PreserveOrder, maintain implicit order data in storage.

Little tested but provided for completeness.
This commit is contained in:
ocornut 2024-06-26 18:41:21 +02:00
parent df664329cb
commit c52346850d
2 changed files with 31 additions and 11 deletions

View File

@ -2835,9 +2835,11 @@ struct ImGuiSelectionRequest
struct ImGuiSelectionBasicStorage struct ImGuiSelectionBasicStorage
{ {
// Members // Members
int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper. int Size; // // Number of selected items, maintained by this helper.
void* UserData; // User data for use by adapter function // e.g. selection.UserData = (void*)my_items; bool PreserveOrder; // = false // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved)
ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; }; void* UserData; // = NULL // User data for use by adapter function // e.g. selection.UserData = (void*)my_items;
ImGuiID (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx); // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; };
int _SelectionOrder;// [Internal] Increasing counter to store selection order
ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem(). ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().
// Methods // Methods

View File

@ -7824,19 +7824,23 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage() ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage()
{ {
Size = 0; Size = 0;
PreserveOrder = false;
UserData = NULL; UserData = NULL;
AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; };
_SelectionOrder = 1; // Always >0
} }
void ImGuiSelectionBasicStorage::Clear() void ImGuiSelectionBasicStorage::Clear()
{ {
Size = 0; Size = 0;
_SelectionOrder = 1; // Always >0
_Storage.Data.resize(0); _Storage.Data.resize(0);
} }
void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r) void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r)
{ {
ImSwap(Size, r.Size); ImSwap(Size, r.Size);
ImSwap(_SelectionOrder, r._SelectionOrder);
_Storage.Data.swap(r._Storage.Data); _Storage.Data.swap(r._Storage.Data);
} }
@ -7845,12 +7849,21 @@ bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const
return _Storage.GetInt(id, 0) != 0; return _Storage.GetInt(id, 0) != 0;
} }
static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs)
{
int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i;
int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i;
return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
}
// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user. // GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user.
// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...) // (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...)
bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id) bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id)
{ {
ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it; ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it;
ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size; ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size;
if (PreserveOrder && it == NULL && it_end != NULL)
ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt()
if (it == NULL) if (it == NULL)
it = _Storage.Data.Data; it = _Storage.Data.Data;
IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); IM_ASSERT(it >= _Storage.Data.Data && it <= it_end);
@ -7860,18 +7873,20 @@ bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID*
const bool has_more = (it != it_end); const bool has_more = (it != it_end);
*opaque_it = has_more ? (void**)(it + 1) : (void**)(it); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it);
*out_id = has_more ? it->key : 0; *out_id = has_more ? it->key : 0;
if (PreserveOrder && !has_more)
_Storage.BuildSortByKey();
return has_more; return has_more;
} }
void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected) void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected)
{ {
int* p_int = _Storage.GetIntRef(id, 0); int* p_int = _Storage.GetIntRef(id, 0);
if (selected && *p_int == 0) { *p_int = 1; Size++; } if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; }
else if (!selected && *p_int != 0) { *p_int = 0; Size--; } else if (!selected && *p_int != 0) { *p_int = 0; Size--; }
} }
// Optimized for batch edits (with same value of 'selected') // Optimized for batch edits (with same value of 'selected')
static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends) static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order)
{ {
ImGuiStorage* storage = &selection->_Storage; ImGuiStorage* storage = &selection->_Storage;
ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id); ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id);
@ -7879,9 +7894,9 @@ static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicS
if (selected == is_contained && it->val_i != 0) if (selected == is_contained && it->val_i != 0)
return; return;
if (selected && !is_contained) if (selected && !is_contained)
storage->Data.push_back(ImGuiStoragePair(id, 1)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish() storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish()
else if (is_contained) else if (is_contained)
it->val_i = selected ? 1 : 0; // Modify in-place. it->val_i = selected ? selection_order : 0; // Modify in-place.
selection->Size += selected ? +1 : -1; selection->Size += selected ? +1 : -1;
} }
@ -7929,16 +7944,19 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
{ {
_Storage.Data.reserve(ms_io->ItemsCount); _Storage.Data.reserve(ms_io->ItemsCount);
const int size_before_amends = _Storage.Data.Size; const int size_before_amends = _Storage.Data.Size;
for (int idx = 0; idx < ms_io->ItemsCount; idx++) for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++)
ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder);
ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);
} }
} }
else if (req.Type == ImGuiSelectionRequestType_SetRange) else if (req.Type == ImGuiSelectionRequestType_SetRange)
{ {
// Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1
const int size_before_amends = _Storage.Data.Size; const int size_before_amends = _Storage.Data.Size;
for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++) int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? (int)req.RangeLastItem - (int)req.RangeFirstItem : 0);
ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends); for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection)
ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order);
_SelectionOrder += (int)req.RangeLastItem - (int)req.RangeFirstItem + 1;
ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends); ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);
} }
} }