Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
kylawl opened this issue Sep 9, 2015 · 17 comments
Closed

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

kylawl opened this issue Sep 9, 2015 · 17 comments
Labels
Milestone

Comments

@kylawl
Copy link
Contributor

kylawl commented Sep 9, 2015

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
Copy link
Owner

ocornut commented Sep 9, 2015

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

@kylawl
Copy link
Contributor Author

kylawl commented Sep 9, 2015

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

@kylawl kylawl closed this as completed Sep 9, 2015
@kylawl kylawl reopened this Sep 9, 2015
@ocornut
Copy link
Owner

ocornut commented Sep 9, 2015

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
Copy link
Contributor Author

kylawl commented Sep 9, 2015

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
Copy link
Owner

ocornut commented Sep 9, 2015

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
Copy link
Contributor Author

kylawl commented Sep 9, 2015

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

@ocornut
Copy link
Owner

ocornut commented Sep 9, 2015

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.

@ocornut
Copy link
Owner

ocornut commented Sep 9, 2015

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
Copy link
Contributor Author

kylawl commented Sep 9, 2015

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
Copy link
Contributor Author

kylawl commented Sep 9, 2015

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
Copy link
Owner

ocornut commented Nov 28, 2015

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

@kylawl
Copy link
Contributor Author

kylawl commented Dec 3, 2015

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
Copy link
Owner

ocornut commented Apr 24, 2016

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
Copy link

Cthutu commented Apr 24, 2016

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
#324 (comment)

@ocornut
Copy link
Owner

ocornut commented May 1, 2016

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.

@ocornut ocornut added this to the v1.49 milestone May 1, 2016
ocornut added a commit that referenced this issue May 1, 2016
@ocornut
Copy link
Owner

ocornut commented May 28, 2016

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
Copy link
Owner

ocornut commented May 28, 2016

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().

@ocornut ocornut closed this as completed May 28, 2016
@ocornut ocornut added tree tree nodes and removed in progress labels Jun 15, 2018
ocornut added a commit that referenced this issue Jul 16, 2019
…tCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete). (#581, #324)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants