[gtk/a11y/buildable: 2/3] a11y: Add an "accessibility" section to the builder XML tree
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/a11y/buildable: 2/3] a11y: Add an "accessibility" section to the builder XML tree
- Date: Wed, 21 Oct 2020 12:34:00 +0000 (UTC)
commit 9e4316bf96cf53d56eac441541906c51a470c4d1
Author: Emmanuele Bassi <ebassi gnome org>
Date: Wed Oct 21 13:24:22 2020 +0100
a11y: Add an "accessibility" section to the builder XML tree
Accessible attributes are not GObject properties. This means that we
need a custom parser for setting attributes in our UI description files.
The new section is defined as a sub-tree with the `<accessibility>`
element at its root, and elements for each type of accessible
attributes, i.e. properties, relations, and states:
```xml
<object class="..." id="...">
<accessibility>
<property name="label">The accessible label</property>
<state name="pressed">false</state>
<relation name="labelled-by">label1</relation>
</accessibility>
</object>
```
The name of the attribute is the enumeration value; the value is defined
by the WAI-ARIA specification.
gtk/gtkwidget.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 327 insertions(+)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 49a16937cb..126712b9c8 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -8446,6 +8446,161 @@ static const GtkBuildableParser layout_parser =
layout_text,
};
+typedef struct
+{
+ char *name;
+ GString *value;
+ char *context;
+ gboolean translatable;
+} AccessibilityAttributeInfo;
+
+typedef struct
+{
+ GObject *object;
+ GtkBuilder *builder;
+
+ AccessibilityAttributeInfo *cur_attribute;
+
+ /* SList<AccessibilityAttributeInfo> */
+ GSList *properties;
+ GSList *states;
+ GSList *relations;
+} AccessibilityParserData;
+
+static void
+accessibility_attribute_info_free (gpointer data)
+{
+ AccessibilityAttributeInfo *pinfo = data;
+
+ if (pinfo == NULL)
+ return;
+
+ g_free (pinfo->name);
+ g_free (pinfo->context);
+ g_string_free (pinfo->value, TRUE);
+ g_free (pinfo);
+}
+
+static void
+accessibility_start_element (GtkBuildableParseContext *context,
+ const char *element_name,
+ const char **names,
+ const char **values,
+ gpointer user_data,
+ GError **error)
+{
+ AccessibilityParserData *accessibility_data = user_data;
+
+ if (strcmp (element_name, "property") == 0 ||
+ strcmp (element_name, "relation") == 0 ||
+ strcmp (element_name, "state") == 0)
+ {
+ const char *name = NULL;
+ const char *ctx = NULL;
+ gboolean translatable = FALSE;
+ AccessibilityAttributeInfo *pinfo;
+
+ if (!_gtk_builder_check_parent (accessibility_data->builder,
+ context,
+ "accessibility",
+ error))
+ return;
+
+ if (!g_markup_collect_attributes (element_name, names, values, error,
+ G_MARKUP_COLLECT_STRING, "name", &name,
+ G_MARKUP_COLLECT_BOOLEAN | G_MARKUP_COLLECT_OPTIONAL,
"translatable", &translatable,
+ G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "context", &ctx,
+ G_MARKUP_COLLECT_INVALID))
+ {
+ _gtk_builder_prefix_error (accessibility_data->builder, context, error);
+ return;
+ }
+
+ pinfo = g_new0 (AccessibilityAttributeInfo, 1);
+ pinfo->name = g_strdup (name);
+ pinfo->translatable = translatable;
+ pinfo->context = g_strdup (ctx);
+ pinfo->value = g_string_new (NULL);
+
+ accessibility_data->cur_attribute = pinfo;
+ }
+ else if (strcmp (element_name, "accessibility") == 0)
+ {
+ if (!_gtk_builder_check_parent (accessibility_data->builder,
+ context,
+ "object",
+ error))
+ return;
+ }
+ else
+ {
+ _gtk_builder_error_unhandled_tag (accessibility_data->builder, context,
+ "GtkWidget", element_name,
+ error);
+ }
+}
+
+static void
+accessibility_text (GtkBuildableParseContext *context,
+ const char *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ AccessibilityParserData *accessibility_data = user_data;
+
+ if (accessibility_data->cur_attribute != NULL)
+ g_string_append_len (accessibility_data->cur_attribute->value, text, text_len);
+}
+
+static void
+accessibility_end_element (GtkBuildableParseContext *context,
+ const char *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ AccessibilityParserData *accessibility_data = user_data;
+
+ if (accessibility_data->cur_attribute != NULL)
+ {
+ AccessibilityAttributeInfo *pinfo = g_steal_pointer (&accessibility_data->cur_attribute);
+
+ /* Translate the string, if needed */
+ if (pinfo->value->len != 0 && pinfo->translatable)
+ {
+ const char *translated;
+ const char *domain;
+
+ domain = gtk_builder_get_translation_domain (accessibility_data->builder);
+
+ translated = _gtk_builder_parser_translate (domain, pinfo->context, pinfo->value->str);
+
+ g_string_assign (pinfo->value, translated);
+ }
+
+ /* We assign all properties at the end of the `accessibility` section */
+ if (strcmp (element_name, "property") == 0)
+ accessibility_data->properties = g_slist_prepend (accessibility_data->properties, pinfo);
+ else if (strcmp (element_name, "relation") == 0)
+ accessibility_data->relations = g_slist_prepend (accessibility_data->relations, pinfo);
+ else if (strcmp (element_name, "state") == 0)
+ accessibility_data->states = g_slist_prepend (accessibility_data->states, pinfo);
+ else
+ {
+ _gtk_builder_error_unhandled_tag (accessibility_data->builder, context,
+ "GtkWidget", element_name,
+ error);
+ accessibility_attribute_info_free (pinfo);
+ }
+ }
+}
+
+static const GtkBuildableParser accessibility_parser = {
+ accessibility_start_element,
+ accessibility_end_element,
+ accessibility_text,
+};
+
static gboolean
gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
@@ -8481,6 +8636,20 @@ gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable,
return TRUE;
}
+ if (strcmp (tagname, "accessibility") == 0)
+ {
+ AccessibilityParserData *data;
+
+ data = g_slice_new0 (AccessibilityParserData);
+ data->builder = builder;
+ data->object = (GObject *) g_object_ref (buildable);
+
+ *parser = accessibility_parser;
+ *parser_data = data;
+
+ return TRUE;
+ }
+
return FALSE;
}
@@ -8559,6 +8728,148 @@ gtk_widget_buildable_finish_layout_properties (GtkWidget *widget,
g_slist_free_full (layout_properties, layout_property_info_free);
}
+static void
+gtk_widget_buildable_finish_accessibility_properties (GtkWidget *widget,
+ gpointer data)
+{
+ AccessibilityParserData *accessibility_data = data;
+ GSList *attributes, *l;
+ GtkATContext *context;
+
+ context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget));
+ if (context == NULL)
+ return;
+
+ attributes = g_slist_reverse (accessibility_data->properties);
+ accessibility_data->properties = NULL;
+
+ for (l = attributes; l != NULL; l = l->next)
+ {
+ AccessibilityAttributeInfo *pinfo = l->data;
+ int property;
+ GError *error = NULL;
+ GtkAccessibleValue *value;
+
+ _gtk_builder_enum_from_string (GTK_TYPE_ACCESSIBLE_PROPERTY,
+ pinfo->name,
+ &property,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to find accessible property “%s”: %s",
+ pinfo->name,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ value = gtk_accessible_value_parse_for_property (property,
+ pinfo->value->str,
+ pinfo->value->len,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to set accessible property “%s” to “%s”: %s",
+ pinfo->name,
+ pinfo->value->str,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ gtk_at_context_set_accessible_property (context, property, value);
+ gtk_accessible_value_unref (value);
+ }
+
+ g_slist_free_full (attributes, accessibility_attribute_info_free);
+
+ attributes = g_slist_reverse (accessibility_data->relations);
+ accessibility_data->relations = NULL;
+
+ for (l = attributes; l != NULL; l = l->next)
+ {
+ AccessibilityAttributeInfo *pinfo = l->data;
+ int relation;
+ GError *error = NULL;
+ GtkAccessibleValue *value;
+
+ _gtk_builder_enum_from_string (GTK_TYPE_ACCESSIBLE_RELATION,
+ pinfo->name,
+ &relation,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to find accessible relation “%s”: %s",
+ pinfo->name,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ value = gtk_accessible_value_parse_for_relation (relation,
+ pinfo->value->str,
+ pinfo->value->len,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to set accessible relation “%s” to “%s”: %s",
+ pinfo->name,
+ pinfo->value->str,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ gtk_at_context_set_accessible_relation (context, relation, value);
+ gtk_accessible_value_unref (value);
+ }
+
+ g_slist_free_full (attributes, accessibility_attribute_info_free);
+
+ attributes = g_slist_reverse (accessibility_data->states);
+ accessibility_data->states = NULL;
+
+ for (l = attributes; l != NULL; l = l->next)
+ {
+ AccessibilityAttributeInfo *pinfo = l->data;
+ int state;
+ GError *error = NULL;
+ GtkAccessibleValue *value;
+
+ _gtk_builder_enum_from_string (GTK_TYPE_ACCESSIBLE_STATE,
+ pinfo->name,
+ &state,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to find accessible state “%s”: %s",
+ pinfo->name,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ value = gtk_accessible_value_parse_for_state (state,
+ pinfo->value->str,
+ pinfo->value->len,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Failed to set accessible state “%s” to “%s”: %s",
+ pinfo->name,
+ pinfo->value->str,
+ error->message);
+ g_error_free (error);
+ continue;
+ }
+
+ gtk_at_context_set_accessible_state (context, state, value);
+ gtk_accessible_value_unref (value);
+ }
+
+ g_slist_free_full (attributes, accessibility_attribute_info_free);
+}
+
static void
gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
@@ -8592,6 +8903,22 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
g_object_unref (layout_data->object);
g_slice_free (LayoutParserData, layout_data);
}
+ else if (strcmp (tagname, "accessibility") == 0)
+ {
+ AccessibilityParserData *accessibility_data = user_data;
+
+ gtk_widget_buildable_finish_accessibility_properties (GTK_WIDGET (buildable),
+ accessibility_data);
+
+ g_slist_free_full (accessibility_data->properties,
+ accessibility_attribute_info_free);
+ g_slist_free_full (accessibility_data->relations,
+ accessibility_attribute_info_free);
+ g_slist_free_full (accessibility_data->states,
+ accessibility_attribute_info_free);
+ g_object_unref (accessibility_data->object);
+ g_slice_free (AccessibilityParserData, accessibility_data);
+ }
}
static GtkSizeRequestMode
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]