diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c index b030d1cfd7..b7ab1458b5 100644 --- a/gtk/a11y/gtkatspicontext.c +++ b/gtk/a11y/gtkatspicontext.c @@ -34,7 +34,6 @@ #include "gtkatspiutilsprivate.h" #include "gtkatspivalueprivate.h" #include "gtkatspicomponentprivate.h" - #include "a11y/atspi/atspi-accessible.h" #include "a11y/atspi/atspi-action.h" #include "a11y/atspi/atspi-editabletext.h" @@ -348,12 +347,12 @@ get_index_in_parent (GtkWidget *widget) child; child = gtk_widget_get_next_sibling (child)) { - if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child))) - continue; - if (child == widget) return idx; + if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child))) + continue; + idx++; } @@ -375,12 +374,12 @@ get_index_in_toplevels (GtkWidget *widget) g_object_unref (window); - if (!gtk_widget_get_visible (window)) - continue; - if (window == widget) return idx; + if (!gtk_accessible_should_present (GTK_ACCESSIBLE (window))) + continue; + idx += 1; } @@ -810,6 +809,23 @@ emit_bounds_changed (GtkAtSpiContext *self, NULL); } +static void +emit_children_changed (GtkAtSpiContext *self, + GtkAtSpiContext *child_context, + int idx, + GtkAccessibleChildState state) +{ + GVariant *child_ref = gtk_at_spi_context_to_ref (child_context); + GVariant *context_ref = gtk_at_spi_context_to_ref (self); + + gtk_at_spi_emit_children_changed (self->connection, + self->context_path, + state, + idx, + child_ref, + context_ref); +} + static void gtk_at_spi_context_state_change (GtkATContext *ctx, GtkAccessibleStateChange changed_states, @@ -821,15 +837,40 @@ gtk_at_spi_context_state_change (GtkATContext *ctx, { GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx); GtkAccessible *accessible = gtk_at_context_get_accessible (ctx); - GtkWidget *widget; GtkAccessibleValue *value; - if (!GTK_IS_WIDGET (accessible)) + if (GTK_IS_WIDGET (accessible) && !gtk_widget_get_realized (GTK_WIDGET (accessible))) return; - widget = GTK_WIDGET (accessible); - if (!gtk_widget_get_realized (widget)) - return; + if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN) + { + GtkWidget *parent; + GtkATContext *context; + GtkAccessibleChildChange change; + + value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_HIDDEN); + if (gtk_boolean_accessible_value_get (value)) + change = GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED; + else + change = GTK_ACCESSIBLE_CHILD_CHANGE_ADDED; + + if (GTK_IS_ROOT (accessible)) + { + gtk_at_spi_root_child_changed (self->root, change, accessible); + } + else + { + if (GTK_IS_WIDGET (accessible)) + parent = gtk_widget_get_parent (GTK_WIDGET (accessible)); + else if (GTK_IS_STACK_PAGE (accessible)) + parent = gtk_widget_get_parent (gtk_stack_page_get_child (GTK_STACK_PAGE (accessible))); + else + g_assert_not_reached (); + + context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent)); + gtk_at_context_child_changed (context, change, accessible); + } + } if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_BUSY) { @@ -1040,6 +1081,120 @@ gtk_at_spi_context_bounds_change (GtkATContext *ctx) emit_bounds_changed (self, (int)x, (int)y, width, height); } + +static void +gtk_at_spi_context_child_change (GtkATContext *ctx, + GtkAccessibleChildChange change, + GtkAccessible *child) +{ + GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx); + GtkAccessible *accessible = gtk_at_context_get_accessible (ctx); + GtkATContext *child_context = gtk_accessible_get_at_context (child); + GtkWidget *parent_widget; + GtkWidget *child_widget; + int idx = 0; + + if (!GTK_IS_WIDGET (accessible)) + return; + + if (child_context == NULL) + return; + + /* handle the stack page special case */ + if (GTK_IS_WIDGET (child) && + GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (child)))) + { + GtkWidget *stack; + GtkStackPage *page; + GListModel *pages; + + stack = gtk_widget_get_parent (GTK_WIDGET (child)); + page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (child)); + pages = G_LIST_MODEL (gtk_stack_get_pages (GTK_STACK (stack))); + idx = 0; + for (guint i = 0; i < g_list_model_get_n_items (pages); i++) + { + GtkStackPage *item = g_list_model_get_item (pages, i); + + g_object_unref (item); + + if (!gtk_accessible_should_present (GTK_ACCESSIBLE (item))) + continue; + + if (item == page) + break; + + idx++; + } + g_object_unref (pages); + + if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED) + { + emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))), + GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))), + idx, + GTK_ACCESSIBLE_CHILD_STATE_ADDED); + + emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))), + GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)), + 0, + GTK_ACCESSIBLE_CHILD_STATE_ADDED); + } + + if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED) + { + emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))), + GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)), + 0, + GTK_ACCESSIBLE_CHILD_STATE_REMOVED); + emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))), + GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))), + idx, + GTK_ACCESSIBLE_CHILD_STATE_REMOVED); + + } + + return; + } + + parent_widget = GTK_WIDGET (accessible); + + if (GTK_IS_STACK_PAGE (child)) + child_widget = gtk_stack_page_get_child (GTK_STACK_PAGE (child)); + else + child_widget = GTK_WIDGET (child); + + if (gtk_widget_get_parent (child_widget) != parent_widget) + { + idx = 0; + } + else + { + for (GtkWidget *iter = gtk_widget_get_first_child (parent_widget); + iter != NULL; + iter = gtk_widget_get_next_sibling (iter)) + { + if (!gtk_accessible_should_present (GTK_ACCESSIBLE (iter))) + continue; + + if (iter == child_widget) + break; + + idx += 1; + } + } + + if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED) + emit_children_changed (self, + GTK_AT_SPI_CONTEXT (child_context), + idx, + GTK_ACCESSIBLE_CHILD_STATE_ADDED); + else if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED) + emit_children_changed (self, + GTK_AT_SPI_CONTEXT (child_context), + idx, + GTK_ACCESSIBLE_CHILD_STATE_REMOVED); +} /* }}} */ /* {{{ D-Bus Registration */ static void @@ -1326,6 +1481,7 @@ gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass) context_class->state_change = gtk_at_spi_context_state_change; context_class->platform_change = gtk_at_spi_context_platform_change; context_class->bounds_change = gtk_at_spi_context_bounds_change; + context_class->child_change = gtk_at_spi_context_child_change; obj_props[PROP_BUS_ADDRESS] = g_param_spec_string ("bus-address", NULL, NULL, diff --git a/gtk/a11y/gtkatspiroot.c b/gtk/a11y/gtkatspiroot.c index 75290623ea..c4c09bdae1 100644 --- a/gtk/a11y/gtkatspiroot.c +++ b/gtk/a11y/gtkatspiroot.c @@ -405,6 +405,65 @@ static const GDBusInterfaceVTable root_accessible_vtable = { NULL, }; +void +gtk_at_spi_root_child_changed (GtkAtSpiRoot *self, + GtkAccessibleChildChange change, + GtkAccessible *child) +{ + guint n, i; + int idx = 0; + GVariant *window_ref; + GtkAccessibleChildState state; + + if (!self->toplevels) + return; + + for (i = 0, n = g_list_model_get_n_items (self->toplevels); i < n; i++) + { + GtkAccessible *item = g_list_model_get_item (self->toplevels, i); + + g_object_unref (item); + + if (item == child) + break; + + if (!gtk_accessible_should_present (item)) + continue; + + idx++; + } + + if (child == NULL) + { + window_ref = gtk_at_spi_null_ref (); + } + else + { + GtkATContext *context = gtk_accessible_get_at_context (child); + + window_ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context)); + } + + switch (change) + { + case GTK_ACCESSIBLE_CHILD_CHANGE_ADDED: + state = GTK_ACCESSIBLE_CHILD_STATE_ADDED; + break; + case GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED: + state = GTK_ACCESSIBLE_CHILD_STATE_REMOVED; + break; + default: + g_assert_not_reached (); + } + + gtk_at_spi_emit_children_changed (self->connection, + self->root_path, + state, + idx, + gtk_at_spi_root_to_ref (self), + window_ref); +} + static void on_registration_reply (GObject *gobject, GAsyncResult *result, @@ -437,7 +496,6 @@ on_registration_reply (GObject *gobject, /* Register the cache object */ self->cache = gtk_at_spi_cache_new (self->connection, ATSPI_CACHE_PATH); - /* Monitor the top levels */ self->toplevels = gtk_window_get_toplevels (); } diff --git a/gtk/a11y/gtkatspirootprivate.h b/gtk/a11y/gtkatspirootprivate.h index 0ce5e6b693..f5ae272999 100644 --- a/gtk/a11y/gtkatspirootprivate.h +++ b/gtk/a11y/gtkatspirootprivate.h @@ -42,4 +42,9 @@ gtk_at_spi_root_get_cache (GtkAtSpiRoot *self); GVariant * gtk_at_spi_root_to_ref (GtkAtSpiRoot *self); +void +gtk_at_spi_root_child_changed (GtkAtSpiRoot *self, + GtkAccessibleChildChange change, + GtkAccessible *child); + G_END_DECLS diff --git a/gtk/a11y/gtkatspiutils.c b/gtk/a11y/gtkatspiutils.c index 1fef628144..fad5ee10fd 100644 --- a/gtk/a11y/gtkatspiutils.c +++ b/gtk/a11y/gtkatspiutils.c @@ -305,3 +305,37 @@ gtk_at_spi_null_ref (void) { return g_variant_new ("(so)", "", "/org/a11y/atspi/null"); } + +void +gtk_at_spi_emit_children_changed (GDBusConnection *connection, + const char *path, + GtkAccessibleChildState state, + int idx, + GVariant *child_ref, + GVariant *sender_ref) +{ + const char *change; + + switch (state) + { + case GTK_ACCESSIBLE_CHILD_STATE_ADDED: + change = "add"; + break; + + case GTK_ACCESSIBLE_CHILD_STATE_REMOVED: + change = "remove"; + break; + + default: + g_assert_not_reached (); + return; + } + + g_dbus_connection_emit_signal (connection, + NULL, + path, + "org.a11y.atspi.Event.Object", + "ChildrenChanged", + g_variant_new ("(siiv@(so))", change, idx, 0, child_ref, sender_ref), + NULL); +} diff --git a/gtk/a11y/gtkatspiutilsprivate.h b/gtk/a11y/gtkatspiutilsprivate.h index 20b0b09305..d54f5896c7 100644 --- a/gtk/a11y/gtkatspiutilsprivate.h +++ b/gtk/a11y/gtkatspiutilsprivate.h @@ -31,4 +31,12 @@ gtk_atspi_role_for_context (GtkATContext *context); GVariant * gtk_at_spi_null_ref (void); +void +gtk_at_spi_emit_children_changed (GDBusConnection *connection, + const char *path, + GtkAccessibleChildState state, + int idx, + GVariant *child_ref, + GVariant *sender_ref); + G_END_DECLS diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index a98bd90ae2..39c31d5b1e 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -742,6 +742,8 @@ gtk_accessible_bounds_changed (GtkAccessible *self) gboolean gtk_accessible_should_present (GtkAccessible *self) { + GtkATContext *context; + if (GTK_IS_WIDGET (self) && !gtk_widget_get_visible (GTK_WIDGET (self))) return FALSE; @@ -750,5 +752,39 @@ gtk_accessible_should_present (GtkAccessible *self) gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_PRESENTATION) return FALSE; + context = gtk_accessible_get_at_context (self); + if (gtk_at_context_has_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN)) + { + GtkAccessibleValue *value; + + value = gtk_at_context_get_accessible_state (context, GTK_ACCESSIBLE_STATE_HIDDEN); + if (gtk_boolean_accessible_value_get (value)) + return FALSE; + } + return TRUE; } + +void +gtk_accessible_update_children (GtkAccessible *self, + GtkAccessible *child, + GtkAccessibleChildState state) +{ + GtkATContext *context; + + if (GTK_IS_WIDGET (self) && + gtk_widget_get_root (GTK_WIDGET (self)) == NULL) + return; + + context = gtk_accessible_get_at_context (self); + + /* propagate changes up from ignored widgets */ + if (gtk_accessible_get_accessible_role (self) == GTK_ACCESSIBLE_ROLE_NONE) + context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (gtk_widget_get_parent (GTK_WIDGET (self)))); + + if (context == NULL) + return; + + gtk_at_context_child_changed (context, 1 << state, child); + gtk_at_context_update (context); +} diff --git a/gtk/gtkaccessibleprivate.h b/gtk/gtkaccessibleprivate.h index 966efc3abb..873af7009a 100644 --- a/gtk/gtkaccessibleprivate.h +++ b/gtk/gtkaccessibleprivate.h @@ -49,4 +49,8 @@ gboolean gtk_accessible_get_platform_state (GtkAccessible *s void gtk_accessible_bounds_changed (GtkAccessible *self); +void gtk_accessible_update_children (GtkAccessible *self, + GtkAccessible *child, + GtkAccessibleChildState state); + G_END_DECLS diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index dd771bc10c..6e0c54693f 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -155,6 +155,13 @@ gtk_at_context_real_bounds_change (GtkATContext *self) { } +static void +gtk_at_context_real_child_change (GtkATContext *self, + GtkAccessibleChildChange change, + GtkAccessible *child) +{ +} + static void gtk_at_context_class_init (GtkATContextClass *klass) { @@ -167,6 +174,7 @@ gtk_at_context_class_init (GtkATContextClass *klass) klass->state_change = gtk_at_context_real_state_change; klass->platform_change = gtk_at_context_real_platform_change; klass->bounds_change = gtk_at_context_real_bounds_change; + klass->child_change = gtk_at_context_real_child_change; /** * GtkATContext:accessible-role: @@ -979,3 +987,11 @@ gtk_at_context_bounds_changed (GtkATContext *self) { GTK_AT_CONTEXT_GET_CLASS (self)->bounds_change (self); } + +void +gtk_at_context_child_changed (GtkATContext *self, + GtkAccessibleChildChange change, + GtkAccessible *child) +{ + GTK_AT_CONTEXT_GET_CLASS (self)->child_change (self, change, child); +} diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h index edc7fa3c94..85d897be90 100644 --- a/gtk/gtkatcontextprivate.h +++ b/gtk/gtkatcontextprivate.h @@ -90,6 +90,16 @@ typedef enum { GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSED = 1 << GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED, } GtkAccessiblePlatformChange; +typedef enum { + GTK_ACCESSIBLE_CHILD_STATE_ADDED, + GTK_ACCESSIBLE_CHILD_STATE_REMOVED +} GtkAccessibleChildState; + +typedef enum { + GTK_ACCESSIBLE_CHILD_CHANGE_ADDED = 1 << GTK_ACCESSIBLE_CHILD_STATE_ADDED, + GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED = 1 << GTK_ACCESSIBLE_CHILD_STATE_REMOVED +} GtkAccessibleChildChange; + struct _GtkATContext { GObject parent_instance; @@ -124,6 +134,10 @@ struct _GtkATContextClass GtkAccessiblePlatformChange changed_platform); void (* bounds_change) (GtkATContext *self); + + void (* child_change) (GtkATContext *self, + GtkAccessibleChildChange changed_child, + GtkAccessible *child); }; GdkDisplay * gtk_at_context_get_display (GtkATContext *self); @@ -158,6 +172,9 @@ char * gtk_at_context_get_description (GtkATContext void gtk_at_context_platform_changed (GtkATContext *self, GtkAccessiblePlatformChange change); void gtk_at_context_bounds_changed (GtkATContext *self); +void gtk_at_context_child_changed (GtkATContext *self, + GtkAccessibleChildChange change, + GtkAccessible *child); const char * gtk_accessible_property_get_attribute_name (GtkAccessibleProperty property); const char * gtk_accessible_relation_get_attribute_name (GtkAccessibleRelation relation); diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index b97ebb54bb..fdf79fbe61 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -1304,6 +1304,10 @@ update_child_visible (GtkStack *stack, gtk_widget_set_child_visible (priv->last_visible_child->widget, FALSE); priv->last_visible_child = NULL; } + + gtk_accessible_update_state (GTK_ACCESSIBLE (child_info), + GTK_ACCESSIBLE_STATE_HIDDEN, !visible, + -1); } static void @@ -1467,29 +1471,24 @@ stack_remove (GtkStack *stack, if (child_info == NULL) return; - priv->children = g_list_remove (priv->children, child_info); - g_signal_handlers_disconnect_by_func (child, stack_child_visibility_notify_cb, stack); was_visible = gtk_widget_get_visible (child); - g_clear_object (&child_info->widget); - if (priv->visible_child == child_info) - { - if (in_dispose) - priv->visible_child = NULL; - else - set_visible_child (stack, NULL, priv->transition_type, priv->transition_duration); - } + priv->visible_child = NULL; if (priv->last_visible_child == child_info) priv->last_visible_child = NULL; gtk_widget_unparent (child); + g_clear_object (&child_info->widget); + + priv->children = g_list_remove (priv->children, child_info); + g_object_unref (child_info); if (!in_dispose && diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index ad61b06674..f9bc34a969 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -2413,6 +2413,10 @@ gtk_widget_unparent (GtkWidget *widget) g_object_freeze_notify (G_OBJECT (widget)); + gtk_accessible_update_children (GTK_ACCESSIBLE (priv->parent), + GTK_ACCESSIBLE (widget), + GTK_ACCESSIBLE_CHILD_STATE_REMOVED); + root = _gtk_widget_get_root (widget); if (GTK_IS_WINDOW (root)) _gtk_window_unset_focus_and_default (GTK_WINDOW (root), widget); @@ -5796,6 +5800,11 @@ gtk_widget_reposition_after (GtkWidget *widget, gtk_widget_queue_compute_expand (parent); } + if (prev_parent == NULL) + gtk_accessible_update_children (GTK_ACCESSIBLE (parent), + GTK_ACCESSIBLE (widget), + GTK_ACCESSIBLE_CHILD_STATE_ADDED); + gtk_widget_pop_verify_invariants (widget); } diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index feec52aa22..6e8f289d0b 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -1554,6 +1554,11 @@ gtk_window_constructed (GObject *object) gtk_widget_add_controller (GTK_WIDGET (object), GTK_EVENT_CONTROLLER (priv->click_gesture)); g_list_store_append (toplevel_list, window); + + gtk_accessible_update_state (GTK_ACCESSIBLE (window), + GTK_ACCESSIBLE_STATE_HIDDEN, TRUE, + -1); + g_object_unref (window); } @@ -7195,11 +7200,16 @@ gtk_window_destroy (GtkWindow *window) return; g_object_ref (window); + gtk_tooltip_unset_surface (GTK_NATIVE (window)); + gtk_window_hide (GTK_WIDGET (window)); + gtk_accessible_update_state (GTK_ACCESSIBLE (window), + GTK_ACCESSIBLE_STATE_HIDDEN, TRUE, + -1); + g_list_store_remove (toplevel_list, i); - gtk_window_hide (GTK_WIDGET (window)); gtk_widget_unrealize (GTK_WIDGET (window)); g_object_unref (window);