@@ -3528,7 +3528,7 @@ void Tree::popup_select(int p_option) {
35283528}
35293529
35303530void Tree::_go_left () {
3531- if (get_tree ()-> is_accessibility_enabled () && selected_button >= 0 ) {
3531+ if (selected_button >= 0 ) {
35323532 selected_button--;
35333533 } else if (selected_col == 0 ) {
35343534 selected_button = -1 ;
@@ -3562,8 +3562,22 @@ void Tree::_go_left() {
35623562
35633563void Tree::_go_right () {
35643564 int buttons = (selected_item && selected_col >= 0 && selected_col < columns.size ()) ? selected_item->cells [selected_col].buttons .size () : 0 ;
3565- if (get_tree ()->is_accessibility_enabled () && selected_button < buttons - 1 ) {
3565+ if (selected_button >= 0 && selected_button < buttons - 1 ) {
3566+ // On a button, move to next button.
35663567 selected_button++;
3568+ } else if (selected_button == -1 && selected_item->get_first_child () != nullptr ) {
3569+ // On cell with children.
3570+ if (selected_item->is_collapsed ()) {
3571+ // Expand collapsed item.
3572+ selected_item->set_collapsed (false );
3573+ } else if (selected_item->get_next_visible ()) {
3574+ // Already expanded, move to first child.
3575+ selected_col = 0 ;
3576+ _go_down ();
3577+ }
3578+ } else if (selected_button == -1 && buttons > 0 ) {
3579+ // On cell with no children but has buttons, move to first button.
3580+ selected_button = 0 ;
35673581 } else if (selected_col == (columns.size () - 1 )) {
35683582 selected_button = -1 ;
35693583 if (selected_item->get_first_child () != nullptr && selected_item->is_collapsed ()) {
@@ -4766,10 +4780,17 @@ void Tree::_accessibility_update_item(Point2 &r_ofs, TreeItem *p_item, int &r_ro
47664780
47674781 DisplayServer::get_singleton ()->accessibility_update_set_table_row_index (p_item->accessibility_row_element , r_row);
47684782 DisplayServer::get_singleton ()->accessibility_update_set_list_item_level (p_item->accessibility_row_element , p_level);
4769- DisplayServer::get_singleton ()->accessibility_update_set_list_item_expanded (p_item->accessibility_row_element , !p_item->collapsed );
4770- DisplayServer::get_singleton ()->accessibility_update_set_flag (p_item->accessibility_row_element , DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !(p_item->visible && !p_item->parent_visible_in_tree ));
4771- DisplayServer::get_singleton ()->accessibility_update_add_action (p_item->accessibility_row_element , DisplayServer::AccessibilityAction::ACTION_COLLAPSE, callable_mp (this , &Tree::_accessibility_action_collapse).bind (p_item));
4772- DisplayServer::get_singleton ()->accessibility_update_add_action (p_item->accessibility_row_element , DisplayServer::AccessibilityAction::ACTION_EXPAND, callable_mp (this , &Tree::_accessibility_action_expand).bind (p_item));
4783+
4784+ // Only set expanded state on items with children (leaf nodes should not have expanded/collapsed state).
4785+ if (p_item->get_first_child () != nullptr ) {
4786+ DisplayServer::get_singleton ()->accessibility_update_set_list_item_expanded (p_item->accessibility_row_element , !p_item->collapsed );
4787+ DisplayServer::get_singleton ()->accessibility_update_add_action (p_item->accessibility_row_element , DisplayServer::AccessibilityAction::ACTION_COLLAPSE, callable_mp (this , &Tree::_accessibility_action_collapse).bind (p_item));
4788+ DisplayServer::get_singleton ()->accessibility_update_add_action (p_item->accessibility_row_element , DisplayServer::AccessibilityAction::ACTION_EXPAND, callable_mp (this , &Tree::_accessibility_action_expand).bind (p_item));
4789+ }
4790+
4791+ // Add focus action to row element so it shows as focusable in accessibility tree.
4792+ DisplayServer::get_singleton ()->accessibility_update_add_action (p_item->accessibility_row_element , DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp (this , &Tree::_accessibility_action_focus).bind (p_item, 0 ));
4793+ DisplayServer::get_singleton ()->accessibility_update_set_flag (p_item->accessibility_row_element , DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !(p_item->visible && p_item->parent_visible_in_tree ));
47734794
47744795 DisplayServer::get_singleton ()->accessibility_update_set_list_item_selected (p_item->accessibility_row_element , selected_item == p_item);
47754796 if (p_item == root && is_root_hidden ()) {
@@ -4809,7 +4830,7 @@ void Tree::_accessibility_update_item(Point2 &r_ofs, TreeItem *p_item, int &r_ro
48094830 }
48104831
48114832 DisplayServer::get_singleton ()->accessibility_update_set_text_align (cell.accessibility_cell_element , cell.text_alignment );
4812- DisplayServer::get_singleton ()->accessibility_update_set_flag (cell.accessibility_cell_element , DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !(p_item->visible && ! p_item->parent_visible_in_tree ));
4833+ DisplayServer::get_singleton ()->accessibility_update_set_flag (cell.accessibility_cell_element , DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !(p_item->visible && p_item->parent_visible_in_tree ));
48134834 DisplayServer::get_singleton ()->accessibility_update_set_flag (cell.accessibility_cell_element , DisplayServer::AccessibilityFlags::FLAG_READONLY, !cell.editable );
48144835 DisplayServer::get_singleton ()->accessibility_update_set_tooltip (cell.accessibility_cell_element , cell.tooltip );
48154836 switch (cell.mode ) {
@@ -4848,7 +4869,9 @@ void Tree::_accessibility_update_item(Point2 &r_ofs, TreeItem *p_item, int &r_ro
48484869 Vector2 ofst = Vector2 (col_offset + cw, 0 );
48494870 for (int j = cell.buttons .size () - 1 ; j >= 0 ; j--) {
48504871 if (cell.buttons [j].accessibility_button_element .is_null ()) {
4851- cell.buttons [j].accessibility_button_element = DisplayServer::get_singleton ()->accessibility_create_sub_element (cell.accessibility_cell_element , DisplayServer::AccessibilityRole::ROLE_BUTTON);
4872+ // Create buttons as children of the row, not the cell.
4873+ // This prevents screen readers from reading button names when focusing cells
4874+ cell.buttons [j].accessibility_button_element = DisplayServer::get_singleton ()->accessibility_create_sub_element (p_item->accessibility_row_element , DisplayServer::AccessibilityRole::ROLE_BUTTON);
48524875 }
48534876
48544877 DisplayServer::get_singleton ()->accessibility_update_add_action (cell.buttons [j].accessibility_button_element , DisplayServer::AccessibilityAction::ACTION_CLICK, callable_mp (this , &Tree::_accessibility_action_button_press).bind (p_item, i, j));
@@ -4904,7 +4927,7 @@ void Tree::_notification(int p_what) {
49044927 RID ae = get_accessibility_element ();
49054928 ERR_FAIL_COND (ae.is_null ());
49064929
4907- DisplayServer::get_singleton ()->accessibility_update_set_role (ae, DisplayServer::AccessibilityRole::ROLE_TREE );
4930+ DisplayServer::get_singleton ()->accessibility_update_set_role (ae, DisplayServer::AccessibilityRole::ROLE_TREE_GRID );
49084931
49094932 DisplayServer::get_singleton ()->accessibility_update_add_action (ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_DOWN, callable_mp (this , &Tree::_accessibility_action_scroll_down));
49104933 DisplayServer::get_singleton ()->accessibility_update_add_action (ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_LEFT, callable_mp (this , &Tree::_accessibility_action_scroll_left));
@@ -4957,6 +4980,12 @@ void Tree::_notification(int p_what) {
49574980 }
49584981 DisplayServer::get_singleton ()->accessibility_update_set_table_row_count (ae, rows);
49594982
4983+ // Set active descendant to the focused element so screen readers announce it.
4984+ RID focused = get_focused_accessibility_element ();
4985+ if (focused.is_valid () && focused != ae) {
4986+ DisplayServer::get_singleton ()->accessibility_update_set_active_descendant (ae, focused);
4987+ }
4988+
49604989 } break ;
49614990
49624991 case NOTIFICATION_FOCUS_ENTER: {
0 commit comments