Recently, I had to use ImGui to produce a “tree-style” UI as part of my assignment. As things are with ImGui, the documentation leaves a lot to be desired and I wasn’t able to easily figure out how it works. So I did some research and after trawling through a couple of different posts by other people and a couple of issue threads on the ImGui GitHub, I figured out how it more or less worked and putting it here with more clarity.

What we are looking for?

The idea of the tree we want to create has the following properties:

  • Begin with a single root node that can have many children
  • Each child node can have more children or no children
    • If it has children, it should show an arrow
    • If it has no children, it shouldn’t show an arrow
  • Both internal and leaf nodes should be selectable

TreeNode vs TreeNodeEx

So great, we know what we want, let’s search for the TreeNode function in the ImGui namespace… And, IntelliSense gives us a couple of options. Of note are, TreeNode and what’s this? A variant called TreeNodeEx?

Understandably, this can be confusing but the general idea I got from reading this issue is that TreeNode is an older function that displays a simple tree like this with no interactivity:

TreeNodeEx on the other hand, is a newer function that supports a ton of more options via the ImGuiTreeNodeFlags enum and looks like what we wanted.

Implementing TreeNodeEx

Now that we know which function to actually use, let’s try to implement it. We want our tree to be open by default and so we will want to set the ImGuiTreeNodeFlags_DefaultOpen flag which will give us something like this:

ImGuiTreeNodeFlags flag = ImGuiTreeNodeFlags_DefaultOpen;
if (ImGui::TreeNodeEx("root", flag))
{
    // Call ImGui::TreeNodeEx() recursively to populate each level of children

    ImGui::TreePop();  // This is required at the end of the if block
}  

The above code will give us the following:

Take note that in real code, you would probably wrap all of this in a function and call it recursively for children nodes.

Rendering Leaves Differently

One thing you will notice is that, on it’s own, nodes with no children still have the arrows rendered, implying that the node has children. To fix this, we need to set the ImGuiTreeNodeFlags_Leaf flag as well.

if (HAS_CHILDREN)
{
    flags |= ImGuiTreeNodeFlags_Leaf;
}

With that, we will get something that looks like this:

Selecting Nodes

Lastly, we need to add the ability to select nodes. Much like rendering the leaves, we simply need to add the ImGuiTreeNodeFlags_Selected flag. We also want to set the ImGuiTreeNodeFlags_OpenOnArrow flag so that selecting a non-leaf node does not toggle it. I’ll leave it to you to write some for of selection tracker to keep track of what’s being selected since the underlying implementation will vary depending on what you’re doing.

flags |= ImGuiTreeNodeFlags_OpenOnArrow;
if (IS_SELECTED)
{
    flags |= ImGuiTreeNodeFlags_Selected;
}

But how do we check if a node is selected? Unlike other ImGui functions, the boolean value returned by TreeNodeEx does not indicate if it was clicked. Instead, it tells you if the tree node is in an “open” state. So we will need to use a different function: ImGui::IsItemClicked() that tells us if the current element has been selected. So putting it all together:

ImGuiTreeNodeFlags flag = ImGuiTreeNodeFlags_DefaultOpen;
if (ImGui::TreeNodeEx("root", flag))
{
    // It is important that this check is done here before you render any other child nodes
    if (ImGui::IsItemClicked())
    {
        // Mark rendered node as being clicked
    }

    // Call ImGui::TreeNodeEx() recursively to populate each level of children

    ImGui::TreePop();  // This is required at the end of the if block
}  

Be careful, I’m not entirely sure why, however, ImGui::IsItemClicked() only works as expected if you use it immediately after calling ImGui::TreeNodeEx(). If you call it instead, for example, just before Imgui::TreePop(), you end up getting some weird behaviour with root nodes being selected when clicking on a child leaf node. If anyone has any idea why this is the case, I’d love to figure it out.

Conclusion

So there you go, that’s how you can make a tree in ImGui that is selectable and behaves like what the average user would expect. Hope this helps!


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.