diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 96ea3c86023..8f4ae10dd67 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -38,7 +38,7 @@ #include "scene/theme/theme_db.h" void Popup::_input_from_window(const Ref &p_event) { - if ((ac_popup || get_flag(FLAG_POPUP)) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { + if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) { hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally. _close_pressed(); } @@ -115,7 +115,7 @@ void Popup::_notification(int p_what) { } break; case NOTIFICATION_APPLICATION_FOCUS_OUT: { - if (!is_in_edited_scene_root() && (get_flag(FLAG_POPUP) || ac_popup)) { + if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) { if (hide_reason == HIDE_REASON_NONE) { hide_reason = HIDE_REASON_UNFOCUSED; } @@ -126,7 +126,7 @@ void Popup::_notification(int p_what) { } void Popup::_parent_focused() { - if (popped_up && (get_flag(FLAG_POPUP) || ac_popup)) { + if (popped_up && get_flag(FLAG_POPUP)) { if (hide_reason == HIDE_REASON_NONE) { hide_reason = HIDE_REASON_UNFOCUSED; } diff --git a/scene/gui/popup.h b/scene/gui/popup.h index de03ec64b72..1afc9a74586 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -40,7 +40,6 @@ class Popup : public Window { GDCLASS(Popup, Window); LocalVector visible_parents; - bool ac_popup = false; bool popped_up = false; public: @@ -60,7 +59,6 @@ protected: void _close_pressed(); virtual Rect2i _popup_adjust_rect() const override; virtual void _input_from_window(const Ref &p_event) override; - void set_ac_popup() { ac_popup = true; } void _notification(int p_what); void _validate_property(PropertyInfo &p_property) const; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 585a1dedf49..8872d580dfd 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -3209,13 +3209,8 @@ void PopupMenu::popup(const Rect2i &p_bounds) { _native_popup(p_bounds != Rect2i() ? p_bounds : Rect2i(get_position(), Size2i())); } else { if (is_inside_tree()) { - bool ac = get_tree()->is_accessibility_enabled(); - // Note: Native popup menus need keyboard focus to work with screen reader. - set_flag(FLAG_POPUP, !ac); - set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac); - if (ac) { - set_ac_popup(); - } + set_flag(FLAG_POPUP, true); + set_flag(FLAG_NO_FOCUS, !is_embedded()); } moved = Vector2(); @@ -3253,13 +3248,8 @@ void PopupMenu::set_visible(bool p_visible) { } } else { if (is_inside_tree()) { - bool ac = get_tree()->is_accessibility_enabled(); - // Note: Native popup menus need keyboard focus to work with screen reader. - set_flag(FLAG_POPUP, !ac); - set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac); - if (ac) { - set_ac_popup(); - } + set_flag(FLAG_POPUP, true); + set_flag(FLAG_NO_FOCUS, !is_embedded()); } Popup::set_visible(p_visible); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 78f546bba09..7bad977132f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -68,7 +68,7 @@ void Node::_notification(int p_notification) { for (int i = 0; i < get_child_count(); i++) { Node *child_node = get_child(i); Window *child_wnd = Object::cast_to(child_node); - if (child_wnd && !child_wnd->is_embedded()) { + if (child_wnd && !(child_wnd->is_visible() && (child_wnd->is_embedded() || child_wnd->is_popup()))) { continue; } if (child_node->is_part_of_edited_scene()) { @@ -2055,6 +2055,14 @@ Window *Node::get_window() const { return nullptr; } +Window *Node::get_non_popup_window() const { + Window *w = get_window(); + while (w && w->is_popup()) { + w = w->get_parent_visible_window(); + } + return w; +} + Window *Node::get_last_exclusive_window() const { Window *w = get_window(); while (w && w->get_exclusive_child()) { @@ -3687,8 +3695,9 @@ RID Node::get_accessibility_element() const { return RID(); } if (unlikely(data.accessibility_element.is_null())) { - if (get_window() && get_window()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) { - data.accessibility_element = DisplayServer::get_singleton()->accessibility_create_element(get_window()->get_window_id(), DisplayServer::ROLE_CONTAINER); + Window *w = get_non_popup_window(); + if (w && w->get_window_id() != DisplayServer::INVALID_WINDOW_ID && get_window()->is_visible()) { + data.accessibility_element = DisplayServer::get_singleton()->accessibility_create_element(w->get_window_id(), DisplayServer::ROLE_CONTAINER); } } return data.accessibility_element; diff --git a/scene/main/node.h b/scene/main/node.h index 9bc0100914b..8276b4715f5 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -500,6 +500,7 @@ public: Node *find_parent(const String &p_pattern) const; Window *get_window() const; + Window *get_non_popup_window() const; Window *get_last_exclusive_window() const; _FORCE_INLINE_ SceneTree *get_tree() const { diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 4fed916de30..210c361fab6 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -246,10 +246,10 @@ void SceneTree::_process_accessibility_changes(DisplayServer::WindowID p_window_ Vector processed; for (const ObjectID &id : accessibility_change_queue) { Node *node = Object::cast_to(ObjectDB::get_instance(id)); - if (!node || !node->get_window()) { + if (!node || !node->get_non_popup_window() || !node->get_window()->is_visible()) { processed.push_back(id); continue; // Invalid node, remove from list and skip. - } else if (node->get_window()->get_window_id() != p_window_id) { + } else if (node->get_non_popup_window()->get_window_id() != p_window_id) { continue; // Another window, skip. } node->notification(Node::NOTIFICATION_ACCESSIBILITY_UPDATE); @@ -267,6 +267,15 @@ void SceneTree::_process_accessibility_changes(DisplayServer::WindowID p_window_ w_this = w_focus; } + // Popups have no native window focus, but have focused element. + DisplayServer::WindowID popup_id = DisplayServer::get_singleton()->window_get_active_popup(); + if (popup_id != DisplayServer::INVALID_WINDOW_ID) { + Window *popup_w = Window::get_from_id(popup_id); + if (popup_w && w_this->is_ancestor_of(popup_w)) { + w_this = popup_w; + } + } + RID new_focus_element; Control *n_focus = w_this->gui_get_focus_owner(); if (n_focus && !n_focus->is_part_of_edited_scene()) { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 4b0e4dbc0a5..5d1694973cf 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -570,6 +570,10 @@ bool Window::get_flag(Flags p_flag) const { return flags[p_flag]; } +bool Window::is_popup() const { + return get_flag(Window::FLAG_POPUP) || get_flag(Window::FLAG_NO_FOCUS); +} + bool Window::is_maximize_allowed() const { ERR_READ_THREAD_GUARD_V(false); if (window_id != DisplayServer::INVALID_WINDOW_ID) { @@ -680,11 +684,6 @@ void Window::_make_window() { } } - if (get_tree() && get_tree()->is_accessibility_supported()) { - get_tree()->_accessibility_force_update(); - _accessibility_notify_enter(this); - } - _update_window_callbacks(); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_VISIBLE); @@ -716,10 +715,6 @@ void Window::_clear_window() { _update_from_window(); - if (get_tree() && get_tree()->is_accessibility_supported()) { - _accessibility_notify_exit(this); - } - DisplayServer::get_singleton()->delete_sub_window(window_id); window_id = DisplayServer::INVALID_WINDOW_ID; @@ -886,7 +881,7 @@ void Window::_accessibility_notify_enter(Node *p_node) { if (p_node != this) { const Window *window = Object::cast_to(p_node); - if (window && !window->is_embedded()) { + if (window) { return; } } @@ -901,7 +896,7 @@ void Window::_accessibility_notify_exit(Node *p_node) { if (p_node != this) { const Window *window = Object::cast_to(p_node); - if (window && !window->is_embedded()) { + if (window) { return; } } @@ -950,23 +945,35 @@ void Window::set_visible(bool p_visible) { } } embedder->_sub_window_register(this); - embedder->queue_accessibility_update(); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_WHEN_PARENT_VISIBLE); } else { embedder->_sub_window_remove(this); - embedder->queue_accessibility_update(); embedder = nullptr; RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); } _update_window_size(); } - if (!visible) { + if (visible) { + if (get_tree() && get_tree()->is_accessibility_supported()) { + get_tree()->_accessibility_force_update(); + _accessibility_notify_enter(this); + } + } else { + if (get_tree() && get_tree()->is_accessibility_supported()) { + _accessibility_notify_exit(this); + } focused = false; if (focused_window == this) { focused_window = nullptr; } } + if (get_parent()) { + get_parent()->queue_accessibility_update(); + } + if (embedder) { + embedder->queue_accessibility_update(); + } notification(NOTIFICATION_VISIBILITY_CHANGED); emit_signal(SceneStringName(visibility_changed)); @@ -1371,10 +1378,10 @@ Viewport *Window::get_embedder() const { } RID Window::get_accessibility_element() const { - if (is_part_of_edited_scene()) { + if (!visible || is_part_of_edited_scene()) { return RID(); } - if (get_embedder()) { + if (get_embedder() || is_popup()) { return Node::get_accessibility_element(); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { return DisplayServer::get_singleton()->accessibility_get_window_root(window_id); @@ -1415,11 +1422,18 @@ void Window::_notification(int p_what) { DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp(this, &Window::_accessibility_action_grab_focus)); DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_HIDDEN, !visible); - if (get_embedder()) { + if (get_embedder() || is_popup()) { Control *parent_ctrl = Object::cast_to(get_parent()); Transform2D parent_tr = parent_ctrl ? parent_ctrl->get_global_transform() : Transform2D(); Transform2D tr; - tr.set_origin(position); + if (window_id == DisplayServer::INVALID_WINDOW_ID) { + tr.set_origin(position); + } else { + Window *np = get_non_popup_window(); + if (np) { + tr.set_origin(get_position() - np->get_position()); + } + } DisplayServer::get_singleton()->accessibility_update_set_transform(ae, parent_tr.affine_inverse() * tr); DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, Rect2(Point2(), size)); @@ -1540,9 +1554,19 @@ void Window::_notification(int p_what) { _make_transient(); } if (visible) { + if (window_id != DisplayServer::MAIN_WINDOW_ID && get_tree() && get_tree()->is_accessibility_supported()) { + get_tree()->_accessibility_force_update(); + _accessibility_notify_enter(this); + } notification(NOTIFICATION_VISIBILITY_CHANGED); emit_signal(SceneStringName(visibility_changed)); RS::get_singleton()->viewport_set_active(get_viewport_rid(), true); + if (get_parent()) { + get_parent()->queue_accessibility_update(); + } + if (embedder) { + embedder->queue_accessibility_update(); + } } // Emits NOTIFICATION_THEME_CHANGED internally. @@ -1584,6 +1608,18 @@ void Window::_notification(int p_what) { set_theme_context(nullptr, false); + if (visible && window_id != DisplayServer::MAIN_WINDOW_ID) { + if (get_tree() && get_tree()->is_accessibility_supported()) { + _accessibility_notify_exit(this); + if (get_parent()) { + get_parent()->queue_accessibility_update(); + } + if (embedder) { + embedder->queue_accessibility_update(); + } + } + } + accessibility_title_element = RID(); accessibility_announcement_element = RID(); @@ -1816,6 +1852,14 @@ Viewport *Window::get_parent_viewport() const { } } +Window *Window::get_non_popup_window() const { + Window *w = const_cast(this); + while (w && w->is_popup()) { + w = w->get_parent_visible_window(); + } + return w; +} + Window *Window::get_parent_visible_window() const { ERR_READ_THREAD_GUARD_V(nullptr); Viewport *vp = get_parent_viewport(); diff --git a/scene/main/window.h b/scene/main/window.h index 444019cb440..64d99a4ff82 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -332,6 +332,8 @@ public: void set_flag(Flags p_flag, bool p_enabled); bool get_flag(Flags p_flag) const; + bool is_popup() const; + bool is_maximize_allowed() const; void request_attention(); @@ -399,6 +401,7 @@ public: Window *get_exclusive_child() const { return exclusive_child; } HashSet get_transient_children() const { return transient_children; } Window *get_parent_visible_window() const; + Window *get_non_popup_window() const; Viewport *get_parent_viewport() const; virtual void popup(const Rect2i &p_screen_rect = Rect2i());