@ -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 ;
// 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 ;
}
// Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high.
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
cas e ImGuiDataTyp e_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 ( ) ;
// Iterate placeholder objects (all the same data)
for ( int obj_i = 0 ; obj_i < 4 ; obj_i + + )
ShowPlaceholderObject ( " Object " , obj_i ) ;
PropertyEditor_ShowTree ( tree_data ) ;
ImGui : : EndTable ( ) ;
}
ImGui : : PopStyleVar ( ) ;
ImGui : : End ( ) ;
}