mirror of
				https://github.com/ocornut/imgui.git
				synced 2025-10-26 12:27:30 +00:00 
			
		
		
		
	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:
		| @@ -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) | ||||
|   | ||||
							
								
								
									
										182
									
								
								imgui_demo.cpp
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								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) | ||||
|             ImGui::TableNextRow(); | ||||
|             ImGui::TableSetColumnIndex(0); | ||||
|             ImGui::AlignTextToFramePadding(); | ||||
|             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) | ||||
|             { | ||||
|                 ShowPlaceholderObject("Child", 424242); | ||||
|             case ImGuiDataType_Bool: | ||||
|             { | ||||
|                 IM_ASSERT(field_desc.DataCount == 1); | ||||
|                 ImGui::Checkbox("##Editor", (bool*)field_ptr); | ||||
|                 break; | ||||
|             } | ||||
|             else | ||||
|             case ImGuiDataType_S32: | ||||
|             { | ||||
|                 // 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::TableSetColumnIndex(1); | ||||
|                 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(); | ||||
|         } | ||||
|         ImGui::TreePop(); | ||||
|     } | ||||
|     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(); | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 ocornut
					ocornut