summaryrefslogtreecommitdiff
path: root/gtk/gtkatcontext.c
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2020-07-08 16:51:57 +0100
committerEmmanuele Bassi <ebassi@gnome.org>2020-07-26 20:31:14 +0100
commita382dfd3bd7d4de1e2285a6d822a3c99506e6f75 (patch)
tree8a4fe767f27c59d6928d60ea59705070a5109d70 /gtk/gtkatcontext.c
parent823ee58332627146b48ea884a9046e0f07f773e9 (diff)
downloadgtk+-a382dfd3bd7d4de1e2285a6d822a3c99506e6f75.tar.gz
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.
Diffstat (limited to 'gtk/gtkatcontext.c')
-rw-r--r--gtk/gtkatcontext.c285
1 files changed, 285 insertions, 0 deletions
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);
+}