Skip to content

Pressing Tab in exported Array and Dictionary fix#121685

Open
bluebirdsan wants to merge 1 commit into
godotengine:masterfrom
bluebirdsan:inspector_array_element_focus_order
Open

Pressing Tab in exported Array and Dictionary fix#121685
bluebirdsan wants to merge 1 commit into
godotengine:masterfrom
bluebirdsan:inspector_array_element_focus_order

Conversation

@bluebirdsan

Copy link
Copy Markdown

What problem(s) does this PR solve?

Changes

Added a simple move_child when iterating slots in the update_property function of EditorPropertyArray and EditorPropertyDictionary. This moves the second child of the EditorProperty to be the last child, which makes pressing Tab focus elements in the expected order. EditorProperty's child is it's right_container as per the constructor:

EditorProperty::EditorProperty() {
set_focus_mode(FOCUS_ACCESSIBILITY);
object = nullptr;
text_size = 0;
property_usage = 0;
selected_focusable = -1;
label_reference = nullptr;
bottom_editor = nullptr;
menu = nullptr;
left_container = memnew(HBoxContainer);
left_container->add_theme_constant_override(SNAME("separation"), 0);
add_child(left_container);
right_container = memnew(HBoxContainer);
right_container->add_theme_constant_override(SNAME("separation"), 0);
add_child(right_container);
set_process_shortcut_input(true);
}

Here's a video showing before and after (starts at 30s):

2026-07-23.16-15-39.mp4

…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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't set_focus_neighbor() be used to handle this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

image

Sorry for the questions, but I want to understand and do this in the best way possible, and also learn (to become less stupid 🐦 )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would something like this be better?

image

Even if not, I still think this is better overall, because the magic index 1 is gone, it's also clear(er) what node is being moved, and there's an additional null check (similar to what you mentioned), just in case rightcontainer would be null for any reason.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pressing Tab in the array in the Inspector focus elements in a weird order

3 participants