ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.08k stars 10.29k forks source link

Collapsing headers & pushID #590

Closed petrihakkinen closed 8 years ago

petrihakkinen commented 8 years ago

In the doc it says "ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen" so I was under the impression that this also applies to collapsing headers, but it seems that this is not the case. Any reason why? I use a lot of collapsing headers and I just noticed many conflicting IDs.

ocornut commented 8 years ago

They don't because the api doesn't require you to "close" the scope (partly because there's no visible effect for it), so I can't pop an id at the "end" of a collapsing header.

Perhaps this should be clarified. (That specific function is also a prime candidate for 2.0 dismantling because of the horrible bool parameters)

petrihakkinen commented 8 years ago

I figured that out but is there anything other than sprinkling user code with PushID/PopID that we could do? It's a little ugly... Maybe something like, CollapsingHeader pushes the ID and the next CollapsingHeader/End etc. call pops it (don't know the exact list of functions that should pop but you get the idea)?

(yeah, I've shortened the API to CollapsingHeader(label, default_open) in my Lua wrapper)

petrihakkinen commented 8 years ago

To clarify, I'm suggesting that the next call to End(), CollapsingHeader() or such function that clearly ends the scope of the collapsing header would automatically pop the ID.

Cthutu commented 8 years ago

If you had a C++ object for a ImGui context (thinking 2.0 API), you could use scoped based pushing and popping such as:

ImGui::Context ctx;

{ ImGui::ScopedIds(ctx); ... ... }

The constructor of ScopedIds would push, and the destructor would pop by calling ctx.PushID and ctx.PopID.

On Fri, 15 Apr 2016 at 04:33 petrihakkinen notifications@github.com wrote:

To clarify, I'm suggesting that the next call to End(), CollapsingHeader() or such function that clearly ends the scope of the collapsing header would automatically pop the ID.

— 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/590#issuecomment-210359617

ocornut commented 8 years ago

@petrihakkinen I feel it may be a little too magic/inconsistent. I imagine it'd work for the basic case but if you start messing up with cursor position, indentation, columns, etc. the "next" CollapsingHeader may not be an obvious notion.

We could add a flag (once we switch to a variant taking flags) to automatically keep the ID pushed but then you'd still have to a TreePop() (or perhaps via a CollapsingHeaderPop() calling TreePop()). At this point it may not be worth the extra?

I think I see where you come from, I'm always using TreeNode() to embed sections of content and perhaps you are using CollapsingHeader) the way I'd use TreeNode() so the Push/Pop may be annoying fs your UI are generated from manually authored code.

Following refactoring those functions it could be that CollapsingHeader() is just a flag over TreeNode(), so you could call TreeNodeEx(... TreeNodeFlags_Framed) and it'd give the same visual with an expectation of doing a TreePop()?

petrihakkinen commented 8 years ago

Yep, I agree. Removing CollapsingHeader and adding support for tree nodes with frames sounds like the right way to go.

ocornut commented 8 years ago

Basic CollapsingHeader would become:

bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
{
    return TreeNodeEx(label, flags | ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoPushIdOnOpen | ImGuiTreeNodeFlags_NoIndentOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog);
}

Which also makes it obvious what you do if you want more custom behavior (aka call TreeNodeEx).

The existing parameter to the old CollapsingHeader() are terrible, probably among the top 3 worst choice of parameters in the original API and I think they were chosen solely to allow TreeNode() to call CollapsingHeader(). Thankfully:

petrihakkinen commented 8 years ago

How about just ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoPushIdOnOpen | ImGuiTreeNodeFlags_NoIndentOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog ? This way you could remove the CollapsingHeader function.

I would also consider dropping ImGuiTreeNodeFlags_NoPushIdOnOpen and just make the collapsing header act like any other tree node (that require explicit pop call). Maybe.

ocornut commented 8 years ago

I would also consider dropping ImGuiTreeNodeFlags_NoPushIdOnOpen and just make the collapsing header act like any other tree node (that require explicit pop call). Maybe.

That would unfortunately break people code commonly and silently, so not a good idea :( Yes to the ImGuiTreeNodeFlags_CollapsingHeader shortcut.

petrihakkinen commented 8 years ago

Maybe I'm missing something but I think it would not break silently old code, because there would be no CollapsingHeader function anymore in the new API? (The deprecated old API would not push the id but once IMGUI_DISABLE_OBSOLETE_FUNCTIONS is defined that functionality would be gone).

ocornut commented 8 years ago

Oh I see. Then we can keep CollapsingHeader() as well, that's not hurting to keep it as a shortcut. I'm still iterating through those api to try to accommodate for various needs, will report. :)

petrihakkinen commented 8 years ago

Idea: could you consider moving the hardcoded ImGuiTreeNodeFlags_NoTreePushOnOpen flag to the function prototype, like this: bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_NoTreePushOnOpen)

This way I could turn off the NoTreePushOnOpen flag cleanly without duplicating the code. (edited: oops, had the concept the wrong way around)

What do you think?

ocornut commented 8 years ago

I agree in principle but the idea here is to remove that function all-together in the API because the aim is to make it a natural fit in user-land (and temporarily a bigger chunk to include in user-land)

petrihakkinen commented 8 years ago

Ok, fair enough!

ocornut commented 8 years ago

Oh wait, no, this only pertain to the bool* version of it. The normal CollapsingHeader() version is of course staying.

Note that normal CollapsingHeader() is more or less only doing:

TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen);

So you can call that yourself without the undesirable flag?

petrihakkinen commented 8 years ago

Sure, I can :) I have a custom version of CollapsingHeader anyway, because I need the close button feature. So there is absolutely no need to change anything just for my sake.

It's just that from my point of view, a collapsing header is not that different from any other tree node, so I could argue that the API for both collapsing headers and "normal" tree nodes should follow the same begin/end pattern and thus push/pop the id automatically. It just feels backwards the way it is now. Feel free to disagree :)

ocornut commented 8 years ago

I'd agree in principle but it's been like that for 18 months and we can't break the API this way. However the new TreeNodeFlags + reorganization + comments makes it much more obvious that you can make your own one-liner variation if you want behavior like TreeNode().