Demo: Property Editor: misc tweaks to reduce noise in upcoming change. Allow node to have more than 65K child.

This commit is contained in:
ocornut
2026-02-23 18:40:28 +01:00
parent f6e0953b38
commit 46f0e2e247
+30 -15
View File
@@ -740,7 +740,7 @@ struct ExampleTreeNode
int UID = 0; int UID = 0;
ExampleTreeNode* Parent = NULL; ExampleTreeNode* Parent = NULL;
ImVector<ExampleTreeNode*> Childs; ImVector<ExampleTreeNode*> Childs;
unsigned short IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily int IndexInParent = 0; // Maintaining this allows us to implement linear traversal more easily
// Leaf Data // Leaf Data
bool HasData = false; // All leaves have data bool HasData = false; // All leaves have data
@@ -774,7 +774,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl
snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name); snprintf(node->Name, IM_COUNTOF(node->Name), "%s", name);
node->UID = uid; node->UID = uid;
node->Parent = parent; node->Parent = parent;
node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0; node->IndexInParent = parent ? parent->Childs.Size : 0;
if (parent) if (parent)
parent->Childs.push_back(node); parent->Childs.push_back(node);
return node; return node;
@@ -788,18 +788,25 @@ static void ExampleTree_DestroyNode(ExampleTreeNode* node)
} }
// Create example tree data // Create example tree data
// (this allocates _many_ more times than most other code in all of Dear ImGui or others demo) // (warning: this can allocates MANY MANY more times than other code in all of Dear ImGui + demo combined)
// (a real application managing one million nodes would likely store its tree data differently)
static ExampleTreeNode* ExampleTree_CreateDemoTree() static ExampleTreeNode* ExampleTree_CreateDemoTree()
{ {
static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; // 20 root nodes -> 211 total nodes, ~261 allocs.
// 1000 root nodes -> ~11K total nodes, ~14K allocs.
// 10000 root nodes -> ~123K total nodes, ~154K allocs.
// 100000 root nodes -> ~1338K total nodes, ~1666K allocs.
const int ROOT_ITEMS_COUNT = 20;
static const char* category_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
const int category_count = IM_COUNTOF(category_names);
const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name); const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
char name_buf[NAME_MAX_LEN]; char name_buf[NAME_MAX_LEN];
int uid = 0; int uid = 0;
ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL); ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
const int root_items_multiplier = 2; for (int idx_L0 = 0; idx_L0 < ROOT_ITEMS_COUNT; idx_L0++)
for (int idx_L0 = 0; idx_L0 < IM_COUNTOF(root_names) * root_items_multiplier; idx_L0++)
{ {
snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); snprintf(name_buf, IM_COUNTOF(name_buf), "%s %d", category_names[idx_L0 / (ROOT_ITEMS_COUNT / category_count)], idx_L0 % (ROOT_ITEMS_COUNT / category_count));
ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
const int number_of_childs = (int)strlen(node_L1->Name); const int number_of_childs = (int)strlen(node_L1->Name);
for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
@@ -9449,18 +9456,16 @@ struct ExampleAppPropertyEditor
// - Currently using a table to benefit from RowBg feature // - Currently using a table to benefit from RowBg feature
if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened)) if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
{ {
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
Filter.Build(); Filter.Build();
ImGui::PopItemFlag(); ImGui::PopItemFlag();
if (ImGui::BeginTable("##list", 1, ImGuiTableFlags_RowBg)) if (ImGui::BeginTable("##list", 1, ImGuiTableFlags_RowBg))
{ {
for (ExampleTreeNode* node : root_node->Childs) DrawTree(root_node);
if (Filter.PassFilter(node->Name)) // Filter root node
DrawTreeNode(node);
ImGui::EndTable(); ImGui::EndTable();
} }
} }
@@ -9533,28 +9538,38 @@ struct ExampleAppPropertyEditor
ImGui::EndGroup(); ImGui::EndGroup();
} }
// Custom search filter
// - Here we apply on root node only.
// - This does a stristr which is pretty heavy. In a real large-scale app you would likely store a filtered list which in turns would be trivial to linearize.
void DrawTree(ExampleTreeNode* node)
{
for (ExampleTreeNode* child : node->Childs)
if (Filter.PassFilter(child->Name)) // Filter root node
DrawTreeNode(child);
}
void DrawTreeNode(ExampleTreeNode* node) void DrawTreeNode(ExampleTreeNode* node)
{ {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::PushID(node->UID); ImGui::PushID(node->UID);
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None; ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Left arrow support
tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
if (node == SelectedNode) if (node == SelectedNode)
tree_flags |= ImGuiTreeNodeFlags_Selected; tree_flags |= ImGuiTreeNodeFlags_Selected; // Draw selection highlight
if (node->Childs.Size == 0) if (node->Childs.Size == 0)
tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet; tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
if (node->DataMyBool == false) if (node->DataMyBool == false)
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name); bool is_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name);
if (node->DataMyBool == false) if (node->DataMyBool == false)
ImGui::PopStyleColor(); ImGui::PopStyleColor();
if (ImGui::IsItemFocused()) if (ImGui::IsItemFocused())
SelectedNode = node; SelectedNode = node;
if (node_open) if (is_open)
{ {
for (ExampleTreeNode* child : node->Childs) for (ExampleTreeNode* child : node->Childs)
DrawTreeNode(child); DrawTreeNode(child);