Merge branch 'fix_custom_accessible_hierarchy' into 'main'

Allow setting accessible parent and next sibling

See merge request GNOME/gtk!5517
This commit is contained in:
Matthias Clasen
2023-02-19 16:15:49 +00:00
5 changed files with 180 additions and 2 deletions

View File

@@ -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 [method@Gtk.Accessible.set_accessible_parent] and
* updating the sibling by [method@Gtk.Accessible.update_next_accessible_sibling].
*/
#include "config.h"
@@ -117,7 +122,85 @@ 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: an accessible object
* @parent: (nullable): the parent accessible object
* @next_sibling: (nullable): the sibling 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
*/
void
gtk_accessible_set_accessible_parent (GtkAccessible *self,
GtkAccessible *parent,
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_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);
}
/**
@@ -153,7 +236,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);
}
/**

View File

@@ -164,11 +164,20 @@ 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);
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,

View File

@@ -447,6 +447,70 @@ 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));
g_set_object (&self->accessible_parent, 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));
g_set_object (&self->next_accessible_sibling, sibling);
}
/*< private >
* gtk_at_context_set_display:
* @self: a `GtkATContext`

View File

@@ -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

View File

@@ -410,6 +410,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);
break;
case CHILD_PROP_NAME: