mirror of
https://github.com/ocornut/imgui.git
synced 2026-01-09 23:54:20 +00:00
Demo: rework Property Editor.
This commit is contained in:
parent
fd994943c2
commit
c3dca77a19
4 changed files with 102 additions and 76 deletions
165
imgui_demo.cpp
165
imgui_demo.cpp
|
|
@ -269,7 +269,7 @@ struct ExampleTreeNode
|
|||
|
||||
// Leaf Data
|
||||
bool HasData = false; // All leaves have data
|
||||
bool DataMyBool = false;
|
||||
bool DataMyBool = true;
|
||||
int DataMyInt = 128;
|
||||
ImVec2 DataMyVec2 = ImVec2(0.0f, 3.141592f);
|
||||
};
|
||||
|
|
@ -304,6 +304,7 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl
|
|||
}
|
||||
|
||||
// Create example tree data
|
||||
// (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
|
||||
static ExampleTreeNode* ExampleTree_CreateDemoTree()
|
||||
{
|
||||
static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
|
||||
|
|
@ -8600,101 +8601,121 @@ static void ShowExampleAppLayout(bool* p_open)
|
|||
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Some of the interactions are a bit lack-luster:
|
||||
// - We would want the table scrolling window to use NavFlattened.
|
||||
// - We would want pressing validating or leaving the filter to somehow restore focus.
|
||||
// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
|
||||
// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ExampleAppPropertyEditor
|
||||
{
|
||||
ImGuiTextFilter Filter;
|
||||
ExampleTreeNode* VisibleNode = NULL;
|
||||
|
||||
void Draw(ExampleTreeNode* root_node)
|
||||
{
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
|
||||
if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
|
||||
Filter.Build();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg;
|
||||
if (ImGui::BeginTable("##split", 2, table_flags))
|
||||
// Left side: draw tree
|
||||
// - Currently using a table to benefit from RowBg feature
|
||||
// - Using ImGuiChildFlags_NavFlattened exhibit a bug where clicking on root window decoration sets focus to root window
|
||||
if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border))
|
||||
{
|
||||
ImGui::TableSetupColumn("Object", ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableSetupColumn("Contents", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
|
||||
//ImGui::TableSetupScrollFreeze(0, 1);
|
||||
//ImGui::TableHeadersRow();
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
|
||||
if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
|
||||
Filter.Build();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
for (ExampleTreeNode* node : root_node->Childs)
|
||||
if (Filter.PassFilter(node->Name)) // Filter root node
|
||||
DrawTreeNode(node);
|
||||
ImGui::EndTable();
|
||||
if (ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg))
|
||||
{
|
||||
for (ExampleTreeNode* node : root_node->Childs)
|
||||
if (Filter.PassFilter(node->Name)) // Filter root node
|
||||
DrawTreeNode(node);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// Right side: draw properties
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup(); // Lock X position
|
||||
if (ExampleTreeNode* node = VisibleNode)
|
||||
{
|
||||
ImGui::Text("%s", node->Name);
|
||||
ImGui::TextDisabled("UID: 0x%08X", node->UID);
|
||||
ImGui::Separator();
|
||||
if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
|
||||
{
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
|
||||
|
||||
if (node && node->HasData)
|
||||
{
|
||||
// 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.
|
||||
for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(field_desc.Name);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(field_desc.Name);
|
||||
ImGui::TableNextColumn();
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void DrawTreeNode(ExampleTreeNode* node)
|
||||
{
|
||||
// Object tree node
|
||||
ImGui::PushID(node->UID);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(node->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::TextDisabled("UID: 0x%08X", node->UID);
|
||||
|
||||
// Display child and data
|
||||
if (node == VisibleNode)
|
||||
tree_flags |= ImGuiTreeNodeFlags_Selected;
|
||||
if (node->Childs.Size == 0)
|
||||
tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
|
||||
if (node->DataMyBool == false)
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
|
||||
bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name);
|
||||
if (node->DataMyBool == false)
|
||||
ImGui::PopStyleColor();
|
||||
if (ImGui::IsItemFocused())
|
||||
VisibleNode = node;
|
||||
if (node_open)
|
||||
for (ExampleTreeNode* child : node->Childs)
|
||||
DrawTreeNode(child);
|
||||
if (node_open && node->HasData)
|
||||
{
|
||||
// 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::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop | ImGuiItemFlags_NoNav, true);
|
||||
ImGui::Selectable(field_desc.Name, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
|
||||
ImGui::PopItemFlag();
|
||||
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);
|
||||
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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue