[gtk/ebassi/new-a11y: 8/29] Add GtkATContext



commit 309e0f8d2f9bfa5ac8ccec41e39710f2477c90bf
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Jul 8 16:51:57 2020 +0100

    Add GtkATContext
    
    The ATContext type is meant to be used as the base class for
    implementations of the assistive technology API—the actual mechanism
    needed to communicate to components like the screen reader, or any other
    AT.
    
    Every time the widget state changes, the ATContext is meant to broadcast
    the state change; and every time the AT queries the state of a UI
    element, the ATContext is meant to provide that information.
    
    We also have a "test" ATContext implementation, which is meant to be
    used to write tests to verify that changes are propagated without
    requiring a whole desktop session.

 gtk/gtk.h                     |   1 +
 gtk/gtkatcontext.c            | 285 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkatcontext.h            |  39 ++++++
 gtk/gtkatcontextprivate.h     |  69 ++++++++++
 gtk/gtktestatcontext.c        |  73 +++++++++++
 gtk/gtktestatcontextprivate.h |  47 +++++++
 gtk/meson.build               |   3 +
 7 files changed, 517 insertions(+)
---
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/gtkatcontext.c b/gtk/gtkatcontext.c
new file mode 100644
index 0000000000..5627160b40
--- /dev/null
+++ b/gtk/gtkatcontext.c
@@ -0,0 +1,285 @@
+/* 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_state_change (GtkATContext             *self,
+                                  GtkAccessibleStateChange  change,
+                                  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->state_change = gtk_at_context_real_state_change;
+
+  /**
+   * GtkATContext:accessible-role:
+   *
+   * The accessible role used by the AT context.
+   *
+   * Depending on the given role, different states and properties can be
+   * set or retrieved.
+   */
+  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);
+
+  /**
+   * GtkATContext:accessible:
+   *
+   * The #GtkAccessible that created the #GtkATContext instance.
+   */
+  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));
+
+  GtkAccessibleStateChange change = 0;
+
+  for (int i = 0; i < GTK_ACCESSIBLE_STATE_SELECTED; i++)
+    {
+      if (gtk_accessible_state_set_contains (self->states, i))
+        change |= (1 << i);
+    }
+
+  GTK_AT_CONTEXT_GET_CLASS (self)->state_change (self, change, 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..8ee334ca86
--- /dev/null
+++ b/gtk/gtkatcontextprivate.h
@@ -0,0 +1,69 @@
+/* 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
+
+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_GRABBED  = 1 << GTK_ACCESSIBLE_STATE_GRABBED,
+ 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;
+
+  GtkAccessibleRole accessible_role;
+  GtkAccessible *accessible;
+
+  GtkAccessibleStateSet *states;
+};
+
+struct _GtkATContextClass
+{
+  GObjectClass parent_class;
+
+  void (* state_change) (GtkATContext             *self,
+                         GtkAccessibleStateChange  changed,
+                         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/gtktestatcontext.c b/gtk/gtktestatcontext.c
new file mode 100644
index 0000000000..8324d44375
--- /dev/null
+++ b/gtk/gtktestatcontext.c
@@ -0,0 +1,73 @@
+/* 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_state_change (GtkATContext             *self,
+                                  GtkAccessibleStateChange  change,
+                                  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->state_change = gtk_test_at_context_state_change;
+}
+
+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/meson.build b/gtk/meson.build
index 4a6cc3982b..eea44c2753 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -138,6 +138,7 @@ gtk_private_sources = files([
   'gtkstyleanimation.c',
   'gtkstylecascade.c',
   'gtkstyleproperty.c',
+  'gtktestatcontext.c',
   'gtktextbtree.c',
   'gtktexthistory.c',
   'gtktextviewchild.c',
@@ -162,6 +163,7 @@ gtk_public_sources = files([
   'gtkapplicationwindow.c',
   'gtkaspectframe.c',
   'gtkassistant.c',
+  'gtkatcontext.c',
   'gtkbinlayout.c',
   'gtkbitset.c',
   'gtkbookmarklist.c',
@@ -449,6 +451,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]