Skip to content

Commit e5b218e

Browse files
committed
TreeNode: added ImGuiTreeNodeFlags_DrawTreeXXX flags. (#2920)
# Conflicts: # docs/CHANGELOG.txt
1 parent faa0303 commit e5b218e

8 files changed

+154
-17
lines changed

docs/CHANGELOG.txt

+12
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ Other changes:
5555
- Windows: loosened code to allow hovering of resize grips, borders, and table
5656
borders while hovering a sibling child window, so that the code in master matches
5757
one in docking (they accidentally diverged). (#8554)
58+
- TreeNode: added flags to draw tree hierarchy outlines linking parent
59+
and tree nodes: (#2920)
60+
- ImGuiTreeNodeFlags_DrawLinesNone: No lines drawn.
61+
- ImGuiTreeNodeFlags_DrawLinesFull: Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents.
62+
- ImGuiTreeNodeFlags_DrawLinesToNodes: Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node.
63+
- Added style.TreeLinesFlags which stores the default setting,
64+
which may be overriden in individual TreeNode() calls.
65+
- Added style.TreeLinesSize (default to 1.0f).
66+
- Added ImGuiCol_TreeLines (in default style this is the same as ImGuiCol_Border).
67+
The color for a given hierarchy level is latched in TreeNode(),
68+
allowing advanced tree drawing code to potentially alter it.
69+
- The feature adds a little cost as extra data needs to be stored.
5870
- Nav: fixed assertion when holding gamepad FaceLeft/West button to open
5971
CTRL+Tab windowing + pressing a keyboard key. (#8525)
6072
- Error Handling: added better error report and recovery for extraneous

imgui.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,8 @@ ImGuiStyle::ImGuiStyle()
13601360
TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar.
13611361
TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
13621362
TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1363+
TreeLinesFlags = ImGuiTreeNodeFlags_DrawLinesNone;
1364+
TreeLinesSize = 1.0f; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
13631365
ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
13641366
ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
13651367
SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
@@ -3415,6 +3417,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] =
34153417
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) }, // ImGuiStyleVar_TabBarOverlineSize
34163418
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)}, // ImGuiStyleVar_TableAngledHeadersAngle
34173419
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3420+
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize
34183421
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign
34193422
{ 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign
34203423
{ 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize
@@ -3563,6 +3566,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
35633566
case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
35643567
case ImGuiCol_TextLink: return "TextLink";
35653568
case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3569+
case ImGuiCol_TreeLines: return "TreeLines";
35663570
case ImGuiCol_DragDropTarget: return "DragDropTarget";
35673571
case ImGuiCol_NavCursor: return "NavCursor";
35683572
case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
@@ -10243,6 +10247,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
1024310247
IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f && "Invalid style setting!"); // Required otherwise cannot resize from borders.
1024410248
IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
1024510249
IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
10250+
IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);
1024610251

1024710252
// Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
1024810253
if (g.IO.ConfigErrorRecovery)
@@ -12794,7 +12799,7 @@ void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
1279412799
}
1279512800

1279612801
// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere
12797-
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiTreeNodeStackData* tree_node_data)
12802+
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data)
1279812803
{
1279912804
ImGuiContext& g = *GImGui;
1280012805
g.NavMoveScoringItems = false;

imgui.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,12 @@ enum ImGuiTreeNodeFlags_
12141214
ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 17, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop)
12151215
ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,
12161216

1217+
// [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920.
1218+
// Default value is pulled from style.TreeLinesFlags. May be overridden in TreeNode calls.
1219+
ImGuiTreeNodeFlags_DrawLinesNone = 1 << 18, // No lines drawn
1220+
ImGuiTreeNodeFlags_DrawLinesFull = 1 << 19, // Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents.
1221+
ImGuiTreeNodeFlags_DrawLinesToNodes = 1 << 20, // Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. A little bit slower.
1222+
12171223
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
12181224
ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7
12191225
ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth,// Renamed in 1.90.7
@@ -1666,7 +1672,8 @@ enum ImGuiCol_
16661672
ImGuiCol_TableRowBg, // Table row background (even rows)
16671673
ImGuiCol_TableRowBgAlt, // Table row background (odd rows)
16681674
ImGuiCol_TextLink, // Hyperlink color
1669-
ImGuiCol_TextSelectedBg,
1675+
ImGuiCol_TextSelectedBg, // Selected text inside an InputText
1676+
ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines
16701677
ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target
16711678
ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible
16721679
ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
@@ -1722,6 +1729,7 @@ enum ImGuiStyleVar_
17221729
ImGuiStyleVar_TabBarOverlineSize, // float TabBarOverlineSize
17231730
ImGuiStyleVar_TableAngledHeadersAngle, // float TableAngledHeadersAngle
17241731
ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign
1732+
ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize
17251733
ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign
17261734
ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign
17271735
ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize
@@ -2181,6 +2189,8 @@ struct ImGuiStyle
21812189
float TabBarOverlineSize; // Thickness of tab-bar overline, which highlights the selected tab-bar.
21822190
float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).
21832191
ImVec2 TableAngledHeadersTextAlign;// Alignment of angled headers within the cell
2192+
ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.
2193+
float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
21842194
ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
21852195
ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
21862196
ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.

imgui_demo.cpp

+60-4
Original file line numberDiff line numberDiff line change
@@ -3920,6 +3920,7 @@ static void DemoWindowWidgetsTreeNodes()
39203920
IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
39213921
if (ImGui::TreeNode("Tree Nodes"))
39223922
{
3923+
// See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
39233924
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
39243925
if (ImGui::TreeNode("Basic trees"))
39253926
{
@@ -3946,6 +3947,35 @@ static void DemoWindowWidgetsTreeNodes()
39463947
ImGui::TreePop();
39473948
}
39483949

3950+
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
3951+
if (ImGui::TreeNode("Hierarchy lines"))
3952+
{
3953+
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
3954+
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
3955+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
3956+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
3957+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
3958+
3959+
if (ImGui::TreeNodeEx("Parent", base_flags))
3960+
{
3961+
if (ImGui::TreeNodeEx("Child 1", base_flags))
3962+
{
3963+
ImGui::Button("Button for Child 1");
3964+
ImGui::TreePop();
3965+
}
3966+
if (ImGui::TreeNodeEx("Child 2", base_flags))
3967+
{
3968+
ImGui::Button("Button for Child 2");
3969+
ImGui::TreePop();
3970+
}
3971+
ImGui::Text("Remaining contents");
3972+
ImGui::Text("Remaining contents");
3973+
ImGui::TreePop();
3974+
}
3975+
3976+
ImGui::TreePop();
3977+
}
3978+
39493979
IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
39503980
if (ImGui::TreeNode("Advanced, with Selectable nodes"))
39513981
{
@@ -3964,6 +3994,12 @@ static void DemoWindowWidgetsTreeNodes()
39643994
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);
39653995
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)");
39663996
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsBackHere", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsBackHere);
3997+
3998+
HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
3999+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4000+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4001+
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4002+
39674003
ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
39684004
ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
39694005
ImGui::Text("Hello!");
@@ -4607,10 +4643,11 @@ static void DemoWindowLayout()
46074643
ImGui::SmallButton("SmallButton()");
46084644

46094645
// Tree
4646+
// (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
46104647
const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
46114648
ImGui::Button("Button##1");
46124649
ImGui::SameLine(0.0f, spacing);
4613-
if (ImGui::TreeNode("Node##1"))
4650+
if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone))
46144651
{
46154652
// Placeholder tree data
46164653
for (int i = 0; i < 6; i++)
@@ -6592,7 +6629,7 @@ static void DemoWindowTables()
65926629
{
65936630
static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
65946631

6595-
static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns;
6632+
static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
65966633
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);
65976634
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);
65986635
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);
@@ -8139,6 +8176,14 @@ bool ImGui::ShowStyleSelector(const char* label)
81398176
return false;
81408177
}
81418178

8179+
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
8180+
{
8181+
if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
8182+
if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
8183+
if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
8184+
return "";
8185+
}
8186+
81428187
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
81438188
{
81448189
IMGUI_DEMO_MARKER("Tools/Style Editor");
@@ -8233,6 +8278,15 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
82338278
style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
82348279

82358280
ImGui::SeparatorText("Widgets");
8281+
if (ImGui::BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags)))
8282+
{
8283+
const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
8284+
for (ImGuiTreeNodeFlags option : options)
8285+
if (ImGui::Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))
8286+
style.TreeLinesFlags = option;
8287+
ImGui::EndCombo();
8288+
}
8289+
ImGui::SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, 1.0f, "%.0f");
82368290
ImGui::Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
82378291
ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
82388292
ImGui::SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
@@ -9285,8 +9339,10 @@ struct ExampleAppPropertyEditor
92859339
ImGui::TableNextColumn();
92869340
ImGui::PushID(node->UID);
92879341
ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9288-
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; // Standard opening mode as we are likely to want to add selection afterwards
9289-
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
9342+
tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards
9343+
tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsBackHere; // Left arrow support
9344+
tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth; // Span full width for easier mouse reach
9345+
tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes; // Always draw hierarchy outlines
92909346
if (node == VisibleNode)
92919347
tree_flags |= ImGuiTreeNodeFlags_Selected;
92929348
if (node->Childs.Size == 0)

imgui_draw.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
236236
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
237237
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
238238
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
239+
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
239240
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
240241
colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
241242
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
@@ -300,6 +301,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
300301
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
301302
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
302303
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
304+
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
303305
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
304306
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
305307
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
@@ -365,6 +367,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
365367
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
366368
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
367369
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
370+
colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border];
368371
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
369372
colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered];
370373
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);

0 commit comments

Comments
 (0)