1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-20 14:45:44 +00:00

Base accessibility API.

This commit is contained in:
Pāvels Nadtočajevs
2025-03-21 16:42:23 +02:00
parent af2c713971
commit b106dfd4f9
124 changed files with 7631 additions and 181 deletions

View File

@@ -48,6 +48,8 @@ void Window::set_root_layout_direction(int p_root_dir) {
// Dynamic properties.
Window *Window::focused_window = nullptr;
bool Window::_set(const StringName &p_name, const Variant &p_value) {
ERR_MAIN_THREAD_GUARD_V(false);
String name = p_name;
@@ -314,6 +316,7 @@ void Window::set_title(const String &p_title) {
EngineDebugger::get_singleton()->send_message("window:title", arr);
}
#endif
queue_accessibility_update();
}
String Window::get_title() const {
@@ -687,6 +690,11 @@ 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);
@@ -718,6 +726,10 @@ 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;
@@ -749,6 +761,14 @@ void Window::_rect_changed_callback(const Rect2i &p_callback) {
size = p_callback.size;
_update_viewport_size();
}
if (window_id != DisplayServer::INVALID_WINDOW_ID) {
Vector2 sz_out = DisplayServer::get_singleton()->window_get_size_with_decorations(window_id);
Vector2 pos_out = DisplayServer::get_singleton()->window_get_position_with_decorations(window_id);
Vector2 sz_in = DisplayServer::get_singleton()->window_get_size(window_id);
Vector2 pos_in = DisplayServer::get_singleton()->window_get_position(window_id);
DisplayServer::get_singleton()->accessibility_set_window_rect(window_id, Rect2(pos_out, sz_out), Rect2(pos_in, sz_in));
}
queue_accessibility_update();
}
void Window::_propagate_window_notification(Node *p_node, int p_notification) {
@@ -807,12 +827,15 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
} break;
case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
focused = true;
focused_window = this;
_propagate_window_notification(this, NOTIFICATION_WM_WINDOW_FOCUS_IN);
emit_signal(SceneStringName(focus_entered));
} break;
case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
focused = false;
if (focused_window == this) {
focused_window = nullptr;
}
_propagate_window_notification(this, NOTIFICATION_WM_WINDOW_FOCUS_OUT);
emit_signal(SceneStringName(focus_exited));
} break;
@@ -868,6 +891,36 @@ void Window::hide() {
set_visible(false);
}
void Window::_accessibility_notify_enter(Node *p_node) {
p_node->queue_accessibility_update();
if (p_node != this) {
const Window *window = Object::cast_to<Window>(p_node);
if (window && !window->is_embedded()) {
return;
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
_accessibility_notify_enter(p_node->get_child(i));
}
}
void Window::_accessibility_notify_exit(Node *p_node) {
p_node->notification(Node::NOTIFICATION_ACCESSIBILITY_INVALIDATE);
if (p_node != this) {
const Window *window = Object::cast_to<Window>(p_node);
if (window && !window->is_embedded()) {
return;
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
_accessibility_notify_exit(p_node->get_child(i));
}
}
void Window::set_visible(bool p_visible) {
ERR_MAIN_THREAD_GUARD;
if (visible == p_visible) {
@@ -907,9 +960,11 @@ 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);
}
@@ -918,7 +973,11 @@ void Window::set_visible(bool p_visible) {
if (!visible) {
focused = false;
if (focused_window == this) {
focused_window = nullptr;
}
}
notification(NOTIFICATION_VISIBILITY_CHANGED);
emit_signal(SceneStringName(visibility_changed));
@@ -1332,9 +1391,78 @@ Viewport *Window::get_embedder() const {
return nullptr;
}
RID Window::get_accessibility_element() const {
if (is_part_of_edited_scene()) {
return RID();
}
if (get_embedder()) {
return Node::get_accessibility_element();
} else if (window_id != DisplayServer::INVALID_WINDOW_ID) {
return DisplayServer::get_singleton()->accessibility_get_window_root(window_id);
} else {
return RID();
}
}
RID Window::get_focused_accessibility_element() const {
if (window_id == DisplayServer::MAIN_WINDOW_ID) {
if (get_child_count() > 0) {
return get_child(0)->get_focused_accessibility_element(); // Try scene tree root node.
}
}
return Node::get_focused_accessibility_element();
}
void Window::_notification(int p_what) {
ERR_MAIN_THREAD_GUARD;
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
accessibility_title_element = RID();
accessibility_announcement_element = RID();
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_WINDOW);
DisplayServer::get_singleton()->accessibility_update_set_name(ae, tr_title);
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_MODAL, exclusive);
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()) {
Control *parent_ctrl = Object::cast_to<Control>(get_parent());
Transform2D parent_tr = parent_ctrl ? parent_ctrl->get_global_transform() : Transform2D();
Transform2D tr;
tr.set_origin(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));
if (accessibility_title_element.is_null()) {
accessibility_title_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_TITLE_BAR);
}
int w = get_theme_constant(SNAME("title_height"));
DisplayServer::get_singleton()->accessibility_update_set_name(accessibility_title_element, tr_title);
DisplayServer::get_singleton()->accessibility_update_set_bounds(accessibility_title_element, Rect2(Vector2(0, -w), Size2(size.x, w)));
} else {
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, Transform2D());
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, Rect2(Point2(), size));
if (accessibility_announcement_element.is_null()) {
accessibility_announcement_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
}
if (announcement.is_empty()) {
DisplayServer::get_singleton()->accessibility_update_set_live(accessibility_announcement_element, DisplayServer::LIVE_OFF);
} else {
DisplayServer::get_singleton()->accessibility_update_set_name(accessibility_announcement_element, announcement);
DisplayServer::get_singleton()->accessibility_update_set_live(accessibility_announcement_element, DisplayServer::LIVE_ASSERTIVE);
}
}
} break;
case NOTIFICATION_POSTINITIALIZE: {
initialized = true;
@@ -1389,6 +1517,7 @@ void Window::_notification(int p_what) {
// It's the root window!
visible = true; // Always visible.
window_id = DisplayServer::MAIN_WINDOW_ID;
focused_window = this;
DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id);
_update_from_window();
// Since this window already exists (created on start), we must update pos and size from it.
@@ -1467,6 +1596,7 @@ void Window::_notification(int p_what) {
}
}
}
queue_accessibility_update();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -1485,6 +1615,9 @@ void Window::_notification(int p_what) {
set_theme_context(nullptr, false);
accessibility_title_element = RID();
accessibility_announcement_element = RID();
if (transient) {
_clear_transient();
}
@@ -1604,7 +1737,12 @@ bool Window::is_using_font_oversampling() const {
DisplayServer::WindowID Window::get_window_id() const {
ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
if (embedder) {
if (get_embedder()) {
#ifdef TOOLS_ENABLED
if (is_part_of_edited_scene()) {
return DisplayServer::MAIN_WINDOW_ID;
}
#endif
return parent->get_window_id();
}
return window_id;
@@ -2080,6 +2218,11 @@ Rect2i Window::get_usable_parent_rect() const {
return parent_rect;
}
void Window::accessibility_announcement(const String &p_announcement) {
announcement = p_announcement;
queue_accessibility_update();
}
void Window::add_child_notify(Node *p_child) {
if (is_inside_tree() && wrap_controls) {
child_controls_changed();
@@ -2906,8 +3049,6 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title);
ClassDB::bind_method(D_METHOD("get_window_id"), &Window::get_window_id);
ClassDB::bind_method(D_METHOD("set_initial_position", "initial_position"), &Window::set_initial_position);
ClassDB::bind_method(D_METHOD("get_initial_position"), &Window::get_initial_position);
@@ -3055,6 +3196,10 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font);
ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Window::get_theme_default_font_size);
ClassDB::bind_method(D_METHOD("get_window_id"), &Window::get_window_id);
ClassDB::bind_static_method("Window", D_METHOD("get_focused_window"), &Window::get_focused_window);
ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction);
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction);
ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl);
@@ -3227,6 +3372,9 @@ Window::Window() {
}
Window::~Window() {
if (focused_window == this) {
focused_window = nullptr;
}
memdelete(theme_owner);
// Resources need to be disconnected.