forked from AuroraMiddleware/gtk
Merge branch 'ebassi/for-master' into 'master'
Ebassi/for master See merge request GNOME/gtk!2720
This commit is contained in:
commit
51f5690ae3
@ -220,3 +220,71 @@ which acts as a proxy to the specific platform's accessibility API:
|
||||
|
||||
Additionally, an ad hoc accessibility backend is available for the GTK
|
||||
testsuite, to ensure reproducibility of issues in the CI pipeline.
|
||||
|
||||
## Authoring practices {#authoring-practices}
|
||||
|
||||
The authoring practices are aimed at application developers, as well as
|
||||
developers of GUI elements based on GTK.
|
||||
|
||||
Functionally, #GtkAccessible roles, states, properties, and relations are
|
||||
analogous to a CSS for assistive technologies. For screen reader users, for
|
||||
instance, the various accessible attributes control the rendering of their
|
||||
non-visual experience. Incorrect roles and attributes may result in a
|
||||
completely inaccessible user interface.
|
||||
|
||||
### A role is a promise
|
||||
|
||||
The following code:
|
||||
|
||||
```c
|
||||
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_BUTTON);
|
||||
```
|
||||
|
||||
is a promise that the widget being created will provide the same keyboard
|
||||
interactions expected for a button. An accessible role of a button will not
|
||||
turn automatically any widget into a #GtkButton; but if your widget behaves
|
||||
like a button, using the %GTK_ACCESSIBLE_ROLE_BUTTON will allow any
|
||||
assistive technology to handle it like they would a #GtkButton.
|
||||
|
||||
### Attributes can both hide and enhance
|
||||
|
||||
Accessible attributes can be used to override the content of a UI element,
|
||||
for instance:
|
||||
|
||||
```c
|
||||
gtk_label_set_text (GTK_LABEL (label), "Some text");
|
||||
gtk_accessible_update_property (GTK_ACCESSIBLE (label),
|
||||
GTK_ACCESSIBLE_PROPERTY_LABEL,
|
||||
"Assistive technologies users will perceive "
|
||||
"this text, not the contents of the label",
|
||||
-1);
|
||||
```
|
||||
|
||||
In the example above, the "label" property will override the contents of the
|
||||
label widget.
|
||||
|
||||
The attributes can also enhance the UI:
|
||||
|
||||
```c
|
||||
gtk_button_set_label (GTK_BUTTON (button), "Download");
|
||||
gtk_box_append (GTK_BOX (button), button);
|
||||
|
||||
gtk_label_set_text (GTK_LABEL (label), "Final report.pdf");
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
gtk_accessible_update_relation (GTK_ACCESSIBLE (button),
|
||||
GTK_ACCESSIBLE_RELATION_LABELLED_BY,
|
||||
g_list_append (NULL, label),
|
||||
-1);
|
||||
```
|
||||
|
||||
In the example above, an assistive technology will read the button's
|
||||
accessible label as "Download Final report.pdf".
|
||||
|
||||
The power of hiding and enhancing can be a double-edged sword, as it can
|
||||
lead to inadvertently overriding the accessible semantics of existing
|
||||
widgets.
|
||||
|
||||
## Design patterns and custom widgets
|
||||
|
||||
...
|
||||
|
@ -593,22 +593,14 @@ handle_accessible_get_property (GDBusConnection *connection,
|
||||
|
||||
if (g_strcmp0 (property_name, "Name") == 0)
|
||||
{
|
||||
if (GTK_IS_WIDGET (accessible))
|
||||
res = g_variant_new_string (gtk_widget_get_name (GTK_WIDGET (accessible)));
|
||||
else if (GTK_IS_STACK_PAGE (accessible))
|
||||
{
|
||||
const char *name = gtk_stack_page_get_name (GTK_STACK_PAGE (accessible));
|
||||
if (name == NULL)
|
||||
name = G_OBJECT_TYPE_NAME (accessible);
|
||||
res = g_variant_new_string (name);
|
||||
}
|
||||
else
|
||||
res = g_variant_new_string (G_OBJECT_TYPE_NAME (accessible));
|
||||
char *label = gtk_at_context_get_name (GTK_AT_CONTEXT (self));
|
||||
res = g_variant_new_string (label ? label : "");
|
||||
g_free (label);
|
||||
}
|
||||
else if (g_strcmp0 (property_name, "Description") == 0)
|
||||
{
|
||||
char *label = gtk_at_context_get_label (GTK_AT_CONTEXT (self));
|
||||
res = g_variant_new_string (label);
|
||||
char *label = gtk_at_context_get_description (GTK_AT_CONTEXT (self));
|
||||
res = g_variant_new_string (label ? label : "");
|
||||
g_free (label);
|
||||
}
|
||||
else if (g_strcmp0 (property_name, "Locale") == 0)
|
||||
@ -953,7 +945,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);
|
||||
}
|
||||
|
@ -739,47 +739,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);
|
||||
@ -793,6 +780,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]))
|
||||
@ -803,7 +791,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;
|
||||
|
||||
@ -811,13 +799,172 @@ gtk_at_context_get_label (GtkATContext *self)
|
||||
break;
|
||||
}
|
||||
|
||||
GEnumClass *enum_class = g_type_class_peek (GTK_TYPE_ACCESSIBLE_ROLE);
|
||||
GEnumValue *enum_value = g_enum_get_value (enum_class, role);
|
||||
/* 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 (enum_value != NULL)
|
||||
return g_strdup (enum_value->value_nick);
|
||||
if (gtk_boolean_accessible_value_get (value))
|
||||
return;
|
||||
}
|
||||
|
||||
return g_strdup ("widget");
|
||||
/* This fallback is in place only for unlabelled elements */
|
||||
if (names->len != 0)
|
||||
return;
|
||||
|
||||
if (self->accessible)
|
||||
g_ptr_array_add (names, (char *)G_OBJECT_TYPE_NAME (self->accessible));
|
||||
}
|
||||
|
||||
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 description or described-by attribute, hidden elements
|
||||
* have no description
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*< 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
|
||||
|
@ -152,7 +152,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);
|
||||
|
@ -1696,6 +1696,10 @@ gtk_label_set_text_internal (GtkLabel *self,
|
||||
g_free (self->text);
|
||||
self->text = str;
|
||||
|
||||
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
||||
GTK_ACCESSIBLE_PROPERTY_LABEL, str,
|
||||
-1);
|
||||
|
||||
gtk_label_select_region_index (self, 0, 0);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user