Commit 1124f250 authored by Emmanuele Bassi's avatar Emmanuele Bassi 👣
Browse files

a11y: Add relations API

Since we split relation attributes from the generic properties, we need
to add API for setting and retrieving their values.
parent d37511f7
......@@ -276,3 +276,90 @@ gtk_accessible_update_property_value (GtkAccessible *self,
gtk_accessible_value_unref (real_value);
gtk_at_context_update (context);
}
/**
* gtk_accessible_update_relation:
* @self: a #GtkAccessible
* @first_relation: the first #GtkAccessibleRelation
* @...: a list of relation and value pairs, terminated by -1
*
* Updates a list of accessible relations.
*
* This function should be called by #GtkWidget types whenever an accessible
* relation change must be communicated to assistive technologies.
*/
void
gtk_accessible_update_relation (GtkAccessible *self,
GtkAccessibleRelation first_relation,
...)
{
GtkAccessibleRelation relation;
GtkATContext *context;
va_list args;
g_return_if_fail (GTK_IS_ACCESSIBLE (self));
context = gtk_accessible_get_at_context (self);
if (context == NULL)
return;
va_start (args, first_relation);
relation = first_relation;
while (relation != -1)
{
GtkAccessibleValue *value = gtk_accessible_value_collect_for_relation (relation, &args);
/* gtk_accessible_value_collect_for_relation() will warn for us */
if (value == NULL)
goto out;
gtk_at_context_set_accessible_relation (context, relation, value);
gtk_accessible_value_unref (value);
relation = va_arg (args, int);
}
gtk_at_context_update (context);
out:
va_end (args);
}
/**
* gtk_accessible_update_relation_value:
* @self: a #GtkAccessible
* @relation: a #GtkAccessibleRelation
* @value: a #GValue with the value for @relation
*
* Updates an accessible relation.
*
* This function should be called by #GtkWidget types whenever an accessible
* relation change must be communicated to assistive technologies.
*
* This function is meant to be used by language bindings.
*/
void
gtk_accessible_update_relation_value (GtkAccessible *self,
GtkAccessibleRelation relation,
const GValue *value)
{
GtkATContext *context;
g_return_if_fail (GTK_IS_ACCESSIBLE (self));
context = gtk_accessible_get_at_context (self);
if (context == NULL)
return;
GtkAccessibleValue *real_value =
gtk_accessible_value_collect_for_relation_value (relation, value);
if (real_value == NULL)
return;
gtk_at_context_set_accessible_relation (context, relation, real_value);
gtk_accessible_value_unref (real_value);
gtk_at_context_update (context);
}
......@@ -43,6 +43,10 @@ void gtk_accessible_update_property (GtkAccessible
GtkAccessibleProperty first_property,
...);
GDK_AVAILABLE_IN_ALL
void gtk_accessible_update_relation (GtkAccessible *self,
GtkAccessibleRelation first_relation,
...);
GDK_AVAILABLE_IN_ALL
void gtk_accessible_update_state_value (GtkAccessible *self,
GtkAccessibleState state,
const GValue *value);
......@@ -50,5 +54,9 @@ GDK_AVAILABLE_IN_ALL
void gtk_accessible_update_property_value (GtkAccessible *self,
GtkAccessibleProperty property,
const GValue *value);
GDK_AVAILABLE_IN_ALL
void gtk_accessible_update_relation_value (GtkAccessible *self,
GtkAccessibleRelation relation,
const GValue *value);
G_END_DECLS
/* gtkaccessiblerelationset.c: Accessible relation set
*
* Copyright 2020 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkaccessiblerelationsetprivate.h"
#include "gtkbitmaskprivate.h"
#include "gtkenums.h"
/* Keep in sync with GtkAccessibleRelation in gtkenums.h */
#define LAST_RELATION GTK_ACCESSIBLE_RELATION_SET_SIZE
struct _GtkAccessibleRelationSet
{
GtkBitmask *relation_set;
GtkAccessibleValue **relation_values;
};
static GtkAccessibleRelationSet *
gtk_accessible_relation_set_init (GtkAccessibleRelationSet *self)
{
self->relation_set = _gtk_bitmask_new ();
self->relation_values = g_new (GtkAccessibleValue *, LAST_RELATION);
/* Initialize all relation values, so we can always get the full set */
for (int i = 0; i < LAST_RELATION; i++)
self->relation_values[i] = gtk_accessible_value_get_default_for_relation (i);
return self;
}
GtkAccessibleRelationSet *
gtk_accessible_relation_set_new (void)
{
GtkAccessibleRelationSet *set = g_rc_box_new0 (GtkAccessibleRelationSet);
return gtk_accessible_relation_set_init (set);
}
GtkAccessibleRelationSet *
gtk_accessible_relation_set_ref (GtkAccessibleRelationSet *self)
{
g_return_val_if_fail (self != NULL, NULL);
return g_rc_box_acquire (self);
}
static void
gtk_accessible_relation_set_free (gpointer data)
{
GtkAccessibleRelationSet *self = data;
for (int i = 0; i < LAST_RELATION; i++)
{
if (self->relation_values[i] != NULL)
gtk_accessible_value_unref (self->relation_values[i]);
}
g_free (self->relation_values);
_gtk_bitmask_free (self->relation_set);
}
void
gtk_accessible_relation_set_unref (GtkAccessibleRelationSet *self)
{
g_rc_box_release_full (self, gtk_accessible_relation_set_free);
}
void
gtk_accessible_relation_set_add (GtkAccessibleRelationSet *self,
GtkAccessibleRelation relation,
GtkAccessibleValue *value)
{
g_return_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE);
if (gtk_accessible_relation_set_contains (self, relation))
gtk_accessible_value_unref (self->relation_values[relation]);
else
self->relation_set = _gtk_bitmask_set (self->relation_set, relation, TRUE);
self->relation_values[relation] = gtk_accessible_value_ref (value);
}
void
gtk_accessible_relation_set_remove (GtkAccessibleRelationSet *self,
GtkAccessibleRelation relation)
{
g_return_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE);
if (gtk_accessible_relation_set_contains (self, relation))
{
g_clear_pointer (&(self->relation_values[relation]), gtk_accessible_value_unref);
self->relation_set = _gtk_bitmask_set (self->relation_set, relation, FALSE);
}
}
gboolean
gtk_accessible_relation_set_contains (GtkAccessibleRelationSet *self,
GtkAccessibleRelation relation)
{
g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE,
FALSE);
return _gtk_bitmask_get (self->relation_set, relation);
}
GtkAccessibleValue *
gtk_accessible_relation_set_get_value (GtkAccessibleRelationSet *self,
GtkAccessibleRelation relation)
{
g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE,
NULL);
return self->relation_values[relation];
}
static const char *relation_names[] = {
[GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = "activedescendant",
[GTK_ACCESSIBLE_RELATION_COL_COUNT] = "colcount",
[GTK_ACCESSIBLE_RELATION_COL_INDEX] = "colindex",
[GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = "colindextext",
[GTK_ACCESSIBLE_RELATION_COL_SPAN] = "colspan",
[GTK_ACCESSIBLE_RELATION_CONTROLS] = "controls",
[GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = "describedby",
[GTK_ACCESSIBLE_RELATION_DETAILS] = "details",
[GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = "errormessage",
[GTK_ACCESSIBLE_RELATION_FLOW_TO] = "flowto",
[GTK_ACCESSIBLE_RELATION_LABELLED_BY] = "labelledby",
[GTK_ACCESSIBLE_RELATION_OWNS] = "owns",
[GTK_ACCESSIBLE_RELATION_POS_IN_SET] = "posinset",
[GTK_ACCESSIBLE_RELATION_ROW_COUNT] = "rowcount",
[GTK_ACCESSIBLE_RELATION_ROW_INDEX] = "rowindex",
[GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = "rowindextext",
[GTK_ACCESSIBLE_RELATION_ROW_SPAN] = "rowspan",
[GTK_ACCESSIBLE_RELATION_SET_SIZE] = "setsize",
};
/*< private >
* gtk_accessible_relation_set_print:
* @self: a #GtkAccessibleRelationSet
* @only_set: %TRUE if only the set relations should be printed
* @buffer: a #GString
*
* Prints the contents of the #GtkAccessibleRelationSet into @buffer.
*/
void
gtk_accessible_relation_set_print (GtkAccessibleRelationSet *self,
gboolean only_set,
GString *buffer)
{
if (only_set && _gtk_bitmask_is_empty (self->relation_set))
{
g_string_append (buffer, "{}");
return;
}
g_string_append (buffer, "{\n");
for (int i = 0; i < G_N_ELEMENTS (relation_names); i++)
{
if (only_set && !_gtk_bitmask_get (self->relation_set, i))
continue;
g_string_append (buffer, " ");
g_string_append (buffer, relation_names[i]);
g_string_append (buffer, ": ");
gtk_accessible_value_print (self->relation_values[i], buffer);
g_string_append (buffer, ",\n");
}
g_string_append (buffer, "}");
}
/*< private >
* gtk_accessible_relation_set_to_string:
* @self: a #GtkAccessibleRelationSet
*
* Prints the contents of a #GtkAccessibleRelationSet into a string.
*
* Returns: (transfer full): a newly allocated string with the contents
* of the #GtkAccessibleRelationSet
*/
char *
gtk_accessible_relation_set_to_string (GtkAccessibleRelationSet *self)
{
GString *buf = g_string_new (NULL);
gtk_accessible_relation_set_print (self, TRUE, buf);
return g_string_free (buf, FALSE);
}
/* gtkaccessiblerelationsetprivate.h: Accessible relations set
*
* Copyright 2020 GNOME Foundation
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "gtkaccessibleprivate.h"
#include "gtkaccessiblevalueprivate.h"
G_BEGIN_DECLS
typedef struct _GtkAccessibleRelationSet GtkAccessibleRelationSet;
GtkAccessibleRelationSet * gtk_accessible_relation_set_new (void);
GtkAccessibleRelationSet * gtk_accessible_relation_set_ref (GtkAccessibleRelationSet *self);
void gtk_accessible_relation_set_unref (GtkAccessibleRelationSet *self);
void gtk_accessible_relation_set_add (GtkAccessibleRelationSet *self,
GtkAccessibleRelation state,
GtkAccessibleValue *value);
void gtk_accessible_relation_set_remove (GtkAccessibleRelationSet *self,
GtkAccessibleRelation state);
gboolean gtk_accessible_relation_set_contains (GtkAccessibleRelationSet *self,
GtkAccessibleRelation state);
GtkAccessibleValue * gtk_accessible_relation_set_get_value (GtkAccessibleRelationSet *self,
GtkAccessibleRelation state);
void gtk_accessible_relation_set_print (GtkAccessibleRelationSet *self,
gboolean only_set,
GString *string);
char * gtk_accessible_relation_set_to_string (GtkAccessibleRelationSet *self);
G_END_DECLS
......@@ -665,6 +665,100 @@ static const GtkAccessibleCollect collect_props[] = {
},
};
/* § 6.6.4 Relationship Attributes */
static const GtkAccessibleCollect collect_rels[] = {
[GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = {
.value = GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "activedescendant"
},
[GTK_ACCESSIBLE_RELATION_COL_COUNT] = {
.value = GTK_ACCESSIBLE_RELATION_COL_COUNT,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "colcount"
},
[GTK_ACCESSIBLE_RELATION_COL_INDEX] = {
.value = GTK_ACCESSIBLE_RELATION_COL_INDEX,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "colindex"
},
[GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = {
.value = GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT,
.ctype = GTK_ACCESSIBLE_COLLECT_STRING,
.name = "colindextext"
},
[GTK_ACCESSIBLE_RELATION_COL_SPAN] = {
.value = GTK_ACCESSIBLE_RELATION_COL_SPAN,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "colspan"
},
[GTK_ACCESSIBLE_RELATION_CONTROLS] = {
.value = GTK_ACCESSIBLE_RELATION_CONTROLS,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "controls"
},
[GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = {
.value = GTK_ACCESSIBLE_RELATION_DESCRIBED_BY,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "describedby"
},
[GTK_ACCESSIBLE_RELATION_DETAILS] = {
.value = GTK_ACCESSIBLE_RELATION_DETAILS,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "details"
},
[GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = {
.value = GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "errormessage"
},
[GTK_ACCESSIBLE_RELATION_FLOW_TO] = {
.value = GTK_ACCESSIBLE_RELATION_FLOW_TO,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "flowto"
},
[GTK_ACCESSIBLE_RELATION_LABELLED_BY] = {
.value = GTK_ACCESSIBLE_RELATION_LABELLED_BY,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "labelledby"
},
[GTK_ACCESSIBLE_RELATION_OWNS] = {
.value = GTK_ACCESSIBLE_RELATION_OWNS,
.ctype = GTK_ACCESSIBLE_COLLECT_REFERENCE,
.name = "owns"
},
[GTK_ACCESSIBLE_RELATION_POS_IN_SET] = {
.value = GTK_ACCESSIBLE_RELATION_POS_IN_SET,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "posinset"
},
[GTK_ACCESSIBLE_RELATION_ROW_COUNT] = {
.value = GTK_ACCESSIBLE_RELATION_ROW_COUNT,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "rowcount"
},
[GTK_ACCESSIBLE_RELATION_ROW_INDEX] = {
.value = GTK_ACCESSIBLE_RELATION_ROW_INDEX,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "rowindex"
},
[GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = {
.value = GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT,
.ctype = GTK_ACCESSIBLE_COLLECT_STRING,
.name = "rowindextext"
},
[GTK_ACCESSIBLE_RELATION_ROW_SPAN] = {
.value = GTK_ACCESSIBLE_RELATION_ROW_SPAN,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "rowspan"
},
[GTK_ACCESSIBLE_RELATION_SET_SIZE] = {
.value = GTK_ACCESSIBLE_RELATION_SET_SIZE,
.ctype = GTK_ACCESSIBLE_COLLECT_INTEGER,
.name = "posinset"
},
};
typedef GtkAccessibleValue * (* GtkAccessibleValueBooleanCtor) (gboolean value);
typedef GtkAccessibleValue * (* GtkAccessibleValueIntCtor) (int value);
typedef GtkAccessibleValue * (* GtkAccessibleValueTristateCtor) (int value);
......@@ -1117,4 +1211,93 @@ gtk_accessible_value_collect_for_property_value (GtkAccessibleProperty property
return gtk_accessible_value_collect_value (cstate, value);
}
/*< private >
* gtk_accessible_value_get_default_for_relation:
* @relation: a #GtkAccessibleRelation
*
* Retrieves the #GtkAccessibleValue that contains the default for the
* given @relation.
*
* Returns: (transfer full): the #GtkAccessibleValue
*/
GtkAccessibleValue *
gtk_accessible_value_get_default_for_relation (GtkAccessibleRelation relation)
{
const GtkAccessibleCollect *cstate = &collect_rels[relation];
switch (cstate->value)
{
/* References */
case GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT:
case GTK_ACCESSIBLE_RELATION_CONTROLS:
case GTK_ACCESSIBLE_RELATION_DESCRIBED_BY:
case GTK_ACCESSIBLE_RELATION_DETAILS:
case GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE:
case GTK_ACCESSIBLE_RELATION_FLOW_TO:
case GTK_ACCESSIBLE_RELATION_LABELLED_BY:
case GTK_ACCESSIBLE_RELATION_OWNS:
return NULL;
/* Integers */
case GTK_ACCESSIBLE_RELATION_COL_COUNT:
case GTK_ACCESSIBLE_RELATION_COL_INDEX:
case GTK_ACCESSIBLE_RELATION_COL_SPAN:
case GTK_ACCESSIBLE_RELATION_POS_IN_SET:
case GTK_ACCESSIBLE_RELATION_ROW_COUNT:
case GTK_ACCESSIBLE_RELATION_ROW_INDEX:
case GTK_ACCESSIBLE_RELATION_ROW_SPAN:
case GTK_ACCESSIBLE_RELATION_SET_SIZE:
return gtk_int_accessible_value_new (0);
/* Strings */
case GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT:
case GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT:
return gtk_string_accessible_value_new ("");
default:
g_critical ("Unknown value for accessible property “%s”", cstate->name);
break;
}
return NULL;
}
/*< private >
* gtk_accessible_value_collect_for_relation:
* @relation: a #GtkAccessibleRelation
* @args: a `va_list` reference
*
* Collects and consumes the next item in the @args variadic arguments list,
* and returns a #GtkAccessibleValue for it.
*
* Returns: (transfer full): a #GtkAccessibleValue
*/
GtkAccessibleValue *
gtk_accessible_value_collect_for_relation (GtkAccessibleRelation relation,
va_list *args)
{
const GtkAccessibleCollect *cstate = &collect_rels[relation];
return gtk_accessible_value_collect_valist (cstate, args);
}
/*< private >
* gtk_accessible_value_collect_for_relation_value:
* @relation: a #GtkAccessibleRelation
* @value: a #GValue
*
* Retrieves the value stored inside @value and returns a #GtkAccessibleValue
* for the given @relation.
*
* Returns: (transfer full): a #GtkAccessibleValue
*/
GtkAccessibleValue *
gtk_accessible_value_collect_for_relation_value (GtkAccessibleRelation relation,
const GValue *value)
{
const GtkAccessibleCollect *cstate = &collect_rels[relation];
return gtk_accessible_value_collect_value (cstate, value);
}
/* }}} */
......@@ -102,6 +102,12 @@ GtkAccessibleValue * gtk_accessible_value_collect_for_property (GtkAcce
GtkAccessibleValue * gtk_accessible_value_collect_for_property_value (GtkAccessibleProperty property,
const GValue *value);
GtkAccessibleValue * gtk_accessible_value_get_default_for_relation (GtkAccessibleRelation relation);
GtkAccessibleValue * gtk_accessible_value_collect_for_relation (GtkAccessibleRelation relation,
va_list *args);
GtkAccessibleValue * gtk_accessible_value_collect_for_relation_value (GtkAccessibleRelation relation,
const GValue *value);
/* Basic values */
GtkAccessibleValue * gtk_undefined_accessible_value_new (void);
int gtk_undefined_accessible_value_get (const GtkAccessibleValue *value);
......
......@@ -56,8 +56,9 @@ gtk_at_context_finalize (GObject *gobject)
{
GtkATContext *self = GTK_AT_CONTEXT (gobject);
gtk_accessible_state_set_unref (self->states);
gtk_accessible_property_set_unref (self->properties);
gtk_accessible_relation_set_unref (self->relations);
gtk_accessible_state_set_unref (self->states);
G_OBJECT_CLASS (gtk_at_context_parent_class)->finalize (gobject);
}
......@@ -111,9 +112,11 @@ gtk_at_context_get_property (GObject *gobject,
static void
gtk_at_context_real_state_change (GtkATContext *self,
GtkAccessibleStateChange changed_states,
GtkAccessiblePropertyChange changed_properies,
GtkAccessiblePropertyChange changed_properties,
GtkAccessibleRelationChange changed_relations,
GtkAccessibleStateSet *states,
GtkAccessiblePropertySet *properties)
GtkAccessiblePropertySet *properties,
GtkAccessibleRelationSet *relations)
{
}
......@@ -168,8 +171,9 @@ gtk_at_context_init (GtkATContext *self)
{
self->accessible_role = GTK_ACCESSIBLE_ROLE_WIDGET;
self->states = gtk_accessible_state_set_new ();
self->properties = gtk_accessible_property_set_new ();
self->relations = gtk_accessible_relation_set_new ();
self->states = gtk_accessible_state_set_new ();
}