Open liorda opened 7 years ago
don't call it every frame
@ratchetfreak I don't understand. I'm already not calling it every frame, only when the user clicks the "fold tree" button.
@liorda Please explain what you are trying to do in more clarity, it is hard to understand at the moment.
So ImGui::GetStateStorage()->SetInt(id, 0)
would effectively close a node. With e.g. id = ImGui::GetId("treenodename")
for a direct children.
Except this will create many '0' entries in the storage for tree nodes that haven't been touched, so it will grow the storage unnecessarily, which is not desirable. So you can do if (GetId(id) == 1) SetId(id, 0)
.
(We are missing a int* GetIntRefIfExist(ImGuiIdKey key)
function to allow doing that optimally (only closing nodes that were opened). But that solution above will work. )
SetStateStorage()
to your own storage, and then call SetAllInt()
on your own storage, but this is sort of tricky/risky if not done well because any other user of that same storage will get data effected.(
Sorry for being vague, I'll try to explain better:
I have an hierarchy of objects, which I show on screen via simple recursion:
void Node::Show() { if (TreeNode(mName)) { for (child : mChildren) child->Show(); } }
.
Whenever I fold/expand any node, its children "remember" their open/closed state, and that's pretty cool.
Now, I implemented an "Expand all" button, which was very easy, by just calling SetNextTreeNodeOpen(true)
before calling TreeNode()
in the recursive function. So far so good. The problem is that for implementing a recursive "fold all", I can't use that. If I call SetNextTreeNodeOpen(false)
, the TreeNode()
call returns false, so the children are rendered / pushed to the id stack.
@ocornut I wasn't aware of ImGui::GetStateStorage()->Set*()
, but I'm not sure I want to go this route since it would be hard finding out the current context data. Your 3rd point is exactly the issue I'm trying to solve. I guess I'll try to solve it myself, but I'll try to do it in a generic way.
OK, that was pretty easy. All I needed to do was change the Show function to:
void Node::Show() {
if (foldAll)
SetNextTreeNodeOpen(false);
if (TreeNode(mName) || foldAll) {
if (foldAll)
TreePush(mName);
for (child : mChildren)
child->Show();
}
}
I have no idea what's the best way to allow this with the current api style. Maybe a new flag to ImGuiTreeNodeFlags_
? or a new state value to ImGuiContext::SetNextTreeNodeOpenVal
and a BeginFold()/EndFold()
?
I tried the solution proposed by @liorda, but in my test (Linux, SDL/OpenGL2, as I'm still on old hardware), it does a quick, one frame "glitch" where all the nodes are temporarily all open. This is most noticeable when the tree is already collapsed.
To test, paste that code in the demo or other working code:
// Oh noes a global!
bool collapse = false;
template <typename F>
void do_tree(const char *l, F &&f)
{
using namespace ImGui;
if (collapse)
SetNextTreeNodeOpen(false);
if (TreeNode(l) || collapse) {
if (collapse)
TreePush(l);
f();
TreePop();
}
}
int main(...)
{
...
{
ImGui::Begin("Test tree");
collapse = ImGui::Button("collapse all");
do_tree("A", [&]() {
do_tree("B", [&]() {
ImGui::Text("a1");
});
do_tree("C", [&]() {
ImGui::Text("foo");
do_tree("D", [&]() {
ImGui::Text("top");
});
});
});
ImGui::End();
}
...
}
Is there an equivalent way to achieve the same effect without this "glitch"?
I want to add a tree view with the default open and I want it to collapse or hide when clicking over a tree node. Since I have to keep all opened I set ImGui::SetNextItemOpen(true); for every instance. Then I cannot get the functionality of hiding the view.
Is anyone have a suggestion on this. My code snippet is this
ImGui::SetNextItemOpen(true); if(ImGui::TreeNode("option1")) { ImGui::Indent(); auto missionStartingBtn = ImGui::SmallButton("option1"); if(missionStartingBtn) { } auto missionEndBtn = ImGui::SmallButton("option2"); if(missionEndBtn) { } ImGui::Unindent(); ImGui::TreePop(); }
@ocornut Sorry for the nitpick, did you mean issue #456, instead of #45?
I suggest adding a way to process a tree node without showing it, either via a flag or sth like TreeNodeExHidden()
, or even just HideNextItem()
.
This solution is generic to all similar problems. For example, Filtering a tree, i.e., hiding all nodes of intermediate levels.
I suggest adding a way to process a tree node without showing it, either via a flag or sth like TreeNodeExHidden(), or even just HideNextItem().
It's been always the plan to allow a "blind output" mode. We'll need a spot to "end" that mode either way, so I've pushed some internal stuff (2d0baaab) in tree node which will lend itself to make that easier, as previously it wasn't easy to do anything in TreePop()
. I will probably need to be doing some work on tree in the following months so I'll probably finish that then.
This solution is generic to all similar problems. For example, Filtering a tree, i.e., hiding all nodes of intermediate levels.
Yes and no. I think actual filtering ought to be implemented by the user (and we can provide any helper we want), but we should aim to have traverse all invisible contents every frame. So "blind output" would be reserved to e.g. processing childs when doing a recurse closure.
I also established that we will want both "close childs on opening parent" and "close childs on closing parent" implemented internally. The later is the intuitive thing you'd expect, but the earlier is much easier/faster to implement since it doesn't need that blind output mode. I suspect we'll need both to run anyhow.
I can use
ImGui::SetNextTreeNodeOpen(false);
, but if I do, the node's children won't be expanded so I won't be able set their open state... Is there anyway to access the internal tree behavior? Thanks!