From 1bdc8b90618b059534d447912c09c11f6cf961ab Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 11:02:35 +0000 Subject: [PATCH 1/7] a11y: Proxy GtkWidget:accessible-role The accessible-role property in GtkWidget has three possible targets: - the :accessible-role of a GtkATContext, if realized - the accessible_role field of GtkWidgetPrivate - the accessible_role field of GtkWidgetClassPrivate When we set the accessible role of a widget using the GObject property mechanism, we want to either set the GtkWidgetPrivate.accessible_role field, if there's no ATContext *or* if the ATContext is not realized. Conversely, when we get the accessible-role property we want to have a series of fallbacks in place: - if GtkAccessible.get_at_context() returns an ATContext, and that ATContext is realized, return the :accessible-role of the context - if GtkWidgetPrivate.accessible_role is not WIDGET, return the stored accessible role - return GtkWidgetClassPrivate.accessible_role This should help catch the case of getting the accessible role of widgets that override GtkAccessible.get_at_context(), like GtkModelButton. See: #3342 --- gtk/gtkwidget.c | 71 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 023f0ceb3a..f3670fa96c 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -640,6 +640,9 @@ static void gtk_widget_buildable_custom_finished (GtkBuildable static void gtk_widget_buildable_parser_finished (GtkBuildable *buildable, GtkBuilder *builder); +static void gtk_widget_set_accessible_role (GtkWidget *self, + GtkAccessibleRole role); +static GtkAccessibleRole gtk_widget_get_accessible_role (GtkWidget *self); static GtkSizeRequestMode gtk_widget_real_get_request_mode (GtkWidget *widget); @@ -1731,21 +1734,7 @@ gtk_widget_set_property (GObject *object, gtk_widget_set_layout_manager (widget, g_value_dup_object (value)); break; case PROP_ACCESSIBLE_ROLE: - if (priv->at_context == NULL || !gtk_at_context_is_realized (priv->at_context)) - { - priv->accessible_role = g_value_get_enum (value); - if (priv->at_context) - g_object_set (priv->at_context, "accessible-role", priv->accessible_role, NULL); - g_object_notify_by_pspec (object, pspec); - } - else - { - char *role_str = g_enum_to_string (GTK_TYPE_ACCESSIBLE_ROLE, priv->accessible_role); - g_critical ("Widget of type “%s” already has an accessible role of type “%s”", - G_OBJECT_TYPE_NAME (object), - role_str); - g_free (role_str); - } + gtk_widget_set_accessible_role (widget, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1878,14 +1867,7 @@ gtk_widget_get_property (GObject *object, g_value_set_object (value, gtk_widget_get_layout_manager (widget)); break; case PROP_ACCESSIBLE_ROLE: - { - GtkAccessibleRole role = priv->accessible_role; - - if (priv->accessible_role == GTK_ACCESSIBLE_ROLE_WIDGET) - role = gtk_widget_class_get_accessible_role (GTK_WIDGET_GET_CLASS (widget)); - - g_value_set_enum (value, role); - } + g_value_set_enum (value, gtk_widget_get_accessible_role (widget)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -12564,6 +12546,49 @@ gtk_widget_update_orientation (GtkWidget *widget, -1); } +static void +gtk_widget_set_accessible_role (GtkWidget *self, + GtkAccessibleRole role) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self); + + if (priv->at_context == NULL || !gtk_at_context_is_realized (priv->at_context)) + { + priv->accessible_role = role; + + if (priv->at_context != NULL) + g_object_set (priv->at_context, "accessible-role", priv->accessible_role, NULL); + + g_object_notify (G_OBJECT (self), "accessible-role"); + } + else + { + char *role_str = g_enum_to_string (GTK_TYPE_ACCESSIBLE_ROLE, priv->accessible_role); + + g_critical ("Widget of type “%s” already has an accessible role of type “%s”", + G_OBJECT_TYPE_NAME (self), + role_str); + g_free (role_str); + } +} + +static GtkAccessibleRole +gtk_widget_get_accessible_role (GtkWidget *self) +{ + GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self); + GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (self)); + + if (context == NULL || !gtk_at_context_is_realized (context)) + { + if (priv->accessible_role == GTK_ACCESSIBLE_ROLE_WIDGET) + return gtk_widget_class_get_accessible_role (GTK_WIDGET_GET_CLASS (self)); + + return priv->accessible_role; + } + + return gtk_at_context_get_accessible_role (context); +} + /** * gtk_widget_class_set_accessible_role: * @widget_class: a #GtkWidgetClass From 6af89e90348ae83d1d88fbf094660205fa45989f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:08:16 +0000 Subject: [PATCH 2/7] a11y: Add GValue initializers for accessible attributes Consumers of the GValue-based API for GtkAccessible need to have a way to initialize the GValue with the correct type for the given attribute. --- gtk/gtkaccessible.h | 10 +++ gtk/gtkaccessiblevalue.c | 140 ++++++++++++++++++++++++++++++++ gtk/gtkaccessiblevalueprivate.h | 4 + gtk/gtkaccessiblevaluestatic.c | 24 ++++++ 4 files changed, 178 insertions(+) diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h index c8b00cd35b..17a31ba4a4 100644 --- a/gtk/gtkaccessible.h +++ b/gtk/gtkaccessible.h @@ -76,4 +76,14 @@ GDK_AVAILABLE_IN_ALL void gtk_accessible_reset_relation (GtkAccessible *self, GtkAccessibleRelation relation); +GDK_AVAILABLE_IN_ALL +void gtk_accessible_state_init_value (GtkAccessibleState state, + GValue *value); +GDK_AVAILABLE_IN_ALL +void gtk_accessible_property_init_value (GtkAccessibleProperty property, + GValue *value); +GDK_AVAILABLE_IN_ALL +void gtk_accessible_relation_init_value (GtkAccessibleRelation relation, + GValue *value); + G_END_DECLS diff --git a/gtk/gtkaccessiblevalue.c b/gtk/gtkaccessiblevalue.c index c459067812..ae9a0626b4 100644 --- a/gtk/gtkaccessiblevalue.c +++ b/gtk/gtkaccessiblevalue.c @@ -641,6 +641,7 @@ typedef struct { GCallback ctor; GCallback getter; GCallback parser; + GCallback init_value; } GtkAccessibleCollect; static const GtkAccessibleCollect collect_states[] = { @@ -676,6 +677,7 @@ static const GtkAccessibleCollect collect_states[] = { .ctor = (GCallback) gtk_invalid_accessible_value_new, .getter = (GCallback) gtk_invalid_accessible_value_get, .parser = (GCallback) gtk_invalid_accessible_value_parse, + .init_value = (GCallback) gtk_invalid_accessible_value_init_value, }, [GTK_ACCESSIBLE_STATE_PRESSED] = { .value = GTK_ACCESSIBLE_STATE_PRESSED, @@ -698,6 +700,7 @@ static const GtkAccessibleCollect collect_props[] = { .ctor = (GCallback) gtk_autocomplete_accessible_value_new, .getter = (GCallback) gtk_autocomplete_accessible_value_get, .parser = (GCallback) gtk_autocomplete_accessible_value_parse, + .init_value = (GCallback) gtk_autocomplete_accessible_value_init_value, }, [GTK_ACCESSIBLE_PROPERTY_DESCRIPTION] = { .value = GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, @@ -750,6 +753,7 @@ static const GtkAccessibleCollect collect_props[] = { .ctor = (GCallback) gtk_orientation_accessible_value_new, .getter = (GCallback) gtk_orientation_accessible_value_get, .parser = (GCallback) gtk_orientation_accessible_value_parse, + .init_value = (GCallback) gtk_orientation_accessible_value_init_value, }, [GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER] = { .value = GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER, @@ -778,6 +782,7 @@ static const GtkAccessibleCollect collect_props[] = { .ctor = (GCallback) gtk_sort_accessible_value_new, .getter = (GCallback) gtk_sort_accessible_value_get, .parser = (GCallback) gtk_sort_accessible_value_parse, + .init_value = (GCallback) gtk_sort_accessible_value_init_value, }, [GTK_ACCESSIBLE_PROPERTY_VALUE_MAX] = { .value = GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, @@ -907,6 +912,7 @@ typedef GtkAccessibleValue * (* GtkAccessibleValueRefListCtor) (GList *value); typedef GtkAccessibleValue * (* GtkAccessibleValueEnumParser) (const char *str, gsize len, GError **error); +typedef void (* GtkAccessibleValueEnumInit) (GValue *value); /*< private > * gtk_accessible_value_get_default_for_state: @@ -1099,6 +1105,8 @@ gtk_accessible_value_collect_valist (const GtkAccessibleCollect *cstate, { if (value != NULL) res = gtk_reference_accessible_value_new (value); + else + res = gtk_undefined_accessible_value_new (); } else { @@ -1448,6 +1456,75 @@ gtk_accessible_value_parse (const GtkAccessibleCollect *cstate, return res; } + +static void +gtk_accessible_attribute_init_value (const GtkAccessibleCollect *cstate, + GValue *value) +{ + GtkAccessibleCollectType ctype = cstate->ctype; + gboolean collects_undef = (ctype & GTK_ACCESSIBLE_COLLECT_UNDEFINED) != 0; + + ctype &= (GTK_ACCESSIBLE_COLLECT_UNDEFINED - 1); + + /* Tristate values include "undefined" by definition */ + if (ctype == GTK_ACCESSIBLE_COLLECT_TRISTATE) + collects_undef = TRUE; + + switch (ctype) + { + case GTK_ACCESSIBLE_COLLECT_BOOLEAN: + { + if (collects_undef) + g_value_init (value, G_TYPE_INT); + else + g_value_init (value, G_TYPE_BOOLEAN); + } + break; + + case GTK_ACCESSIBLE_COLLECT_TRISTATE: + g_value_init (value, GTK_TYPE_ACCESSIBLE_TRISTATE); + break; + + case GTK_ACCESSIBLE_COLLECT_TOKEN: + if (cstate->init_value != NULL) + { + GtkAccessibleValueEnumInit init_value = + (GtkAccessibleValueEnumInit) cstate->init_value; + + (* init_value) (value); + } + else + g_value_init (value, G_TYPE_INT); + break; + + case GTK_ACCESSIBLE_COLLECT_INTEGER: + g_value_init (value, G_TYPE_INT); + break; + + case GTK_ACCESSIBLE_COLLECT_NUMBER: + g_value_init (value, G_TYPE_DOUBLE); + break; + + case GTK_ACCESSIBLE_COLLECT_STRING: + g_value_init (value, G_TYPE_STRING); + break; + + case GTK_ACCESSIBLE_COLLECT_REFERENCE: + g_value_init (value, GTK_TYPE_ACCESSIBLE); + break; + + case GTK_ACCESSIBLE_COLLECT_REFERENCE_LIST: + g_value_init (value, G_TYPE_POINTER); + break; + + case GTK_ACCESSIBLE_COLLECT_UNDEFINED: + case GTK_ACCESSIBLE_COLLECT_INVALID: + default: + g_assert_not_reached (); + break; + } +} + /*< private > * gtk_accessible_value_collect_for_state: * @state: a #GtkAccessibleState @@ -1517,6 +1594,27 @@ gtk_accessible_value_parse_for_state (GtkAccessibleState state, return gtk_accessible_value_parse (cstate, str, len, error); } +/** + * gtk_accessible_state_init_value: + * @state: a #GtkAccessibleState + * @value: an uninitialized #GValue + * + * Initializes @value with the appropriate type for the @state. + * + * This function is mostly meant for language bindings, in conjunction + * with gtk_accessible_update_relation_state(). + */ +void +gtk_accessible_state_init_value (GtkAccessibleState state, + GValue *value) +{ + const GtkAccessibleCollect *cstate = &collect_states[state]; + + g_return_if_fail (state <= GTK_ACCESSIBLE_STATE_SELECTED); + + gtk_accessible_attribute_init_value (cstate, value); +} + /*< private > * gtk_accessible_value_get_default_for_property: * @property: a #GtkAccessibleProperty @@ -1647,6 +1745,27 @@ gtk_accessible_value_parse_for_property (GtkAccessibleProperty property, return gtk_accessible_value_parse (cstate, str, len, error); } +/** + * gtk_accessible_property_init_value: + * @property: a #GtkAccessibleProperty + * @value: an uninitialized #GValue + * + * Initializes @value with the appropriate type for the @property. + * + * This function is mostly meant for language bindings, in conjunction + * with gtk_accessible_update_property_value(). + */ +void +gtk_accessible_property_init_value (GtkAccessibleProperty property, + GValue *value) +{ + const GtkAccessibleCollect *cstate = &collect_props[property]; + + g_return_if_fail (property <= GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT); + + gtk_accessible_attribute_init_value (cstate, value); +} + /*< private > * gtk_accessible_value_get_default_for_relation: * @relation: a #GtkAccessibleRelation @@ -1769,4 +1888,25 @@ gtk_accessible_value_parse_for_relation (GtkAccessibleRelation relation, return gtk_accessible_value_parse (cstate, str, len, error); } +/** + * gtk_accessible_relation_init_value: + * @relation: a #GtkAccessibleRelation + * @value: an uninitialized #GValue + * + * Initializes @value with the appropriate type for the @relation. + * + * This function is mostly meant for language bindings, in conjunction + * with gtk_accessible_update_relation_value(). + */ +void +gtk_accessible_relation_init_value (GtkAccessibleRelation relation, + GValue *value) +{ + const GtkAccessibleCollect *cstate = &collect_rels[relation]; + + g_return_if_fail (relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE); + + gtk_accessible_attribute_init_value (cstate, value); +} + /* }}} */ diff --git a/gtk/gtkaccessiblevalueprivate.h b/gtk/gtkaccessiblevalueprivate.h index 3290fc1e6e..5e878aee91 100644 --- a/gtk/gtkaccessiblevalueprivate.h +++ b/gtk/gtkaccessiblevalueprivate.h @@ -158,23 +158,27 @@ GtkAccessibleInvalidState gtk_invalid_accessible_value_get (const G GtkAccessibleValue * gtk_invalid_accessible_value_parse (const char *str, gsize len, GError **error); +void gtk_invalid_accessible_value_init_value (GValue *value); GtkAccessibleValue * gtk_autocomplete_accessible_value_new (GtkAccessibleAutocomplete value); GtkAccessibleAutocomplete gtk_autocomplete_accessible_value_get (const GtkAccessibleValue *value); GtkAccessibleValue * gtk_autocomplete_accessible_value_parse (const char *str, gsize len, GError **error); +void gtk_autocomplete_accessible_value_init_value (GValue *value); GtkAccessibleValue * gtk_orientation_accessible_value_new (GtkOrientation value); GtkOrientation gtk_orientation_accessible_value_get (const GtkAccessibleValue *value); GtkAccessibleValue * gtk_orientation_accessible_value_parse (const char *str, gsize len, GError **error); +void gtk_orientation_accessible_value_init_value (GValue *value); GtkAccessibleValue * gtk_sort_accessible_value_new (GtkAccessibleSort value); GtkAccessibleSort gtk_sort_accessible_value_get (const GtkAccessibleValue *value); GtkAccessibleValue * gtk_sort_accessible_value_parse (const char *str, gsize len, GError **error); +void gtk_sort_accessible_value_init_value (GValue *value); G_END_DECLS diff --git a/gtk/gtkaccessiblevaluestatic.c b/gtk/gtkaccessiblevaluestatic.c index 07600def08..601064c08f 100644 --- a/gtk/gtkaccessiblevaluestatic.c +++ b/gtk/gtkaccessiblevaluestatic.c @@ -298,6 +298,12 @@ gtk_invalid_accessible_value_parse (const char *str, return NULL; } +void +gtk_invalid_accessible_value_init_value (GValue *value) +{ + g_value_init (value, GTK_TYPE_ACCESSIBLE_INVALID_STATE); +} + static const GtkAccessibleValueClass GTK_AUTOCOMPLETE_ACCESSIBLE_VALUE = { .type = GTK_ACCESSIBLE_VALUE_TYPE_TOKEN, .type_name = "GtkAutocompleteAccessibleValue", @@ -356,6 +362,12 @@ gtk_autocomplete_accessible_value_parse (const char *str, return NULL; } +void +gtk_autocomplete_accessible_value_init_value (GValue *value) +{ + g_value_init (value, GTK_TYPE_ACCESSIBLE_AUTOCOMPLETE); +} + static const GtkAccessibleValueClass GTK_ORIENTATION_ACCESSIBLE_VALUE = { .type = GTK_ACCESSIBLE_VALUE_TYPE_TOKEN, .type_name = "GtkOrientationAccessibleValue", @@ -408,6 +420,12 @@ gtk_orientation_accessible_value_parse (const char *str, return NULL; } +void +gtk_orientation_accessible_value_init_value (GValue *value) +{ + g_value_init (value, GTK_TYPE_ORIENTATION); +} + static const GtkAccessibleValueClass GTK_SORT_ACCESSIBLE_VALUE = { .type = GTK_ACCESSIBLE_VALUE_TYPE_TOKEN, .type_name = "GtkSortAccessibleValue", @@ -466,4 +484,10 @@ gtk_sort_accessible_value_parse (const char *str, return NULL; } +void +gtk_sort_accessible_value_init_value (GValue *value) +{ + g_value_init (value, GTK_TYPE_ACCESSIBLE_SORT); +} + /* }}} */ From c264254d4bc2e641042db744a9ada7504d53351f Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:14:47 +0000 Subject: [PATCH 3/7] a11y: Parse reference lists using varargs Using GList is a bit lame, and makes the API more complicated to use than necessary in the common case. The only real use case for a GList is gtk_widget_add_mnemonic_label(), and for that we can use the GValue-based API instead. Fixes: #3343 --- gtk/gtkaccessiblevalue.c | 31 ++++++++++++++++++++++++------- gtk/gtkbutton.c | 3 +-- gtk/gtkexpander.c | 18 +++++++++--------- gtk/gtkframe.c | 2 +- gtk/gtklabel.c | 2 +- gtk/gtkmenubutton.c | 3 +-- gtk/gtkmodelbutton.c | 4 ++-- gtk/gtknotebook.c | 2 +- gtk/gtkpopovermenubar.c | 4 ++-- gtk/gtkscrolledwindow.c | 24 ++++++++++++++---------- gtk/gtkstackswitcher.c | 2 +- gtk/gtktestatcontext.c | 21 ++++++++++++++++++--- gtk/gtktestatcontext.h | 30 +++++++++++++++--------------- gtk/gtkwidget.c | 14 +++++++++----- testsuite/a11y/accessible.c | 19 ++++++------------- testsuite/a11y/button.c | 6 ++---- testsuite/a11y/expander.c | 5 +---- testsuite/a11y/general.c | 5 +---- testsuite/a11y/label.c | 10 ++-------- testsuite/a11y/scrollbar.c | 7 ++----- testsuite/a11y/stack.c | 9 ++------- 21 files changed, 115 insertions(+), 106 deletions(-) diff --git a/gtk/gtkaccessiblevalue.c b/gtk/gtkaccessiblevalue.c index ae9a0626b4..63cf0321d2 100644 --- a/gtk/gtkaccessiblevalue.c +++ b/gtk/gtkaccessiblevalue.c @@ -1120,18 +1120,35 @@ gtk_accessible_value_collect_valist (const GtkAccessibleCollect *cstate, GtkAccessibleValueRefListCtor ctor = (GtkAccessibleValueRefListCtor) cstate->ctor; - GList *value = va_arg (*args, gpointer); + GtkAccessible *ref = va_arg (*args, gpointer); + GList *value = NULL; - if (ctor == NULL) + while (ref != NULL) { - if (value == NULL) - res = NULL; - else - res = gtk_reference_list_accessible_value_new (value); + if (!GTK_IS_ACCESSIBLE (ref)) + { + g_set_error (error, GTK_ACCESSIBLE_VALUE_ERROR, + GTK_ACCESSIBLE_VALUE_ERROR_INVALID_VALUE, + "Reference of type “%s” [%p] does not implement GtkAccessible", + G_OBJECT_TYPE_NAME (ref), ref); + return NULL; + } + + value = g_list_prepend (value, ref); + + ref = va_arg (*args, gpointer); } + + if (value == NULL) + res = gtk_undefined_accessible_value_new (); else { - res = (* ctor) (value); + value = g_list_reverse (value); + + if (ctor == NULL) + res = gtk_reference_list_accessible_value_new (value); + else + res = (* ctor) (value); } } break; diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c index f964eb7495..059853106a 100644 --- a/gtk/gtkbutton.c +++ b/gtk/gtkbutton.c @@ -939,8 +939,7 @@ gtk_button_set_icon_name (GtkButton *button, } gtk_accessible_update_relation (GTK_ACCESSIBLE (button), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, - g_list_append (NULL, priv->child), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, priv->child, NULL, -1); gtk_button_set_child_type (button, ICON_CHILD); diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c index 533b567252..9b60db04a4 100644 --- a/gtk/gtkexpander.c +++ b/gtk/gtkexpander.c @@ -1177,8 +1177,6 @@ void gtk_expander_set_child (GtkExpander *expander, GtkWidget *child) { - GList *list = NULL; - g_return_if_fail (GTK_IS_EXPANDER (expander)); g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); @@ -1201,14 +1199,16 @@ gtk_expander_set_child (GtkExpander *expander, g_object_ref_sink (expander->child); g_object_ref (expander->child); } - } - if (expander->child) - list = g_list_append (list, expander->child); - gtk_accessible_update_relation (GTK_ACCESSIBLE (expander), - GTK_ACCESSIBLE_RELATION_CONTROLS, list, - -1); - g_list_free (list); + gtk_accessible_update_relation (GTK_ACCESSIBLE (expander), + GTK_ACCESSIBLE_RELATION_CONTROLS, expander->child, NULL, + -1); + } + else + { + gtk_accessible_reset_relation (GTK_ACCESSIBLE (expander), + GTK_ACCESSIBLE_RELATION_CONTROLS); + } g_object_notify (G_OBJECT (expander), "child"); } diff --git a/gtk/gtkframe.c b/gtk/gtkframe.c index a841af7777..a8aa980779 100644 --- a/gtk/gtkframe.c +++ b/gtk/gtkframe.c @@ -376,7 +376,7 @@ update_accessible_relation (GtkFrame *frame) if (priv->label_widget) gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->child), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, g_list_append (NULL, priv->label_widget), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, priv->label_widget, NULL, -1); else gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->child), diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 2b3ee4c102..6aedef6dd0 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -1696,7 +1696,7 @@ gtk_label_set_text_internal (GtkLabel *self, self->text = str; gtk_accessible_update_property (GTK_ACCESSIBLE (self), - GTK_ACCESSIBLE_PROPERTY_LABEL, str, + GTK_ACCESSIBLE_PROPERTY_LABEL, self->text, -1); gtk_label_select_region_index (self, 0, 0); diff --git a/gtk/gtkmenubutton.c b/gtk/gtkmenubutton.c index 4807be3994..93376641c0 100644 --- a/gtk/gtkmenubutton.c +++ b/gtk/gtkmenubutton.c @@ -520,8 +520,7 @@ update_sensitivity (GtkMenuButton *self) -1); if (self->popover != NULL) gtk_accessible_update_relation (GTK_ACCESSIBLE (self), - GTK_ACCESSIBLE_RELATION_CONTROLS, - g_list_append (NULL, self->popover), + GTK_ACCESSIBLE_RELATION_CONTROLS, self->popover, NULL, -1); else gtk_accessible_reset_relation (GTK_ACCESSIBLE (self), diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index 4e4815fdc6..f5dc05286a 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -582,7 +582,7 @@ update_accessible_properties (GtkModelButton *button) if (button->popover) gtk_accessible_update_relation (GTK_ACCESSIBLE (button), - GTK_ACCESSIBLE_RELATION_CONTROLS, g_list_append (NULL, button->popover), + GTK_ACCESSIBLE_RELATION_CONTROLS, button->popover, NULL, -1); else gtk_accessible_reset_relation (GTK_ACCESSIBLE (button), @@ -683,7 +683,7 @@ gtk_model_button_set_text (GtkModelButton *button, update_visibility (button); gtk_accessible_update_relation (GTK_ACCESSIBLE (button), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, g_list_append (NULL, button->label), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, button->label, NULL, -1); g_object_notify_by_pspec (G_OBJECT (button), properties[PROP_TEXT]); diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index d210c48fde..6f96faeee1 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -3968,7 +3968,7 @@ gtk_notebook_insert_notebook_page (GtkNotebook *notebook, stack_page = gtk_stack_get_page (GTK_STACK (notebook->stack_widget), page->child); gtk_accessible_update_relation (GTK_ACCESSIBLE (page->tab_widget), - GTK_ACCESSIBLE_RELATION_CONTROLS, g_list_append (NULL, stack_page), + GTK_ACCESSIBLE_RELATION_CONTROLS, stack_page, NULL, -1); gtk_accessible_update_state (GTK_ACCESSIBLE (page->tab_widget), diff --git a/gtk/gtkpopovermenubar.c b/gtk/gtkpopovermenubar.c index 8e7f2a22d0..447dc5fa65 100644 --- a/gtk/gtkpopovermenubar.c +++ b/gtk/gtkpopovermenubar.c @@ -341,8 +341,8 @@ gtk_popover_menu_bar_item_root (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_popover_menu_bar_item_parent_class)->root (widget); gtk_accessible_update_relation (GTK_ACCESSIBLE (widget), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, g_list_append (NULL, item->label), - GTK_ACCESSIBLE_RELATION_CONTROLS, g_list_append (NULL, item->popover), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, item->label, NULL, + GTK_ACCESSIBLE_RELATION_CONTROLS, item->popover, NULL, -1); gtk_accessible_update_property (GTK_ACCESSIBLE (widget), GTK_ACCESSIBLE_PROPERTY_HAS_POPUP, TRUE, diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index 85cdacacd5..afa250c846 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -4106,7 +4106,6 @@ gtk_scrolled_window_set_child (GtkScrolledWindow *scrolled_window, { GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window); GtkWidget *scrollable_child; - GList *list; g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window)); @@ -4163,16 +4162,21 @@ gtk_scrolled_window_set_child (GtkScrolledWindow *scrolled_window, } if (priv->child) - list = g_list_append (NULL, priv->child); + { + gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->hscrollbar), + GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL, + -1); + gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->vscrollbar), + GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL, + -1); + } else - list = NULL; - gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->hscrollbar), - GTK_ACCESSIBLE_RELATION_CONTROLS, list, - -1); - gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->vscrollbar), - GTK_ACCESSIBLE_RELATION_CONTROLS, list, - -1); - g_list_free (list); + { + gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->hscrollbar), + GTK_ACCESSIBLE_RELATION_CONTROLS); + gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->vscrollbar), + GTK_ACCESSIBLE_RELATION_CONTROLS); + } g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_CHILD]); } diff --git a/gtk/gtkstackswitcher.c b/gtk/gtkstackswitcher.c index 86fe74d418..29b1962ec5 100644 --- a/gtk/gtkstackswitcher.c +++ b/gtk/gtkstackswitcher.c @@ -276,7 +276,7 @@ add_child (guint position, -1); gtk_accessible_update_relation (GTK_ACCESSIBLE (button), - GTK_ACCESSIBLE_RELATION_CONTROLS, g_list_append (NULL, page), + GTK_ACCESSIBLE_RELATION_CONTROLS, page, NULL, -1); g_signal_connect (button, "notify::active", G_CALLBACK (on_button_toggled), self); diff --git a/gtk/gtktestatcontext.c b/gtk/gtktestatcontext.c index 4812bc38ae..c8bd222d1a 100644 --- a/gtk/gtktestatcontext.c +++ b/gtk/gtktestatcontext.c @@ -171,7 +171,12 @@ gtk_test_accessible_check_property (GtkAccessible *accessible, va_end (args); - g_assert_no_error (error); + if (error != NULL) + { + res = g_strdup (error->message); + g_error_free (error); + return res; + } if (check_value == NULL) check_value = gtk_accessible_value_get_default_for_property (property); @@ -233,7 +238,12 @@ gtk_test_accessible_check_state (GtkAccessible *accessible, va_end (args); - g_assert_no_error (error); + if (error != NULL) + { + res = g_strdup (error->message); + g_error_free (error); + return res; + } if (check_value == NULL) check_value = gtk_accessible_value_get_default_for_state (state); @@ -295,7 +305,12 @@ gtk_test_accessible_check_relation (GtkAccessible *accessible, va_end (args); - g_assert_no_error (error); + if (error != NULL) + { + res = g_strdup (error->message); + g_error_free (error); + return res; + } if (check_value == NULL) check_value = gtk_accessible_value_get_default_for_relation (relation); diff --git a/gtk/gtktestatcontext.h b/gtk/gtktestatcontext.h index 430e7c405d..70d248b19a 100644 --- a/gtk/gtktestatcontext.h +++ b/gtk/gtktestatcontext.h @@ -52,19 +52,19 @@ G_STMT_START { \ * gtk_test_accessible_assert_property: * @accessible: a #GtkAccessible * @property: a #GtkAccessibleProperty - * @value: the value of @property + * @...: the value of @property * * Checks whether a #GtkAccessible implementation has its accessible - * property set to the expected @value, and raises an assertion if the + * property set to the expected value, and raises an assertion if the * condition is not satisfied. */ -#define gtk_test_accessible_assert_property(accessible,property,value) \ +#define gtk_test_accessible_assert_property(accessible,property,...) \ G_STMT_START { \ GtkAccessible *__a = GTK_ACCESSIBLE (accessible); \ GtkAccessibleProperty __p = (property); \ - char *__value = gtk_test_accessible_check_property (__a, __p, (value)); \ + char *__value = gtk_test_accessible_check_property (__a, __p, __VA_ARGS__); \ if (__value == NULL) ; else { \ - char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-property(" #property ") == " #value "): value = '%s'", __value); \ + char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-property(" #property ") == " # __VA_ARGS__ "): value = '%s'", __value); \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ g_free (__msg); \ } \ @@ -74,19 +74,19 @@ G_STMT_START { \ * gtk_test_accessible_assert_relation: * @accessible: a #GtkAccessible * @relation: a #GtkAccessibleRelation - * @value: the expected value of @relation + * @...: the expected value of @relation * * Checks whether a #GtkAccessible implementation has its accessible - * relation set to the expected @value, and raises an assertion if the + * relation set to the expected value, and raises an assertion if the * condition is not satisfied. */ -#define gtk_test_accessible_assert_relation(accessible,relation,value) \ +#define gtk_test_accessible_assert_relation(accessible,relation,...) \ G_STMT_START { \ GtkAccessible *__a = GTK_ACCESSIBLE (accessible); \ GtkAccessibleRelation __r = (relation); \ - char *__value = gtk_test_accessible_check_relation (__a, __r, (value)); \ + char *__value = gtk_test_accessible_check_relation (__a, __r, __VA_ARGS__); \ if (__value == NULL); else { \ - char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-relation(" #relation ") == " #value "): value = '%s'", __value); \ + char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-relation(" #relation ") == " # __VA_ARGS__ "): value = '%s'", __value); \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ g_free (__msg); \ } \ @@ -96,19 +96,19 @@ G_STMT_START { \ * gtk_test_accessible_assert_state: * @accessible: a #GtkAccessible * @state: a #GtkAccessibleRelation - * @value: the expected value of @state + * @...: the expected value of @state * * Checks whether a #GtkAccessible implementation has its accessible - * state set to the expected @value, and raises an assertion if the + * state set to the expected value, and raises an assertion if the * condition is not satisfied. */ -#define gtk_test_accessible_assert_state(accessible,state,value) \ +#define gtk_test_accessible_assert_state(accessible,state,...) \ G_STMT_START { \ GtkAccessible *__a = GTK_ACCESSIBLE (accessible); \ GtkAccessibleState __s = (state); \ - char *__value = gtk_test_accessible_check_state (__a, __s, (value)); \ + char *__value = gtk_test_accessible_check_state (__a, __s, __VA_ARGS__); \ if (__value == NULL); else { \ - char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-state(" #state ") == " #value "): value = '%s'", __value); \ + char *__msg = g_strdup_printf ("assertion failed: (" #accessible ".accessible-state(" #state ") == " # __VA_ARGS__ "): value = '%s'", __value); \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ g_free (__msg); \ } \ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index f3670fa96c..c29cc88ec0 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -9329,7 +9329,8 @@ gtk_widget_add_mnemonic_label (GtkWidget *widget, GtkWidget *label) { GSList *old_list, *new_list; - GList *list; + GtkAccessibleRelation relation = GTK_ACCESSIBLE_RELATION_LABELLED_BY; + GValue value = G_VALUE_INIT; g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (GTK_IS_WIDGET (label)); @@ -9340,10 +9341,13 @@ gtk_widget_add_mnemonic_label (GtkWidget *widget, g_object_set_qdata_full (G_OBJECT (widget), quark_mnemonic_labels, new_list, (GDestroyNotify) g_slist_free); - list = gtk_widget_list_mnemonic_labels (widget); - gtk_accessible_update_relation (GTK_ACCESSIBLE (widget), - GTK_ACCESSIBLE_RELATION_LABELLED_BY, list, - -1); + /* The ATContext takes ownership of the GList returned by list_mnemonic_labels(), + * so we don't need to free it + */ + gtk_accessible_relation_init_value (relation, &value); + g_value_set_pointer (&value, gtk_widget_list_mnemonic_labels (widget)); + gtk_accessible_update_relation_value (GTK_ACCESSIBLE (widget), 1, &relation, &value); + g_value_unset (&value); } /** diff --git a/testsuite/a11y/accessible.c b/testsuite/a11y/accessible.c index b353d9e94e..2ea857ac3e 100644 --- a/testsuite/a11y/accessible.c +++ b/testsuite/a11y/accessible.c @@ -590,7 +590,6 @@ test_reflist_relation (gconstpointer data) TestObject *object; TestObject *other; TestObject *third; - GList *refs; object = test_object_new (GTK_ACCESSIBLE_ROLE_CHECKBOX); other = test_object_new (GTK_ACCESSIBLE_ROLE_CHECKBOX); @@ -600,21 +599,15 @@ test_reflist_relation (gconstpointer data) gtk_test_accessible_assert_relation (object, relation, NULL); - refs = g_list_append (NULL, other); + gtk_accessible_update_relation (GTK_ACCESSIBLE (object), + relation, other, NULL, + -1); + gtk_test_accessible_assert_relation (object, relation, other, NULL); gtk_accessible_update_relation (GTK_ACCESSIBLE (object), - relation, refs, + relation, other, third, NULL, -1); - gtk_test_accessible_assert_relation (object, relation, refs); - - refs = g_list_append (refs, third); - - gtk_accessible_update_relation (GTK_ACCESSIBLE (object), - relation, refs, - -1); - gtk_test_accessible_assert_relation (object, relation, refs); - - g_list_free (refs); + gtk_test_accessible_assert_relation (object, relation, other, third, NULL); g_object_unref (object); g_object_unref (other); diff --git a/testsuite/a11y/button.c b/testsuite/a11y/button.c index 803a2fecf8..105e424f0c 100644 --- a/testsuite/a11y/button.c +++ b/testsuite/a11y/button.c @@ -32,13 +32,11 @@ static void button_relation (void) { GtkWidget *button = gtk_button_new_with_mnemonic ("_Hello"); - GList *list; g_object_ref_sink (button); - list = g_list_append (NULL, gtk_widget_get_first_child (button)); - gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (button), GTK_ACCESSIBLE_RELATION_LABELLED_BY, list); - g_list_free (list); + gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (button), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, gtk_widget_get_first_child (button), NULL); g_object_unref (button); } diff --git a/testsuite/a11y/expander.c b/testsuite/a11y/expander.c index 169898f796..07d605622b 100644 --- a/testsuite/a11y/expander.c +++ b/testsuite/a11y/expander.c @@ -31,15 +31,12 @@ expander_relations (void) { GtkWidget *widget = gtk_expander_new ("Hello"); GtkWidget *child = gtk_label_new ("Child"); - GList *list; g_object_ref_sink (widget); gtk_expander_set_child (GTK_EXPANDER (widget), child); - list = g_list_append (NULL, child); - gtk_test_accessible_assert_relation (widget, GTK_ACCESSIBLE_RELATION_CONTROLS, list); - g_list_free (list); + gtk_test_accessible_assert_relation (widget, GTK_ACCESSIBLE_RELATION_CONTROLS, child, NULL); g_object_unref (widget); } diff --git a/testsuite/a11y/general.c b/testsuite/a11y/general.c index fe37d05632..c459b5029c 100644 --- a/testsuite/a11y/general.c +++ b/testsuite/a11y/general.c @@ -61,7 +61,6 @@ test_labelled_by (void) { GtkWidget *widget; GtkWidget *label; - GList *list; widget = gtk_switch_new (); g_object_ref_sink (widget); @@ -72,9 +71,7 @@ test_labelled_by (void) g_object_ref_sink (label); gtk_widget_add_mnemonic_label (widget, label); - list = g_list_append (NULL, label); - gtk_test_accessible_assert_relation (widget, GTK_ACCESSIBLE_RELATION_LABELLED_BY, list); - g_list_free (list); + gtk_test_accessible_assert_relation (widget, GTK_ACCESSIBLE_RELATION_LABELLED_BY, label, NULL); g_object_unref (widget); g_object_unref (label); diff --git a/testsuite/a11y/label.c b/testsuite/a11y/label.c index 3eba936bdf..5b6814af49 100644 --- a/testsuite/a11y/label.c +++ b/testsuite/a11y/label.c @@ -18,7 +18,6 @@ label_relations (void) GtkWidget *label = gtk_label_new ("a"); GtkWidget *label2 = gtk_label_new ("b"); GtkWidget *entry = gtk_entry_new (); - GList *list; g_object_ref_sink (label); g_object_ref_sink (label2); @@ -28,16 +27,11 @@ label_relations (void) gtk_widget_add_mnemonic_label (entry, label); - list = g_list_append (NULL, label); - gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (entry), GTK_ACCESSIBLE_RELATION_LABELLED_BY, list); - g_list_free (list); + gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (entry), GTK_ACCESSIBLE_RELATION_LABELLED_BY, label, NULL); gtk_widget_add_mnemonic_label (entry, label2); - list = g_list_append (NULL, label); - list = g_list_append (list, label2); - gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (entry), GTK_ACCESSIBLE_RELATION_LABELLED_BY, list); - g_list_free (list); + gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (entry), GTK_ACCESSIBLE_RELATION_LABELLED_BY, label, label2, NULL); g_object_unref (entry); g_object_unref (label); diff --git a/testsuite/a11y/scrollbar.c b/testsuite/a11y/scrollbar.c index 7120953151..c17ccb74ef 100644 --- a/testsuite/a11y/scrollbar.c +++ b/testsuite/a11y/scrollbar.c @@ -57,7 +57,6 @@ scrollbar_relations (void) GtkWidget *hscrollbar; GtkWidget *vscrollbar; GtkWidget *child; - GList *list; g_object_ref_sink (sw); @@ -70,10 +69,8 @@ scrollbar_relations (void) child = gtk_text_view_new (); gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child); - list = g_list_append (NULL, child); - gtk_test_accessible_assert_relation (hscrollbar, GTK_ACCESSIBLE_RELATION_CONTROLS, list); - gtk_test_accessible_assert_relation (vscrollbar, GTK_ACCESSIBLE_RELATION_CONTROLS, list); - g_list_free (list); + gtk_test_accessible_assert_relation (hscrollbar, GTK_ACCESSIBLE_RELATION_CONTROLS, child, NULL); + gtk_test_accessible_assert_relation (vscrollbar, GTK_ACCESSIBLE_RELATION_CONTROLS, child, NULL); g_object_unref (sw); } diff --git a/testsuite/a11y/stack.c b/testsuite/a11y/stack.c index 3f092c9f55..cfa01e229e 100644 --- a/testsuite/a11y/stack.c +++ b/testsuite/a11y/stack.c @@ -71,7 +71,6 @@ stack_relations (void) GtkWidget *switcher = gtk_stack_switcher_new (); GtkWidget *child; GtkStackPage *page; - GList *list; g_object_ref_sink (stack); g_object_ref_sink (switcher); @@ -83,15 +82,11 @@ stack_relations (void) child = gtk_widget_get_first_child (switcher); page = gtk_stack_get_page (GTK_STACK (stack), child1); - list = g_list_append (NULL, page); - gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (child), GTK_ACCESSIBLE_RELATION_CONTROLS, list); - g_list_free (list); + gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (child), GTK_ACCESSIBLE_RELATION_CONTROLS, page, NULL); child = gtk_widget_get_last_child (switcher); page = gtk_stack_get_page (GTK_STACK (stack), child2); - list = g_list_append (NULL, page); - gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (child), GTK_ACCESSIBLE_RELATION_CONTROLS, list); - g_list_free (list); + gtk_test_accessible_assert_relation (GTK_ACCESSIBLE (child), GTK_ACCESSIBLE_RELATION_CONTROLS, page, NULL); g_object_unref (stack); g_object_unref (switcher); From f9db651f32d9ab032b533cb9961e765d0bdc3ff3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:17:11 +0000 Subject: [PATCH 4/7] a11y: Do not copy the list of references The constructor for GtkReferenceListAccessibleValue is transfer full, which means we should not be copying the GList around. Fixes: #3343 --- gtk/gtkaccessiblevalue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk/gtkaccessiblevalue.c b/gtk/gtkaccessiblevalue.c index 63cf0321d2..bc893ae742 100644 --- a/gtk/gtkaccessiblevalue.c +++ b/gtk/gtkaccessiblevalue.c @@ -569,7 +569,7 @@ gtk_reference_list_accessible_value_new (GList *value) GtkReferenceListAccessibleValue *self = (GtkReferenceListAccessibleValue *) res; - self->refs = g_list_copy (value); + self->refs = value; if (self->refs != NULL) { for (GList *l = self->refs; l != NULL; l = l->next) From 7ed06480cf46768a42462363ac33b6bb8b8a3cdf Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:19:32 +0000 Subject: [PATCH 5/7] a11y: Add private API to clone a GtkATContext Some widgets might want to override GtkAccessible and create their own context in order to control the accessible role post-construction time. To avoid explicitly copying the existing state over from the original ATContext to the new one, we need a way to clone the context's state from inside the ATContext itself. --- gtk/gtkatcontext.c | 55 +++++++++++++++++++++++++++++++++++++++ gtk/gtkatcontextprivate.h | 5 ++++ 2 files changed, 60 insertions(+) diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c index afdf478067..661ac848b1 100644 --- a/gtk/gtkatcontext.c +++ b/gtk/gtkatcontext.c @@ -538,6 +538,61 @@ gtk_at_context_create (GtkAccessibleRole accessible_role, return res; } +/*< private > + * gtk_at_context_clone: (constructor) + * @self: the #GtkATContext to clone + * @role: the accessible role of the clone, or %GTK_ACCESSIBLE_ROLE_NONE to + * use the same accessible role of @self + * @accessible: (nullable): the accessible creating the context, or %NULL to + * use the same #GtkAccessible of @self + * @display: (nullable): the display connection, or %NULL to use the same + * #GdkDisplay of @self + * + * Clones the state of the given #GtkATContext, using @role, @accessible, + * and @display. + * + * If @self is realized, the returned #GtkATContext will also be realized. + * + * Returns: (transfer full): the newly created #GtkATContext + */ +GtkATContext * +gtk_at_context_clone (GtkATContext *self, + GtkAccessibleRole role, + GtkAccessible *accessible, + GdkDisplay *display) +{ + g_return_val_if_fail (self == NULL || GTK_IS_AT_CONTEXT (self), NULL); + g_return_val_if_fail (accessible == NULL || GTK_IS_ACCESSIBLE (accessible), NULL); + g_return_val_if_fail (display == NULL || GDK_IS_DISPLAY (display), NULL); + + if (self != NULL && role == GTK_ACCESSIBLE_ROLE_NONE) + role = self->accessible_role; + + if (self != NULL && accessible == NULL) + accessible = self->accessible; + + if (self != NULL && display == NULL) + display = self->display; + + GtkATContext *res = gtk_at_context_create (role, accessible, display); + + if (self != NULL) + { + g_clear_pointer (&res->states, gtk_accessible_attribute_set_unref); + g_clear_pointer (&res->properties, gtk_accessible_attribute_set_unref); + g_clear_pointer (&res->relations, gtk_accessible_attribute_set_unref); + + res->states = gtk_accessible_attribute_set_ref (self->states); + res->properties = gtk_accessible_attribute_set_ref (self->properties); + res->relations = gtk_accessible_attribute_set_ref (self->relations); + + if (self->realized) + gtk_at_context_realize (res); + } + + return res; +} + gboolean gtk_at_context_is_realized (GtkATContext *self) { diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h index 9f161e9f20..f1b62f8cb8 100644 --- a/gtk/gtkatcontextprivate.h +++ b/gtk/gtkatcontextprivate.h @@ -145,6 +145,11 @@ struct _GtkATContextClass void (* unrealize) (GtkATContext *self); }; +GtkATContext * gtk_at_context_clone (GtkATContext *self, + GtkAccessibleRole role, + GtkAccessible *accessible, + GdkDisplay *display); + GdkDisplay * gtk_at_context_get_display (GtkATContext *self); void gtk_at_context_realize (GtkATContext *self); From ed7619ec1667e0113bc454e1e09aa26c3f57be5e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:21:32 +0000 Subject: [PATCH 6/7] a11y: Clone GtkATContext in GtkModelButton The hypothetical widget that needs to clone ATContext instances because it needs to control the accessible role post-construction is really GtkModelButton. Fixes: #3342 --- gtk/gtkmodelbutton.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/gtk/gtkmodelbutton.c b/gtk/gtkmodelbutton.c index f5dc05286a..6bcf11eb9b 100644 --- a/gtk/gtkmodelbutton.c +++ b/gtk/gtkmodelbutton.c @@ -303,7 +303,8 @@ gtk_model_button_actionable_iface_init (GtkActionableInterface *iface) } static GtkATContext * -create_at_context (GtkModelButton *button) +create_at_context (GtkModelButton *button, + GtkATContext *old_context) { GdkDisplay *display = _gtk_widget_get_display (GTK_WIDGET (button)); GtkAccessibleRole role; @@ -323,6 +324,9 @@ create_at_context (GtkModelButton *button) break; } + if (old_context != NULL) + return gtk_at_context_clone (old_context, role, GTK_ACCESSIBLE (button), display); + return gtk_at_context_create (role, GTK_ACCESSIBLE (button), display); } @@ -332,7 +336,7 @@ gtk_model_button_get_at_context (GtkAccessible *accessible) GtkModelButton *button = GTK_MODEL_BUTTON (accessible); if (button->at_context == NULL) - button->at_context = create_at_context (button); + button->at_context = create_at_context (button, NULL); return button->at_context; } @@ -596,12 +600,18 @@ update_accessible_properties (GtkModelButton *button) else gtk_accessible_reset_state (GTK_ACCESSIBLE (button), GTK_ACCESSIBLE_STATE_CHECKED); + + gtk_accessible_update_relation (GTK_ACCESSIBLE (button), + GTK_ACCESSIBLE_RELATION_LABELLED_BY, button->label, NULL, + -1); } static void gtk_model_button_set_role (GtkModelButton *self, GtkButtonRole role) { + GtkATContext *old_context; + if (role == self->role) return; @@ -621,7 +631,10 @@ gtk_model_button_set_role (GtkModelButton *self, update_node_name (self); gtk_model_button_update_state (self); - g_set_object (&self->at_context, create_at_context (self)); + /* Replace the old context, if any, with a new context */ + old_context = g_steal_pointer (&self->at_context); + self->at_context = create_at_context (self, old_context); + g_clear_object (&old_context); update_accessible_properties (self); From 2229d7f66c91fb44f828af1325e7b0233b34ba37 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Tue, 10 Nov 2020 14:38:09 +0000 Subject: [PATCH 7/7] docs: Add example of relations with lists of references --- docs/reference/gtk/section-accessibility.md | 16 ++++++++++------ gtk/gtkaccessible.c | 12 ++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/reference/gtk/section-accessibility.md b/docs/reference/gtk/section-accessibility.md index b86aac419b..b8145e0ad3 100644 --- a/docs/reference/gtk/section-accessibility.md +++ b/docs/reference/gtk/section-accessibility.md @@ -165,13 +165,13 @@ Each relation name is part of the #GtkAccessibleRelation enumeration. | %GTK_ACCESSIBLE_RELATION_COL_INDEX | “aria-colindex” | integer | | %GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT | “aria-colindextext” | translatable string | | %GTK_ACCESSIBLE_RELATION_COL_SPAN | “aria-colspan” | integer | -| %GTK_ACCESSIBLE_RELATION_CONTROLS | “aria-controls” | a #GList of #GtkAccessible | -| %GTK_ACCESSIBLE_RELATION_DESCRIBED_BY | “aria-describedby” | a #GList of #GtkAccessible | -| %GTK_ACCESSIBLE_RELATION_DETAILS | “aria-details” | a #GList of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_CONTROLS | “aria-controls” | a list of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_DESCRIBED_BY | “aria-describedby” | a list of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_DETAILS | “aria-details” | a list of #GtkAccessible | | %GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE | “aria-errormessage” | #GtkAccessible | -| %GTK_ACCESSIBLE_RELATION_FLOW_TO | “aria-flowto” | a #GList of #GtkAccessible | -| %GTK_ACCESSIBLE_RELATION_LABELLED_BY | “aria-labelledby” | a #GList of #GtkAccessible | -| %GTK_ACCESSIBLE_RELATION_OWNS | “aria-owns” | a #GList of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_FLOW_TO | “aria-flowto” | a list of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_LABELLED_BY | “aria-labelledby” | a list of #GtkAccessible | +| %GTK_ACCESSIBLE_RELATION_OWNS | “aria-owns” | a list of #GtkAccessible | | %GTK_ACCESSIBLE_RELATION_POS_IN_SET | “aria-posinset” | integer | | %GTK_ACCESSIBLE_RELATION_ROW_COUNT | “aria-rowcount” | integer | | %GTK_ACCESSIBLE_RELATION_ROW_INDEX | “aria-rowindex” | integer | @@ -179,6 +179,10 @@ Each relation name is part of the #GtkAccessibleRelation enumeration. | %GTK_ACCESSIBLE_RELATION_ROW_SPAN | “aria-rowspan” | integer | | %GTK_ACCESSIBLE_RELATION_SET_SIZE | “aria-setsize” | integer | +*Note*: When using gtk_accessible_update_relation() with a relation that +requires a list of #GtkAccessible instances, you should pass every +accessible object separately, followed by %NULL. + ## Application development rules Even if standard UI controls provided by GTK have accessibility information diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 39c31d5b1e..13c4cb35ed 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -411,6 +411,18 @@ gtk_accessible_reset_property (GtkAccessible *self, * * This function should be called by #GtkWidget types whenever an accessible * relation change must be communicated to assistive technologies. + * + * If the #GtkAccessibleRelation requires a list of references, you should + * pass each reference individually, followed by %NULL, e.g. + * + * |[ + * gtk_accessible_update_relation (accessible, + * GTK_ACCESSIBLE_RELATION_CONTROLS, + * ref1, NULL, + * GTK_ACCESSIBLE_LABELLED_BY, + * ref1, ref2, ref3, NULL, + * -1); + * ]| */ void gtk_accessible_update_relation (GtkAccessible *self,