forked from AuroraMiddleware/gtk
a11y: Rework accessible name/description computation
The ARIA spec determines the name and description of accessible elements in a more complex way that simply mapping to a single property; instead, it will chain up multiple definitions (if it finds them). For instance, let's assume we have a button that saves a file selected from a file selection widget; the widgets have the following attributes: - the file selection widget has a "label" attribute set to the selected file, e.g. "Final paper.pdf" - the "download" button has a "label" attribute set to the "Download" string - the "download" button has a "labelled-by" attribute set to reference the file selection widget The ARIA spec says that the accessible name of the "Download" button should be computed as "Download Final paper.pdf". The algorithm defined in section 4.3 of the WAI-ARIA specification applies to both accessible names (using the "label" and "labelled-by" attributes), and to accessible descriptions (using the "description" and "described-by" attributes).
This commit is contained in:
parent
94729b4880
commit
7c7dabae8c
@ -606,7 +606,14 @@ handle_accessible_get_property (GDBusConnection *connection,
|
||||
}
|
||||
else if (g_strcmp0 (property_name, "Description") == 0)
|
||||
{
|
||||
char *label = gtk_at_context_get_label (GTK_AT_CONTEXT (self));
|
||||
char *label = gtk_at_context_get_description (GTK_AT_CONTEXT (self));
|
||||
|
||||
if (label == NULL || *label == '\0')
|
||||
{
|
||||
g_free (label);
|
||||
label = gtk_at_context_get_name (GTK_AT_CONTEXT (self));
|
||||
}
|
||||
|
||||
res = g_variant_new_string (label);
|
||||
g_free (label);
|
||||
}
|
||||
@ -932,7 +939,14 @@ gtk_at_spi_context_state_change (GtkATContext *ctx,
|
||||
|
||||
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_LABEL)
|
||||
{
|
||||
char *label = gtk_at_context_get_label (GTK_AT_CONTEXT (self));
|
||||
char *label = gtk_at_context_get_name (GTK_AT_CONTEXT (self));
|
||||
GVariant *v = g_variant_new_take_string (label);
|
||||
emit_property_changed (self, "accessible-name", v);
|
||||
}
|
||||
|
||||
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_DESCRIPTION)
|
||||
{
|
||||
char *label = gtk_at_context_get_description (GTK_AT_CONTEXT (self));
|
||||
GVariant *v = g_variant_new_take_string (label);
|
||||
emit_property_changed (self, "accessible-description", v);
|
||||
}
|
||||
|
@ -730,47 +730,34 @@ gtk_at_context_get_accessible_relation (GtkATContext *self,
|
||||
return gtk_accessible_attribute_set_get_value (self->relations, relation);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gtk_at_context_get_label:
|
||||
* @self: a #GtkATContext
|
||||
*
|
||||
* Retrieves the accessible label of the #GtkATContext.
|
||||
*
|
||||
* This is a convenience function meant to be used by #GtkATContext implementations.
|
||||
*
|
||||
* Returns: (transfer full): the label of the #GtkATContext
|
||||
*/
|
||||
char *
|
||||
gtk_at_context_get_label (GtkATContext *self)
|
||||
/* See the WAI-ARIA § 4.3, "Accessible Name and Description Computation" */
|
||||
static void
|
||||
gtk_at_context_get_name_accumulate (GtkATContext *self,
|
||||
GPtrArray *names,
|
||||
gboolean recurse)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
||||
|
||||
GtkAccessibleValue *value = NULL;
|
||||
|
||||
if (gtk_accessible_attribute_set_contains (self->states, GTK_ACCESSIBLE_STATE_HIDDEN))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->states, GTK_ACCESSIBLE_STATE_HIDDEN);
|
||||
|
||||
if (gtk_boolean_accessible_value_get (value))
|
||||
return g_strdup ("");
|
||||
}
|
||||
|
||||
if (gtk_accessible_attribute_set_contains (self->properties, GTK_ACCESSIBLE_PROPERTY_LABEL))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->properties, GTK_ACCESSIBLE_PROPERTY_LABEL);
|
||||
|
||||
return g_strdup (gtk_string_accessible_value_get (value));
|
||||
g_ptr_array_add (names, (char *) gtk_string_accessible_value_get (value));
|
||||
}
|
||||
|
||||
if (gtk_accessible_attribute_set_contains (self->relations, GTK_ACCESSIBLE_RELATION_LABELLED_BY))
|
||||
if (recurse && gtk_accessible_attribute_set_contains (self->relations, GTK_ACCESSIBLE_RELATION_LABELLED_BY))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->relations, GTK_ACCESSIBLE_RELATION_LABELLED_BY);
|
||||
|
||||
GList *list = gtk_reference_list_accessible_value_get (value);
|
||||
GtkAccessible *rel = GTK_ACCESSIBLE (list->data);
|
||||
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
|
||||
|
||||
return gtk_at_context_get_label (rel_context);
|
||||
for (GList *l = list; l != NULL; l = l->data)
|
||||
{
|
||||
GtkAccessible *rel = GTK_ACCESSIBLE (l->data);
|
||||
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
|
||||
|
||||
gtk_at_context_get_name_accumulate (rel_context, names, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
GtkAccessibleRole role = gtk_at_context_get_accessible_role (self);
|
||||
@ -784,6 +771,7 @@ gtk_at_context_get_label (GtkATContext *self)
|
||||
GTK_ACCESSIBLE_PROPERTY_VALUE_NOW,
|
||||
};
|
||||
|
||||
value = NULL;
|
||||
for (int i = 0; i < G_N_ELEMENTS (range_attrs); i++)
|
||||
{
|
||||
if (gtk_accessible_attribute_set_contains (self->properties, range_attrs[i]))
|
||||
@ -794,7 +782,7 @@ gtk_at_context_get_label (GtkATContext *self)
|
||||
}
|
||||
|
||||
if (value != NULL)
|
||||
return g_strdup (gtk_string_accessible_value_get (value));
|
||||
g_ptr_array_add (names, (char *) gtk_string_accessible_value_get (value));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -802,13 +790,185 @@ gtk_at_context_get_label (GtkATContext *self)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there is no label or labelled-by attribute, hidden elements
|
||||
* have no name
|
||||
*/
|
||||
if (gtk_accessible_attribute_set_contains (self->states, GTK_ACCESSIBLE_STATE_HIDDEN))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->states, GTK_ACCESSIBLE_STATE_HIDDEN);
|
||||
|
||||
if (gtk_boolean_accessible_value_get (value))
|
||||
return;
|
||||
}
|
||||
|
||||
/* This fallback is in place only for unlabelled elements */
|
||||
if (names->len != 0)
|
||||
return;
|
||||
|
||||
GEnumClass *enum_class = g_type_class_peek (GTK_TYPE_ACCESSIBLE_ROLE);
|
||||
GEnumValue *enum_value = g_enum_get_value (enum_class, role);
|
||||
|
||||
if (enum_value != NULL)
|
||||
return g_strdup (enum_value->value_nick);
|
||||
g_ptr_array_add (names, (char *) enum_value->value_nick);
|
||||
}
|
||||
|
||||
return g_strdup ("widget");
|
||||
static void
|
||||
gtk_at_context_get_description_accumulate (GtkATContext *self,
|
||||
GPtrArray *labels,
|
||||
gboolean recurse)
|
||||
{
|
||||
GtkAccessibleValue *value = NULL;
|
||||
|
||||
if (gtk_accessible_attribute_set_contains (self->properties, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->properties, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION);
|
||||
|
||||
g_ptr_array_add (labels, (char *) gtk_string_accessible_value_get (value));
|
||||
}
|
||||
|
||||
if (recurse && gtk_accessible_attribute_set_contains (self->relations, GTK_ACCESSIBLE_RELATION_DESCRIBED_BY))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->relations, GTK_ACCESSIBLE_RELATION_DESCRIBED_BY);
|
||||
|
||||
GList *list = gtk_reference_list_accessible_value_get (value);
|
||||
|
||||
for (GList *l = list; l != NULL; l = l->data)
|
||||
{
|
||||
GtkAccessible *rel = GTK_ACCESSIBLE (l->data);
|
||||
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
|
||||
|
||||
gtk_at_context_get_description_accumulate (rel_context, labels, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
GtkAccessibleRole role = gtk_at_context_get_accessible_role (self);
|
||||
|
||||
switch ((int) role)
|
||||
{
|
||||
case GTK_ACCESSIBLE_ROLE_RANGE:
|
||||
{
|
||||
int range_attrs[] = {
|
||||
GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT,
|
||||
GTK_ACCESSIBLE_PROPERTY_VALUE_NOW,
|
||||
};
|
||||
|
||||
value = NULL;
|
||||
for (int i = 0; i < G_N_ELEMENTS (range_attrs); i++)
|
||||
{
|
||||
if (gtk_accessible_attribute_set_contains (self->properties, range_attrs[i]))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->properties, range_attrs[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value != NULL)
|
||||
g_ptr_array_add (labels, (char *) gtk_string_accessible_value_get (value));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there is no label or labelled-by attribute, hidden elements
|
||||
* have no name
|
||||
*/
|
||||
if (gtk_accessible_attribute_set_contains (self->states, GTK_ACCESSIBLE_STATE_HIDDEN))
|
||||
{
|
||||
value = gtk_accessible_attribute_set_get_value (self->states, GTK_ACCESSIBLE_STATE_HIDDEN);
|
||||
|
||||
if (gtk_boolean_accessible_value_get (value))
|
||||
return;
|
||||
}
|
||||
|
||||
/* This fallback is in place only for unlabelled elements */
|
||||
if (labels->len != 0)
|
||||
return;
|
||||
|
||||
GEnumClass *enum_class = g_type_class_peek (GTK_TYPE_ACCESSIBLE_ROLE);
|
||||
GEnumValue *enum_value = g_enum_get_value (enum_class, role);
|
||||
|
||||
if (enum_value != NULL)
|
||||
g_ptr_array_add (labels, (char *) enum_value->value_nick);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gtk_at_context_get_name:
|
||||
* @self: a #GtkATContext
|
||||
*
|
||||
* Retrieves the accessible name of the #GtkATContext.
|
||||
*
|
||||
* This is a convenience function meant to be used by #GtkATContext implementations.
|
||||
*
|
||||
* Returns: (transfer full): the label of the #GtkATContext
|
||||
*/
|
||||
char *
|
||||
gtk_at_context_get_name (GtkATContext *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
||||
|
||||
GPtrArray *names = g_ptr_array_new ();
|
||||
|
||||
gtk_at_context_get_name_accumulate (self, names, TRUE);
|
||||
|
||||
if (names->len == 0)
|
||||
{
|
||||
g_ptr_array_unref (names);
|
||||
return g_strdup ("");
|
||||
}
|
||||
|
||||
GString *res = g_string_new ("");
|
||||
g_string_append (res, g_ptr_array_index (names, 0));
|
||||
|
||||
for (guint i = 1; i < names->len; i++)
|
||||
{
|
||||
g_string_append (res, " ");
|
||||
g_string_append (res, g_ptr_array_index (names, i));
|
||||
}
|
||||
|
||||
g_ptr_array_unref (names);
|
||||
|
||||
return g_string_free (res, FALSE);
|
||||
}
|
||||
|
||||
/*< private >
|
||||
* gtk_at_context_get_description:
|
||||
* @self: a #GtkATContext
|
||||
*
|
||||
* Retrieves the accessible description of the #GtkATContext.
|
||||
*
|
||||
* This is a convenience function meant to be used by #GtkATContext implementations.
|
||||
*
|
||||
* Returns: (transfer full): the label of the #GtkATContext
|
||||
*/
|
||||
char *
|
||||
gtk_at_context_get_description (GtkATContext *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
||||
|
||||
GPtrArray *names = g_ptr_array_new ();
|
||||
|
||||
gtk_at_context_get_description_accumulate (self, names, TRUE);
|
||||
|
||||
if (names->len == 0)
|
||||
{
|
||||
g_ptr_array_unref (names);
|
||||
return g_strdup ("");
|
||||
}
|
||||
|
||||
GString *res = g_string_new ("");
|
||||
g_string_append (res, g_ptr_array_index (names, 0));
|
||||
|
||||
for (guint i = 1; i < names->len; i++)
|
||||
{
|
||||
g_string_append (res, " ");
|
||||
g_string_append (res, g_ptr_array_index (names, i));
|
||||
}
|
||||
|
||||
g_ptr_array_unref (names);
|
||||
|
||||
return g_string_free (res, FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -148,7 +148,8 @@ gboolean gtk_at_context_has_accessible_relation (GtkATContext
|
||||
GtkAccessibleValue * gtk_at_context_get_accessible_relation (GtkATContext *self,
|
||||
GtkAccessibleRelation relation);
|
||||
|
||||
char * gtk_at_context_get_label (GtkATContext *self);
|
||||
char * gtk_at_context_get_name (GtkATContext *self);
|
||||
char * gtk_at_context_get_description (GtkATContext *self);
|
||||
|
||||
void gtk_at_context_platform_changed (GtkATContext *self,
|
||||
GtkAccessiblePlatformChange change);
|
||||
|
Loading…
Reference in New Issue
Block a user