-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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 SplitContainer to have more than two children #90411
base: master
Are you sure you want to change the base?
Conversation
For that, you could use an
Hmm, maybe I misunderstand how things are implemented, but I believe we should avoid that. I don't remember exactly why, but this was how it used to be implemented before and it created problems when resizing the container, or when children components change their minimum size. Edit: here is one of the issues that used to happen: #43749 |
I can't use it in split_container.cpp, right? Undoing adding a child should undo adding the split_offset, but it happens in the sort children notification, so I don't know how to add it to undo redo.
If I didn't account for the minimum size, then the children with expand flags would not be the same proportions with their stretch ratio. If the minimum size of A increases, then B and D wouldn't be the same size any more. So C should be moved to keep them the at same ratio. I don't think this is a problem since it only affects draggers that are in between two expanding children. So its default position will only be moved to keep them at the same ratio, and its split offset will be the offset from that position. |
Ah right. This has been a problem forever... A ton of properties (size flags, stretch ratio, etc...) are in the Control node, in case they end up used as a child of a container. But often they end up unused.
This does not sound right, it was added by #65355 to allow the dragger to have a bigger area than the underlying SplitContainer node. TBH, I feel this is a lot of changes. I can't really wrap my head around all changes done there. So I believe this should be split into a core PR (SplitContainer) and an editor PR (editor changes). SplitContainer has been the source of tons of small issues in the past, so we need to test the new behavior thoroughly. |
44f1c47
to
4916519
Compare
Changed back to use SplitContainerDragger
For reference: |
Would this be useful to implement godotengine/godot-proposals#9676? |
Yes, this (along with #90439) will make it easier to implement. |
scene/gui/split_container.cpp
Outdated
@@ -39,7 +39,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) { | |||
|
|||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent()); | |||
|
|||
if (sc->collapsed || !sc->get_containable_child(0) || !sc->get_containable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) { | |||
if (sc->collapsed || !sc->get_containable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) { |
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.
sc->get_containable_child(1)
This seems like it is still hardcoded for two children.
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 actually checks for at least two children. When there is 0 or 1 children, get_containable_child(1)
will be false and when there is 2 or more it will be true.
I changed this in a few places, but get_containable_child(1)
implies get_containable_child(0)
.
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 ok that makes sense. In such case, I think it would be good to write a comment. Or maybe be explicit about it by defining a variable instead:
bool has_at_least_two_containable_child = sc->get_containable_child(1);
if (sc->collapsed || has_at_least_two_containable_child || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) {
...
Something like that I'd say.
d9ee083
to
a1b546e
Compare
I found a crash when duplicating SplitContainers with multiple children, I think it's related to how I dynamically add SplitContainerDragger as internal children. I add and remove them in the sort children notification. Edit: I fixed it with |
Rebased and fixed conflicts. When duplicating a SplitContainer with 3+ children I'm getting: |
Do draggers need to be nodes? 🤔 |
Yes, since the grab area needs to be able to be bigger than the Split Container. Maybe there could be just one dragger handler node? It would complicate the dragging logic but it might work. |
What's the consensus on this? It's marked for 4.3 and even though it's a nice feature, we are in feature freeze right now. |
In my opinion this shouldn't count as a feature and should be included with 4.3 since SplitContainers should've always been like this. For me it counts as a "fix" since it also fixes sizing inconsistencies within the editor #90439 |
Unfortunately there are some issues with this and I'm not sure how to fix it. (#90411 (comment)) This has the potential to break things a lot with the Editor so I don't expect #90439 to be merged in 4.3 even if this was fixed now. |
scene/gui/split_container.cpp
Outdated
} | ||
} | ||
} | ||
stretch_complete = true; |
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 set in every loop iteration, no? So while
looks redundant.
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 like I messed up the logic, it should work like it does in BoxContainer::_resort
.
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 did some (not super-extensive) testing and it works correctly. The code looks fine overall, but it's not like I'm very familiar with SplitContainer. Would be nice if @groud can take a look too.
I spotted one minor issue that adding a child will add new entry to split_offsets
, but deleting it won't delete the entry. It doesn't really have adverse effect though.
Fixed the stretching algorithm, it should now match BoxContainer in the case that the not-first-stretching child has a minimum size greater than its desired size.
Yeah, this is because removing a child and undoing it won't restore the split offset, so I don't resize split_offsets to prevent loss of data. I think the undo system needs more functionality for this, so it can be fixed in the future. |
Note: |
They have been deprecated, please check the code |
Sorry i just noticed the variable was removed, and thought they have missed this, Amazing work and thanks for making this ^^. |
I was testing it since i have had a hard time making a similar
Test2.mp4have a look on this godotengine/godot-proposals#10610 and check the splitter_container.cpp for some ideas (I have dealt with offsets as a ratio array instead of position array to easily resize the In addition, #include "scene/gui/label.h"
#include "scene/gui/margin_container.h" |
dfd357a
to
d4b6611
Compare
Rebased and removed unused includes.
Other Containers ignore hidden children intentionally, so only having enough split offsets for the valid visible children is expected.
Ignoring internal children was the behavior beforehand, but other Containers work like that so it makes sense to not ignore them all. Since it is not directly related and may need more review this can be done in another PR.
This would mean changing size flags after starting does nothing, which doesn't seem very useful. Also what if the user adds a child and then sets a size flag for it after? It won't be recognized. Other containers like BoxContainer also don't do it like this. Having it saved as a ratio seems nice, but it would break compatibility.
|
Let's say we use offsets as a ratio which seems the best solution for this, it won't break compatibility since we can control the old behaviour in the deprecated method by converting old offset Size flags are made for static containers because we can't control their size manually, Containers are numbers and characters are the dragger. // [ 1 |a| 2 |b| 3 |c| 4 |d| 5 ]
// [ 2 |b| 3 |c| 4 ]
// Hiding 1 and 5 should make 2 use b offset and 4 expands till the end. dragger_offsets should not be changed at all when hiding/showing children.
// [ 1 |a| 2 |b| 3 |c| 4 |d| 5 ]
// [ 2 |b| 4 ]
// Hiding 3 should make 2 use b offset and 4 expands to the end.
// [ 1 |a| 2 |b| 3 |c| 4 |d| 5 ]
// [ 2 |b| 4 |d| 5 ]
// Showing 5 should make 4 use the d offset and 5 expands to the end,
// To achieve this, we should get the sortable children first and we save them and then we loop them to get the visible children.
// split_container.h
Vector<Control *> children;
// split_container.cpp
void SplitterContainer::sort_children() {
children.clear();
Vector<Control *> visible_children;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || get_child(i)->is_class("SplitContainerDragger")) {
continue;
}
if (!c->is_top_level_control()) {
// get all sortable children even if they are hidden.
children.append(c);
if (c->is_visible_in_tree()) {
// get all visible children that will be sorted.
visible_children.append(c);
}
}
}
}
// To get the correct offset index, let `c` be the visible child.
int index = children.find(c);
I will have a look on it. Edit: Size flags are useful for initializing the dragger_offsets when it's invalid or empty. if the node is inside the edited scene, the offsets should be tottaly ignored. |
A workaround for controlling offsets in the editor is by using the |
I don't think converting it is enough, since if the size changes, the value changes as well.
We can't make SplitContainer not use its children's size flags, that breaks compatibility. Even the editor uses size flags in SplitContainers. It would make the implementation simpler, but it wouldn't have much benefit to the user.
Yes, it should be simple to implement, but it is not really related to this PR. It's better to have a separate PR for it. |
I have found an issue, by hiding the left or the right dock completly, their draggers are not removed. 31.08.2024_02.01.46_REC.mp4 |
Draggers are now hidden when there is 0 or 1 child. |
Great work. |
There are some issues when docks are moved (when a child visibility changes), docks can jump around when they get another's split offset. There is also a case where the minimum size isn't respected, so this still needs some work. |
In cases like hiding a child, it's dragger offset should be kept but ignored and it's dragger should hide, the next child should use an offset based on it's child index. this will preserve the layout when showing/hiding a child control. In cases like adding/removing children in game or editor plugin, i see that the layout is reset based on the size flags, which will ignore the current offsets that was set by user, it means that if one child is set to expand, and others are not, all the children that aren't expanding will be shrinked to their minimum size, which will force the user to set the offsets again from start, this seems wrong and offsets set by user should also be respected in game and editor plugins. to fix this [in game and editor plugins only] children should be arranged the first time based on their size flags, then once the offsets are set, they should always be respected and not be overrided by size flags except when expanding, the offsets should be tweaked and not reset. In cases like resizing, it seems to work as expected, children with size flag Just make some tweaks to the Edit: The editor for example is using 5 vsplits currently [left dock, left dock 2, center/botom screen, right dock 2, right dock] all of them can become hidden/visible on demand except the center/bottom screen which is set to expand horizontally, and all other docks are set to fill, and when setting their offsets manually then resizing the editor window, they can shrink to their minimum size, and expand back to their offset that was set by the user. this behavior should be the same when you make all the vsplits inside one hsplit container instead of four, and if you do that, hiding any dock completly will reset the layout to make all the visible docks shrink and the main screen expands. |
Still working on the visibility issue,
|
SplitContainers can now work with more than two children. Set the main editor HSplit and the script editor debugger to use this, so they are resized consistently.
This should not break compatibility.
Split offsets are just offsets from their default positions (aka wished_middle_offset).
The default positions are calculated similar to how BoxContainers sort their children. However, if the dragger is before or after all expand flags, then it will be at the start or end of the SplitContainer to keep the same behavior as before.
The number of split offsets increase whenever there is a new valid child, but it won't decrease since that can lose data and won't undo.