-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
Allow TreeItem nodes to toggle visibility #60061
Allow TreeItem nodes to toggle visibility #60061
Conversation
The editor's node tree filtering could be modified to make use of this new Tree visibility. It will likely speed up filtering large scene trees. |
I get crash with this code: func _ready() -> void:
var tree = $Tree as Tree
var root = tree.create_item()
root.set_text(0, "Test0")
for i in 8:
var item = tree.create_item(root)
item.set_text(0, "Test1")
for i in 4:
var item = root.get_first_child().create_child()
item.set_text(0, "Test2")
root.get_first_child().visible = false
Caused by the last line. |
Thanks for finding this! I have fixed this issue but it made me realise another issue which I'll need to fix. When the parent |
ed276a3
to
92993c1
Compare
Ok. I have made a fix for the above issues mentioned. |
Ah, yes.
The same behavior is in e.g. Node2D. If it's
Yes. But even with shallow tree, if you have e.g. 1000 items on 2 levels, each of the items would check parent and grandparent visibility needlessly every time you update the tree. |
Ok cool. If there is precedent for this behaviour and it's understood, then I'm happy with that. I was just concerned because I felt it seemed unintuitive at first glance.
Yep, very true. I'll work on this more tonight and see what I can do. |
92993c1
to
22b4b74
Compare
TreeItem *TreeItem::get_child(int p_idx) { | ||
_create_children_cache(); | ||
ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); | ||
return children_cache.get(p_idx); | ||
} | ||
|
||
int TreeItem::get_visible_child_count() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't publicly expose this function, but if it might be useful then I can add it. I can see it having use, but it's also not as efficient for determining whether to conditionally hide something based on whether it has any visible children as simply counting how many children are not hidden as you hide them to avoid doing a second loop.
It still may be useful though in other situations...
TreeItem *TreeItem::get_next_visible(bool p_wrap) { | ||
TreeItem *TreeItem::get_prev_visible(bool p_wrap) { | ||
TreeItem *loop = this; | ||
TreeItem *prev = this->_get_prev_visible(p_wrap); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird that _get_prev_visible()
can return a TreeItem that isn't visible. We should rename this method, but it can be done in later PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree. I really couldn't think of a better name 😅
@@ -4150,7 +4220,7 @@ int Tree::get_column_minimum_width(int p_column) const { | |||
depth += 1; | |||
} else { | |||
TreeItem *common_parent = item->get_parent(); | |||
while (common_parent != next->get_parent()) { | |||
while (common_parent != next->get_parent() && common_parent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? Doesn't look related.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to fix the issue that you raised near the top of this review.
Without this change your example code that didn't work will still not work.
I am not 100% sure why it is needed either, but adding this indeed fixes it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see why is this needed now. I mean, I know when it could crash without this check, just not sure when this happens. Probably not important 🤔
You could swap the order though, so common_parent
check is first.
} | ||
visible = p_visible; | ||
if (tree) { | ||
tree->update(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will update the tree even if a parent item is invisible (so needlessly). I wonder if it's worth to optimize it 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if there is a way to do this without checking parents upwards until one is found to be invisible.
I am guessing that this would still be more efficient than updating the entire tree each time.
If you think it's worth including this in this PR I can do that otherwise we can leave for a future one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had some further thought on this and realised I don't think that the set_collapsed
method has an optimisation like you described above either (so if you collapse or un-collapse a node which is underneath an already collapsed node you would toggle an update of the whole tree.
Maybe it might be worth doing an investigation on some optimisations in a separate ticket/PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TreeItem should have a second property called parent_visible_in_tree
which is set when TreeItem is added to Tree. The first TreeItem has it always as true, then its children will have parent_visible_in_tree
true if the parent has both visible
and parent_visible_in_tree
set to true. So it's only checked once and propagated as nodes are added. When item changes visibility, child items get notified and update accordingly.
This is how it works in CanvasItem. It can be done in a separate PR, as it requires some more changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall. I left some comments, but any bigger changes can be done in a future PR.
22b4b74
to
7feb364
Compare
7feb364
to
31381f8
Compare
Thanks very much for the review. I have made the suggested small changes, and have left the others with comments here. |
Thanks! And congrats for your first merged Godot contribution 🎉 |
closes godotengine/godot-proposals#4309
I have attached an example godot project (it's pretty lazy and dirty without some optimisations but it's simply to show how the functionality works).
filtered_tree.zipfiltered_tree2.zip
As far as I can see for testing, the functionality works really nicely. One thing that may be confusing for people using the property can be explained by an example:
Let's say I have a Tree with TreeItem's as follows (assuming a hidden root, and everything is current expanded):
Fruits
├─ apple
└─ pear
Vegetables
└─ cucumber
If I set
apple
to be invisible then it will not be drawn, and everything else will.If I then set
Fruits
to be invisble then that branch will not be drawn.If I then set
Fruits
to be visible I'd expect it to be drawn again and same withpear
, but not apple.This is how it will currently work, however, if I checked
pear.visible
whenFruits.visible = false
, then it will betrue
since I am not propagating the value to children (so that if a parent TreeItem is made visible again, it doesn't cause unintentded changes of state of child TreeItems.This could be slightly confusing to users, however it could be potentially clarified by documentation, or potentially have the
is_visible()
getter return a computed value based on the visibility of the parent(s).Very open to discussion and suggestions regarding these points 😄