Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and struct description data that a real application would want to use.
This commit is contained in:
parent
bc9e5b62b6
commit
46691d172e
@ -62,6 +62,8 @@ Other changes:
|
||||
Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and
|
||||
other configurable locations. Always call IMGUI_DEBUG_LOG() for maximum stripping in caller code.
|
||||
- Debug Tools: Debug Log: Added "Configure Outputs.." button. (#5855)
|
||||
- Demo: Reworked "Property Editor" demo in a manner that more ressemble the tree data and
|
||||
struct description data that a real application would want to use.
|
||||
- Backends: Win32: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN.
|
||||
(#7768, #4858, #2622) [@Aemony]
|
||||
- Backends: SDL3: Update for API changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762)
|
||||
|
180
imgui_demo.cpp
180
imgui_demo.cpp
@ -7749,53 +7749,162 @@ static void ShowExampleAppLayout(bool* p_open)
|
||||
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ShowPlaceholderObject(const char* prefix, int uid)
|
||||
// Simple representation for a tree
|
||||
// (this is designed to be simple to understand for our demos, not to be efficient etc.)
|
||||
struct ExampleTreeNode
|
||||
{
|
||||
// Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.
|
||||
ImGui::PushID(uid);
|
||||
char Name[28];
|
||||
ImGuiID UID = 0;
|
||||
ExampleTreeNode* Parent = NULL;
|
||||
ImVector<ExampleTreeNode*> Childs;
|
||||
|
||||
// Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high.
|
||||
// Data
|
||||
bool HasData = false; // All leaves have data
|
||||
bool DataIsEnabled = false;
|
||||
int DataInt = 128;
|
||||
ImVec2 DataVec2 = ImVec2(0.0f, 3.141592f);
|
||||
};
|
||||
|
||||
// Simple representation of struct metadata/serialization data.
|
||||
// (this is a minimal version of what a typical advanced application may provide)
|
||||
struct ExampleMemberInfo
|
||||
{
|
||||
const char* Name;
|
||||
ImGuiDataType DataType;
|
||||
int DataCount;
|
||||
int Offset;
|
||||
};
|
||||
|
||||
// Metadata description of ExampleTreeNode struct.
|
||||
static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
|
||||
{
|
||||
{ "Enabled", ImGuiDataType_Bool, 1, offsetof(ExampleTreeNode, DataIsEnabled) },
|
||||
{ "MyInt", ImGuiDataType_S32, 1, offsetof(ExampleTreeNode, DataInt) },
|
||||
{ "MyVec2", ImGuiDataType_Float, 2, offsetof(ExampleTreeNode, DataVec2) },
|
||||
};
|
||||
|
||||
static ExampleTreeNode* ExampleTree_CreateNode(const char* name, const ImGuiID uid, ExampleTreeNode* parent)
|
||||
{
|
||||
ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
|
||||
snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name);
|
||||
node->UID = uid;
|
||||
node->Parent = parent;
|
||||
if (parent)
|
||||
parent->Childs.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
static ExampleTreeNode* ExampleTree_CreateDemoTree()
|
||||
{
|
||||
static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
|
||||
char name_buf[32];
|
||||
ImGuiID uid = 0;
|
||||
ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
|
||||
for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * 2; idx_L0++)
|
||||
{
|
||||
snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / 2], idx_L0 % 2);
|
||||
ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
|
||||
const int number_of_childs = (int)strlen(node_L1->Name);
|
||||
for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
|
||||
{
|
||||
snprintf(name_buf, 32, "Child %d", idx_L1);
|
||||
ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);
|
||||
node_L2->HasData = true;
|
||||
if (idx_L1 == 0)
|
||||
{
|
||||
snprintf(name_buf, 32, "Sub-child %d", 0);
|
||||
ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);
|
||||
node_L3->HasData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return node_L0;
|
||||
}
|
||||
|
||||
static void PropertyEditor_ShowTreeNode(ExampleTreeNode* node)
|
||||
{
|
||||
// Object tree node
|
||||
ImGui::PushID((int)node->UID);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
bool node_open = ImGui::TreeNode("##Object", "%s_%u", prefix, uid);
|
||||
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
|
||||
tree_flags |= ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_AllowOverlap; // Highlight whole row for visibility
|
||||
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
|
||||
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
|
||||
bool node_open = ImGui::TreeNodeEx("##Object", tree_flags, "%s", node->Name);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("my sailor is rich");
|
||||
ImGui::TextDisabled("UID: 0x%08X", node->UID);
|
||||
|
||||
// Display child and data
|
||||
if (node_open)
|
||||
for (ExampleTreeNode* child : node->Childs)
|
||||
PropertyEditor_ShowTreeNode(child);
|
||||
if (node_open && node->HasData)
|
||||
{
|
||||
static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f };
|
||||
for (int i = 0; i < 8; i++)
|
||||
// In a typical application, the structure description would be derived from a data-driven system.
|
||||
// - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
|
||||
// - Limits and some details are hard-coded to simplify the demo.
|
||||
// - Text and Selectable are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the selectable lines equal high.
|
||||
for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
|
||||
{
|
||||
ImGui::PushID(i); // Use field index as identifier.
|
||||
if (i < 2)
|
||||
{
|
||||
ShowPlaceholderObject("Child", 424242);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well)
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet;
|
||||
ImGui::TreeNodeEx("Field", flags, "Field_%d", i);
|
||||
|
||||
ImGui::PushTabStop(false); // We didn't expose ImGuiItemFlags_NoNav yet, so arrow navigation will still pass through this
|
||||
ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
|
||||
ImGui::PopTabStop();
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::PushID(field_desc.Name);
|
||||
void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
|
||||
switch (field_desc.DataType)
|
||||
{
|
||||
case ImGuiDataType_Bool:
|
||||
{
|
||||
IM_ASSERT(field_desc.DataCount == 1);
|
||||
ImGui::Checkbox("##Editor", (bool*)field_ptr);
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32:
|
||||
{
|
||||
int v_min = INT_MIN, v_max = INT_MAX;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (i >= 5)
|
||||
ImGui::InputFloat("##value", &placeholder_members[i], 1.0f);
|
||||
else
|
||||
ImGui::DragFloat("##value", &placeholder_members[i], 0.01f);
|
||||
ImGui::NextColumn();
|
||||
ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max);
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float:
|
||||
{
|
||||
float v_min = 0.0f, v_max = 1.0f;
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
if (node_open)
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
static void PropertyEditor_ShowTree(ExampleTreeNode* root_node)
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
|
||||
ImGuiTableFlags table_flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg;
|
||||
if (ImGui::BeginTable("##split", 2, table_flags))
|
||||
{
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
|
||||
ImGui::TableHeadersRow();
|
||||
for (ExampleTreeNode* node : root_node->Childs)
|
||||
PropertyEditor_ShowTreeNode(node);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
// Demonstrate create a simple property editor.
|
||||
// This demo is a bit lackluster nowadays, would be nice to improve.
|
||||
static void ShowExampleAppPropertyEditor(bool* p_open)
|
||||
@ -7808,25 +7917,12 @@ static void ShowExampleAppPropertyEditor(bool* p_open)
|
||||
}
|
||||
|
||||
IMGUI_DEMO_MARKER("Examples/Property Editor");
|
||||
HelpMarker(
|
||||
"This example shows how you may implement a property editor using two columns.\n"
|
||||
"All objects/fields data are dummies here.\n");
|
||||
static ExampleTreeNode* tree_data = NULL;
|
||||
if (tree_data == NULL)
|
||||
tree_data = ExampleTree_CreateDemoTree();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
|
||||
if (ImGui::BeginTable("##split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
|
||||
{
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Object");
|
||||
ImGui::TableSetupColumn("Contents");
|
||||
ImGui::TableHeadersRow();
|
||||
PropertyEditor_ShowTree(tree_data);
|
||||
|
||||
// Iterate placeholder objects (all the same data)
|
||||
for (int obj_i = 0; obj_i < 4; obj_i++)
|
||||
ShowPlaceholderObject("Object", obj_i);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user