[gtk/wip/ebassi/a11y-drop-atk: 433/434] Implement accessible states



commit 377f704937ba8ac06659afe5aa8e93d145b91c18
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Mon Jul 6 16:35:52 2020 +0100

    Implement accessible states
    
    Accessible objects have three components:
    
     - properties, which represent its contents, and won't usually change
       often
     - states, which are used to notify of state changes, and can change
       depending on the user interaction
     - a role, which specifies what an object can do, and the states and
       properties can be used for it; roles cannot change
    
    Properties and states work more or less in the same way: they are well
    defined names and each has a well defined value type.

 gtk/gtk.h                          |   1 +
 gtk/gtkaccessible.c                |  75 +++++-
 gtk/gtkaccessible.h                |   8 +-
 gtk/gtkaccessibleprivate.h         |  37 +++
 gtk/gtkaccessiblestateset.c        | 159 ++++++++++++
 gtk/gtkaccessiblestatesetprivate.h |  45 ++++
 gtk/gtkaccessiblevalue.c           | 287 +++++++++++++++++++++
 gtk/gtkaccessiblevalueprivate.h    | 131 ++++++++++
 gtk/gtkaccessiblevaluestatic.c     | 495 +++++++++++++++++++++++++++++++++++++
 gtk/gtkatcontext.c                 | 263 ++++++++++++++++++++
 gtk/gtkatcontext.h                 |  39 +++
 gtk/gtkatcontextprivate.h          |  56 +++++
 gtk/gtkenums.h                     |  58 +++++
 gtk/gtktestatcontext.c             |  72 ++++++
 gtk/gtktestatcontextprivate.h      |  47 ++++
 gtk/gtkwidget.c                    |  48 +++-
 gtk/gtkwidget.h                    |   4 +
 gtk/gtkwidgetprivate.h             |   5 +
 gtk/meson.build                    |   6 +
 19 files changed, 1830 insertions(+), 6 deletions(-)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 50c23caf26..6d897998aa 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -46,6 +46,7 @@
 #include <gtk/gtkapplicationwindow.h>
 #include <gtk/gtkaspectframe.h>
 #include <gtk/gtkassistant.h>
+#include <gtk/gtkatcontext.h>
 #include <gtk/gtkbinlayout.h>
 #include <gtk/gtkbitset.h>
 #include <gtk/gtkbookmarklist.h>
diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c
index d8187b60c0..3c0a9ffbdb 100644
--- a/gtk/gtkaccessible.c
+++ b/gtk/gtkaccessible.c
@@ -18,9 +18,36 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+/**
+ * SECTION:gtkaccessible
+ * @Title: GtkAccessible
+ * @Short_description: Accessible interface
+ *
+ * GtkAccessible provides an interface for describing a UI element, like a
+ * #GtkWidget, in a way that can be consumed by Assistive Technologies, or
+ * “AT”. Every accessible implementation has:
+ *
+ *  - a “role”, represented by a value of the #GtkAccessibleRole enumeration
+ *  - a “state”, represented by a set of #GtkAccessibleState and
+ *    #GtkAccessibleProperty values
+ *
+ * The role cannot be changed after instantiating a #GtkAccessible
+ * implementation.
+ *
+ * The state is updated every time a UI element's state changes in a way that
+ * should be reflected by assistive technologies. For instance, if a #GtkWidget
+ * visibility changes, the %GTK_ACCESSIBLE_STATE_HIDDEN state will also change
+ * to reflect the #GtkWidget:visible property.
+ */
+
 #include "config.h"
 
-#include "gtkaccessible.h"
+#include "gtkaccessibleprivate.h"
+
+#include "gtkatcontextprivate.h"
+#include "gtkenums.h"
+
+#include <stdarg.h>
 
 G_DEFINE_INTERFACE (GtkAccessible, gtk_accessible, G_TYPE_OBJECT)
 
@@ -28,3 +55,49 @@ static void
 gtk_accessible_default_init (GtkAccessibleInterface *iface)
 {
 }
+
+GtkATContext *
+gtk_accessible_get_at_context (GtkAccessible *self)
+{
+  g_return_val_if_fail (GTK_IS_ACCESSIBLE (self), NULL);
+
+  return GTK_ACCESSIBLE_GET_IFACE (self)->get_at_context (self);
+}
+
+void
+gtk_accessible_update_state (GtkAccessible *self,
+                             GtkAccessibleState  first_state,
+                             ...)
+{
+  GtkAccessibleState state;
+  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_state);
+
+  state = first_state;
+
+  while (state != GTK_ACCESSIBLE_STATE_NONE)
+    {
+      GtkAccessibleValue *value = gtk_accessible_value_collect_for_state (state, &args);
+
+      if (value == NULL)
+        goto out;
+
+      gtk_at_context_set_state (context, state, value);
+      gtk_accessible_value_unref (value);
+
+      state = va_arg (args, int);
+    }
+
+  gtk_at_context_update_state (context);
+
+out:
+  va_end (args);
+}
diff --git a/gtk/gtkaccessible.h b/gtk/gtkaccessible.h
index d54d21a2d0..6946916980 100644
--- a/gtk/gtkaccessible.h
+++ b/gtk/gtkaccessible.h
@@ -31,9 +31,9 @@ G_BEGIN_DECLS
 GDK_AVAILABLE_IN_ALL
 G_DECLARE_INTERFACE (GtkAccessible, gtk_accessible, GTK, ACCESSIBLE, GObject)
 
-struct _GtkAccessibleInterface
-{
-  GTypeInterface g_iface;
-};
+GDK_AVAILABLE_IN_ALL
+void    gtk_accessible_update_state     (GtkAccessible      *self,
+                                         GtkAccessibleState  first_state,
+                                         ...);
 
 G_END_DECLS
diff --git a/gtk/gtkaccessibleprivate.h b/gtk/gtkaccessibleprivate.h
new file mode 100644
index 0000000000..aa5701cf50
--- /dev/null
+++ b/gtk/gtkaccessibleprivate.h
@@ -0,0 +1,37 @@
+/* gtkaccessibleprivate.h: Accessible interface
+ *
+ * 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 "gtkaccessible.h"
+#include "gtkatcontext.h"
+
+G_BEGIN_DECLS
+
+struct _GtkAccessibleInterface
+{
+  GTypeInterface g_iface;
+
+  GtkATContext *        (* get_at_context)      (GtkAccessible *self);
+};
+
+GtkATContext *  gtk_accessible_get_at_context   (GtkAccessible *self);
+
+G_END_DECLS
diff --git a/gtk/gtkaccessiblestateset.c b/gtk/gtkaccessiblestateset.c
new file mode 100644
index 0000000000..05b992da9e
--- /dev/null
+++ b/gtk/gtkaccessiblestateset.c
@@ -0,0 +1,159 @@
+/* gtkaccessiblestateset.c: Accessible state
+ *
+ * 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 "gtkaccessiblestatesetprivate.h"
+
+#include "gtkbitmaskprivate.h"
+#include "gtkenums.h"
+
+/* Keep in sync with GtkAccessibleState in gtkenums.h */
+#define LAST_STATE GTK_ACCESSIBLE_STATE_SELECTED
+
+struct _GtkAccessibleStateSet
+{
+  GtkBitmask *states_set;
+
+  GtkAccessibleValue **state_values;
+};
+
+GtkAccessibleStateSet *
+gtk_accessible_state_set_new (void)
+{
+  GtkAccessibleStateSet *set = g_rc_box_new (GtkAccessibleStateSet);
+
+  set->states_set = _gtk_bitmask_new ();
+  set->state_values = g_new0 (GtkAccessibleValue*, LAST_STATE);
+
+  return set;
+}
+
+GtkAccessibleStateSet *
+gtk_accessible_state_set_ref (GtkAccessibleStateSet *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return g_rc_box_acquire (self);
+}
+
+static void
+gtk_accessible_state_set_free (gpointer data)
+{
+  GtkAccessibleStateSet *self = data;
+
+  for (int i = 0; i < LAST_STATE; i++)
+    {
+      if (self->state_values[i] != NULL)
+        gtk_accessible_value_unref (self->state_values[i]);
+    }
+
+  g_free (self->state_values);
+
+  _gtk_bitmask_free (self->states_set);
+}
+
+void
+gtk_accessible_state_set_unref (GtkAccessibleStateSet *self)
+{
+  g_rc_box_release_full (self, gtk_accessible_state_set_free);
+}
+
+void
+gtk_accessible_state_set_add (GtkAccessibleStateSet *self,
+                              GtkAccessibleState     state,
+                              GtkAccessibleValue    *value)
+{
+  if (gtk_accessible_state_set_contains (self, state))
+    gtk_accessible_value_unref (self->state_values[state]);
+  else
+    self->states_set = _gtk_bitmask_set (self->states_set, state, TRUE);
+
+  self->state_values[state] = gtk_accessible_value_ref (value);
+}
+
+void
+gtk_accessible_state_set_remove (GtkAccessibleStateSet *self,
+                                 GtkAccessibleState     state)
+{
+  if (gtk_accessible_state_set_contains (self, state))
+    {
+      g_clear_pointer (&(self->state_values[state]), gtk_accessible_value_unref);
+      self->states_set = _gtk_bitmask_set (self->states_set, state, FALSE);
+    }
+}
+
+gboolean
+gtk_accessible_state_set_contains (GtkAccessibleStateSet *self,
+                                   GtkAccessibleState     state)
+{
+  return _gtk_bitmask_get (self->states_set, state);
+}
+
+static const char *state_names[] = {
+  [GTK_ACCESSIBLE_STATE_BUSY]     = "busy",
+  [GTK_ACCESSIBLE_STATE_CHECKED]  = "checked",
+  [GTK_ACCESSIBLE_STATE_DISABLED] = "disabled",
+  [GTK_ACCESSIBLE_STATE_EXPANDED] = "expanded",
+  [GTK_ACCESSIBLE_STATE_GRABBED]  = "grabbed",
+  [GTK_ACCESSIBLE_STATE_HIDDEN]   = "hidden",
+  [GTK_ACCESSIBLE_STATE_INVALID]  = "invalid",
+  [GTK_ACCESSIBLE_STATE_PRESSED]  = "pressed",
+  [GTK_ACCESSIBLE_STATE_SELECTED] = "selected",
+};
+
+static void
+gtk_accessible_state_set_print (GtkAccessibleStateSet *self,
+                                GString               *buffer)
+{
+  if (_gtk_bitmask_is_empty (self->states_set))
+    {
+      g_string_append (buffer, "{}");
+      return;
+    }
+
+  g_string_append (buffer, "{\n");
+
+  for (int i = 0; i < G_N_ELEMENTS (state_names); i++)
+    {
+      if (_gtk_bitmask_get (self->states_set, i))
+        {
+          g_string_append (buffer, "    ");
+          g_string_append (buffer, state_names[i]);
+          g_string_append (buffer, ": ");
+
+          gtk_accessible_value_print (self->state_values[i], buffer);
+
+          g_string_append (buffer, ",\n");
+        }
+    }
+
+  g_string_append (buffer, "}");
+}
+
+char *
+gtk_accessible_state_set_to_string (GtkAccessibleStateSet *self)
+{
+  GString *buf = g_string_new (NULL);
+
+  gtk_accessible_state_set_print (self, buf);
+
+  return g_string_free (buf, FALSE);
+}
diff --git a/gtk/gtkaccessiblestatesetprivate.h b/gtk/gtkaccessiblestatesetprivate.h
new file mode 100644
index 0000000000..881eb8a07c
--- /dev/null
+++ b/gtk/gtkaccessiblestatesetprivate.h
@@ -0,0 +1,45 @@
+/* gtkaccessiblestatesetprivate.h: Accessible state container
+ *
+ * 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 _GtkAccessibleStateSet   GtkAccessibleStateSet;
+
+GtkAccessibleStateSet * gtk_accessible_state_set_new            (void);
+GtkAccessibleStateSet * gtk_accessible_state_set_ref            (GtkAccessibleStateSet *self);
+void                    gtk_accessible_state_set_unref          (GtkAccessibleStateSet *self);
+
+void                    gtk_accessible_state_set_add            (GtkAccessibleStateSet *self,
+                                                                 GtkAccessibleState     state,
+                                                                 GtkAccessibleValue    *value);
+void                    gtk_accessible_state_set_remove         (GtkAccessibleStateSet *self,
+                                                                 GtkAccessibleState     state);
+
+gboolean                gtk_accessible_state_set_contains       (GtkAccessibleStateSet *self,
+                                                                 GtkAccessibleState     state);
+
+char *                  gtk_accessible_state_set_to_string      (GtkAccessibleStateSet *self);
+
+G_END_DECLS
diff --git a/gtk/gtkaccessiblevalue.c b/gtk/gtkaccessiblevalue.c
new file mode 100644
index 0000000000..d6ae9ed270
--- /dev/null
+++ b/gtk/gtkaccessiblevalue.c
@@ -0,0 +1,287 @@
+/* gtkaccessiblevalue.c: Accessible value
+ *
+ * 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/>.
+ */
+
+/*< private >
+ * SECTION: gtkaccessiblevalue
+ * @Title: GtkAccessibleValue
+ * @Short_description: Container for accessible state and property values
+ *
+ * GtkAccessibleValue is a reference counted, generic container for values used
+ * to represent the state and properties of a #GtkAccessible implementation.
+ *
+ * There are two kinds of accessible value types:
+ *
+ *  - hard coded, static values; GTK owns them, and their contents, and are
+ *    guaranteed to exist for the duration of the application's life time
+ *  - dynamic values; the accessible state owns the value and their contents,
+ *    and they can be allocated and freed
+ *
+ * Typically, the former type of values is used for boolean, tristate, and
+ * token value; the latter is used for numbers, strings, and token lists.
+ *
+ * For more information on the types of values, see the 
[WAI-ARIA](https://www.w3.org/WAI/PF/aria/states_and_properties#propcharacteristic_value)
+ * reference.
+ */
+
+#include "config.h"
+
+#include "gtkaccessiblevalueprivate.h"
+
+#include "gtkenums.h"
+
+G_DEFINE_QUARK (gtk-accessible-value-error-quark, gtk_accessible_value_error)
+
+G_DEFINE_BOXED_TYPE (GtkAccessibleValue, gtk_accessible_value,
+                     gtk_accessible_value_ref,
+                     gtk_accessible_value_unref)
+
+/*< private >
+ * gtk_accessible_value_alloc:
+ * @value_class: a #GtkAccessibleValueClass structure
+ *
+ * Allocates a new #GtkAccessibleValue subclass using @value_class as the
+ * type definition.
+ *
+ * Returns: (transfer full): the newly allocated #GtkAccessibleValue
+ */
+GtkAccessibleValue *
+gtk_accessible_value_alloc (const GtkAccessibleValueClass *value_class)
+{
+  g_return_val_if_fail (value_class != NULL, NULL);
+  g_return_val_if_fail (value_class->instance_size >= sizeof (GtkAccessibleValue), NULL);
+
+  GtkAccessibleValue *res = g_malloc0 (value_class->instance_size);
+
+  /* We do not use grefcount, here, because we want to have statically
+   * allocated GtkAccessibleValue subclasses, and those cannot be initialized
+   * with g_ref_count_init()
+   */
+  res->ref_count = 1;
+  res->value_class = value_class;
+
+  if (res->value_class->init != NULL)
+    res->value_class->init (res);
+
+  return res;
+}
+
+/*< private >
+ * gtk_accessible_value_ref:
+ * @self: a #GtkAccessibleValue
+ *
+ * Acquires a reference on the given #GtkAccessibleValue.
+ *
+ * Returns: (transfer full): the value, with an additional reference
+ */
+GtkAccessibleValue *
+gtk_accessible_value_ref (GtkAccessibleValue *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  self->ref_count += 1;
+
+  return self;
+}
+
+/*< private >
+ * gtk_accessible_value_unref:
+ * @self: (transfer full): a #GtkAccessibleValue
+ *
+ * Releases a reference on the given #GtkAccessibleValue.
+ */
+void
+gtk_accessible_value_unref (GtkAccessibleValue *self)
+{
+  g_return_if_fail (self != NULL);
+
+  self->ref_count -= 1;
+  if (self->ref_count == 0)
+    {
+      if (self->value_class->finalize != NULL)
+        self->value_class->finalize (self);
+
+      g_free (self);
+    }
+}
+
+/*< private >
+ * gtk_accessible_value_print:
+ * @self: a #GtkAccessibleValue
+ * @buffer: a #GString
+ *
+ * Prints the contents of a #GtkAccessibleValue into the given @buffer.
+ */
+void
+gtk_accessible_value_print (const GtkAccessibleValue *self,
+                            GString                  *buffer)
+{
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (buffer != NULL);
+
+  if (self->value_class->print != NULL)
+    self->value_class->print (self, buffer);
+}
+
+/*< private >
+ * gtk_accessible_value_to_string:
+ * @self: a #GtkAccessibleValue
+ *
+ * Fills a string with the contents of the given #GtkAccessibleValue.
+ *
+ * Returns: (transfer full): a string with the contents of the value
+ */
+char *
+gtk_accessible_value_to_string (const GtkAccessibleValue *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  GString *buffer = g_string_new (NULL);
+
+  gtk_accessible_value_print (self, buffer);
+
+  return g_string_free (buffer, FALSE);
+}
+
+/*< private >
+ * gtk_accessible_value_equal:
+ * @value_a: (nullable): the first #GtkAccessibleValue
+ * @value_b: (nullable): the second #GtkAccessibleValue
+ *
+ * Checks whether @value_a and @value_b are equal.
+ *
+ * This function is %NULL-safe.
+ *
+ * Returns: %TRUE if the given #GtkAccessibleValue instances are equal,
+ *   and %FALSE otherwise
+ */
+gboolean
+gtk_accessible_value_equal (const GtkAccessibleValue *value_a,
+                            const GtkAccessibleValue *value_b)
+{
+  if (value_a == value_b)
+    return TRUE;
+
+  if (value_a == NULL || value_b == NULL)
+    return FALSE;
+
+  return value_a->value_class->equal (value_a, value_b);
+}
+
+typedef enum {
+  GTK_ACCESSIBLE_COLLECT_INVALID,
+  GTK_ACCESSIBLE_COLLECT_BOOLEAN,
+  GTK_ACCESSIBLE_COLLECT_INTEGER,
+  GTK_ACCESSIBLE_COLLECT_TRISTATE,
+  GTK_ACCESSIBLE_COLLECT_ENUM
+} GtkAccessibleCollectType;
+
+typedef GtkAccessibleValue *(* GtkAccessibleValueConstructor) (void);
+
+typedef struct {
+  GtkAccessibleState state;
+  GtkAccessibleCollectType ctype;
+  const char *state_name;
+  GCallback ctor;
+} GtkAccessibleCollectState;
+
+static const GtkAccessibleCollectState collect_states[] = {
+/* | State                         | Collected type                 | Name      | Constructor                
      |
+ * 
|-------------------------------|--------------------------------|-----------|----------------------------------|
+ */
+  { GTK_ACCESSIBLE_STATE_BUSY,     GTK_ACCESSIBLE_COLLECT_BOOLEAN,  "busy",     (GCallback) 
gtk_busy_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_CHECKED,  GTK_ACCESSIBLE_COLLECT_ENUM,     "checked",  (GCallback) 
gtk_checked_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_DISABLED, GTK_ACCESSIBLE_COLLECT_BOOLEAN,  "disabled", (GCallback) 
gtk_disabled_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_EXPANDED, GTK_ACCESSIBLE_COLLECT_TRISTATE, "expanded", (GCallback) 
gtk_expanded_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_GRABBED,  GTK_ACCESSIBLE_COLLECT_TRISTATE, "grabbed",  (GCallback) 
gtk_grabbed_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_HIDDEN,   GTK_ACCESSIBLE_COLLECT_BOOLEAN,  "hidden",   (GCallback) 
gtk_hidden_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_INVALID,  GTK_ACCESSIBLE_COLLECT_ENUM,     "invalid",  (GCallback) 
gtk_invalid_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_PRESSED,  GTK_ACCESSIBLE_COLLECT_ENUM,     "pressed",  (GCallback) 
gtk_pressed_accessible_value_new },
+  { GTK_ACCESSIBLE_STATE_SELECTED, GTK_ACCESSIBLE_COLLECT_TRISTATE, "selected", (GCallback) 
gtk_selected_accessible_value_new },
+};
+
+typedef GtkAccessibleValue * (* GtkAccessibleValueBooleanCtor)  (gboolean state);
+typedef GtkAccessibleValue * (* GtkAccessibleValueIntCtor)      (int state);
+typedef GtkAccessibleValue * (* GtkAccessibleValueTristateCtor) (int state);
+typedef GtkAccessibleValue * (* GtkAccessibleValueEnumCtor)     (int state);
+
+GtkAccessibleValue *
+gtk_accessible_value_collect_for_state (GtkAccessibleState  state,
+                                        va_list            *args)
+{
+  g_assert (state != GTK_ACCESSIBLE_STATE_NONE);
+
+  const GtkAccessibleCollectState *cstate = &collect_states[state];
+
+  GtkAccessibleValue *res = NULL;
+
+  switch (cstate->ctype)
+    {
+    case GTK_ACCESSIBLE_COLLECT_BOOLEAN:
+      {
+        GtkAccessibleValueBooleanCtor ctor =
+          (GtkAccessibleValueBooleanCtor) cstate->ctor;
+
+        gboolean value = va_arg (*args, gboolean);
+
+        res = (* ctor) (value);
+      }
+      break;
+
+    case GTK_ACCESSIBLE_COLLECT_INTEGER:
+      {
+        GtkAccessibleValueIntCtor ctor =
+          (GtkAccessibleValueIntCtor) cstate->ctor;
+
+        int value = va_arg (*args, int);
+
+        res = (* ctor) (value);
+      }
+      break;
+
+    case GTK_ACCESSIBLE_COLLECT_TRISTATE:
+      {
+        GtkAccessibleValueTristateCtor ctor =
+          (GtkAccessibleValueIntCtor) cstate->ctor;
+
+        int value = va_arg (*args, int);
+
+        res = (* ctor) (value);
+      }
+      break;
+
+    case GTK_ACCESSIBLE_COLLECT_ENUM:
+      {
+        GtkAccessibleValueEnumCtor ctor =
+          (GtkAccessibleValueEnumCtor) cstate->ctor;
+
+        int value = va_arg (*args, int);
+
+        res = (* ctor) (value);
+      }
+      break;
+
+    case GTK_ACCESSIBLE_COLLECT_INVALID:
+    default:
+      g_critical ("Unknown type for accessible state “%s”", cstate->state_name);
+      break;
+    }
+
+  return res;
+}
diff --git a/gtk/gtkaccessiblevalueprivate.h b/gtk/gtkaccessiblevalueprivate.h
new file mode 100644
index 0000000000..6798bce331
--- /dev/null
+++ b/gtk/gtkaccessiblevalueprivate.h
@@ -0,0 +1,131 @@
+/* gtkaccessiblevalueprivate.h: Accessible value
+ *
+ * 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 <glib-object.h>
+
+#include "gtkenums.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACCESSIBLE_VALUE (gtk_accessible_value_get_type())
+
+#define GTK_ACCESSIBLE_VALUE_ERROR (gtk_accessible_value_error_quark())
+
+typedef struct _GtkAccessibleValue      GtkAccessibleValue;
+typedef struct _GtkAccessibleValueClass GtkAccessibleValueClass;
+
+struct _GtkAccessibleValue
+{
+  const GtkAccessibleValueClass *value_class;
+
+  int ref_count;
+};
+
+struct _GtkAccessibleValueClass
+{
+  const char *type_name;
+  gsize instance_size;
+
+  void (* init) (GtkAccessibleValue *self);
+  void (* finalize) (GtkAccessibleValue *self);
+  void (* print) (const GtkAccessibleValue *self,
+                  GString *string);
+  gboolean (* equal) (const GtkAccessibleValue *value_a,
+                      const GtkAccessibleValue *value_b);
+};
+
+typedef enum {
+  GTK_ACCESSIBLE_VALUE_ERROR_READ_ONLY,
+  GTK_ACCESSIBLE_VALUE_ERROR_INVALID_VALUE,
+  GTK_ACCESSIBLE_VALUE_ERROR_INVALID_RANGE,
+  GTK_ACCESSIBLE_VALUE_ERROR_INVALID_TOKEN
+} GtkAccessibleValueError;
+
+GType                   gtk_accessible_value_get_type                   (void) G_GNUC_CONST;
+GQuark                  gtk_accessible_value_error_quark                (void);
+
+GtkAccessibleValue *    gtk_accessible_value_alloc                      (const GtkAccessibleValueClass 
*klass);
+GtkAccessibleValue *    gtk_accessible_value_ref                        (GtkAccessibleValue            
*self);
+void                    gtk_accessible_value_unref                      (GtkAccessibleValue            
*self);
+void                    gtk_accessible_value_print                      (const GtkAccessibleValue      *self,
+                                                                         GString                       
*buffer);
+char *                  gtk_accessible_value_to_string                  (const GtkAccessibleValue      
*self);
+gboolean                gtk_accessible_value_equal                      (const GtkAccessibleValue      
*value_a,
+                                                                         const GtkAccessibleValue      
*value_b);
+
+GtkAccessibleValue *    gtk_accessible_value_collect_for_state          (GtkAccessibleState             
state,
+                                                                         va_list                       
*args);
+
+typedef enum { /*< prefix=GTK_ACCESSIBLE_CHECKED >*/
+  GTK_ACCESSIBLE_CHECKED_FALSE     = 0,
+  GTK_ACCESSIBLE_CHECKED_TRUE      = 1,
+  GTK_ACCESSIBLE_CHECKED_UNDEFINED = -1,
+  GTK_ACCESSIBLE_CHECKED_MIXED     = -2
+} GtkAccessibleCheckedState;
+
+typedef enum { /*< prefix=GTK_ACCESSIBLE_PRESSED >*/
+  GTK_ACCESSIBLE_PRESSED_FALSE     = 0,
+  GTK_ACCESSIBLE_PRESSED_TRUE      = 1,
+  GTK_ACCESSIBLE_PRESSED_UNDEFINED = -1,
+  GTK_ACCESSIBLE_PRESSED_MIXED     = -2
+} GtkAccessiblePressedState;
+
+typedef enum { /*< prefix=GTK_ACCESSIBLE_INVALID >*/
+  GTK_ACCESSIBLE_INVALID_FALSE,
+  GTK_ACCESSIBLE_INVALID_TRUE,
+  GTK_ACCESSIBLE_INVALID_GRAMMAR,
+  GTK_ACCESSIBLE_INVALID_SPELLING,
+} GtkAccessibleInvalidState;
+
+#define GTK_ACCESSIBLE_STATE_UNDEFINED  (-1)
+
+/* Boolean states */
+GtkAccessibleValue *            gtk_busy_accessible_value_new           (gboolean                  state);
+gboolean                        gtk_busy_accessible_value_get           (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_disabled_accessible_value_new       (gboolean                  state);
+gboolean                        gtk_disabled_accessible_value_get       (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_hidden_accessible_value_new         (gboolean                  state);
+gboolean                        gtk_hidden_accessible_value_get       (const GtkAccessibleValue *value);
+
+/* Tri-state states */
+GtkAccessibleValue *            gtk_expanded_accessible_value_new       (int                       state);
+int                             gtk_expanded_accessible_value_get       (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_grabbed_accessible_value_new        (int                       state);
+int                             gtk_grabbed_accessible_value_get        (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_selected_accessible_value_new       (int                       state);
+int                             gtk_selected_accessible_value_get       (const GtkAccessibleValue *value);
+
+/* Token states */
+GtkAccessibleValue *            gtk_pressed_accessible_value_new        (GtkAccessiblePressedState state);
+GtkAccessiblePressedState       gtk_pressed_accessible_value_get        (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_checked_accessible_value_new        (GtkAccessibleCheckedState state);
+GtkAccessibleCheckedState       gtk_checked_accessible_value_get        (const GtkAccessibleValue *value);
+
+GtkAccessibleValue *            gtk_invalid_accessible_value_new        (GtkAccessibleInvalidState state);
+GtkAccessibleInvalidState       gtk_invalid_accessible_value_get        (const GtkAccessibleValue *value);
+
+G_END_DECLS
diff --git a/gtk/gtkaccessiblevaluestatic.c b/gtk/gtkaccessiblevaluestatic.c
new file mode 100644
index 0000000000..c41f92cac7
--- /dev/null
+++ b/gtk/gtkaccessiblevaluestatic.c
@@ -0,0 +1,495 @@
+/* gtkaccessiblevaluestatic.c: GtkAccessibleValue static implementations
+ *
+ * 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 "gtkaccessiblevalueprivate.h"
+
+/* {{{ Boolean values */ 
+
+typedef struct
+{
+  GtkAccessibleValue parent;
+
+  gboolean value;
+} GtkBooleanAccessibleValue;
+
+static gboolean 
+gtk_boolean_accessible_value_equal (const GtkAccessibleValue *value_a,
+                                    const GtkAccessibleValue *value_b)
+{
+  const GtkBooleanAccessibleValue *bool_a = (GtkBooleanAccessibleValue *) value_a;
+  const GtkBooleanAccessibleValue *bool_b = (GtkBooleanAccessibleValue *) value_b;
+
+  return bool_a->value == bool_b->value;
+}
+
+static void
+gtk_boolean_accessible_value_print (const GtkAccessibleValue *value,
+                                    GString                  *buffer)
+{
+  const GtkBooleanAccessibleValue *self = (GtkBooleanAccessibleValue *) value;
+
+  g_string_append_printf (buffer, "%s", self->value ? "true" : "false");
+}
+
+static const GtkAccessibleValueClass GTK_BUSY_ACCESSIBLE_VALUE = {
+  .type_name = "GtkBusyAccessibleValue",
+  .instance_size = sizeof (GtkBooleanAccessibleValue),
+  .equal = gtk_boolean_accessible_value_equal,
+  .print = gtk_boolean_accessible_value_print,
+};
+
+static GtkBooleanAccessibleValue busy_values[] = {
+ { { &GTK_BUSY_ACCESSIBLE_VALUE, 1 }, FALSE },
+ { { &GTK_BUSY_ACCESSIBLE_VALUE, 1 }, TRUE },
+};
+
+GtkAccessibleValue *
+gtk_busy_accessible_value_new (gboolean state)
+{
+  if (state)
+    return gtk_accessible_value_ref ((GtkAccessibleValue *) &busy_values[1]);
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &busy_values[0]);
+}
+
+gboolean
+gtk_busy_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkBooleanAccessibleValue *self = (GtkBooleanAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (value->value_class == &GTK_BUSY_ACCESSIBLE_VALUE, FALSE);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_DISABLED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkDisabledAccessibleValue",
+  .instance_size = sizeof (GtkBooleanAccessibleValue),
+  .equal = gtk_boolean_accessible_value_equal,
+  .print = gtk_boolean_accessible_value_print,
+};
+
+static GtkBooleanAccessibleValue disabled_values[] = {
+ { { &GTK_DISABLED_ACCESSIBLE_VALUE, 1 }, FALSE },
+ { { &GTK_DISABLED_ACCESSIBLE_VALUE, 1 }, TRUE },
+};
+
+GtkAccessibleValue *
+gtk_disabled_accessible_value_new (gboolean state)
+{
+  if (state)
+    return gtk_accessible_value_ref ((GtkAccessibleValue *) &disabled_values[1]);
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &disabled_values[0]);
+}
+
+gboolean
+gtk_disabled_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkBooleanAccessibleValue *self = (GtkBooleanAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (value->value_class == &GTK_DISABLED_ACCESSIBLE_VALUE, FALSE);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_HIDDEN_ACCESSIBLE_VALUE = {
+  .type_name = "GtkHiddenAccessibleValue",
+  .instance_size = sizeof (GtkBooleanAccessibleValue),
+  .equal = gtk_boolean_accessible_value_equal,
+  .print = gtk_boolean_accessible_value_print,
+};
+
+static GtkBooleanAccessibleValue hidden_values[] = {
+ { { &GTK_HIDDEN_ACCESSIBLE_VALUE, 1 }, FALSE },
+ { { &GTK_HIDDEN_ACCESSIBLE_VALUE, 1 }, TRUE },
+};
+
+GtkAccessibleValue *
+gtk_hidden_accessible_value_new (gboolean state)
+{
+  if (state)
+    return gtk_accessible_value_ref ((GtkAccessibleValue *) &hidden_values[1]);
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &hidden_values[0]);
+}
+
+gboolean
+gtk_hidden_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkBooleanAccessibleValue *self = (GtkBooleanAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (value->value_class == &GTK_HIDDEN_ACCESSIBLE_VALUE, FALSE);
+
+  return self->value;
+}
+
+/* }}} */
+
+/* {{{ Tri-state values */
+
+typedef struct {
+  GtkAccessibleValue parent;
+
+  int value;
+} GtkTristateAccessibleValue;
+
+static gboolean
+gtk_tristate_accessible_value_equal (const GtkAccessibleValue *value_a,
+                                     const GtkAccessibleValue *value_b)
+{
+  const GtkTristateAccessibleValue *tri_a = (GtkTristateAccessibleValue *) value_a;
+  const GtkTristateAccessibleValue *tri_b = (GtkTristateAccessibleValue *) value_b;
+
+  return tri_a->value == tri_b->value;
+}
+
+static void
+gtk_tristate_accessible_value_print (const GtkAccessibleValue *value,
+                                     GString                  *buffer)
+{
+  const GtkTristateAccessibleValue *self = (GtkTristateAccessibleValue *) value;
+
+  switch (self->value)
+    {
+    case 0:
+      g_string_append (buffer, "false");
+      break;
+
+    case 1:
+      g_string_append (buffer, "true");
+      break;
+
+    case -1:
+      g_string_append (buffer, "undefined");
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static const GtkAccessibleValueClass GTK_EXPANDED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkExpandedAccessibleValue",
+  .instance_size = sizeof (GtkTristateAccessibleValue),
+  .equal = gtk_tristate_accessible_value_equal,
+  .print = gtk_tristate_accessible_value_print,
+};
+
+static GtkTristateAccessibleValue expanded_values[] = {
+  { { &GTK_EXPANDED_ACCESSIBLE_VALUE, 1 }, -1 },
+  { { &GTK_EXPANDED_ACCESSIBLE_VALUE, 1 },  0 },
+  { { &GTK_EXPANDED_ACCESSIBLE_VALUE, 1 },  1 },
+};
+
+GtkAccessibleValue *
+gtk_expanded_accessible_value_new (int state)
+{
+  int index_;
+
+  if (state < 0)
+    index_ = 0;
+  else if (state == 0)
+    index_ = 1;
+  else
+    index_ = 2;
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &expanded_values[index_]);
+}
+
+int
+gtk_expanded_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkTristateAccessibleValue *self = (GtkTristateAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_STATE_UNDEFINED);
+  g_return_val_if_fail (value->value_class == &GTK_EXPANDED_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_STATE_UNDEFINED);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_GRABBED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkGrabbedAccessibleValue",
+  .instance_size = sizeof (GtkTristateAccessibleValue),
+  .equal = gtk_tristate_accessible_value_equal,
+  .print = gtk_tristate_accessible_value_print,
+};
+
+static GtkTristateAccessibleValue grabbed_values[] = {
+  { { &GTK_GRABBED_ACCESSIBLE_VALUE, 1 }, -1 },
+  { { &GTK_GRABBED_ACCESSIBLE_VALUE, 1 },  0 },
+  { { &GTK_GRABBED_ACCESSIBLE_VALUE, 1 },  1 },
+};
+
+GtkAccessibleValue *
+gtk_grabbed_accessible_value_new (int state)
+{
+  int index_;
+
+  if (state < 0)
+    index_ = 0;
+  else if (state == 0)
+    index_ = 1;
+  else
+    index_ = 2;
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &grabbed_values[index_]);
+}
+
+int
+gtk_grabbed_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkTristateAccessibleValue *self = (GtkTristateAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_STATE_UNDEFINED);
+  g_return_val_if_fail (value->value_class == &GTK_GRABBED_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_STATE_UNDEFINED);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_SELECTED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkSelectedAccessibleValue",
+  .instance_size = sizeof (GtkTristateAccessibleValue),
+  .equal = gtk_tristate_accessible_value_equal,
+  .print = gtk_tristate_accessible_value_print,
+};
+
+static GtkTristateAccessibleValue selected_values[] = {
+  { { &GTK_SELECTED_ACCESSIBLE_VALUE, 1 }, -1 },
+  { { &GTK_SELECTED_ACCESSIBLE_VALUE, 1 },  0 },
+  { { &GTK_SELECTED_ACCESSIBLE_VALUE, 1 },  1 },
+};
+
+GtkAccessibleValue *
+gtk_selected_accessible_value_new (int state)
+{
+  int index_;
+
+  if (state < 0)
+    index_ = 0;
+  else if (state == 0)
+    index_ = 1;
+  else
+    index_ = 2;
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &selected_values[index_]);
+}
+
+int
+gtk_selected_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkTristateAccessibleValue *self = (GtkTristateAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_STATE_UNDEFINED);
+  g_return_val_if_fail (value->value_class == &GTK_SELECTED_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_STATE_UNDEFINED);
+
+  return self->value;
+}
+
+/* }}} */
+
+/* {{{ Enumeration values */
+
+typedef struct {
+  GtkAccessibleValue parent;
+
+  int value;
+  const char *token;
+} GtkEnumAccessibleValue;
+
+static gboolean
+gtk_enum_accessible_value_equal (const GtkAccessibleValue *value_a,
+                                 const GtkAccessibleValue *value_b)
+{
+  const GtkEnumAccessibleValue *enum_a = (GtkEnumAccessibleValue *) value_a;
+  const GtkEnumAccessibleValue *enum_b = (GtkEnumAccessibleValue *) value_b;
+
+  return enum_a->value == enum_b->value;
+}
+
+static void
+gtk_enum_accessible_value_print (const GtkAccessibleValue *value,
+                                 GString                  *buffer)
+{
+  const GtkEnumAccessibleValue *self = (GtkEnumAccessibleValue *) value;
+
+  g_string_append (buffer, self->token);
+}
+
+static const GtkAccessibleValueClass GTK_CHECKED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkCheckedAccessibleValue",
+  .instance_size = sizeof (GtkEnumAccessibleValue),
+  .equal = gtk_enum_accessible_value_equal,
+  .print = gtk_enum_accessible_value_print,
+};
+
+static GtkEnumAccessibleValue checked_values[] = {
+  { { &GTK_CHECKED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_CHECKED_FALSE,     "false" },
+  { { &GTK_CHECKED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_CHECKED_TRUE,      "true" },
+  { { &GTK_CHECKED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_PRESSED_UNDEFINED, "undefined" },
+  { { &GTK_CHECKED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_CHECKED_MIXED,     "mixed" },
+};
+
+GtkAccessibleValue *
+gtk_checked_accessible_value_new (GtkAccessibleCheckedState state)
+{
+  int index_;
+
+  switch (state)
+    {
+    case GTK_ACCESSIBLE_CHECKED_FALSE:
+      index_ = 0;
+      break;
+
+    case GTK_ACCESSIBLE_CHECKED_TRUE:
+      index_ = 1;
+      break;
+
+    case GTK_ACCESSIBLE_CHECKED_UNDEFINED:
+      index_ = 2;
+      break;
+
+    case GTK_ACCESSIBLE_CHECKED_MIXED:
+      index_ = 3;
+      break;
+
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &checked_values[index_]);
+}
+
+GtkAccessibleCheckedState
+gtk_checked_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkEnumAccessibleValue *self = (GtkEnumAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_CHECKED_UNDEFINED);
+  g_return_val_if_fail (value->value_class == &GTK_CHECKED_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_CHECKED_UNDEFINED);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_PRESSED_ACCESSIBLE_VALUE = {
+  .type_name = "GtkPressedAccessibleValue",
+  .instance_size = sizeof (GtkEnumAccessibleValue),
+  .equal = gtk_enum_accessible_value_equal,
+  .print = gtk_enum_accessible_value_print,
+};
+
+static GtkEnumAccessibleValue pressed_values[] = {
+  { { &GTK_PRESSED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_PRESSED_FALSE,     "false" },
+  { { &GTK_PRESSED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_PRESSED_TRUE,      "true" },
+  { { &GTK_PRESSED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_PRESSED_UNDEFINED, "undefined" },
+  { { &GTK_PRESSED_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_PRESSED_MIXED,     "mixed" },
+};
+
+GtkAccessibleValue *
+gtk_pressed_accessible_value_new (GtkAccessiblePressedState state)
+{
+  int index_;
+
+  switch (state)
+    {
+    case GTK_ACCESSIBLE_PRESSED_FALSE:
+      index_ = 0;
+      break;
+
+    case GTK_ACCESSIBLE_PRESSED_TRUE:
+      index_ = 1;
+      break;
+
+    case GTK_ACCESSIBLE_PRESSED_UNDEFINED:
+      index_ = 2;
+      break;
+
+    case GTK_ACCESSIBLE_PRESSED_MIXED:
+      index_ = 3;
+      break;
+
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &pressed_values[index_]);
+}
+
+GtkAccessiblePressedState
+gtk_pressed_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkEnumAccessibleValue *self = (GtkEnumAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_PRESSED_UNDEFINED);
+  g_return_val_if_fail (value->value_class == &GTK_PRESSED_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_PRESSED_UNDEFINED);
+
+  return self->value;
+}
+
+static const GtkAccessibleValueClass GTK_INVALID_ACCESSIBLE_VALUE = {
+  .type_name = "GtkInvalidAccessibleValue",
+  .instance_size = sizeof (GtkEnumAccessibleValue),
+  .equal = gtk_enum_accessible_value_equal,
+  .print = gtk_enum_accessible_value_print,
+};
+
+static GtkEnumAccessibleValue invalid_values[] = {
+  { { &GTK_INVALID_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_INVALID_FALSE,    "false" },
+  { { &GTK_INVALID_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_INVALID_TRUE,     "true" },
+  { { &GTK_INVALID_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_INVALID_GRAMMAR,  "grammar" },
+  { { &GTK_INVALID_ACCESSIBLE_VALUE, 1 }, GTK_ACCESSIBLE_INVALID_SPELLING, "spelling" },
+};
+
+GtkAccessibleValue *
+gtk_invalid_accessible_value_new (GtkAccessibleInvalidState state)
+{
+  g_return_val_if_fail (state >= GTK_ACCESSIBLE_INVALID_FALSE &&
+                        state <= GTK_ACCESSIBLE_INVALID_SPELLING,
+                        NULL);
+
+  return gtk_accessible_value_ref ((GtkAccessibleValue *) &invalid_values[state]);
+}
+
+GtkAccessibleInvalidState
+gtk_invalid_accessible_value_get (const GtkAccessibleValue *value)
+{
+  GtkEnumAccessibleValue *self = (GtkEnumAccessibleValue *) value;
+
+  g_return_val_if_fail (value != NULL, GTK_ACCESSIBLE_INVALID_FALSE);
+  g_return_val_if_fail (value->value_class == &GTK_INVALID_ACCESSIBLE_VALUE,
+                        GTK_ACCESSIBLE_INVALID_FALSE);
+
+  return self->value;
+}
+
+/* }}} */
diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c
new file mode 100644
index 0000000000..599587b37b
--- /dev/null
+++ b/gtk/gtkatcontext.c
@@ -0,0 +1,263 @@
+/* gtkatcontext.c: Assistive technology context
+ *
+ * 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/>.
+ */
+
+/**
+ * SECTION:gtkatcontext
+ * @Title: GtkATContext
+ * @Short_description: An object communicating to Assistive Technologies
+ *
+ * GtkATContext is an abstract class provided by GTK to communicate to
+ * platform-specific assistive technologies API.
+ *
+ * Each platform supported by GTK implements a #GtkATContext subclass, and
+ * is responsible for updating the accessible state in response to state
+ * changes in #GtkAccessible.
+ */
+
+#include "config.h"
+
+#include "gtkatcontextprivate.h"
+
+#include "gtkaccessiblevalueprivate.h"
+#include "gtktestatcontextprivate.h"
+#include "gtktypebuiltins.h"
+
+G_DEFINE_ABSTRACT_TYPE (GtkATContext, gtk_at_context, G_TYPE_OBJECT)
+
+enum
+{
+  PROP_ACCESSIBLE_ROLE = 1,
+  PROP_ACCESSIBLE,
+
+  N_PROPS
+};
+
+static GParamSpec *obj_props[N_PROPS];
+
+static void
+gtk_at_context_finalize (GObject *gobject)
+{
+  GtkATContext *self = GTK_AT_CONTEXT (gobject);
+
+  gtk_accessible_state_set_unref (self->states);
+
+  G_OBJECT_CLASS (gtk_at_context_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_at_context_set_property (GObject      *gobject,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GtkATContext *self = GTK_AT_CONTEXT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ACCESSIBLE_ROLE:
+      self->accessible_role = g_value_get_enum (value);
+      break;
+
+    case PROP_ACCESSIBLE:
+      self->accessible = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_at_context_get_property (GObject    *gobject,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GtkATContext *self = GTK_AT_CONTEXT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ACCESSIBLE_ROLE:
+      g_value_set_enum (value, self->accessible_role);
+      break;
+
+    case PROP_ACCESSIBLE:
+      g_value_set_object (value, self->accessible);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gtk_at_context_real_update_state (GtkATContext *self,
+                                  GtkAccessibleStateSet *states)
+{
+}
+
+static void
+gtk_at_context_class_init (GtkATContextClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gtk_at_context_set_property;
+  gobject_class->get_property = gtk_at_context_get_property;
+  gobject_class->finalize = gtk_at_context_finalize;
+
+  klass->update_state = gtk_at_context_real_update_state;
+
+  obj_props[PROP_ACCESSIBLE_ROLE] =
+    g_param_spec_enum ("accessible-role",
+                       "Accessible Role",
+                       "The accessible role of the AT context",
+                       GTK_TYPE_ACCESSIBLE_ROLE,
+                       GTK_ACCESSIBLE_ROLE_WIDGET,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS);
+
+  obj_props[PROP_ACCESSIBLE] =
+    g_param_spec_object ("accessible",
+                         "Accessible",
+                         "The accessible implementation",
+                         GTK_TYPE_ACCESSIBLE,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
+}
+
+static void
+gtk_at_context_init (GtkATContext *self)
+{
+  self->accessible_role = GTK_ACCESSIBLE_ROLE_WIDGET;
+
+  self->states = gtk_accessible_state_set_new ();
+}
+
+/**
+ * gtk_at_context_get_accessible:
+ * @self: a #GtkATContext
+ *
+ * Retrieves the #GtkAccessible using this context.
+ *
+ * Returns: (transfer none): a #GtkAccessible
+ */
+GtkAccessible *
+gtk_at_context_get_accessible (GtkATContext *self)
+{
+  g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
+
+  return self->accessible;
+}
+
+/**
+ * gtk_at_context_get_accessible_role:
+ * @self: a #GtkATContext
+ *
+ * Retrieves the accessible role of this context.
+ *
+ * Returns: a #GtkAccessibleRole
+ */
+GtkAccessibleRole
+gtk_at_context_get_accessible_role (GtkATContext *self)
+{
+  g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), GTK_ACCESSIBLE_ROLE_WIDGET);
+
+  return self->accessible_role;
+}
+
+/*< private >
+ * gtk_at_context_create:
+ * @accessible_role: the accessible role used by the #GtkATContext
+ * @accessible: the #GtkAccessible implementation using the #GtkATContext
+ *
+ * Creates a new #GtkATContext instance for the given accessible role and
+ * accessible instance.
+ *
+ * The #GtkATContext implementation being instantiated will depend on the
+ * platform.
+ *
+ * Returns: (nullable): the #GtkATContext
+ */
+GtkATContext *
+gtk_at_context_create (GtkAccessibleRole  accessible_role,
+                       GtkAccessible     *accessible)
+{
+  static const char *gtk_test_accessible;
+  static const char *gtk_no_a11y;
+
+  if (G_UNLIKELY (gtk_test_accessible == NULL))
+    {
+      const char *env = g_getenv ("GTK_TEST_ACCESSIBLE");
+
+      if (env != NULL && *env !='\0')
+        gtk_test_accessible = "1";
+      else
+        gtk_test_accessible = "0";
+    }
+
+  if (G_UNLIKELY (gtk_no_a11y == NULL))
+    {
+      const char *env = g_getenv ("GTK_NO_A11Y");
+
+      if (env != NULL && *env != '\0')
+        gtk_no_a11y = "1";
+      else
+        gtk_no_a11y = "0";
+    }
+
+  /* Shortcut everything if we're running with the test AT context */
+  if (gtk_test_accessible[0] == '1')
+    return gtk_test_at_context_new (accessible_role, accessible);
+
+  if (gtk_no_a11y[0] == '1')
+    return NULL;
+
+  /* FIXME: Add GIOExtension for AT contexts */
+  return gtk_test_at_context_new (accessible_role, accessible);
+}
+
+/*< private >
+ * gtk_at_context_update_state:
+ * @self: a #GtkATContext
+ *
+ * Notifies the AT connected to this #GtkATContext that the accessible
+ * state has changed.
+ */
+void
+gtk_at_context_update_state (GtkATContext *self)
+{
+  g_return_if_fail (GTK_IS_AT_CONTEXT (self));
+
+  GTK_AT_CONTEXT_GET_CLASS (self)->update_state (self, self->states);
+}
+
+void
+gtk_at_context_set_state (GtkATContext       *self,
+                          GtkAccessibleState  state,
+                          GtkAccessibleValue *value)
+{
+  g_return_if_fail (GTK_IS_AT_CONTEXT (self));
+
+  gtk_accessible_state_set_add (self->states, state, value);
+}
diff --git a/gtk/gtkatcontext.h b/gtk/gtkatcontext.h
new file mode 100644
index 0000000000..e2fca0f564
--- /dev/null
+++ b/gtk/gtkatcontext.h
@@ -0,0 +1,39 @@
+/* gtkatcontext.h: Assistive technology context
+ *
+ * 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 <gtk/gtktypes.h>
+#include <gtk/gtkenums.h>
+#include <gtk/gtkaccessible.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_AT_CONTEXT (gtk_at_context_get_type())
+
+GDK_AVAILABLE_IN_ALL
+GDK_DECLARE_INTERNAL_TYPE (GtkATContext, gtk_at_context, GTK, AT_CONTEXT, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkAccessible *         gtk_at_context_get_accessible           (GtkATContext *self);
+GDK_AVAILABLE_IN_ALL
+GtkAccessibleRole       gtk_at_context_get_accessible_role      (GtkATContext *self);
+
+G_END_DECLS
diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h
new file mode 100644
index 0000000000..e75e600ece
--- /dev/null
+++ b/gtk/gtkatcontextprivate.h
@@ -0,0 +1,56 @@
+/* gtkatcontextprivate.h: Private header for GtkATContext
+ *
+ * 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 "gtkatcontext.h"
+
+#include "gtkaccessiblestatesetprivate.h"
+
+G_BEGIN_DECLS
+
+struct _GtkATContext
+{
+  GObject parent_instance;
+
+  GtkAccessibleRole accessible_role;
+  GtkAccessible *accessible;
+
+  GtkAccessibleStateSet *states;
+};
+
+struct _GtkATContextClass
+{
+  GObjectClass parent_class;
+
+  void (* update_state) (GtkATContext          *self,
+                         GtkAccessibleStateSet *states);
+};
+
+GtkATContext *  gtk_at_context_create           (GtkAccessibleRole   accessible_role,
+                                                 GtkAccessible      *accessible);
+
+void            gtk_at_context_update_state     (GtkATContext       *self);
+
+void            gtk_at_context_set_state        (GtkATContext       *self,
+                                                 GtkAccessibleState  state,
+                                                 GtkAccessibleValue *value);
+
+G_END_DECLS
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index c406fa0e48..f48620afb7 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -1300,4 +1300,62 @@ typedef enum {
   GTK_ACCESSIBLE_ROLE_WINDOW
 } GtkAccessibleRole;
 
+/**
+ * GtkAccessibleState:
+ * @GTK_ACCESSIBLE_STATE_NONE: An invalid state, used as a sentinel value
+ * @GTK_ACCESSIBLE_STATE_BUSY: A “busy” state
+ * @GTK_ACCESSIBLE_STATE_CHECKED: A “checked” state; corresponds to the
+ *   #GtkToggleButton:active property on #GtkToggleButton
+ * @GTK_ACCESSIBLE_STATE_DISABLED: A “disabled” state; corresponds to the
+ *   #GtkWidget:sensitive property on #GtkWidget
+ * @GTK_ACCESSIBLE_STATE_EXPANDED: An “expanded” state; corresponds to the
+ *   #GtkExpander:expanded property on #GtkExpander
+ * @GTK_ACCESSIBLE_STATE_GRABBED: A “grabbed” state; set when a widget is
+ *   being grabbed in a drag and drop operation
+ * @GTK_ACCESSIBLE_STATE_HIDDEN: A “hidden” state; corresponds to the
+ *   #GtkWidget:visible property on #GtkWidget
+ * @GTK_ACCESSIBLE_STATE_INVALID: An “invalid” state; set when a widget
+ *   is showing an error
+ * @GTK_ACCESSIBLE_STATE_PRESSED: A “pressed” state; set when a widget
+ *   is being activated by a pointer
+ * @GTK_ACCESSIBLE_STATE_SELECTED: A “selected” state; set when a widget
+ *   is selected
+ *
+ * The possible accessible state of a #GtkAccessible.
+ */
+typedef enum {
+  GTK_ACCESSIBLE_STATE_NONE = -1,
+  GTK_ACCESSIBLE_STATE_BUSY = 0,
+  GTK_ACCESSIBLE_STATE_CHECKED,
+  GTK_ACCESSIBLE_STATE_DISABLED,
+  GTK_ACCESSIBLE_STATE_EXPANDED,
+  GTK_ACCESSIBLE_STATE_GRABBED,
+  GTK_ACCESSIBLE_STATE_HIDDEN,
+  GTK_ACCESSIBLE_STATE_INVALID,
+  GTK_ACCESSIBLE_STATE_PRESSED,
+  GTK_ACCESSIBLE_STATE_SELECTED
+} GtkAccessibleState;
+
+typedef enum {
+  GTK_ACCESSIBLE_PROPERTY_ACTIVE_DESCENDANT,
+  GTK_ACCESSIBLE_PROPERTY_DESCRIBED_BY,
+  GTK_ACCESSIBLE_PROPERTY_HAS_POPUP,
+  GTK_ACCESSIBLE_PROPERTY_LABEL,
+  GTK_ACCESSIBLE_PROPERTY_LABELLED_BY,
+  GTK_ACCESSIBLE_PROPERTY_LEVEL,
+  GTK_ACCESSIBLE_PROPERTY_MULTI_LINE,
+  GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE,
+  GTK_ACCESSIBLE_PROPERTY_ORIENTATION,
+  GTK_ACCESSIBLE_PROPERTY_POS_IN_SET,
+  GTK_ACCESSIBLE_PROPERTY_READ_ONLY,
+  GTK_ACCESSIBLE_PROPERTY_RELEVANT,
+  GTK_ACCESSIBLE_PROPERTY_REQUIRED,
+  GTK_ACCESSIBLE_PROPERTY_SET_SIZE,
+  GTK_ACCESSIBLE_PROPERTY_SORT,
+  GTK_ACCESSIBLE_PROPERTY_VALUE_MAX,
+  GTK_ACCESSIBLE_PROPERTY_VALUE_MIN,
+  GTK_ACCESSIBLE_PROPERTY_VALUE_NOW,
+  GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT
+} GtkAccessibleProperty;
+
 #endif /* __GTK_ENUMS_H__ */
diff --git a/gtk/gtktestatcontext.c b/gtk/gtktestatcontext.c
new file mode 100644
index 0000000000..c569b30ebc
--- /dev/null
+++ b/gtk/gtktestatcontext.c
@@ -0,0 +1,72 @@
+/* gtktestatcontext.c: Test AT context
+ *
+ * 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 "gtktestatcontextprivate.h"
+
+#include "gtkatcontextprivate.h"
+#include "gtkenums.h"
+
+struct _GtkTestATContext
+{
+  GtkATContext parent_instance;
+};
+
+G_DEFINE_TYPE (GtkTestATContext, gtk_test_at_context, GTK_TYPE_AT_CONTEXT)
+
+static void
+gtk_test_at_context_update_state (GtkATContext          *self,
+                                  GtkAccessibleStateSet *states)
+{
+  GtkAccessible *accessible = gtk_at_context_get_accessible (self);
+  GtkAccessibleRole role = gtk_at_context_get_accessible_role (self);
+  char *state = gtk_accessible_state_set_to_string (states);
+
+  g_print ("Accessible state changed for accessible “%s”, with role %d: %s\n",
+           G_OBJECT_TYPE_NAME (accessible),
+           role,
+           state);
+
+  g_free (state);
+}
+
+static void
+gtk_test_at_context_class_init (GtkTestATContextClass *klass)
+{
+  GtkATContextClass *context_class = GTK_AT_CONTEXT_CLASS (klass);
+
+  context_class->update_state = gtk_test_at_context_update_state;
+}
+
+static void
+gtk_test_at_context_init (GtkTestATContext *self)
+{
+}
+
+GtkATContext *
+gtk_test_at_context_new (GtkAccessibleRole  accessible_role,
+                         GtkAccessible     *accessible)
+{
+  return g_object_new (GTK_TYPE_TEST_AT_CONTEXT,
+                       "accessible-role", accessible_role,
+                       "accessible", accessible,
+                       NULL);
+}
diff --git a/gtk/gtktestatcontextprivate.h b/gtk/gtktestatcontextprivate.h
new file mode 100644
index 0000000000..2b5cc108e3
--- /dev/null
+++ b/gtk/gtktestatcontextprivate.h
@@ -0,0 +1,47 @@
+/* gtktestatcontextprivate.h: Private test AT context
+ *
+ * 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 "gtkatcontextprivate.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TEST_AT_CONTEXT (gtk_test_at_context_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkTestATContext, gtk_test_at_context, GTK, TEST_AT_CONTEXT, GtkATContext)
+
+GtkATContext *
+gtk_test_at_context_new (GtkAccessibleRole  accessible_role,
+                         GtkAccessible     *accessible);
+
+void
+gtk_test_at_context_assert_role (GtkTestATContext  *self,
+                                 GtkAccessibleRole  role);
+
+void
+gtk_test_at_context_assert_state_added (GtkTestATContext   *self,
+                                        GtkAccessibleState  state);
+
+void
+gtk_test_at_context_assert_state_removed (GtkTestATContext   *self,
+                                          GtkAccessibleState  state);
+
+G_END_DECLS
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 00c7b8144b..d32e3d3174 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -27,7 +27,7 @@
 #include "gtkwidgetprivate.h"
 
 #include "gtkaccelgroupprivate.h"
-#include "gtkaccessible.h"
+#include "gtkaccessibleprivate.h"
 #include "gtkapplicationprivate.h"
 #include "gtkbuildable.h"
 #include "gtkbuilderprivate.h"
@@ -755,6 +755,8 @@ gtk_widget_base_class_init (gpointer g_class)
           g_object_unref (shortcut);
         }
     }
+
+  priv->accessible_role = GTK_ACCESSIBLE_ROLE_WIDGET;
 }
 
 static void
@@ -7008,6 +7010,8 @@ gtk_widget_dispose (GObject *object)
     gtk_layout_manager_set_widget (priv->layout_manager, NULL);
   g_clear_object (&priv->layout_manager);
 
+  g_clear_object (&priv->at_context);
+
   priv->visible = FALSE;
   if (_gtk_widget_get_realized (widget))
     gtk_widget_unrealize (widget);
@@ -8061,9 +8065,29 @@ gtk_widget_set_vexpand_set (GtkWidget      *widget,
 /*
  * GtkAccessible implementation
  */
+
+static GtkATContext *
+gtk_widget_accessible_get_at_context (GtkAccessible *accessible)
+{
+  GtkWidget *self = GTK_WIDGET (accessible);
+  GtkWidgetPrivate *priv = gtk_widget_get_instance_private (self);
+
+  if (priv->at_context == NULL)
+    {
+      GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (self);
+      GtkWidgetClassPrivate *class_priv = widget_class->priv;
+
+      priv->at_context =
+        gtk_at_context_create (class_priv->accessible_role, accessible);
+    }
+
+  return priv->at_context;
+}
+
 static void
 gtk_widget_accessible_interface_init (GtkAccessibleInterface *iface)
 {
+  iface->get_at_context = gtk_widget_accessible_get_at_context;
 }
 
 /*
@@ -12124,3 +12148,25 @@ gtk_widget_update_orientation (GtkWidget      *widget,
       gtk_widget_remove_css_class (widget, GTK_STYLE_CLASS_HORIZONTAL);
     }
 }
+
+/**
+ * gtk_widget_class_set_accessible_role:
+ * @widget_class: a #GtkWidgetClass
+ * @accessible_role: the #GtkAccessibleRole used by the @widget_class
+ *
+ * Sets the accessible role used by the given #GtkWidget class.
+ *
+ * Different accessible roles have different states, and are rendered
+ * differently by assistive technologies.
+ */
+void
+gtk_widget_class_set_accessible_role (GtkWidgetClass    *widget_class,
+                                      GtkAccessibleRole  accessible_role)
+{
+  GtkWidgetClassPrivate *priv;
+
+  g_return_if_fail (GTK_IS_WIDGET_CLASS (widget_class));
+
+  priv = widget_class->priv;
+  priv->accessible_role = accessible_role;
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 384619f2d2..a897237860 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -977,6 +977,10 @@ void                    gtk_widget_action_set_enabled (GtkWidget  *widget,
                                                        gboolean    enabled);
 
 
+GDK_AVAILABLE_IN_ALL
+void                    gtk_widget_class_set_accessible_role    (GtkWidgetClass    *widget_class,
+                                                                 GtkAccessibleRole  accessible_role);
+
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkWidget, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkRequisition, gtk_requisition_free)
 
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 9cbe4fdc2e..1528149e80 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -28,6 +28,7 @@
 #include "gtkwidget.h"
 
 #include "gtkactionmuxerprivate.h"
+#include "gtkatcontextprivate.h"
 #include "gtkcsstypesprivate.h"
 #include "gtkeventcontrollerprivate.h"
 #include "gtklistlistmodelprivate.h"
@@ -193,6 +194,9 @@ struct _GtkWidgetPrivate
   /* Tooltip */
   char *tooltip_markup;
   char *tooltip_text;
+
+  /* Accessible context */
+  GtkATContext *at_context;
 };
 
 typedef struct
@@ -209,6 +213,7 @@ struct _GtkWidgetClassPrivate
   GQuark css_name;
   GType layout_manager_type;
   GtkWidgetAction *actions;
+  GtkAccessibleRole accessible_role;
 };
 
 void          gtk_widget_root               (GtkWidget *widget);
diff --git a/gtk/meson.build b/gtk/meson.build
index fb0c7f9e41..76c22be7b4 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -16,6 +16,9 @@ gtk_private_sources = files([
   'fnmatch.c',
   'tools/gdkpixbufutils.c',
   'gsettings-mapping.c',
+  'gtkaccessiblestateset.c',
+  'gtkaccessiblevalue.c',
+  'gtkaccessiblevaluestatic.c',
   'gtkactionhelper.c',
   'gtkactionmuxer.c',
   'gtkactionobservable.c',
@@ -137,6 +140,7 @@ gtk_private_sources = files([
   'gtkstyleanimation.c',
   'gtkstylecascade.c',
   'gtkstyleproperty.c',
+  'gtktestatcontext.c',
   'gtktextbtree.c',
   'gtktexthistory.c',
   'gtktextviewchild.c',
@@ -161,6 +165,7 @@ gtk_public_sources = files([
   'gtkapplicationwindow.c',
   'gtkaspectframe.c',
   'gtkassistant.c',
+  'gtkatcontext.c',
   'gtkbinlayout.c',
   'gtkbitset.c',
   'gtkbookmarklist.c',
@@ -448,6 +453,7 @@ gtk_public_headers = files([
   'gtkapplicationwindow.h',
   'gtkaspectframe.h',
   'gtkassistant.h',
+  'gtkatcontext.h',
   'gtkbinlayout.h',
   'gtkbitset.h',
   'gtkbookmarklist.h',


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