Skip to content

Commit 40c96b8

Browse files
committed
Merge pull request #109046 from bruvzg/ac_win_x
[Accessibility] Process non-focusable windows (popups, menus) as part of the parent window tree.
2 parents 3797c1e + 7b47f5e commit 40c96b8

File tree

8 files changed

+96
-42
lines changed

8 files changed

+96
-42
lines changed

scene/gui/popup.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
#include "scene/theme/theme_db.h"
3939

4040
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
41-
if ((ac_popup || get_flag(FLAG_POPUP)) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
41+
if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
4242
hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally.
4343
_close_pressed();
4444
}
@@ -115,7 +115,7 @@ void Popup::_notification(int p_what) {
115115
} break;
116116

117117
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
118-
if (!is_in_edited_scene_root() && (get_flag(FLAG_POPUP) || ac_popup)) {
118+
if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) {
119119
if (hide_reason == HIDE_REASON_NONE) {
120120
hide_reason = HIDE_REASON_UNFOCUSED;
121121
}
@@ -126,7 +126,7 @@ void Popup::_notification(int p_what) {
126126
}
127127

128128
void Popup::_parent_focused() {
129-
if (popped_up && (get_flag(FLAG_POPUP) || ac_popup)) {
129+
if (popped_up && get_flag(FLAG_POPUP)) {
130130
if (hide_reason == HIDE_REASON_NONE) {
131131
hide_reason = HIDE_REASON_UNFOCUSED;
132132
}

scene/gui/popup.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ class Popup : public Window {
4040
GDCLASS(Popup, Window);
4141

4242
LocalVector<Window *> visible_parents;
43-
bool ac_popup = false;
4443
bool popped_up = false;
4544

4645
public:
@@ -60,7 +59,6 @@ class Popup : public Window {
6059
void _close_pressed();
6160
virtual Rect2i _popup_adjust_rect() const override;
6261
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
63-
void set_ac_popup() { ac_popup = true; }
6462

6563
void _notification(int p_what);
6664
void _validate_property(PropertyInfo &p_property) const;

scene/gui/popup_menu.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3209,13 +3209,8 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
32093209
_native_popup(p_bounds != Rect2i() ? p_bounds : Rect2i(get_position(), Size2i()));
32103210
} else {
32113211
if (is_inside_tree()) {
3212-
bool ac = get_tree()->is_accessibility_enabled();
3213-
// Note: Native popup menus need keyboard focus to work with screen reader.
3214-
set_flag(FLAG_POPUP, !ac);
3215-
set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac);
3216-
if (ac) {
3217-
set_ac_popup();
3218-
}
3212+
set_flag(FLAG_POPUP, true);
3213+
set_flag(FLAG_NO_FOCUS, !is_embedded());
32193214
}
32203215

32213216
moved = Vector2();
@@ -3253,13 +3248,8 @@ void PopupMenu::set_visible(bool p_visible) {
32533248
}
32543249
} else {
32553250
if (is_inside_tree()) {
3256-
bool ac = get_tree()->is_accessibility_enabled();
3257-
// Note: Native popup menus need keyboard focus to work with screen reader.
3258-
set_flag(FLAG_POPUP, !ac);
3259-
set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac);
3260-
if (ac) {
3261-
set_ac_popup();
3262-
}
3251+
set_flag(FLAG_POPUP, true);
3252+
set_flag(FLAG_NO_FOCUS, !is_embedded());
32633253
}
32643254

32653255
Popup::set_visible(p_visible);

scene/main/node.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ void Node::_notification(int p_notification) {
6868
for (int i = 0; i < get_child_count(); i++) {
6969
Node *child_node = get_child(i);
7070
Window *child_wnd = Object::cast_to<Window>(child_node);
71-
if (child_wnd && !child_wnd->is_embedded()) {
71+
if (child_wnd && !(child_wnd->is_visible() && (child_wnd->is_embedded() || child_wnd->is_popup()))) {
7272
continue;
7373
}
7474
if (child_node->is_part_of_edited_scene()) {
@@ -2055,6 +2055,14 @@ Window *Node::get_window() const {
20552055
return nullptr;
20562056
}
20572057

2058+
Window *Node::get_non_popup_window() const {
2059+
Window *w = get_window();
2060+
while (w && w->is_popup()) {
2061+
w = w->get_parent_visible_window();
2062+
}
2063+
return w;
2064+
}
2065+
20582066
Window *Node::get_last_exclusive_window() const {
20592067
Window *w = get_window();
20602068
while (w && w->get_exclusive_child()) {
@@ -3687,8 +3695,9 @@ RID Node::get_accessibility_element() const {
36873695
return RID();
36883696
}
36893697
if (unlikely(data.accessibility_element.is_null())) {
3690-
if (get_window() && get_window()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
3691-
data.accessibility_element = DisplayServer::get_singleton()->accessibility_create_element(get_window()->get_window_id(), DisplayServer::ROLE_CONTAINER);
3698+
Window *w = get_non_popup_window();
3699+
if (w && w->get_window_id() != DisplayServer::INVALID_WINDOW_ID && get_window()->is_visible()) {
3700+
data.accessibility_element = DisplayServer::get_singleton()->accessibility_create_element(w->get_window_id(), DisplayServer::ROLE_CONTAINER);
36923701
}
36933702
}
36943703
return data.accessibility_element;

scene/main/node.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ class Node : public Object {
500500
Node *find_parent(const String &p_pattern) const;
501501

502502
Window *get_window() const;
503+
Window *get_non_popup_window() const;
503504
Window *get_last_exclusive_window() const;
504505

505506
_FORCE_INLINE_ SceneTree *get_tree() const {

scene/main/scene_tree.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,10 @@ void SceneTree::_process_accessibility_changes(DisplayServer::WindowID p_window_
246246
Vector<ObjectID> processed;
247247
for (const ObjectID &id : accessibility_change_queue) {
248248
Node *node = Object::cast_to<Node>(ObjectDB::get_instance(id));
249-
if (!node || !node->get_window()) {
249+
if (!node || !node->get_non_popup_window() || !node->get_window()->is_visible()) {
250250
processed.push_back(id);
251251
continue; // Invalid node, remove from list and skip.
252-
} else if (node->get_window()->get_window_id() != p_window_id) {
252+
} else if (node->get_non_popup_window()->get_window_id() != p_window_id) {
253253
continue; // Another window, skip.
254254
}
255255
node->notification(Node::NOTIFICATION_ACCESSIBILITY_UPDATE);
@@ -267,6 +267,15 @@ void SceneTree::_process_accessibility_changes(DisplayServer::WindowID p_window_
267267
w_this = w_focus;
268268
}
269269

270+
// Popups have no native window focus, but have focused element.
271+
DisplayServer::WindowID popup_id = DisplayServer::get_singleton()->window_get_active_popup();
272+
if (popup_id != DisplayServer::INVALID_WINDOW_ID) {
273+
Window *popup_w = Window::get_from_id(popup_id);
274+
if (popup_w && w_this->is_ancestor_of(popup_w)) {
275+
w_this = popup_w;
276+
}
277+
}
278+
270279
RID new_focus_element;
271280
Control *n_focus = w_this->gui_get_focus_owner();
272281
if (n_focus && !n_focus->is_part_of_edited_scene()) {

scene/main/window.cpp

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ bool Window::get_flag(Flags p_flag) const {
570570
return flags[p_flag];
571571
}
572572

573+
bool Window::is_popup() const {
574+
return get_flag(Window::FLAG_POPUP) || get_flag(Window::FLAG_NO_FOCUS);
575+
}
576+
573577
bool Window::is_maximize_allowed() const {
574578
ERR_READ_THREAD_GUARD_V(false);
575579
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
@@ -680,11 +684,6 @@ void Window::_make_window() {
680684
}
681685
}
682686

683-
if (get_tree() && get_tree()->is_accessibility_supported()) {
684-
get_tree()->_accessibility_force_update();
685-
_accessibility_notify_enter(this);
686-
}
687-
688687
_update_window_callbacks();
689688

690689
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE);
@@ -716,10 +715,6 @@ void Window::_clear_window() {
716715

717716
_update_from_window();
718717

719-
if (get_tree() && get_tree()->is_accessibility_supported()) {
720-
_accessibility_notify_exit(this);
721-
}
722-
723718
DisplayServer::get_singleton()->delete_sub_window(window_id);
724719
window_id = DisplayServer::INVALID_WINDOW_ID;
725720

@@ -886,7 +881,7 @@ void Window::_accessibility_notify_enter(Node *p_node) {
886881

887882
if (p_node != this) {
888883
const Window *window = Object::cast_to<Window>(p_node);
889-
if (window && !window->is_embedded()) {
884+
if (window) {
890885
return;
891886
}
892887
}
@@ -901,7 +896,7 @@ void Window::_accessibility_notify_exit(Node *p_node) {
901896

902897
if (p_node != this) {
903898
const Window *window = Object::cast_to<Window>(p_node);
904-
if (window && !window->is_embedded()) {
899+
if (window) {
905900
return;
906901
}
907902
}
@@ -950,23 +945,35 @@ void Window::set_visible(bool p_visible) {
950945
}
951946
}
952947
embedder->_sub_window_register(this);
953-
embedder->queue_accessibility_update();
954948
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE);
955949
} else {
956950
embedder->_sub_window_remove(this);
957-
embedder->queue_accessibility_update();
958951
embedder = nullptr;
959952
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
960953
}
961954
_update_window_size();
962955
}
963956

964-
if (!visible) {
957+
if (visible) {
958+
if (get_tree() && get_tree()->is_accessibility_supported()) {
959+
get_tree()->_accessibility_force_update();
960+
_accessibility_notify_enter(this);
961+
}
962+
} else {
963+
if (get_tree() && get_tree()->is_accessibility_supported()) {
964+
_accessibility_notify_exit(this);
965+
}
965966
focused = false;
966967
if (focused_window == this) {
967968
focused_window = nullptr;
968969
}
969970
}
971+
if (get_parent()) {
972+
get_parent()->queue_accessibility_update();
973+
}
974+
if (embedder) {
975+
embedder->queue_accessibility_update();
976+
}
970977

971978
notification(NOTIFICATION_VISIBILITY_CHANGED);
972979
emit_signal(SceneStringName(visibility_changed));
@@ -1371,10 +1378,10 @@ Viewport *Window::get_embedder() const {
13711378
}
13721379

13731380
RID Window::get_accessibility_element() const {
1374-
if (is_part_of_edited_scene()) {
1381+
if (!visible || is_part_of_edited_scene()) {
13751382
return RID();
13761383
}
1377-
if (get_embedder()) {
1384+
if (get_embedder() || is_popup()) {
13781385
return Node::get_accessibility_element();
13791386
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
13801387
return DisplayServer::get_singleton()->accessibility_get_window_root(window_id);
@@ -1415,11 +1422,18 @@ void Window::_notification(int p_what) {
14151422
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp(this, &Window::_accessibility_action_grab_focus));
14161423
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !visible);
14171424

1418-
if (get_embedder()) {
1425+
if (get_embedder() || is_popup()) {
14191426
Control *parent_ctrl = Object::cast_to<Control>(get_parent());
14201427
Transform2D parent_tr = parent_ctrl ? parent_ctrl->get_global_transform() : Transform2D();
14211428
Transform2D tr;
1422-
tr.set_origin(position);
1429+
if (window_id == DisplayServer::INVALID_WINDOW_ID) {
1430+
tr.set_origin(position);
1431+
} else {
1432+
Window *np = get_non_popup_window();
1433+
if (np) {
1434+
tr.set_origin(get_position() - np->get_position());
1435+
}
1436+
}
14231437
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, parent_tr.affine_inverse() * tr);
14241438
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, Rect2(Point2(), size));
14251439

@@ -1540,9 +1554,19 @@ void Window::_notification(int p_what) {
15401554
_make_transient();
15411555
}
15421556
if (visible) {
1557+
if (window_id != DisplayServer::MAIN_WINDOW_ID && get_tree() && get_tree()->is_accessibility_supported()) {
1558+
get_tree()->_accessibility_force_update();
1559+
_accessibility_notify_enter(this);
1560+
}
15431561
notification(NOTIFICATION_VISIBILITY_CHANGED);
15441562
emit_signal(SceneStringName(visibility_changed));
15451563
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
1564+
if (get_parent()) {
1565+
get_parent()->queue_accessibility_update();
1566+
}
1567+
if (embedder) {
1568+
embedder->queue_accessibility_update();
1569+
}
15461570
}
15471571

15481572
// Emits NOTIFICATION_THEME_CHANGED internally.
@@ -1584,6 +1608,18 @@ void Window::_notification(int p_what) {
15841608

15851609
set_theme_context(nullptr, false);
15861610

1611+
if (visible && window_id != DisplayServer::MAIN_WINDOW_ID) {
1612+
if (get_tree() && get_tree()->is_accessibility_supported()) {
1613+
_accessibility_notify_exit(this);
1614+
if (get_parent()) {
1615+
get_parent()->queue_accessibility_update();
1616+
}
1617+
if (embedder) {
1618+
embedder->queue_accessibility_update();
1619+
}
1620+
}
1621+
}
1622+
15871623
accessibility_title_element = RID();
15881624
accessibility_announcement_element = RID();
15891625

@@ -1816,6 +1852,14 @@ Viewport *Window::get_parent_viewport() const {
18161852
}
18171853
}
18181854

1855+
Window *Window::get_non_popup_window() const {
1856+
Window *w = const_cast<Window *>(this);
1857+
while (w && w->is_popup()) {
1858+
w = w->get_parent_visible_window();
1859+
}
1860+
return w;
1861+
}
1862+
18191863
Window *Window::get_parent_visible_window() const {
18201864
ERR_READ_THREAD_GUARD_V(nullptr);
18211865
Viewport *vp = get_parent_viewport();

scene/main/window.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ class Window : public Viewport {
332332
void set_flag(Flags p_flag, bool p_enabled);
333333
bool get_flag(Flags p_flag) const;
334334

335+
bool is_popup() const;
336+
335337
bool is_maximize_allowed() const;
336338

337339
void request_attention();
@@ -399,6 +401,7 @@ class Window : public Viewport {
399401
Window *get_exclusive_child() const { return exclusive_child; }
400402
HashSet<Window *> get_transient_children() const { return transient_children; }
401403
Window *get_parent_visible_window() const;
404+
Window *get_non_popup_window() const;
402405
Viewport *get_parent_viewport() const;
403406

404407
virtual void popup(const Rect2i &p_screen_rect = Rect2i());

0 commit comments

Comments
 (0)