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 TreeNode
Ex?
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