mirror of
https://github.com/shadps4-emu/ext-imgui.git
synced 2024-11-23 10:19:55 +00:00
MultiSelect: ImGuiSelectionBasicStorage: added GetNextSelectedItem() to abstract selection storage from user. Amend Assets Browser demo to handle drag and drop correctly.
This commit is contained in:
parent
c3d7aa252b
commit
e1fd25051e
34
imgui.h
34
imgui.h
@ -2817,35 +2817,39 @@ struct ImGuiSelectionRequest
|
||||
|
||||
// Optional helper to store multi-selection state + apply multi-selection requests.
|
||||
// - Used by our demos and provided as a convenience to easily implement basic multi-selection.
|
||||
// - Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }'
|
||||
// Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate.
|
||||
// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API.
|
||||
// To store a multi-selection, in your application you could:
|
||||
// - A) Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set<ImGuiID> replacement.
|
||||
// - B) Use your own external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, interval trees, etc.
|
||||
// - C) Use intrusively stored selection (e.g. 'bool IsSelected' inside objects). Cannot have multiple views over same objects.
|
||||
// - Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set<ImGuiID> replacement.
|
||||
// - Use your own external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, interval trees, intrusively stored selection etc.
|
||||
// In ImGuiSelectionBasicStorage we:
|
||||
// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO)
|
||||
// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index.
|
||||
// - use decently optimized logic to allow queries and insertion of very large selection sets.
|
||||
// - do not preserve selection order.
|
||||
// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection.
|
||||
// Large applications are likely to eventually want to get rid of this indirection layer and do their own thing.
|
||||
// See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper.
|
||||
struct ImGuiSelectionBasicStorage
|
||||
{
|
||||
// Members
|
||||
ImGuiStorage Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>
|
||||
ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().
|
||||
int Size; // Number of selected items (== number of 1 in the Storage), maintained by this helper.
|
||||
void* UserData; // 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; };
|
||||
|
||||
// Methods: apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. Uses 'items_count' passed to BeginMultiSelect()
|
||||
IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io);
|
||||
// Methods
|
||||
IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io);// Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect()
|
||||
IMGUI_API ImGuiID GetNextSelectedItem(void** opaque_it); // Iterate selection with 'void* it = NULL; while (ImGuiId id = selection.GetNextSelectedItem(&it)) { ... }'
|
||||
bool Contains(ImGuiID id) const { return _Storage.GetInt(id, 0) != 0; } // Query if an item id is in selection.
|
||||
ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); } // Convert index to item id based on provided adapter.
|
||||
|
||||
// Methods: selection storage
|
||||
ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; }
|
||||
void Clear() { Storage.Data.resize(0); Size = 0; }
|
||||
void Swap(ImGuiSelectionBasicStorage& r) { Storage.Data.swap(r.Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; }
|
||||
bool Contains(ImGuiID id) const { return Storage.GetInt(id, 0) != 0; }
|
||||
void SetItemSelected(ImGuiID id, bool v) { int* p_int = Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } }
|
||||
ImGuiID GetStorageIdFromIndex(int idx) { return AdapterIndexToStorageId(this, idx); }
|
||||
// [Internal, rarely called directly by end-user]
|
||||
ImGuiSelectionBasicStorage() { Size = 0; UserData = NULL; AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; }; }
|
||||
void Clear() { _Storage.Data.resize(0); Size = 0; }
|
||||
void Swap(ImGuiSelectionBasicStorage& r) { _Storage.Data.swap(r._Storage.Data); int lhs_size = Size; Size = r.Size; r.Size = lhs_size; }
|
||||
void SetItemSelected(ImGuiID id, bool v) { int* p_int = _Storage.GetIntRef(id, 0); if (v && *p_int == 0) { *p_int = 1; Size++; } else if (!v && *p_int != 0) { *p_int = 0; Size--; } }
|
||||
};
|
||||
|
||||
// Optional helper to apply multi-selection requests to existing randomly accessible storage.
|
||||
@ -2857,8 +2861,8 @@ struct ImGuiSelectionExternalStorage
|
||||
void (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; }
|
||||
|
||||
// Methods
|
||||
ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; }
|
||||
IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected()
|
||||
ImGuiSelectionExternalStorage() { UserData = NULL; AdapterSetItemSelected = NULL; }
|
||||
IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Generic function, using AdapterSetItemSelected()
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -3441,24 +3441,24 @@ static void ShowDemoWindowMultiSelect()
|
||||
// Drag and Drop
|
||||
if (use_drag_drop && ImGui::BeginDragDropSource())
|
||||
{
|
||||
// Consider payload to be full selection OR single unselected item.
|
||||
// Create payload with full selection OR single unselected item.
|
||||
// (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
|
||||
if (ImGui::GetDragDropPayload() == NULL)
|
||||
{
|
||||
ImVector<int> payload_items;
|
||||
void* it = NULL;
|
||||
if (!item_is_selected)
|
||||
payload_items.push_back(item_id);
|
||||
else
|
||||
for (const ImGuiStoragePair& pair : selection.Storage.Data)
|
||||
if (pair.val_i)
|
||||
payload_items.push_back((int)pair.key);
|
||||
while (int id = (int)selection.GetNextSelectedItem(&it))
|
||||
payload_items.push_back(id);
|
||||
ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
|
||||
}
|
||||
|
||||
// Display payload content in tooltip
|
||||
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
|
||||
const int* payload_items = (int*)payload->Data;
|
||||
const int payload_count = (int)payload->DataSize / (int)sizeof(payload_items[0]);
|
||||
const int payload_count = (int)payload->DataSize / (int)sizeof(int);
|
||||
if (payload_count == 1)
|
||||
ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
|
||||
else
|
||||
@ -9896,13 +9896,26 @@ struct ExampleAssetsBrowser
|
||||
// Drag and drop
|
||||
if (ImGui::BeginDragDropSource())
|
||||
{
|
||||
// Consider payload to be full selection OR single unselected item
|
||||
// Create payload with full selection OR single unselected item.
|
||||
// (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
|
||||
int payload_size = item_is_selected ? Selection.Size : 1;
|
||||
if (ImGui::GetDragDropPayload() == NULL)
|
||||
ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", "Dummy", 5); // Dummy payload
|
||||
{
|
||||
ImVector<ImGuiID> payload_items;
|
||||
void* it = NULL;
|
||||
if (!item_is_selected)
|
||||
payload_items.push_back(item_data->ID);
|
||||
else
|
||||
while (ImGuiID id = Selection.GetNextSelectedItem(&it))
|
||||
payload_items.push_back(id);
|
||||
ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
|
||||
}
|
||||
|
||||
// Display payload content in tooltip, by extracting it from the payload data
|
||||
// (we could read from selection, but it is more correct and reusable to read from payload)
|
||||
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
|
||||
const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
|
||||
ImGui::Text("%d assets", payload_count);
|
||||
|
||||
ImGui::Text("%d assets", payload_size);
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
|
@ -7821,6 +7821,23 @@ void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
|
||||
// - ImGuiSelectionExternalStorage
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// 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...)
|
||||
ImGuiID ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it)
|
||||
{
|
||||
ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it;
|
||||
ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size;
|
||||
if (it == NULL)
|
||||
it = _Storage.Data.Data;
|
||||
IM_ASSERT(it >= _Storage.Data.Data && it <= it_end);
|
||||
if (it != it_end)
|
||||
while (it->val_i == 0 && it < it_end)
|
||||
it++;
|
||||
const bool has_more = (it != it_end);
|
||||
*opaque_it = has_more ? (void**)(it + 1) : (void**)(it);
|
||||
return has_more ? it->key : 0;
|
||||
}
|
||||
|
||||
// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
|
||||
// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
|
||||
// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
|
||||
@ -7852,7 +7869,7 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
|
||||
Clear();
|
||||
if (req.Selected)
|
||||
{
|
||||
Storage.Data.reserve(ms_io->ItemsCount);
|
||||
_Storage.Data.reserve(ms_io->ItemsCount);
|
||||
for (int idx = 0; idx < ms_io->ItemsCount; idx++)
|
||||
SetItemSelected(GetStorageIdFromIndex(idx), true);
|
||||
}
|
||||
@ -7863,6 +7880,8 @@ void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
|
||||
// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage
|
||||
// This makes no assumption about underlying storage.
|
||||
|
Loading…
Reference in New Issue
Block a user