gtk2/gtk/gtkatcontext.c
Emmanuele Bassi da1ce01a6f Add accessible properties to GtkAccessible
We propagate the accessible state and properties to each ATContext in
the same virtual function, since they are functionally similar.
2020-07-26 20:31:15 +01:00

343 lines
9.7 KiB
C

/* 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);
gtk_accessible_property_set_unref (self->properties);
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 changed_states,
GtkAccessiblePropertyChange changed_properies,
GtkAccessibleStateSet *states,
GtkAccessiblePropertySet *properties)
{
}
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 ();
self->properties = gtk_accessible_property_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:
* @self: a #GtkATContext
*
* Notifies the AT connected to this #GtkATContext that the accessible
* state and its properties have changed.
*/
void
gtk_at_context_update (GtkATContext *self)
{
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
GtkAccessibleStateChange changed_states = 0;
GtkAccessiblePropertyChange changed_properties = 0;
for (int i = 0; i < GTK_ACCESSIBLE_STATE_SELECTED; i++)
{
if (gtk_accessible_state_set_contains (self->states, i))
changed_states |= (1 << i);
}
for (int i = 0; i < GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT; i++)
{
if (gtk_accessible_property_set_contains (self->properties, i))
changed_properties |= (1 << i);
}
GTK_AT_CONTEXT_GET_CLASS (self)->state_change (self,
changed_states,
changed_properties,
self->states,
self->properties);
}
/*< private >
* gtk_at_context_set_state:
* @self: a #GtkATContext
* @state: a #GtkAccessibleState
* @value: (nullable): #GtkAccessibleValue
*
* Sets the @value for the given @state of a #GtkATContext.
*
* If @value is %NULL, the state is unset.
*
* This function will accumulate state changes until gtk_at_context_update_state()
* is called.
*/
void
gtk_at_context_set_accessible_state (GtkATContext *self,
GtkAccessibleState state,
GtkAccessibleValue *value)
{
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
if (value != NULL)
gtk_accessible_state_set_add (self->states, state, value);
else
gtk_accessible_state_set_remove (self->states, state);
}
/*< private >
* gtk_at_context_set_accessible_property:
* @self: a #GtkATContext
* @property: a #GtkAccessibleProperty
* @value: (nullable): #GtkAccessibleValue
*
* Sets the @value for the given @property of a #GtkATContext.
*
* If @value is %NULL, the property is unset.
*
* This function will accumulate property changes until gtk_at_context_update_state()
* is called.
*/
void
gtk_at_context_set_accessible_property (GtkATContext *self,
GtkAccessibleProperty property,
GtkAccessibleValue *value)
{
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
if (value != NULL)
gtk_accessible_property_set_add (self->properties, property, value);
else
gtk_accessible_property_set_remove (self->properties, property);
}