[gtk/ebassi/new-a11y: 57/71] a11y: Add relations API



commit f77c83db10ca49efbfd726437c6b7b064cdcd566
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Fri Jul 17 12:00:31 2020 +0100

    a11y: Add relations API
    
    Since we split relation attributes from the generic properties, we need
    to add API for setting and retrieving their values.

 gtk/gtkaccessible.c                   |  87 ++++++++++++++
 gtk/gtkaccessible.h                   |   8 ++
 gtk/gtkaccessiblerelationset.c        | 217 ++++++++++++++++++++++++++++++++++
 gtk/gtkaccessiblerelationsetprivate.h |  49 ++++++++
 gtk/gtkaccessiblevalue.c              | 183 ++++++++++++++++++++++++++++
 gtk/gtkaccessiblevalueprivate.h       |   6 +
 gtk/gtkatcontext.c                    |  49 +++++++-
 gtk/gtkatcontextprivate.h             |  54 +++++++--
 gtk/gtktestatcontext.c                |  14 ++-
 gtk/meson.build                       |   1 +
 10 files changed, 646 insertions(+), 22 deletions(-)
---
diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c
index 8cace46611..4f90d04e24 100644
--- a/gtk/gtkaccessible.c
+++ b/gtk/gtkaccessible.c
@@ -276,3 +276,90 @@ gtk_accessible_update_property_value (GtkAccessible         *self,
   gtk_accessible_value_unref (real_value);
   gtk_at_context_update (context);
 }
+
+/**
+ * gtk_accessible_update_relation:
+ * @self: a #GtkAccessible
+ * @first_relation: the first #GtkAccessibleRelation
+ * @...: a list of relation and value pairs, terminated by -1
+ *
+ * Updates a list of accessible relations.
+ *
+ * This function should be called by #GtkWidget types whenever an accessible
+ * relation change must be communicated to assistive technologies.
+ */
+void
+gtk_accessible_update_relation (GtkAccessible         *self,
+                                GtkAccessibleRelation  first_relation,
+                                ...)
+{
+  GtkAccessibleRelation relation;
+  GtkATContext *context;
+  va_list args;
+
+  g_return_if_fail (GTK_IS_ACCESSIBLE (self));
+
+  context = gtk_accessible_get_at_context (self);
+  if (context == NULL)
+    return;
+
+  va_start (args, first_relation);
+
+  relation = first_relation;
+
+  while (relation != -1)
+    {
+      GtkAccessibleValue *value = gtk_accessible_value_collect_for_relation (relation, &args);
+
+      /* gtk_accessible_value_collect_for_relation() will warn for us */
+      if (value == NULL)
+        goto out;
+
+      gtk_at_context_set_accessible_relation (context, relation, value);
+      gtk_accessible_value_unref (value);
+
+      relation = va_arg (args, int);
+    }
+
+  gtk_at_context_update (context);
+
+out:
+  va_end (args);
+}
+
+/**
+ * gtk_accessible_update_relation_value:
+ * @self: a #GtkAccessible
+ * @relation: a #GtkAccessibleRelation
+ * @value: a #GValue with the value for @relation
+ *
+ * Updates an accessible relation.
+ *
+ * This function should be called by #GtkWidget types whenever an accessible
+ * relation change must be communicated to assistive technologies.
+ *
+ * This function is meant to be used by language bindings.
+ */
+void
+gtk_accessible_update_relation_value (GtkAccessible         *self,
+                                      GtkAccessibleRelation  relation,
+                                      const GValue          *value)
+{
+  GtkATContext *context;
+
+  g_return_if_fail (GTK_IS_ACCESSIBLE (self));
+
+  context = gtk_accessible_get_at_context (self);
+  if (context == NULL)
+    return;
+
+  GtkAccessibleValue *real_value =
+    gtk_accessible_value_collect_for_relation_value (relation, value);
+
+  if (real_value == NULL)
+    return;
+
+  gtk_at_context_set_accessible_relation (context, relation, real_value);
+  gtk_accessible_value_unref (real_value);
+  gtk_at_context_update (context);
+}
diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h
index 9ad56784c7..d0a115d205 100644
--- a/gtk/gtkaccessible.h
+++ b/gtk/gtkaccessible.h
@@ -43,6 +43,10 @@ void                    gtk_accessible_update_property          (GtkAccessible
                                                                  GtkAccessibleProperty  first_property,
                                                                  ...);
 GDK_AVAILABLE_IN_ALL
+void                    gtk_accessible_update_relation          (GtkAccessible         *self,
+                                                                 GtkAccessibleRelation  first_relation,
+                                                                 ...);
+GDK_AVAILABLE_IN_ALL
 void                    gtk_accessible_update_state_value       (GtkAccessible         *self,
                                                                  GtkAccessibleState     state,
                                                                  const GValue          *value);
@@ -50,5 +54,9 @@ GDK_AVAILABLE_IN_ALL
 void                    gtk_accessible_update_property_value    (GtkAccessible         *self,
                                                                  GtkAccessibleProperty  property,
                                                                  const GValue          *value);
+GDK_AVAILABLE_IN_ALL
+void                    gtk_accessible_update_relation_value    (GtkAccessible         *self,
+                                                                 GtkAccessibleRelation  relation,
+                                                                 const GValue          *value);
 
 G_END_DECLS
diff --git a/gtk/gtkaccessiblerelationset.c b/gtk/gtkaccessiblerelationset.c
new file mode 100644
index 0000000000..2e6a64b41f
--- /dev/null
+++ b/gtk/gtkaccessiblerelationset.c
@@ -0,0 +1,217 @@
+/* gtkaccessiblerelationset.c: Accessible relation set
+ *
+ * Copyright 2020  GNOME Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkaccessiblerelationsetprivate.h"
+
+#include "gtkbitmaskprivate.h"
+#include "gtkenums.h"
+
+/* Keep in sync with GtkAccessibleRelation in gtkenums.h */
+#define LAST_RELATION GTK_ACCESSIBLE_RELATION_SET_SIZE
+
+struct _GtkAccessibleRelationSet
+{
+  GtkBitmask *relation_set;
+
+  GtkAccessibleValue **relation_values;
+};
+
+static GtkAccessibleRelationSet *
+gtk_accessible_relation_set_init (GtkAccessibleRelationSet *self)
+{
+  self->relation_set = _gtk_bitmask_new ();
+  self->relation_values = g_new (GtkAccessibleValue *, LAST_RELATION);
+
+  /* Initialize all relation values, so we can always get the full set */
+  for (int i = 0; i < LAST_RELATION; i++)
+    self->relation_values[i] = gtk_accessible_value_get_default_for_relation (i);
+
+  return self;
+}
+
+GtkAccessibleRelationSet *
+gtk_accessible_relation_set_new (void)
+{
+  GtkAccessibleRelationSet *set = g_rc_box_new0 (GtkAccessibleRelationSet);
+
+  return gtk_accessible_relation_set_init (set);
+}
+
+GtkAccessibleRelationSet *
+gtk_accessible_relation_set_ref (GtkAccessibleRelationSet *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return g_rc_box_acquire (self);
+}
+
+static void
+gtk_accessible_relation_set_free (gpointer data)
+{
+  GtkAccessibleRelationSet *self = data;
+
+  for (int i = 0; i < LAST_RELATION; i++)
+    {
+      if (self->relation_values[i] != NULL)
+        gtk_accessible_value_unref (self->relation_values[i]);
+    }
+
+  g_free (self->relation_values);
+
+  _gtk_bitmask_free (self->relation_set);
+}
+
+void
+gtk_accessible_relation_set_unref (GtkAccessibleRelationSet *self)
+{
+  g_rc_box_release_full (self, gtk_accessible_relation_set_free);
+}
+
+void
+gtk_accessible_relation_set_add (GtkAccessibleRelationSet *self,
+                                 GtkAccessibleRelation     relation,
+                                 GtkAccessibleValue       *value)
+{
+  g_return_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
+                    relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE);
+
+  if (gtk_accessible_relation_set_contains (self, relation))
+    gtk_accessible_value_unref (self->relation_values[relation]);
+  else
+    self->relation_set = _gtk_bitmask_set (self->relation_set, relation, TRUE);
+
+  self->relation_values[relation] = gtk_accessible_value_ref (value);
+}
+
+void
+gtk_accessible_relation_set_remove (GtkAccessibleRelationSet *self,
+                                    GtkAccessibleRelation     relation)
+{
+  g_return_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
+                    relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE);
+
+  if (gtk_accessible_relation_set_contains (self, relation))
+    {
+      g_clear_pointer (&(self->relation_values[relation]), gtk_accessible_value_unref);
+      self->relation_set = _gtk_bitmask_set (self->relation_set, relation, FALSE);
+    }
+}
+
+gboolean
+gtk_accessible_relation_set_contains (GtkAccessibleRelationSet *self,
+                                      GtkAccessibleRelation     relation)
+{
+  g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
+                        relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE,
+                        FALSE);
+
+  return _gtk_bitmask_get (self->relation_set, relation);
+}
+
+GtkAccessibleValue *
+gtk_accessible_relation_set_get_value (GtkAccessibleRelationSet *self,
+                                       GtkAccessibleRelation     relation)
+{
+  g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
+                        relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE,
+                        NULL);
+
+  return self->relation_values[relation];
+}
+
+static const char *relation_names[] = {
+  [GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = "activedescendant",
+  [GTK_ACCESSIBLE_RELATION_COL_COUNT] = "colcount",
+  [GTK_ACCESSIBLE_RELATION_COL_INDEX] = "colindex",
+  [GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = "colindextext",
+  [GTK_ACCESSIBLE_RELATION_COL_SPAN] = "colspan",
+  [GTK_ACCESSIBLE_RELATION_CONTROLS] = "controls",
+  [GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = "describedby",
+  [GTK_ACCESSIBLE_RELATION_DETAILS] = "details",
+  [GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = "errormessage",
+  [GTK_ACCESSIBLE_RELATION_FLOW_TO] = "flowto",
+  [GTK_ACCESSIBLE_RELATION_LABELLED_BY] = "labelledby",
+  [GTK_ACCESSIBLE_RELATION_OWNS] = "owns",
+  [GTK_ACCESSIBLE_RELATION_POS_IN_SET] = "posinset",
+  [GTK_ACCESSIBLE_RELATION_ROW_COUNT] = "rowcount",
+  [GTK_ACCESSIBLE_RELATION_ROW_INDEX] = "rowindex",
+  [GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = "rowindextext",
+  [GTK_ACCESSIBLE_RELATION_ROW_SPAN] = "rowspan",
+  [GTK_ACCESSIBLE_RELATION_SET_SIZE] = "setsize",
+};
+
+/*< private >
+ * gtk_accessible_relation_set_print:
+ * @self: a #GtkAccessibleRelationSet
+ * @only_set: %TRUE if only the set relations should be printed
+ * @buffer: a #GString
+ *
+ * Prints the contents of the #GtkAccessibleRelationSet into @buffer.
+ */
+void
+gtk_accessible_relation_set_print (GtkAccessibleRelationSet *self,
+                                   gboolean               only_set,
+                                   GString               *buffer)
+{
+  if (only_set && _gtk_bitmask_is_empty (self->relation_set))
+    {
+      g_string_append (buffer, "{}");
+      return;
+    }
+
+  g_string_append (buffer, "{\n");
+
+  for (int i = 0; i < G_N_ELEMENTS (relation_names); i++)
+    {
+      if (only_set && !_gtk_bitmask_get (self->relation_set, i))
+        continue;
+
+      g_string_append (buffer, "    ");
+      g_string_append (buffer, relation_names[i]);
+      g_string_append (buffer, ": ");
+
+      gtk_accessible_value_print (self->relation_values[i], buffer);
+
+      g_string_append (buffer, ",\n");
+    }
+
+  g_string_append (buffer, "}");
+}
+
+/*< private >
+ * gtk_accessible_relation_set_to_string:
+ * @self: a #GtkAccessibleRelationSet
+ *
+ * Prints the contents of a #GtkAccessibleRelationSet into a string.
+ *
+ * Returns: (transfer full): a newly allocated string with the contents
+ *   of the #GtkAccessibleRelationSet
+ */
+char *
+gtk_accessible_relation_set_to_string (GtkAccessibleRelationSet *self)
+{
+  GString *buf = g_string_new (NULL);
+
+  gtk_accessible_relation_set_print (self, TRUE, buf);
+
+  return g_string_free (buf, FALSE);
+}
diff --git a/gtk/gtkaccessiblerelationsetprivate.h b/gtk/gtkaccessiblerelationsetprivate.h
new file mode 100644
index 0000000000..a2a33f9717
--- /dev/null
+++ b/gtk/gtkaccessiblerelationsetprivate.h
@@ -0,0 +1,49 @@
+/* gtkaccessiblerelationsetprivate.h: Accessible relations set
+ *
+ * Copyright 2020  GNOME Foundation
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "gtkaccessibleprivate.h"
+#include "gtkaccessiblevalueprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkAccessibleRelationSet   GtkAccessibleRelationSet;
+
+GtkAccessibleRelationSet *      gtk_accessible_relation_set_new         (void);
+GtkAccessibleRelationSet *      gtk_accessible_relation_set_ref         (GtkAccessibleRelationSet *self);
+void                            gtk_accessible_relation_set_unref       (GtkAccessibleRelationSet *self);
+
+void                            gtk_accessible_relation_set_add         (GtkAccessibleRelationSet *self,
+                                                                         GtkAccessibleRelation     state,
+                                                                         GtkAccessibleValue       *value);
+void                            gtk_accessible_relation_set_remove      (GtkAccessibleRelationSet *self,
+                                                                         GtkAccessibleRelation     state);
+gboolean                        gtk_accessible_relation_set_contains    (GtkAccessibleRelationSet *self,
+                                                                         GtkAccessibleRelation     state);
+GtkAccessibleValue *            gtk_accessible_relation_set_get_value   (GtkAccessibleRelationSet *self,
+                                                                         GtkAccessibleRelation     state);
+
+void                            gtk_accessible_relation_set_print       (GtkAccessibleRelationSet *self,
+                                                                         gboolean                  only_set,
+                                                                         GString                  *string);
+char *                          gtk_accessible_relation_set_to_string   (GtkAccessibleRelationSet *self);
+
+G_END_DECLS
diff --git a/gtk/gtkaccessiblevalue.c b/gtk/gtkaccessiblevalue.c
index 58e3dd15d1..4ccd8a837d 100644
--- a/gtk/gtkaccessiblevalue.c
+++ b/gtk/gtkaccessiblevalue.c
@@ -665,6 +665,100 @@ static const GtkAccessibleCollect collect_props[] = {
   },
 };
 
+/* § 6.6.4 Relationship Attributes */
+static const GtkAccessibleCollect collect_rels[] = {
+  [GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = {
+    .value = GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "activedescendant"
+  },
+  [GTK_ACCESSIBLE_RELATION_COL_COUNT] = {
+    .value = GTK_ACCESSIBLE_RELATION_COL_COUNT,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "colcount"
+  },
+  [GTK_ACCESSIBLE_RELATION_COL_INDEX] = {
+    .value = GTK_ACCESSIBLE_RELATION_COL_INDEX,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "colindex"
+  },
+  [GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = {
+    .value = GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT,
+    .ctype = GTK_ACCESSIBLE_COLLECT_STRING,
+    .name = "colindextext"
+  },
+  [GTK_ACCESSIBLE_RELATION_COL_SPAN] = {
+    .value = GTK_ACCESSIBLE_RELATION_COL_SPAN,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "colspan"
+  },
+  [GTK_ACCESSIBLE_RELATION_CONTROLS] = {
+    .value = GTK_ACCESSIBLE_RELATION_CONTROLS,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "controls"
+  },
+  [GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = {
+    .value = GTK_ACCESSIBLE_RELATION_DESCRIBED_BY,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "describedby"
+  },
+  [GTK_ACCESSIBLE_RELATION_DETAILS] = {
+    .value = GTK_ACCESSIBLE_RELATION_DETAILS,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "details"
+  },
+  [GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = {
+    .value = GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "errormessage"
+  },
+  [GTK_ACCESSIBLE_RELATION_FLOW_TO] = {
+    .value = GTK_ACCESSIBLE_RELATION_FLOW_TO,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "flowto"
+  },
+  [GTK_ACCESSIBLE_RELATION_LABELLED_BY] = {
+    .value = GTK_ACCESSIBLE_RELATION_LABELLED_BY,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "labelledby"
+  },
+  [GTK_ACCESSIBLE_RELATION_OWNS] = {
+    .value = GTK_ACCESSIBLE_RELATION_OWNS,
+    .ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
+    .name = "owns"
+  },
+  [GTK_ACCESSIBLE_RELATION_POS_IN_SET] = {
+    .value = GTK_ACCESSIBLE_RELATION_POS_IN_SET,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "posinset"
+  },
+  [GTK_ACCESSIBLE_RELATION_ROW_COUNT] = {
+    .value = GTK_ACCESSIBLE_RELATION_ROW_COUNT,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "rowcount"
+  },
+  [GTK_ACCESSIBLE_RELATION_ROW_INDEX] = {
+    .value = GTK_ACCESSIBLE_RELATION_ROW_INDEX,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "rowindex"
+  },
+  [GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = {
+    .value = GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT,
+    .ctype = GTK_ACCESSIBLE_COLLECT_STRING,
+    .name = "rowindextext"
+  },
+  [GTK_ACCESSIBLE_RELATION_ROW_SPAN] = {
+    .value = GTK_ACCESSIBLE_RELATION_ROW_SPAN,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "rowspan"
+  },
+  [GTK_ACCESSIBLE_RELATION_SET_SIZE] = {
+    .value = GTK_ACCESSIBLE_RELATION_SET_SIZE,
+    .ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
+    .name = "posinset"
+  },
+};
+
 typedef GtkAccessibleValue * (* GtkAccessibleValueBooleanCtor)  (gboolean value);
 typedef GtkAccessibleValue * (* GtkAccessibleValueIntCtor)      (int value);
 typedef GtkAccessibleValue * (* GtkAccessibleValueTristateCtor) (int value);
@@ -1117,4 +1211,93 @@ gtk_accessible_value_collect_for_property_value (GtkAccessibleProperty  property
   return gtk_accessible_value_collect_value (cstate, value);
 }
 
+/*< private >
+ * gtk_accessible_value_get_default_for_relation:
+ * @relation: a #GtkAccessibleRelation
+ *
+ * Retrieves the #GtkAccessibleValue that contains the default for the
+ * given @relation.
+ *
+ * Returns: (transfer full): the #GtkAccessibleValue
+ */
+GtkAccessibleValue *
+gtk_accessible_value_get_default_for_relation (GtkAccessibleRelation relation)
+{
+  const GtkAccessibleCollect *cstate = &collect_rels[relation];
+
+  switch (cstate->value)
+    {
+    /* References */
+    case GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT:
+    case GTK_ACCESSIBLE_RELATION_CONTROLS:
+    case GTK_ACCESSIBLE_RELATION_DESCRIBED_BY:
+    case GTK_ACCESSIBLE_RELATION_DETAILS:
+    case GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE:
+    case GTK_ACCESSIBLE_RELATION_FLOW_TO:
+    case GTK_ACCESSIBLE_RELATION_LABELLED_BY:
+    case GTK_ACCESSIBLE_RELATION_OWNS:
+      return NULL;
+
+    /* Integers */
+    case GTK_ACCESSIBLE_RELATION_COL_COUNT:
+    case GTK_ACCESSIBLE_RELATION_COL_INDEX:
+    case GTK_ACCESSIBLE_RELATION_COL_SPAN:
+    case GTK_ACCESSIBLE_RELATION_POS_IN_SET:
+    case GTK_ACCESSIBLE_RELATION_ROW_COUNT:
+    case GTK_ACCESSIBLE_RELATION_ROW_INDEX:
+    case GTK_ACCESSIBLE_RELATION_ROW_SPAN:
+    case GTK_ACCESSIBLE_RELATION_SET_SIZE:
+      return gtk_int_accessible_value_new (0);
+
+    /* Strings */
+    case GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT:
+    case GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT:
+      return gtk_string_accessible_value_new ("");
+
+    default:
+      g_critical ("Unknown value for accessible property “%s”", cstate->name);
+      break;
+    }
+
+  return NULL;
+}
+
+/*< private >
+ * gtk_accessible_value_collect_for_relation:
+ * @relation: a #GtkAccessibleRelation
+ * @args: a `va_list` reference
+ *
+ * Collects and consumes the next item in the @args variadic arguments list,
+ * and returns a #GtkAccessibleValue for it.
+ *
+ * Returns: (transfer full): a #GtkAccessibleValue
+ */
+GtkAccessibleValue *
+gtk_accessible_value_collect_for_relation (GtkAccessibleRelation  relation,
+                                           va_list               *args)
+{
+  const GtkAccessibleCollect *cstate = &collect_rels[relation];
+
+  return gtk_accessible_value_collect_valist (cstate, args);
+}
+
+/*< private >
+ * gtk_accessible_value_collect_for_relation_value:
+ * @relation: a #GtkAccessibleRelation
+ * @value: a #GValue
+ *
+ * Retrieves the value stored inside @value and returns a #GtkAccessibleValue
+ * for the given @relation.
+ *
+ * Returns: (transfer full): a #GtkAccessibleValue
+ */
+GtkAccessibleValue *
+gtk_accessible_value_collect_for_relation_value (GtkAccessibleRelation  relation,
+                                                 const GValue          *value)
+{
+  const GtkAccessibleCollect *cstate = &collect_rels[relation];
+
+  return gtk_accessible_value_collect_value (cstate, value);
+}
+
 /* }}} */
diff --git a/gtk/gtkaccessiblevalueprivate.h b/gtk/gtkaccessiblevalueprivate.h
index 9f80edac92..30648d1fb0 100644
--- a/gtk/gtkaccessiblevalueprivate.h
+++ b/gtk/gtkaccessiblevalueprivate.h
@@ -102,6 +102,12 @@ GtkAccessibleValue *    gtk_accessible_value_collect_for_property       (GtkAcce
 GtkAccessibleValue *    gtk_accessible_value_collect_for_property_value (GtkAccessibleProperty          
property,
                                                                          const GValue                  
*value);
 
+GtkAccessibleValue *    gtk_accessible_value_get_default_for_relation   (GtkAccessibleRelation          
relation);
+GtkAccessibleValue *    gtk_accessible_value_collect_for_relation       (GtkAccessibleRelation          
relation,
+                                                                         va_list                       
*args);
+GtkAccessibleValue *    gtk_accessible_value_collect_for_relation_value (GtkAccessibleRelation          
relation,
+                                                                         const GValue                  
*value);
+
 /* Basic values */
 GtkAccessibleValue *            gtk_undefined_accessible_value_new      (void);
 int                             gtk_undefined_accessible_value_get      (const GtkAccessibleValue *value);
diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c
index 4fe0b8176f..6466678ad7 100644
--- a/gtk/gtkatcontext.c
+++ b/gtk/gtkatcontext.c
@@ -56,8 +56,9 @@ gtk_at_context_finalize (GObject *gobject)
 {
   GtkATContext *self = GTK_AT_CONTEXT (gobject);
 
-  gtk_accessible_state_set_unref (self->states);
   gtk_accessible_property_set_unref (self->properties);
+  gtk_accessible_relation_set_unref (self->relations);
+  gtk_accessible_state_set_unref (self->states);
 
   G_OBJECT_CLASS (gtk_at_context_parent_class)->finalize (gobject);
 }
@@ -111,9 +112,11 @@ gtk_at_context_get_property (GObject    *gobject,
 static void
 gtk_at_context_real_state_change (GtkATContext                *self,
                                   GtkAccessibleStateChange     changed_states,
-                                  GtkAccessiblePropertyChange  changed_properies,
+                                  GtkAccessiblePropertyChange  changed_properties,
+                                  GtkAccessibleRelationChange  changed_relations,
                                   GtkAccessibleStateSet       *states,
-                                  GtkAccessiblePropertySet    *properties)
+                                  GtkAccessiblePropertySet    *properties,
+                                  GtkAccessibleRelationSet    *relations)
 {
 }
 
@@ -168,8 +171,9 @@ gtk_at_context_init (GtkATContext *self)
 {
   self->accessible_role = GTK_ACCESSIBLE_ROLE_WIDGET;
 
-  self->states = gtk_accessible_state_set_new ();
   self->properties = gtk_accessible_property_set_new ();
+  self->relations = gtk_accessible_relation_set_new ();
+  self->states = gtk_accessible_state_set_new ();
 }
 
 /**
@@ -269,6 +273,7 @@ gtk_at_context_update (GtkATContext *self)
 
   GtkAccessibleStateChange changed_states = 0;
   GtkAccessiblePropertyChange changed_properties = 0;
+  GtkAccessibleRelationChange changed_relations = 0;
 
   for (int i = 0; i < GTK_ACCESSIBLE_STATE_SELECTED; i++)
     {
@@ -282,11 +287,19 @@ gtk_at_context_update (GtkATContext *self)
         changed_properties |= (1 << i);
     }
 
+  for (int i = 0; i < GTK_ACCESSIBLE_RELATION_SET_SIZE; i++)
+    {
+      if (gtk_accessible_relation_set_contains (self->relations, i))
+        changed_relations |= (1 << i);
+    }
+
   GTK_AT_CONTEXT_GET_CLASS (self)->state_change (self,
                                                  changed_states,
                                                  changed_properties,
+                                                 changed_relations,
                                                  self->states,
-                                                 self->properties);
+                                                 self->properties,
+                                                 self->relations);
 }
 
 /*< private >
@@ -340,3 +353,29 @@ gtk_at_context_set_accessible_property (GtkATContext          *self,
   else
     gtk_accessible_property_set_remove (self->properties, property);
 }
+
+/*< private >
+ * gtk_at_context_set_accessible_relation:
+ * @self: a #GtkATContext
+ * @relation: a #GtkAccessibleRelation
+ * @value: (nullable): #GtkAccessibleValue
+ *
+ * Sets the @value for the given @relation of a #GtkATContext.
+ *
+ * If @value is %NULL, the relation is unset.
+ *
+ * This function will accumulate relation changes until gtk_at_context_update_state()
+ * is called.
+ */
+void
+gtk_at_context_set_accessible_relation (GtkATContext          *self,
+                                        GtkAccessibleRelation  relation,
+                                        GtkAccessibleValue    *value)
+{
+  g_return_if_fail (GTK_IS_AT_CONTEXT (self));
+
+  if (value != NULL)
+    gtk_accessible_relation_set_add (self->relations, relation, value);
+  else
+    gtk_accessible_relation_set_remove (self->relations, relation);
+}
diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h
index ec152a2060..ee7a6dea7c 100644
--- a/gtk/gtkatcontextprivate.h
+++ b/gtk/gtkatcontextprivate.h
@@ -22,22 +22,12 @@
 
 #include "gtkatcontext.h"
 
-#include "gtkaccessiblestatesetprivate.h"
 #include "gtkaccessiblepropertysetprivate.h"
+#include "gtkaccessiblerelationsetprivate.h"
+#include "gtkaccessiblestatesetprivate.h"
 
 G_BEGIN_DECLS
 
-typedef enum {
-  GTK_ACCESSIBLE_STATE_CHANGE_BUSY     = 1 << GTK_ACCESSIBLE_STATE_BUSY,
-  GTK_ACCESSIBLE_STATE_CHANGE_CHECKED  = 1 << GTK_ACCESSIBLE_STATE_CHECKED,
-  GTK_ACCESSIBLE_STATE_CHANGE_DISABLED = 1 << GTK_ACCESSIBLE_STATE_DISABLED,
-  GTK_ACCESSIBLE_STATE_CHANGE_EXPANDED = 1 << GTK_ACCESSIBLE_STATE_EXPANDED,
-  GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN   = 1 << GTK_ACCESSIBLE_STATE_HIDDEN,
-  GTK_ACCESSIBLE_STATE_CHANGE_INVALID  = 1 << GTK_ACCESSIBLE_STATE_INVALID,
-  GTK_ACCESSIBLE_STATE_CHANGE_PRESSED  = 1 << GTK_ACCESSIBLE_STATE_PRESSED,
-  GTK_ACCESSIBLE_STATE_CHANGE_SELECTED = 1 << GTK_ACCESSIBLE_STATE_SELECTED
-} GtkAccessibleStateChange;
-
 typedef enum {
   GTK_ACCESSIBLE_PROPERTY_CHANGE_AUTOCOMPLETE     = 1 << GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE,
   GTK_ACCESSIBLE_PROPERTY_CHANGE_DESCRIPTION      = 1 << GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
@@ -60,6 +50,38 @@ typedef enum {
   GTK_ACCESSIBLE_PROPERTY_CHANGE_VALUE_TEXT       = 1 << GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT
 } GtkAccessiblePropertyChange;
 
+typedef enum {
+  GTK_ACCESSIBLE_RELATION_CHANGE_ACTIVE_DESCENDANT = 1 << GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT,
+  GTK_ACCESSIBLE_RELATION_CHANGE_COL_COUNT         = 1 << GTK_ACCESSIBLE_RELATION_COL_COUNT,
+  GTK_ACCESSIBLE_RELATION_CHANGE_COL_INDEX         = 1 << GTK_ACCESSIBLE_RELATION_COL_INDEX,
+  GTK_ACCESSIBLE_RELATION_CHANGE_COL_INDEX_TEXT    = 1 << GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT,
+  GTK_ACCESSIBLE_RELATION_CHANGE_COL_SPAN          = 1 << GTK_ACCESSIBLE_RELATION_COL_SPAN,
+  GTK_ACCESSIBLE_RELATION_CHANGE_CONTROLS          = 1 << GTK_ACCESSIBLE_RELATION_CONTROLS,
+  GTK_ACCESSIBLE_RELATION_CHANGE_DESCRIBED_BY      = 1 << GTK_ACCESSIBLE_RELATION_DESCRIBED_BY,
+  GTK_ACCESSIBLE_RELATION_CHANGE_DETAILS           = 1 << GTK_ACCESSIBLE_RELATION_DETAILS,
+  GTK_ACCESSIBLE_RELATION_CHANGE_ERROR_MESSAGE     = 1 << GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE,
+  GTK_ACCESSIBLE_RELATION_CHANGE_FLOW_TO           = 1 << GTK_ACCESSIBLE_RELATION_FLOW_TO,
+  GTK_ACCESSIBLE_RELATION_CHANGE_LABELLED_BY       = 1 << GTK_ACCESSIBLE_RELATION_LABELLED_BY,
+  GTK_ACCESSIBLE_RELATION_CHANGE_OWNS              = 1 << GTK_ACCESSIBLE_RELATION_OWNS,
+  GTK_ACCESSIBLE_RELATION_CHANGE_POS_IN_SET        = 1 << GTK_ACCESSIBLE_RELATION_POS_IN_SET,
+  GTK_ACCESSIBLE_RELATION_CHANGE_ROW_COUNT         = 1 << GTK_ACCESSIBLE_RELATION_ROW_COUNT,
+  GTK_ACCESSIBLE_RELATION_CHANGE_ROW_INDEX         = 1 << GTK_ACCESSIBLE_RELATION_ROW_INDEX,
+  GTK_ACCESSIBLE_RELATION_CHANGE_ROW_INDEX_TEXT    = 1 << GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT,
+  GTK_ACCESSIBLE_RELATION_CHANGE_ROW_SPAN          = 1 << GTK_ACCESSIBLE_RELATION_ROW_SPAN,
+  GTK_ACCESSIBLE_RELATION_CHANGE_SET_SIZE          = 1 << GTK_ACCESSIBLE_RELATION_SET_SIZE
+} GtkAccessibleRelationChange;
+
+typedef enum {
+  GTK_ACCESSIBLE_STATE_CHANGE_BUSY     = 1 << GTK_ACCESSIBLE_STATE_BUSY,
+  GTK_ACCESSIBLE_STATE_CHANGE_CHECKED  = 1 << GTK_ACCESSIBLE_STATE_CHECKED,
+  GTK_ACCESSIBLE_STATE_CHANGE_DISABLED = 1 << GTK_ACCESSIBLE_STATE_DISABLED,
+  GTK_ACCESSIBLE_STATE_CHANGE_EXPANDED = 1 << GTK_ACCESSIBLE_STATE_EXPANDED,
+  GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN   = 1 << GTK_ACCESSIBLE_STATE_HIDDEN,
+  GTK_ACCESSIBLE_STATE_CHANGE_INVALID  = 1 << GTK_ACCESSIBLE_STATE_INVALID,
+  GTK_ACCESSIBLE_STATE_CHANGE_PRESSED  = 1 << GTK_ACCESSIBLE_STATE_PRESSED,
+  GTK_ACCESSIBLE_STATE_CHANGE_SELECTED = 1 << GTK_ACCESSIBLE_STATE_SELECTED
+} GtkAccessibleStateChange;
+
 struct _GtkATContext
 {
   GObject parent_instance;
@@ -69,6 +91,7 @@ struct _GtkATContext
 
   GtkAccessibleStateSet *states;
   GtkAccessiblePropertySet *properties;
+  GtkAccessibleRelationSet *relations;
 };
 
 struct _GtkATContextClass
@@ -78,8 +101,10 @@ struct _GtkATContextClass
   void (* state_change) (GtkATContext                *self,
                          GtkAccessibleStateChange     changed_states,
                          GtkAccessiblePropertyChange  changed_properties,
+                         GtkAccessibleRelationChange  changed_relations,
                          GtkAccessibleStateSet       *states,
-                         GtkAccessiblePropertySet    *properties);
+                         GtkAccessiblePropertySet    *properties,
+                         GtkAccessibleRelationSet    *relations);
 };
 
 GtkATContext *  gtk_at_context_create                   (GtkAccessibleRole      accessible_role,
@@ -93,5 +118,8 @@ void            gtk_at_context_set_accessible_state     (GtkATContext          *
 void            gtk_at_context_set_accessible_property  (GtkATContext          *self,
                                                          GtkAccessibleProperty  property,
                                                          GtkAccessibleValue    *value);
+void            gtk_at_context_set_accessible_relation  (GtkATContext          *self,
+                                                         GtkAccessibleRelation  property,
+                                                         GtkAccessibleValue    *value);
 
 G_END_DECLS
diff --git a/gtk/gtktestatcontext.c b/gtk/gtktestatcontext.c
index f89e354451..e9b7e8c0b4 100644
--- a/gtk/gtktestatcontext.c
+++ b/gtk/gtktestatcontext.c
@@ -36,24 +36,30 @@ static void
 gtk_test_at_context_state_change (GtkATContext                *self,
                                   GtkAccessibleStateChange     changed_states,
                                   GtkAccessiblePropertyChange  changed_properties,
+                                  GtkAccessibleRelationChange  changed_relations,
                                   GtkAccessibleStateSet       *states,
-                                  GtkAccessiblePropertySet    *properties)
+                                  GtkAccessiblePropertySet    *properties,
+                                  GtkAccessibleRelationSet    *relations)
 {
   GtkAccessible *accessible = gtk_at_context_get_accessible (self);
   GtkAccessibleRole role = gtk_at_context_get_accessible_role (self);
   char *states_str = gtk_accessible_state_set_to_string (states);
   char *properties_str = gtk_accessible_property_set_to_string (properties);
+  char *relations_str = gtk_accessible_relation_set_to_string (relations);
 
   g_print ("*** Accessible state changed for accessible “%s”, with role %d:\n"
-           "*** states = %s\n"
-           "*** properties = %s\n",
+           "***     states = %s\n"
+           "*** properties = %s\n"
+           "***  relations = %s\n",
            G_OBJECT_TYPE_NAME (accessible),
            role,
            states_str,
-           properties_str);
+           properties_str,
+           relations_str);
 
   g_free (states_str);
   g_free (properties_str);
+  g_free (relations_str);
 }
 
 static void
diff --git a/gtk/meson.build b/gtk/meson.build
index 22df77a76e..2de6ebe420 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -17,6 +17,7 @@ gtk_private_sources = files([
   'tools/gdkpixbufutils.c',
   'gsettings-mapping.c',
   'gtkaccessiblepropertyset.c',
+  'gtkaccessiblerelationset.c',
   'gtkaccessiblestateset.c',
   'gtkaccessiblevalue.c',
   'gtkaccessiblevaluestatic.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]