Pressing Tab in exported Array and Dictionary fix#121685
Conversation
…tainer to be the last child, so that pressing Tab focuses elements in the expected order.
| } else if (slot.remove_button) { | ||
| new_prop->add_inline_control(slot.remove_button, INLINE_CONTROL_RIGHT); | ||
| } | ||
| new_prop->move_child(new_prop->get_child(1), new_prop->get_child_count() - 1); |
There was a problem hiding this comment.
No. We should not be moving children we don't own. This creates problems down the line when the children of those classes are modified.
There was a problem hiding this comment.
Would adding a middle_container to EditorProperty, to which we would attach the children, be an acceptable solution? Or would it have the same problems?
There was a problem hiding this comment.
Can't set_focus_neighbor() be used to handle this?
There was a problem hiding this comment.
I tried that and also set_focus_next() and set_focus_previous. For these you have to put a NodePath as an argument, but the Node has to be a focusable Control. Each EditorPropertyX has different children, some have the first child focusable, for others you have to get the child of a child, so you can't just get_child(0). I botched something up, but then the problem is when you focus on the Remove button and press Tab, you go back to the middle. To fix this I have to get the next element in the slots array, or the "Add Element" button, or the paginator's first button.
I might be stupid for not thinking something better, but this feels like it should require very little code (like the PR's oneliner). The built-in solution to get next/previous focus control already takes care of which node to focus on based on the order of the nodes in the tree, so the move_child() would just put the controls in that order.
Could you elaborate a bit more on how move_child() creates problems down the line? I tested it with most EditorPropertyX types and don't notice any problems in the editor.
Additionally, move_child() gets called a few lines above, how does that call differ?
Sorry for the questions, but I want to understand and do this in the best way possible, and also learn (to become less stupid 🐦 )
There was a problem hiding this comment.
The move_child() above is using the variable that holds the node we created to identify which child to move, making it more clear what's happening by just looking at the code.
In your PR, it's moving the child using the index, which would not guarantee that in the future we're moving the correct one, and it also makes the code more obtuse. It should instead move it the same way the former one does, plus a comment on top explaining why that is being done.
Sorry for the questions
No such a thing, don't worry.
There was a problem hiding this comment.
There was a problem hiding this comment.
This also works:
new_prop->move_child(slot.edit_button ? slot.edit_button : slot.remove_button, new_prop->get_child_count() - 1);
I think the null check is overkill.

What problem(s) does this PR solve?
Changes
Added a simple
move_childwhen iteratingslotsin theupdate_propertyfunction ofEditorPropertyArrayandEditorPropertyDictionary. This moves the second child of theEditorPropertyto be the last child, which makes pressingTabfocus elements in the expected order.EditorProperty's child is it'sright_containeras per the constructor:godot/editor/inspector/editor_inspector.cpp
Lines 1691 to 1711 in 30a0296
Here's a video showing before and after (starts at 30s):
2026-07-23.16-15-39.mp4