From 5f72b51105f3072e14e5c3065e6c8eb7cce2d33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= Date: Tue, 14 Feb 2023 14:09:54 +0100 Subject: [PATCH 1/5] Allow setting accessible parent and sibling explicitly This allows to maintain the correct a11y hierarchy even when it contains objects which are only GtkAccessibles. --- gtk/gtkaccessible.c | 51 +++++++++++++++++++++++++-- gtk/gtkaccessible.h | 5 +++ gtk/gtkatcontext.c | 74 +++++++++++++++++++++++++++++++++++++++ gtk/gtkatcontextprivate.h | 13 +++++++ 4 files changed, 141 insertions(+), 2 deletions(-) diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 7143c14006..7d1da7d398 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -45,6 +45,11 @@ * [vfunc@Gtk.Accessible.get_next_accessible_sibling] virtual functions. * Note that you can not create a top-level accessible object as of now, * which means that you must always have a parent accessible object. + * Also note that when an accessible object does not correspond to a widget, + * and it has children, whose implementation you don't control, + * it is necessary to ensure the correct shape of the a11y tree + * by calling gtk_accessible_set_accessible_parent() and + * gtk_accessible_set_next_accessible_sibling() as appropriate. */ #include "config.h" @@ -115,7 +120,43 @@ gtk_accessible_get_accessible_parent (GtkAccessible *self) { g_return_val_if_fail (GTK_IS_ACCESSIBLE (self), NULL); - return GTK_ACCESSIBLE_GET_IFACE (self)->get_accessible_parent (self); + GtkATContext *context; + GtkAccessible *parent = NULL; + + context = gtk_accessible_get_at_context (self); + if (context != NULL) + parent = gtk_at_context_get_accessible_parent (context); + + if (parent != NULL) + return parent; + else + return GTK_ACCESSIBLE_GET_IFACE (self)->get_accessible_parent (self); +} + +/** + * gtk_accessible_set_accessible_parent: + * @self: a `GtkAccessible` + * @parent: the parent `GtkAccessible` + * @next_sibling: the next accessible sibling of this `GtkAccessible` + * + * Sets the parent and next sibling accessible of an accessible object + * + * Since: 4.10 + */ +void +gtk_accessible_set_accessible_parent (GtkAccessible *self, + GtkAccessible *parent, + GtkAccessible *next_sibling) +{ + g_return_if_fail (GTK_IS_ACCESSIBLE (self)); + GtkATContext *context; + + context = gtk_accessible_get_at_context (self); + if (context != NULL) + { + gtk_at_context_set_accessible_parent (context, parent); + gtk_at_context_set_next_accessible_sibling (context, next_sibling); + } } /** @@ -151,7 +192,13 @@ gtk_accessible_get_next_accessible_sibling (GtkAccessible *self) { g_return_val_if_fail (GTK_IS_ACCESSIBLE (self), NULL); - return GTK_ACCESSIBLE_GET_IFACE (self)->get_next_accessible_sibling (self); + GtkATContext *context; + + context = gtk_accessible_get_at_context (self); + if (context != NULL && gtk_at_context_get_accessible_parent (context) != NULL) + return gtk_at_context_get_next_accessible_sibling (context); + else + return GTK_ACCESSIBLE_GET_IFACE (self)->get_next_accessible_sibling (self); } /** diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h index 713a9b5ccf..b9bb5476a5 100644 --- a/gtk/gtkaccessible.h +++ b/gtk/gtkaccessible.h @@ -164,6 +164,11 @@ gboolean gtk_accessible_get_platform_state (GtkAccessible *self, GDK_AVAILABLE_IN_4_10 GtkAccessible * gtk_accessible_get_accessible_parent (GtkAccessible *self); +GDK_AVAILABLE_IN_4_10 +void gtk_accessible_set_accessible_parent (GtkAccessible *self, + GtkAccessible *parent, + GtkAccessible *next_sibling); + GDK_AVAILABLE_IN_4_10 GtkAccessible * gtk_accessible_get_first_accessible_child (GtkAccessible *self); diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index 07d330d59e..040bc43850 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -447,6 +447,80 @@ gtk_at_context_get_accessible_role (GtkATContext *self) return self->accessible_role; } +/*< private > + * gtk_at_context_get_accessible_parent: + * @self: a `GtkAtContext` + * + * Retrieves the parent accessible object of the given `GtkAtContext`. + * + * Returns: (nullable) (transfer none): the parent accessible object, or `NULL` if not set. + */ +GtkAccessible * +gtk_at_context_get_accessible_parent (GtkATContext *self) +{ + g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); + + return self->accessible_parent; +} + +/*< private > + * gtk_at_context_set_accessible_parent: + * @self: a `GtkAtContext` + * @parent: the parent `GtkAccessible` to set + * + * Sets the parent accessible object of the given `GtkAtContext`. + */ +void +gtk_at_context_set_accessible_parent (GtkATContext *self, + GtkAccessible *parent) +{ + g_return_if_fail (GTK_IS_AT_CONTEXT (self)); + + if (self->accessible_parent != parent) + { + if (self->accessible_parent != NULL) + g_object_unref (self->accessible_parent); + self->accessible_parent = g_object_ref (parent); + } +} + +/*< private > + * gtk_at_context_get_next_accessible_sibling: + * @self: a `GtkAtContext` + * + * Retrieves the next accessible sibling of the given `GtkAtContext`. + * + * Returns: (nullable) (transfer none): the next accessible sibling. + */ +GtkAccessible * +gtk_at_context_get_next_accessible_sibling (GtkATContext *self) +{ + g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL); + + return self->next_accessible_sibling; +} + +/*< private > + * gtk_at_context_set_next_accessible_sibling: + * @self: a `GtkAtContext` + * @sibling: (nullable): the next accessible sibling + * + * Sets the next accessible sibling object of the given `GtkAtContext`. + */ +void +gtk_at_context_set_next_accessible_sibling (GtkATContext *self, + GtkAccessible *sibling) +{ + g_return_if_fail (GTK_IS_AT_CONTEXT (self)); + + if (self->next_accessible_sibling != sibling) + { + if (self->next_accessible_sibling != NULL) + g_object_unref (self->next_accessible_sibling); + self->next_accessible_sibling = g_object_ref (sibling); + } +} + /*< private > * gtk_at_context_set_display: * @self: a `GtkATContext` diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h index c0acfc7793..e7ea717243 100644 --- a/gtk/gtkatcontextprivate.h +++ b/gtk/gtkatcontextprivate.h @@ -87,6 +87,8 @@ struct _GtkATContext GtkAccessibleRole accessible_role; GtkAccessible *accessible; + GtkAccessible *accessible_parent; + GtkAccessible *next_accessible_sibling; GdkDisplay *display; GtkAccessibleAttributeSet *states; @@ -179,4 +181,15 @@ const char * gtk_accessible_property_get_attribute_name (GtkAccessiblePr const char * gtk_accessible_relation_get_attribute_name (GtkAccessibleRelation relation); const char * gtk_accessible_state_get_attribute_name (GtkAccessibleState state); +GtkAccessible * +gtk_at_context_get_accessible_parent (GtkATContext *self); +void +gtk_at_context_set_accessible_parent (GtkATContext *self, + GtkAccessible *parent); +GtkAccessible * +gtk_at_context_get_next_accessible_sibling (GtkATContext *self); +void +gtk_at_context_set_next_accessible_sibling (GtkATContext *self, + GtkAccessible *sibling); + G_END_DECLS From d566771262e1a934b04da72c99710db2e3366b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= Date: Tue, 14 Feb 2023 14:44:20 +0100 Subject: [PATCH 2/5] Preserve the correct a11y tree in GtkStack Now, when we can, ensure that the GtkStackPage is the parent of its widget. --- gtk/gtkstack.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index 64bc3ae7d0..025bd663c7 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -410,6 +410,10 @@ gtk_stack_page_set_property (GObject *object, { case CHILD_PROP_CHILD: g_set_object (&info->widget, g_value_get_object (value)); + gtk_accessible_set_accessible_parent ( + GTK_ACCESSIBLE (info->widget), + GTK_ACCESSIBLE (info), + NULL); break; case CHILD_PROP_NAME: From f507dd4962a7fcf84374e702fa118f40723111b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tyrychtr?= Date: Wed, 15 Feb 2023 12:29:16 +0100 Subject: [PATCH 3/5] Allow updating the next accessible sibling for a GtkAccessible --- gtk/gtkaccessible.c | 34 +++++++++++++++++++++++++++++++++- gtk/gtkaccessible.h | 4 ++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 7d1da7d398..dc56e7d3b3 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -49,7 +49,7 @@ * and it has children, whose implementation you don't control, * it is necessary to ensure the correct shape of the a11y tree * by calling gtk_accessible_set_accessible_parent() and - * gtk_accessible_set_next_accessible_sibling() as appropriate. + * updating the sibling by gtk_accessible_update_next_accessible_sibling(). */ #include "config.h" @@ -159,6 +159,38 @@ gtk_accessible_set_accessible_parent (GtkAccessible *self, } } +/** + * gtk_accessible_update_next_accessible_sibling: + * @self: a `GtkAccessible` + * @new_sibling: (nullable): the new next accessible sibling to set + * + * Updates the next accessible sibling of @self. + * That might be useful when a new child of a custom `GtkAccessible` + * is created, and it needs to be linked to a previous child. + * + * Since: 4.10 + */ +void +gtk_accessible_update_next_accessible_sibling (GtkAccessible *self, + GtkAccessible *new_sibling) +{ + GtkATContext *context; + + g_return_if_fail (GTK_IS_ACCESSIBLE (self)); + + context = gtk_accessible_get_at_context (self); + if (!context) + return; + + if (gtk_at_context_get_accessible_parent (context) == NULL) + { + g_critical ("Failed to update next accessible sibling: no parent accessible set for this accessible"); + return; + } + + gtk_at_context_set_next_accessible_sibling (context, new_sibling); +} + /** * gtk_accessible_get_first_accessible_child: * @self: an accessible object diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h index b9bb5476a5..8b40cf8272 100644 --- a/gtk/gtkaccessible.h +++ b/gtk/gtkaccessible.h @@ -174,6 +174,10 @@ GtkAccessible * gtk_accessible_get_first_accessible_child (GtkAccessible *self); GDK_AVAILABLE_IN_4_10 GtkAccessible * gtk_accessible_get_next_accessible_sibling (GtkAccessible *self); +GDK_AVAILABLE_IN_4_10 +void gtk_accessible_update_next_accessible_sibling (GtkAccessible *self, + GtkAccessible *new_sibling); + GDK_AVAILABLE_IN_4_10 gboolean gtk_accessible_get_bounds (GtkAccessible *self, From 5717b95cad9d3d016edd2b7d5968bb562590e687 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 19 Feb 2023 15:09:25 +0000 Subject: [PATCH 4/5] Miscellaneous formatting and documentation fixes --- gtk/gtkaccessible.c | 30 ++++++++++++++++++++---------- gtk/gtkatcontext.c | 14 ++------------ gtk/gtkstack.c | 6 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index dc56e7d3b3..3f1619e708 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -48,8 +48,8 @@ * Also note that when an accessible object does not correspond to a widget, * and it has children, whose implementation you don't control, * it is necessary to ensure the correct shape of the a11y tree - * by calling gtk_accessible_set_accessible_parent() and - * updating the sibling by gtk_accessible_update_next_accessible_sibling(). + * by calling [method@Gtk.Accessible.set_accessible_parent] and + * updating the sibling by [method@Gtk.Accessible.update_next_accessible_sibling]. */ #include "config.h" @@ -135,11 +135,19 @@ gtk_accessible_get_accessible_parent (GtkAccessible *self) /** * gtk_accessible_set_accessible_parent: - * @self: a `GtkAccessible` - * @parent: the parent `GtkAccessible` - * @next_sibling: the next accessible sibling of this `GtkAccessible` + * @self: an accessible object + * @parent: (nullable): the parent accessible object + * @next_sibling: (nullable): the sibling accessible object * - * Sets the parent and next sibling accessible of an accessible object + * Sets the parent and sibling of an accessible object. + * + * This function is meant to be used by accessible implementations that are + * not part of the widget hierarchy, and but act as a logical bridge between + * widgets. For instance, if a widget creates an object that holds metadata + * for each child, and you want that object to implement the `GtkAccessible` + * interface, you will use this function to ensure that the parent of each + * child widget is the metadata object, and the parent of each metadata + * object is the container widget. * * Since: 4.10 */ @@ -149,14 +157,16 @@ gtk_accessible_set_accessible_parent (GtkAccessible *self, GtkAccessible *next_sibling) { g_return_if_fail (GTK_IS_ACCESSIBLE (self)); + g_return_if_fail (parent == NULL || GTK_IS_ACCESSIBLE (parent)); + g_return_if_fail (next_sibling == NULL || GTK_IS_ACCESSIBLE (parent)); GtkATContext *context; context = gtk_accessible_get_at_context (self); if (context != NULL) - { - gtk_at_context_set_accessible_parent (context, parent); - gtk_at_context_set_next_accessible_sibling (context, next_sibling); - } + { + gtk_at_context_set_accessible_parent (context, parent); + gtk_at_context_set_next_accessible_sibling (context, next_sibling); + } } /** diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index 040bc43850..5b25a74703 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -476,12 +476,7 @@ gtk_at_context_set_accessible_parent (GtkATContext *self, { g_return_if_fail (GTK_IS_AT_CONTEXT (self)); - if (self->accessible_parent != parent) - { - if (self->accessible_parent != NULL) - g_object_unref (self->accessible_parent); - self->accessible_parent = g_object_ref (parent); - } + g_set_object (&self->accessible_parent, parent); } /*< private > @@ -513,12 +508,7 @@ gtk_at_context_set_next_accessible_sibling (GtkATContext *self, { g_return_if_fail (GTK_IS_AT_CONTEXT (self)); - if (self->next_accessible_sibling != sibling) - { - if (self->next_accessible_sibling != NULL) - g_object_unref (self->next_accessible_sibling); - self->next_accessible_sibling = g_object_ref (sibling); - } + g_set_object (&self->next_accessible_sibling, sibling); } /*< private > diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index 025bd663c7..e2e070a103 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -411,9 +411,9 @@ gtk_stack_page_set_property (GObject *object, case CHILD_PROP_CHILD: g_set_object (&info->widget, g_value_get_object (value)); gtk_accessible_set_accessible_parent ( - GTK_ACCESSIBLE (info->widget), - GTK_ACCESSIBLE (info), - NULL); + gtk_accessible_set_accessible_parent (GTK_ACCESSIBLE (info->widget), + GTK_ACCESSIBLE (info), + NULL); break; case CHILD_PROP_NAME: From 70a711882c536db5b4cce73cf93f6e0062363fb5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 19 Feb 2023 15:26:27 +0000 Subject: [PATCH 5/5] Fix the build --- gtk/gtkstack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gtk/gtkstack.c b/gtk/gtkstack.c index e2e070a103..05364388a2 100644 --- a/gtk/gtkstack.c +++ b/gtk/gtkstack.c @@ -410,7 +410,6 @@ gtk_stack_page_set_property (GObject *object, { case CHILD_PROP_CHILD: g_set_object (&info->widget, g_value_get_object (value)); - gtk_accessible_set_accessible_parent ( gtk_accessible_set_accessible_parent (GTK_ACCESSIBLE (info->widget), GTK_ACCESSIBLE (info), NULL);