Accessibility: Fix text field character count and line navigation

Two fixes for text field accessibility:

1. Fix character count being off by one due to trailing newline always
   being appended to the last line. Add `is_last_line` parameter to
   `accessibility_create_sub_text_edit_elements()` to control this.

2. Link adjacent TextRuns via `previous_on_line`/`next_on_line` so
   screen readers can properly navigate lines. Without these links,
   AccessKit treats each TextRun as a separate line, causing incorrect
   announcements when arrowing through multi-line text.
This commit is contained in:
Nolan Darilek 2025-12-02 14:54:14 -05:00
parent 7a228b4b91
commit 6304e9f876
10 changed files with 50 additions and 28 deletions

View file

@ -38,9 +38,14 @@ Error DisplayServer::_file_dialog_with_options_show_bind_compat_98194(const Stri
return file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, MAIN_WINDOW_ID);
}
RID DisplayServer::_accessibility_create_sub_text_edit_elements_bind_compat_113459(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos) {
return accessibility_create_sub_text_edit_elements(p_parent_rid, p_shaped_text, p_min_height, p_insert_pos, false);
}
void DisplayServer::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("file_dialog_show", "title", "current_directory", "filename", "show_hidden", "mode", "filters", "callback"), &DisplayServer::_file_dialog_show_bind_compat_98194);
ClassDB::bind_compatibility_method(D_METHOD("file_dialog_with_options_show", "title", "current_directory", "root", "filename", "show_hidden", "mode", "filters", "options", "callback"), &DisplayServer::_file_dialog_with_options_show_bind_compat_98194);
ClassDB::bind_compatibility_method(D_METHOD("accessibility_create_sub_text_edit_elements", "parent_rid", "shaped_text", "min_height", "insert_pos"), &DisplayServer::_accessibility_create_sub_text_edit_elements_bind_compat_113459, DEFVAL(-1));
}
#endif

View file

@ -648,9 +648,9 @@ RID DisplayServer::accessibility_create_sub_element(const RID &p_parent_rid, Dis
}
}
RID DisplayServer::accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos) {
RID DisplayServer::accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos, bool p_is_last_line) {
if (accessibility_driver) {
return accessibility_driver->accessibility_create_sub_text_edit_elements(p_parent_rid, p_shaped_text, p_min_height, p_insert_pos);
return accessibility_driver->accessibility_create_sub_text_edit_elements(p_parent_rid, p_shaped_text, p_min_height, p_insert_pos, p_is_last_line);
} else {
return RID();
}
@ -1485,7 +1485,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("accessibility_create_element", "window_id", "role"), &DisplayServer::accessibility_create_element);
ClassDB::bind_method(D_METHOD("accessibility_create_sub_element", "parent_rid", "role", "insert_pos"), &DisplayServer::accessibility_create_sub_element, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("accessibility_create_sub_text_edit_elements", "parent_rid", "shaped_text", "min_height", "insert_pos"), &DisplayServer::accessibility_create_sub_text_edit_elements, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("accessibility_create_sub_text_edit_elements", "parent_rid", "shaped_text", "min_height", "insert_pos", "is_last_line"), &DisplayServer::accessibility_create_sub_text_edit_elements, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("accessibility_has_element", "id"), &DisplayServer::accessibility_has_element);
ClassDB::bind_method(D_METHOD("accessibility_free_element", "id"), &DisplayServer::accessibility_free_element);
ClassDB::bind_method(D_METHOD("accessibility_element_set_meta", "id", "meta"), &DisplayServer::accessibility_element_set_meta);

View file

@ -50,6 +50,7 @@ class DisplayServer : public Object {
mutable HashMap<String, RID> menu_names;
RID _get_rid_from_name(NativeMenu *p_nmenu, const String &p_menu_root) const;
RID _accessibility_create_sub_text_edit_elements_bind_compat_113459(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1);
#endif
LocalVector<ObjectID> additional_outputs;
@ -678,7 +679,7 @@ public:
virtual RID accessibility_create_element(WindowID p_window_id, DisplayServer::AccessibilityRole p_role);
virtual RID accessibility_create_sub_element(const RID &p_parent_rid, DisplayServer::AccessibilityRole p_role, int p_insert_pos = -1);
virtual RID accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1);
virtual RID accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1, bool p_is_last_line = false);
virtual bool accessibility_has_element(const RID &p_id) const;
virtual void accessibility_free_element(const RID &p_id);
@ -910,7 +911,7 @@ public:
virtual RID accessibility_create_element(DisplayServer::WindowID p_window_id, DisplayServer::AccessibilityRole p_role) = 0;
virtual RID accessibility_create_sub_element(const RID &p_parent_rid, DisplayServer::AccessibilityRole p_role, int p_insert_pos = -1) = 0;
virtual RID accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1) = 0;
virtual RID accessibility_create_sub_text_edit_elements(const RID &p_parent_rid, const RID &p_shaped_text, float p_min_height, int p_insert_pos = -1, bool p_is_last_line = false) = 0;
virtual bool accessibility_has_element(const RID &p_id) const = 0;
virtual void accessibility_free_element(const RID &p_id) = 0;