Hide arrow on TreeNode and Collapsibles when children are empty #324

Closed
opened 2015-09-09 05:07:26 +00:00 by kylawl · 17 comments
kylawl commented 2015-09-09 05:07:26 +00:00 (Migrated from github.com)

When you're building a tree view it's nice to be able to keep all items at the same level aligned as well as hide the arrow on items which have no children.

Currently we're swapping between text items and borderless collapsibles and just putting 3 spaces before the text items which keeps everything roughly aligned but it that's really a fluke.

- Node (has children)
     Child
     Child
Node (no children, alignment is off) :(
- Node (has children)
     Child
     Child
When you're building a tree view it's nice to be able to keep all items at the same level aligned as well as hide the arrow on items which have no children. Currently we're swapping between text items and borderless collapsibles and just putting 3 spaces before the text items which keeps everything roughly aligned but it that's really a fluke. ``` - Node (has children) Child Child Node (no children, alignment is off) :( - Node (has children) Child Child ```
ocornut commented 2015-09-09 08:13:07 +00:00 (Migrated from github.com)

You can use BulletText() / Bullet() for that ?

You can use BulletText() / Bullet() for that ?
kylawl commented 2015-09-09 17:13:12 +00:00 (Migrated from github.com)

Is there a way to hide the bullet graphic? This is really all about alignment, having the extra marker there is kind of annoying.

This is what we're trying to achieve

nobulletexample

Is there a way to hide the bullet graphic? This is really all about alignment, having the extra marker there is kind of annoying. This is what we're trying to achieve ![nobulletexample](https://cloud.githubusercontent.com/assets/4952023/9768884/71e8097e-56db-11e5-9e79-13b501b82162.png)
ocornut commented 2015-09-09 17:20:25 +00:00 (Migrated from github.com)

Indent()/Unindent() ?

All those things are variations around a same thing. It's trivial to create a custom helper if you need a different behavior.

Indent()/Unindent() ? All those things are variations around a same thing. It's trivial to create a custom helper if you need a different behavior.
kylawl commented 2015-09-09 17:42:27 +00:00 (Migrated from github.com)

The issue with indent is that it doesn't keep the labels at the indent computes for the bullet/expand arrow graphic. As soon as you start on a selectable treenode this behavior will probably be what you want as well.

Anyway it's fine, like you say. With 1.45 I can trivially add this behavior myself now.

The issue with indent is that it doesn't keep the labels at the indent computes for the bullet/expand arrow graphic. As soon as you start on a selectable treenode this behavior will probably be what you want as well. Anyway it's fine, like you say. With 1.45 I can trivially add this behavior myself now.
ocornut commented 2015-09-09 17:59:18 +00:00 (Migrated from github.com)

I'm a little confused, could you elaborate with a more detailed example?
How would it look if you added more tree levels to your screenshot above?

I'm a little confused, could you elaborate with a more detailed example? How would it look if you added more tree levels to your screenshot above?
kylawl commented 2015-09-09 18:04:16 +00:00 (Migrated from github.com)

Sure!

Common convention in tree views is to not provide an expander if there are not children to expand while obviously keeping all the text/icons aligned as though the expander was there. You can find this behavior in Windows Explorer, OSX and the Hierarchy view in Maya for example

This is a screen grab of a simple scene graph in our game. You'll notice that there are a number of nodes expanded yet do not have any children and are therefore kind of misleading.

hierarchy

Here's an example where I've caught most of them but again using my cheat by just inserting 3 spaces before a normal text widget when there are no children

detailexample

Sure! Common convention in tree views is to not provide an expander if there are not children to expand while obviously keeping all the text/icons aligned as though the expander was there. You can find this behavior in Windows Explorer, OSX and the Hierarchy view in Maya for example This is a screen grab of a simple scene graph in our game. You'll notice that there are a number of nodes expanded yet do not have any children and are therefore kind of misleading. ![hierarchy](https://cloud.githubusercontent.com/assets/4952023/9769741/d189a0e0-56e0-11e5-97bd-a01dd3acc6a7.png) Here's an example where I've caught most of them but again using my cheat by just inserting 3 spaces before a normal text widget when there are no children ![detailexample](https://cloud.githubusercontent.com/assets/4952023/9769984/4a4df098-56e2-11e5-9247-2483cb523e0e.png)
ocornut commented 2015-09-09 18:15:57 +00:00 (Migrated from github.com)

So I suppose the problem here is that although you can do it with Indent(), you'd have to Indent/Unident manually for each item depending on whether the item is a tree node or not. Not a big problem for generated UI but still a still cumbersome and feels unnecessary?

indents

ImGui::Begin("test");
if (ImGui::TreeNode("Loxel Entities"))
{
    if (ImGui::TreeNode("Base"))
    {
        ImGui::Indent();
        ImGui::Text("Num Slots");
        ImGui::Text("Count");
        ImGui::Unindent();
        ImGui::TreePop();
    }
    if (ImGui::TreeNode("Slots"))
    {
        ImGui::TreePop();
    }
    ImGui::TreePop();
}
ImGui::Indent();
ImGui::Text("Previous Modifications");
ImGui::Text("Debug Ticks");
ImGui::Unindent();
ImGui::End();

I'll have to think of a solution on how to handle that neatly, I don't really know yet. If you have a suggestion.

So I suppose the problem here is that although you can do it with Indent(), you'd have to Indent/Unident manually for each item depending on whether the item is a tree node or not. Not a big problem for generated UI but still a still cumbersome and feels unnecessary? ![indents](https://cloud.githubusercontent.com/assets/8225057/9770252/0d350ad0-5727-11e5-97ce-242a702bb23f.PNG) ``` ImGui::Begin("test"); if (ImGui::TreeNode("Loxel Entities")) { if (ImGui::TreeNode("Base")) { ImGui::Indent(); ImGui::Text("Num Slots"); ImGui::Text("Count"); ImGui::Unindent(); ImGui::TreePop(); } if (ImGui::TreeNode("Slots")) { ImGui::TreePop(); } ImGui::TreePop(); } ImGui::Indent(); ImGui::Text("Previous Modifications"); ImGui::Text("Debug Ticks"); ImGui::Unindent(); ImGui::End(); ``` I'll have to think of a solution on how to handle that neatly, I don't really know yet. If you have a suggestion.
ocornut commented 2015-09-09 18:21:27 +00:00 (Migrated from github.com)

The solution might to just change how tree node behave to follow this standard but that would require to start from an indented position for a first tree node to be consistent (aka tree node shows on the LEFT of the current cursor position). It's also less flexible for quick layout.

The solution might to just change how tree node behave to follow this standard but that would require to start from an indented position for a first tree node to be consistent (aka tree node shows on the LEFT of the current cursor position). It's also less flexible for quick layout.
kylawl commented 2015-09-09 18:31:35 +00:00 (Migrated from github.com)

No, not at all. Using indent would be perfectly fine. However if you collapse Loxel Entities, There is actually a miss alignment on the labels.

Basically what I'm proposing is a flag on collapsible header prevents it from expanding and hides the arrow (something to the effect of "ImGui_DisableCollapse". Or a bullet text that can have an empty bullet I guess, but It seems like it should really just be on the collapsible.

No, not at all. Using indent would be perfectly fine. However if you collapse Loxel Entities, There is actually a miss alignment on the labels. Basically what I'm proposing is a flag on collapsible header prevents it from expanding and hides the arrow (something to the effect of "ImGui_DisableCollapse". Or a bullet text that can have an empty bullet I guess, but It seems like it should really just be on the collapsible.
kylawl commented 2015-09-09 18:49:17 +00:00 (Migrated from github.com)

So my current approach has been to simply make a copy of the BulletTextV and comment out the

window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text));

So my current approach has been to simply make a copy of the BulletTextV and comment out the `window->DrawList->AddCircleFilled(bb.Min + ImVec2(style.FramePadding.x + line_height*0.5f, line_height*0.5f), bullet_size, window->Color(ImGuiCol_Text));`
ocornut commented 2015-11-28 17:30:38 +00:00 (Migrated from github.com)

FYI @kylawl I have fixed a bug today with indenting within trees with might affect your code - not specifically related to the main topic of this thread, but on your property widget. I am not sure how you got the right column to align correctly in this screenshot, did you reposition the cursor on the X axis? What does the code looks like? Anyway, if you have time to double-check it with the latest fixes it'd be super helpful. Thanks :)

detailexample

FYI @kylawl I have fixed a bug today with indenting within trees with might affect your code - not specifically related to the main topic of this thread, but on your property widget. I am not sure how you got the right column to align correctly in this screenshot, did you reposition the cursor on the X axis? What does the code looks like? Anyway, if you have time to double-check it with the latest fixes it'd be super helpful. Thanks :) ![detailexample](https://cloud.githubusercontent.com/assets/4952023/9769984/4a4df098-56e2-11e5-9247-2483cb523e0e.png)
kylawl commented 2015-12-03 00:12:33 +00:00 (Migrated from github.com)

For each time I enter the right hand column I call ImGui::PushItemWidth(-1.0f);

What I ended up doing for the label was just copying the collapsible header and removing the rendering of the arrow

void rImGuiEmptyTreeNodeItem(const char* text)
{
    ImGuiWindow* window = ImGui::GetCurrentWindow();
    if( window->SkipItems )
        return;

    ImGuiState&         g       = *GImGui;
    const ImGuiStyle&   style   = ImGui::GetStyle();

    rMemCpy(g.TempBuffer, text, strlen(text) + 1);

    const char* text_begin      = g.TempBuffer;
    const char* text_end        = text_begin + strlen(text) + 1;


    const float line_height     = g.FontSize;
    const ::ImVec2 label_size   = ImGui::CalcTextSize(text_begin, text_end, true);
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ::ImVec2(line_height + (label_size.x > 0.0f ? (style.FramePadding.x * 2) : 0.0f), 0) + label_size);  // Empty text doesn't add padding
    ImGui::ItemSize(bb);
    if( !ImGui::ItemAdd(bb, NULL) )
        return;

    // Render

    ImGui::RenderText(bb.Min + ::ImVec2(g.FontSize + style.FramePadding.x * 2, 0), text_begin, text_end);
}
For each time I enter the right hand column I call ImGui::PushItemWidth(-1.0f); What I ended up doing for the label was just copying the collapsible header and removing the rendering of the arrow ``` void rImGuiEmptyTreeNodeItem(const char* text) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if( window->SkipItems ) return; ImGuiState& g = *GImGui; const ImGuiStyle& style = ImGui::GetStyle(); rMemCpy(g.TempBuffer, text, strlen(text) + 1); const char* text_begin = g.TempBuffer; const char* text_end = text_begin + strlen(text) + 1; const float line_height = g.FontSize; const ::ImVec2 label_size = ImGui::CalcTextSize(text_begin, text_end, true); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ::ImVec2(line_height + (label_size.x > 0.0f ? (style.FramePadding.x * 2) : 0.0f), 0) + label_size); // Empty text doesn't add padding ImGui::ItemSize(bb); if( !ImGui::ItemAdd(bb, NULL) ) return; // Render ImGui::RenderText(bb.Min + ::ImVec2(g.FontSize + style.FramePadding.x * 2, 0), text_begin, text_end); } ```
ocornut commented 2016-04-24 10:18:39 +00:00 (Migrated from github.com)

Looking at this today along with several other TreeNode related issues, and I think what we could do is add a flag, .e.g ImGuiTreeNodeFlags_UnindentArrow the modify the tree behavior to allow this with more ease. It would probably require the user to manually Indent() once at the beginning to leave room for the arrow, may alter Indent() to be more flexible.

Just theorizing at this point, I'm looking at all those Tree issues and trying to come with a general design.

(exact details left to be decided).

Looking at this today along with several other TreeNode related issues, and I think what we could do is add a flag, .e.g `ImGuiTreeNodeFlags_UnindentArrow` the modify the tree behavior to allow this with more ease. It would probably require the user to manually Indent() once at the beginning to leave room for the arrow, may alter Indent() to be more flexible. Just theorizing at this point, I'm looking at all those Tree issues and trying to come with a general design. (exact details left to be decided).
Cthutu commented 2016-04-24 13:16:13 +00:00 (Migrated from github.com)

I managed to get a version of TreeNodeEx working, which took some flags and
allowed arrow hiding and node selection. I will post some code in a couple
of days but it's not integrated with the main code yet.

On Sun, 24 Apr 2016 at 06:18 omar notifications@github.com wrote:

Looking at this today along with several other TreeNode related issues,
and I think what we could do is add a flag, .e.g
ImGuiTreeNodeFlags_UnindentArrow the modify the tree behavior to allow
this with more ease. It would probably require the user to manually
Indent() once at the beginning to leave room for the arrow, may alter
Indent() to be more flexible.

Just theorizing at this point, I'm looking at all those Tree issues and
trying to come with a general design.

(exact details left to be decided).

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub
https://github.com/ocornut/imgui/issues/324#issuecomment-213926741

I managed to get a version of TreeNodeEx working, which took some flags and allowed arrow hiding and node selection. I will post some code in a couple of days but it's not integrated with the main code yet. On Sun, 24 Apr 2016 at 06:18 omar notifications@github.com wrote: > Looking at this today along with several other TreeNode related issues, > and I think what we could do is add a flag, .e.g > ImGuiTreeNodeFlags_UnindentArrow the modify the tree behavior to allow > this with more ease. It would probably require the user to manually > Indent() once at the beginning to leave room for the arrow, may alter > Indent() to be more flexible. > > Just theorizing at this point, I'm looking at all those Tree issues and > trying to come with a general design. > > (exact details left to be decided). > > — > You are receiving this because you are subscribed to this thread. > Reply to this email directly or view it on GitHub > https://github.com/ocornut/imgui/issues/324#issuecomment-213926741
ocornut commented 2016-05-01 19:25:10 +00:00 (Migrated from github.com)

Thinking out loud here..

No, not at all. Using indent would be perfectly fine. However if you collapse Loxel Entities, There is actually a miss alignment on the labels.
Basically what I'm proposing is a flag on collapsible header prevents it from expanding and hides the arrow (something to the effect of "ImGui_DisableCollapse". Or a bullet text that can have an empty bullet I guess, but It seems like it should really just be on the collapsible.

This topic is made confusing partly because collapsing triangles are using FontSize + FramePadding.x*2 amount of width and within the tree we ident with style.IndentSpacing.

With default settings:
(A) FontSize + FramePadding.x*2 = 13+4*2 == 21
and
(B) IndentSpacing == 22

So things appears to lines up perfectly.
Note that the equation leading to (A) is never explicitly mentioned anywhere.
Now, within a large tree it is useful to alter and reduce IndentSpacing to save horizontal space. With the tree scheme pushed by ImGui it isn't a problem that (A) != (B).

And BulletText() also uses FontSize + FramePadding.x*2 FYI.

Where this becomes a problem is when you want to want to introduce a concept of "leaf", because there's different ways to define what a leaf may be. In most of examples provided by @Cthutu and @kylawl you are showing one-liner label leaves, which are equivalent to calling BulletText(), and no other items. In this situation it doesn't matter that (A) and (B) may have different values.

leavesa

(A) == 21, (B) == 22

leavesb

(A) == 21, (B) == 14, no problem.

However, once you want to go not one-liner, and add extra content in the leaf item, (A) and (B) being different cause a problem.

leaves
(A) == 21, (B) == 14

It's not a big problem, but the fact that

  • Indent() uses (B) and not (A)
  • The equation of (A) isn't explicitly told to the user.
    Makes thing more confusing.

Solution could involve a mix of some of those tasks:

  • Make a helper to return the value of (A), That would definitively makes lots of use cases less confusing and less hacky.
  • Add a TreeNodeEx flag to automatically unindent the arrow by (-A) as a convenience.
  • Allow indenting by any number of pixels?
  • Add a flag to hide the bullet (currently the AlwaysOpen flag shows a bullet? or make it the default that no bullet is shown).

Ideally that would have been easier from day 1 if tree label where displayed at the current cursor X position and the arrow was displayed to the left of that (which is what the proposal flag would do) - going to experiment with that and consider side-effects.

Thinking out loud here.. > No, not at all. Using indent would be perfectly fine. However if you collapse Loxel Entities, There is actually a miss alignment on the labels. > Basically what I'm proposing is a flag on collapsible header prevents it from expanding and hides the arrow (something to the effect of "ImGui_DisableCollapse". Or a bullet text that can have an empty bullet I guess, but It seems like it should really just be on the collapsible. This topic is made confusing partly because collapsing triangles are using `FontSize + FramePadding.x*2` amount of width and within the tree we ident with `style.IndentSpacing`. With default settings: (A) `FontSize + FramePadding.x*2 = 13+4*2 == 21` and (B) `IndentSpacing == 22` So things appears to lines up perfectly. Note that the equation leading to (A) is never explicitly mentioned anywhere. Now, within a large tree it is useful to alter and reduce IndentSpacing to save horizontal space. With the tree scheme pushed by ImGui it isn't a problem that (A) != (B). And BulletText() also uses `FontSize + FramePadding.x*2` FYI. Where this becomes a problem is when you want to want to introduce a concept of "leaf", because there's different ways to define what a leaf may be. In most of examples provided by @Cthutu and @kylawl you are showing one-liner label leaves, which are equivalent to calling BulletText(), and no other items. In this situation it doesn't matter that (A) and (B) may have different values. ![leavesa](https://cloud.githubusercontent.com/assets/8225057/14943593/6de88248-0fdd-11e6-96be-fa22c044ad0b.PNG) (A) == 21, (B) == 22 ![leavesb](https://cloud.githubusercontent.com/assets/8225057/14943594/71cf1e9e-0fdd-11e6-8dcc-6c77bd8ad25f.PNG) (A) == 21, (B) == 14, no problem. However, once you want to go not one-liner, and add extra content in the leaf item, (A) and (B) being different cause a problem. ![leaves](https://cloud.githubusercontent.com/assets/8225057/14943577/07cf9294-0fdd-11e6-8238-13087ea85e7a.PNG) (A) == 21, (B) == 14 It's not a big problem, but the fact that - Indent() uses (B) and not (A) - The equation of (A) isn't explicitly told to the user. Makes thing more confusing. Solution could involve a mix of some of those tasks: - [x] Make a helper to return the value of (A), That would definitively makes lots of use cases less confusing and less hacky. - [ ] Add a TreeNodeEx flag to automatically unindent the arrow by (-A) as a convenience. - [x] Allow indenting by any number of pixels? - [x] Add a flag to hide the bullet (currently the AlwaysOpen flag shows a bullet? or make it the default that no bullet is shown). Ideally that would have been easier from day 1 if tree label where displayed at the current cursor X position and the arrow was displayed to the left of that (which is what the proposal flag would do) - going to experiment with that and consider side-effects.
ocornut commented 2016-05-28 16:40:10 +00:00 (Migrated from github.com)

AFAIK your initial request should be full-fillled.

You can use either:

  • TreeNodeEx() with ImGuiTreeNodeFlags_Leaf flag + TreePop()
  • or Indent(GetTreeNodeToLabelSpacing()), Unindent(GetTreeNodeToLabelSpacing());
  • or TreeAdvanceToLabelPos() + Text() for simple one-liner situation where selection isn't needed.

@kylawl @Cthutu would be nice to confirm!

AFAIK your initial request should be full-fillled. You can use either: - TreeNodeEx() with ImGuiTreeNodeFlags_Leaf flag + TreePop() - or Indent(GetTreeNodeToLabelSpacing()), Unindent(GetTreeNodeToLabelSpacing()); - or TreeAdvanceToLabelPos() + Text() for simple one-liner situation where selection isn't needed. @kylawl @Cthutu would be nice to confirm!
ocornut commented 2016-05-28 17:34:50 +00:00 (Migrated from github.com)

Also added an example of basically doing:

ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
[...] tree stuff
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());

Effectively making the text label of the node aligned with the current X position.

Originally aimed at adding a flag e.g. ImGuiTreeNodeFlags_Unindent however this the unindented amount would need to stored to allow TreePop() to undo it, so would need to turn Indent/Unindent to be stack based, and I'd rather avoid adding extra heap usage until we extend ImVector to carry an optional local buffer. Either way I believe it is just easier and more logical to un-indent once.

To replicate
nobulletexample
The more natural way is still to just call TreeAdvanceToLabelPos() + Text().

Also added an example of basically doing: ``` ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); [...] tree stuff ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ``` Effectively making the text label of the node aligned with the current X position. Originally aimed at adding a flag e.g. `ImGuiTreeNodeFlags_Unindent` however this the unindented amount would need to stored to allow TreePop() to undo it, so would need to turn Indent/Unindent to be stack based, and I'd rather avoid adding extra heap usage until we extend ImVector to carry an optional local buffer. Either way I believe it is just easier and more logical to un-indent once. To replicate ![nobulletexample](https://cloud.githubusercontent.com/assets/4952023/9768884/71e8097e-56db-11e5-9e79-13b501b82162.png) The more natural way is still to just call TreeAdvanceToLabelPos() + Text().
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ocornut/imgui#324
No description provided.