Imgui: Alternate rendering mode with lines in a tree view?

Created on 5 Dec 2019  路  5Comments  路  Source: ocornut/imgui

Version/Branch of Dear ImGui:

Version: 1.75
Branch: master

Complex trees can be hard to read

When editing complex trees, it is often hard to figure out which node a node is the child of.
Here is an example with a behavior tree:
Tree view

Here, it is difficult to tell at first glance that the parent of the node called "do JumpSafeFastAntiAir"
is the "Parallel Selector".

In that case, an alternate rendering mode 脿 l脿 old school windows would be useful :
image

Tentative :
image

Here I am imagining a version that detects when the lines are usefull (only when there are several levels under a node, or one or several a nodes at the end). It may be a little bit tricky to implement in immediate mode.

tree

Most helpful comment

Hello @ocornut,
I've implemented a simple tree line rendering in my application with the following algorithm (not the actual code, just an approximation/pseudocode):

//returns the node's rectangle
ImRect RenderTree(Node* n)
{
    const bool recurse = ImGui::TreeNode(...);
    const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());

    if (recurse)
    {
        const ImColor TreeLineColor = ImGui::GetColorU32(ImGuiCol_Text);
        const float SmallOffsetX = 11.0f; //for now, a hardcoded value; should take into account tree indent size
        ImDrawList* drawList = ImGui::GetWindowDrawList();

        ImVec2 verticalLineStart = ImGui::GetCursorScreenPos();
        verticalLineStart.x += SmallOffsetX; //to nicely line up with the arrow symbol
        ImVec2 verticalLineEnd = verticalLineStart;

        for (Node* child : *n)
        {
            const float HorizontalTreeLineSize = 8.0f; //chosen arbitrarily
            const ImRect childRect = RenderTree(child);
            const float midpoint = (childRect.Min.y + childRect.Max.y) / 2.0f;
            drawList->AddLine(ImVec2(verticalLineStart.x, midpoint), ImVec(verticalLineStart.x + HorizontalTreeLineSize, midpoint), TreeLineColor);
            verticalLineEnd.y = midpoint;
        }

        drawList->AddLine(verticalLineStart, verticalLineEnd, TreeLineColor);
    }

    return nodeRect;
}

Attaching screenshots from the app with and without bulleted leaf nodes:

image

image

I can implement it directly in the library if it's OK for you, though I'd need some suggestions about any API changes that would have to be introduced. I imagine that not everyone wants to have the treelines drawn, so maybe there should be a flag exposed for this. And/or another color in ImGuiCol_ enum?

All 5 comments

It looks possible to implement, the problem being that it would be harder to combine with clipping nodes.

Hello @ocornut,
I've implemented a simple tree line rendering in my application with the following algorithm (not the actual code, just an approximation/pseudocode):

//returns the node's rectangle
ImRect RenderTree(Node* n)
{
    const bool recurse = ImGui::TreeNode(...);
    const ImRect nodeRect = ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());

    if (recurse)
    {
        const ImColor TreeLineColor = ImGui::GetColorU32(ImGuiCol_Text);
        const float SmallOffsetX = 11.0f; //for now, a hardcoded value; should take into account tree indent size
        ImDrawList* drawList = ImGui::GetWindowDrawList();

        ImVec2 verticalLineStart = ImGui::GetCursorScreenPos();
        verticalLineStart.x += SmallOffsetX; //to nicely line up with the arrow symbol
        ImVec2 verticalLineEnd = verticalLineStart;

        for (Node* child : *n)
        {
            const float HorizontalTreeLineSize = 8.0f; //chosen arbitrarily
            const ImRect childRect = RenderTree(child);
            const float midpoint = (childRect.Min.y + childRect.Max.y) / 2.0f;
            drawList->AddLine(ImVec2(verticalLineStart.x, midpoint), ImVec(verticalLineStart.x + HorizontalTreeLineSize, midpoint), TreeLineColor);
            verticalLineEnd.y = midpoint;
        }

        drawList->AddLine(verticalLineStart, verticalLineEnd, TreeLineColor);
    }

    return nodeRect;
}

Attaching screenshots from the app with and without bulleted leaf nodes:

image

image

I can implement it directly in the library if it's OK for you, though I'd need some suggestions about any API changes that would have to be introduced. I imagine that not everyone wants to have the treelines drawn, so maybe there should be a flag exposed for this. And/or another color in ImGuiCol_ enum?

Looks a lot nicer when the treelines are drawn with ImColor(128, 128, 128, 255):

image

I would imagine

  • Make it optional with a flag.
  • Another color in ImGuiStyle.
  • Making it work with TreeNode/TreePop will require some custom internal storage
  • As mentioned, figuring out how to handle clipping patterns would be god (TreeNode not visible, or using of ImGuiListClipper leading to not submit TreeNode - even if today tree nodes are not frequently clipped.)

Hello @ocornut,
I need some advice on implementing this feature.

What I wanted to do is to add a ImGuiTreeNodeFlags_IndentLines flag that, when turned on, would indicate the need to draw the tree indent lines. Also, I would add a ImVector<ImVec2> field to the struct ImGuiWindowTempData, where I can store the coordinates of the stack of tree elements. Each TreePush() pushes a coordinate, TreePop() pops the coordinate and draws the vertical line.

Unfortunately, TreePop() has no knowledge about flags used for drawing the corresponding tree node, therefore it cannot decide whether to draw the vertical lines or not. I can extend the aforementioned stack in ImGuiWindowTempData with such information, though I need your thoughts on this idea. Is extending ImGuiWindowTempData in such way is acceptable?

Was this page helpful?
0 / 5 - 0 ratings