forked from AuroraMiddleware/gtk
800eb76a2d
It is a bit ambiguous, but I think link buttons are more links than buttons. See WAI-ARIA for a discussion of the differences. Update tests to match.
340 lines
17 KiB
Markdown
340 lines
17 KiB
Markdown
# GTK Accessibility {#gtk-accessibility}
|
|
|
|
## The standard accessibility interface
|
|
|
|
The #GtkAccessible interface provides the accessibility information about
|
|
an application's user interface elements. Assistive technology (AT)
|
|
applications, like Orca, convey this information to users with disabilities,
|
|
or reduced abilities, to help them use the application.
|
|
|
|
Standard GTK controls implement the #GtkAccessible interface and are thus
|
|
accessible to ATs by default. This means that if you use GTK controls such
|
|
as #GtkButton, #GtkEntry, or #GtkListView, you only need to supply
|
|
application-specific details when the defaults values are incomplete. You
|
|
can do this by setting the appropriate properties in your #GtkBuilder
|
|
template and UI definition files, or by setting the properties defined by
|
|
the #GtkAccessible interface.
|
|
|
|
If you are implementing your own #GtkWidget derived type, you will need to
|
|
set the #GtkAccessible properties yourself, and provide an implementation
|
|
of the #GtkAccessible virtual functions.
|
|
|
|
## Accessible roles and attributes
|
|
|
|
The fundamental concepts of an accessible widget are *roles* and
|
|
*attributes*; each GTK control has a role, while its functionality is
|
|
described by a set of *attributes*.
|
|
|
|
### Roles
|
|
|
|
Roles define the taxonomy and semantics of a UI control to any assistive
|
|
technology application; for instance, a button will have a role of
|
|
`GTK_ACCESSIBLE_ROLE_BUTTON`; an entry will have a role of
|
|
`GTK_ACCESSIBLE_ROLE_TEXTBOX`; a toggle button will have a role of
|
|
`GTK_ACCESSIBLE_ROLE_CHECKBOX`; etc.
|
|
|
|
Each role is part of the widget's instance, and **cannot** be changed over
|
|
time or as the result of a user action. Roles allows assistive technology
|
|
applications to identify a UI control and decide how to present it to a
|
|
user; if a part of the application's UI changes role, the control needs to
|
|
be removed and replaced with another one with the appropriate role.
|
|
|
|
#### List of accessible roles
|
|
|
|
Each role name is part of the #GtkAccessibleRole enumeration.
|
|
|
|
| Role name | Description | Related GTK widget |
|
|
|-----------|-------------|--------------------|
|
|
| `BUTTON` | A control that performs an action when pressed | #GtkButton, #GtkLinkButton, #GtkExpander |
|
|
| `CHECKBOX` | A control that has three possible value: `true`, `false`, or `undefined` | #GtkCheckButton |
|
|
| `COMBOBOX` | A control that can be expanded to show a list of possible values to select | #GtkComboBox |
|
|
| `COLUMN_HEADER` | A header in a columned list | #GtkColumnView |
|
|
| `DIALOG` | A dialog that prompts the user to enter information or require a response | #GtkDialog and subclasses |
|
|
| `GRID` | A grid of items | #GtkFlowBox, #GtkGridView |
|
|
| `GRID_CELL` | An item in a grid | #GtkFlowBoxChild, #GtkGridView, #GtkColumnView |
|
|
| `IMG` | An image | #GtkImage, #GtkPicture |
|
|
| `LABEL` | A visible name or caption for a user interface component | #GtkLabel |
|
|
| `LINK` | A clickable hyperlink | #GtkLinkButton |
|
|
| `LIST` | A list of items | #GtkListBox |
|
|
| `LIST_ITEM` | An item in a list | #GtkListBoxRow |
|
|
| `MENU` | A menu | #GtkPopoverMenu |
|
|
| `MENU_BAR` | A menubar | #GtkPopoverMenuBar |
|
|
| `MENU_ITEM` | A menu item | Items in #GtkPopoverMenu |
|
|
| `MENU_ITEM_CHECKBOX` | Check menu item | Items in #GtkPopoverMenu |
|
|
| `MENU_ITEM_RADIO` | Radio menu item | Items in #GtkPopoverMenu |
|
|
| `METER` | Represents a value within a known range | #GtkLevelBar |
|
|
| `NONE` | Not represented in the accessibility tree | the slider of a #GtkScale |
|
|
| `PROGRESS_BAR` | An element that display progress | #GtkProgressBar |
|
|
| `RADIO` | A checkable input in a group of radio roles | #GtkCheckButton |
|
|
| `ROW` | A row in a columned list | #GtkColumnView |
|
|
| `SCROLLBAR` | A graphical object controlling the scrolling of content | #GtkScrollbar |
|
|
| `SEARCH_BOX` | A text box for entering search criteria | #GtkSearchEntry |
|
|
| `SEPARATOR` | A divider that separates sections of content or groups of items | #GtkSeparator |
|
|
| `SPIN_BUTTON` | A range control that allows seelcting among discrete choices | #GtkSpinButton |
|
|
| `SWITCH` | A control that represents on/off values | #GtkSwitch |
|
|
| `TAB` | A tab in a list of tabs for switching pages | #GtkStackSwitcher, #GtkNotebook |
|
|
| `TAB_LIST` | A list of tabs for switching pages | #GtkStackSwitcher, #GtkNotebook |
|
|
| `TAB_PANEL` | A page in a notebook or stack | #GtkStack |
|
|
| `TEXT_BOX` | A type of input that allows free-form text as its value. | #GtkEntry, #GtkPasswordEntry, #GtkTextView |
|
|
| `TREE_GRID` | A treeview-like columned list | #GtkColumnView |
|
|
| `WINDOW` | An application window | #GtkWindow |
|
|
| `...` | … |
|
|
|
|
See the [WAI-ARIA](https://www.w3.org/WAI/PF/aria/appendices#quickref) list
|
|
of roles for additional information.
|
|
|
|
### Attributes
|
|
|
|
Attributes provide specific information about an accessible UI
|
|
control, and describe it for the assistive technology applications. GTK
|
|
divides the accessible attributes into three categories:
|
|
|
|
- *properties*, described by the values of the #GtkAccessibleProperty
|
|
enumeration
|
|
- *relations*, described by the values of the #GtkAccessibleRelation
|
|
enumeration
|
|
- *states*, described by the values of the #GtkAccessibleState enumeration
|
|
|
|
Each attribute accepts a value of a specific type.
|
|
|
|
Unlike roles, attributes may change over time, or in response to user action;
|
|
for instance:
|
|
|
|
- a toggle button will change its %GTK_ACCESSIBLE_STATE_CHECKED state every
|
|
time it is toggled, either by the user or programmatically
|
|
- setting the mnemonic widget on a #GtkLabel will update the
|
|
%GTK_ACCESSIBLE_RELATION_LABELLED_BY relation on the widget with a
|
|
reference to the label
|
|
- changing the #GtkAdjustment instance on a #GtkScrollbar will change the
|
|
%GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, %GTK_ACCESSIBLE_PROPERTY_VALUE_MIN,
|
|
and %GTK_ACCESSIBLE_PROPERTY_VALUE_NOW properties with the upper, lower,
|
|
and value properties of the #GtkAdjustment
|
|
|
|
See the [WAI-ARIA](https://www.w3.org/WAI/PF/aria/appendices#quickref) list
|
|
of attributes for additional information.
|
|
|
|
#### List of accessible states
|
|
|
|
Each state name is part of the #GtkAccessibleState enumeration.
|
|
|
|
| State name | ARIA attribute | Value type | Notes |
|
|
|------------|----------------|------------|-------|
|
|
| %GTK_ACCESSIBLE_STATE_BUSY | “aria-busy” | boolean |
|
|
| %GTK_ACCESSIBLE_STATE_CHECKED | “aria-checked” | #GtkAccessibleTristate | Indicates the current state of a #GtkCheckButton |
|
|
| %GTK_ACCESSIBLE_STATE_DISABLED | “aria-disabled” | boolean | Corresponds to the #GtkWidget:sensitive property on #GtkWidget |
|
|
| %GTK_ACCESSIBLE_STATE_EXPANDED | “aria-expanded” | boolean or undefined | Corresponds to the #GtkExpander:expanded property on #GtkExpander |
|
|
| %GTK_ACCESSIBLE_STATE_HIDDEN | “aria-hidden” | boolean | Corresponds to the #GtkWidget:visible property on #GtkWidget |
|
|
| %GTK_ACCESSIBLE_STATE_INVALID | “aria-invalid” | #GtkAccessibleInvalidState | Set when a widget is showing an error |
|
|
| %GTK_ACCESSIBLE_STATE_PRESSED | “aria-pressed” | #GtkAccessibleTristate | Indicates the current state of a #GtkToggleButton |
|
|
| %GTK_ACCESSIBLE_STATE_SELECTED | “aria-selected” | boolean or undefined | Set when a widget is selected |
|
|
|
|
#### List of accessible properties
|
|
|
|
Each property name is part of the #GtkAccessibleProperty enumeration.
|
|
|
|
| State name | ARIA attribute | Value type |
|
|
|------------|----------------|------------|
|
|
| %GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE | “aria-autocomplete” | #GtkAccessibleAutocomplete |
|
|
| %GTK_ACCESSIBLE_PROPERTY_DESCRIPTION | “aria-description” | translatable string |
|
|
| %GTK_ACCESSIBLE_PROPERTY_HAS_POPUP | “aria-haspopup” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_KEY_SHORTCUTS | “aria-keyshortcuts” | string |
|
|
| %GTK_ACCESSIBLE_PROPERTY_LABEL | “aria-label” | translatable string |
|
|
| %GTK_ACCESSIBLE_PROPERTY_LEVEL | “aria-level” | integer |
|
|
| %GTK_ACCESSIBLE_PROPERTY_MODAL | “aria-modal” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_MULTI_LINE | “aria-multiline” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE | “aria-multiselectable” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_ORIENTATION | “aria-orientation” | #GtkOrientation |
|
|
| %GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER | “aria-placeholder” | translatable string |
|
|
| %GTK_ACCESSIBLE_PROPERTY_READ_ONLY | “aria-readonly” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_REQUIRED | “aria-required” | boolean |
|
|
| %GTK_ACCESSIBLE_PROPERTY_ROLE_DESCRIPTION | “aria-roledescription” | translatable string |
|
|
| %GTK_ACCESSIBLE_PROPERTY_SORT | “aria-sort” | #GtkAccessibleSort |
|
|
| %GTK_ACCESSIBLE_PROPERTY_VALUE_MAX | “aria-valuemax” | double |
|
|
| %GTK_ACCESSIBLE_PROPERTY_VALUE_MIN | “aria-valuemin” | double |
|
|
| %GTK_ACCESSIBLE_PROPERTY_VALUE_NOW | “aria-valuenow” | double |
|
|
| %GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT | “aria-valuetext” | translatable string |
|
|
|
|
#### List of accessible relations
|
|
|
|
Each relation name is part of the #GtkAccessibleRelation enumeration.
|
|
|
|
| State name | ARIA attribute | Value type |
|
|
|------------|----------------|------------|
|
|
| %GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT | “aria-activedescendant” | #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_COL_COUNT | “aria-colcount” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_COL_INDEX | “aria-colindex” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT | “aria-colindextext” | translatable string |
|
|
| %GTK_ACCESSIBLE_RELATION_COL_SPAN | “aria-colspan” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_CONTROLS | “aria-controls” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_DESCRIBED_BY | “aria-describedby” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_DETAILS | “aria-details” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE | “aria-errormessage” | #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_FLOW_TO | “aria-flowto” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_LABELLED_BY | “aria-labelledby” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_OWNS | “aria-owns” | a #GList of #GtkAccessible |
|
|
| %GTK_ACCESSIBLE_RELATION_POS_IN_SET | “aria-posinset” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_ROW_COUNT | “aria-rowcount” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_ROW_INDEX | “aria-rowindex” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT | “aria-rowindextext” | translatable string |
|
|
| %GTK_ACCESSIBLE_RELATION_ROW_SPAN | “aria-rowspan” | integer |
|
|
| %GTK_ACCESSIBLE_RELATION_SET_SIZE | “aria-setsize” | integer |
|
|
|
|
## Application development rules
|
|
|
|
Even if standard UI controls provided by GTK have accessibility information
|
|
out of the box, there are some additional properties and considerations for
|
|
application developers. For instance, if your application presents the user
|
|
with a form to fill out, you should ensure that:
|
|
|
|
* the container of the form has a `GTK_ACCESSIBLE_ROLE_FORM` role
|
|
* each text entry widget in the form has the `GTK_ACCESSIBLE_RELATION_LABELLED_BY`
|
|
relation pointing to the label widget that describes it
|
|
|
|
Another example: if you create a toolbar containing buttons with only icons,
|
|
you should ensure that:
|
|
|
|
* the container has a `GTK_ACCESSIBLE_ROLE_TOOLBAR` role
|
|
* each button has a `GTK_ACCESSIBLE_PROPERTY_LABEL` property set with the user
|
|
readable and localised action performed when pressed; for instance "Copy",
|
|
"Paste", "Add layer", or "Remove"
|
|
|
|
GTK will try to fill in some information by using ancillary UI control
|
|
properties, for instance the accessible label will be taken from the label or
|
|
placeholder text used by the UI control, or from its tooltip, if the
|
|
`GTK_ACCESSIBLE_PROPERTY_LABEL` property or the `GTK_ACCESSIBLE_RELATION_LABELLED_BY`
|
|
relation are unset. Nevertheless, it is good practice and project hygiene
|
|
to explicitly specify the accessible properties, just like it's good practice
|
|
to specify tooltips and style classes.
|
|
|
|
Application developers using GTK **should** ensure that their UI controls
|
|
are accessible as part of the development process. When using `GtkBuilder`
|
|
templates and UI definition files, GTK provides a validation tool that
|
|
verifies that each UI element has a valid role and properties; this tool can
|
|
be used as part of the application's test suite to avoid regressions.
|
|
|
|
## Implementations
|
|
|
|
Each UI control implements the #GtkAccessible interface to allow widget and
|
|
application developers to specify the roles, state, and relations between UI
|
|
controls. This API is purely descriptive.
|
|
|
|
Each `GtkAccessible` implementation must provide a #GtkATContext instance,
|
|
which acts as a proxy to the specific platform's accessibility API:
|
|
|
|
* AT-SPI on Linux/BSD
|
|
* NSAccessibility on macOS
|
|
* Active Accessibility on Windows
|
|
|
|
Additionally, an ad hoc accessibility backend is available for the GTK
|
|
testsuite, to ensure reproducibility of issues in the CI pipeline.
|
|
|
|
## Authoring practices {#authoring-practices}
|
|
|
|
The authoring practices are aimed at application developers, as well as
|
|
developers of GUI elements based on GTK.
|
|
|
|
Functionally, #GtkAccessible roles, states, properties, and relations are
|
|
analogous to a CSS for assistive technologies. For screen reader users, for
|
|
instance, the various accessible attributes control the rendering of their
|
|
non-visual experience. Incorrect roles and attributes may result in a
|
|
completely inaccessible user interface.
|
|
|
|
### A role is a promise
|
|
|
|
The following code:
|
|
|
|
```c
|
|
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_BUTTON);
|
|
```
|
|
|
|
is a promise that the widget being created will provide the same keyboard
|
|
interactions expected for a button. An accessible role of a button will not
|
|
turn automatically any widget into a #GtkButton; but if your widget behaves
|
|
like a button, using the %GTK_ACCESSIBLE_ROLE_BUTTON will allow any
|
|
assistive technology to handle it like they would a #GtkButton.
|
|
|
|
### Attributes can both hide and enhance
|
|
|
|
Accessible attributes can be used to override the content of a UI element,
|
|
for instance:
|
|
|
|
```c
|
|
gtk_label_set_text (GTK_LABEL (label), "Some text");
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (label),
|
|
GTK_ACCESSIBLE_PROPERTY_LABEL,
|
|
"Assistive technologies users will perceive "
|
|
"this text, not the contents of the label",
|
|
-1);
|
|
```
|
|
|
|
In the example above, the "label" property will override the contents of the
|
|
label widget.
|
|
|
|
The attributes can also enhance the UI:
|
|
|
|
```c
|
|
gtk_button_set_label (GTK_BUTTON (button), "Download");
|
|
gtk_box_append (GTK_BOX (button), button);
|
|
|
|
gtk_label_set_text (GTK_LABEL (label), "Final report.pdf");
|
|
gtk_box_append (GTK_BOX (box), label);
|
|
|
|
gtk_accessible_update_relation (GTK_ACCESSIBLE (button),
|
|
GTK_ACCESSIBLE_RELATION_LABELLED_BY,
|
|
g_list_append (NULL, label),
|
|
-1);
|
|
```
|
|
|
|
In the example above, an assistive technology will read the button's
|
|
accessible label as "Download Final report.pdf".
|
|
|
|
The power of hiding and enhancing can be a double-edged sword, as it can
|
|
lead to inadvertently overriding the accessible semantics of existing
|
|
widgets.
|
|
|
|
## Design patterns and custom widgets
|
|
|
|
When creating custom widgets, following established patterns can help
|
|
ensuring that the widgets work well for users of accessible technologies
|
|
as well.
|
|
|
|
### Custom entries
|
|
|
|
For custom entries, it is highly recommended that you implement the
|
|
#GtkEditable interface by using a #GtkText widget as delegate. If you
|
|
do this, GTK will make your widgets text editing functionality accessible
|
|
in the same way as a #GtkSpinButton or #GtkSearchEntry.
|
|
|
|
### Tab-based UI
|
|
|
|
If you make a tab-based interface, you should consider using #GtkStack
|
|
as the core, and just make a custom tab widget to control the active
|
|
stack page. When doing so, the following extra steps will ensure that
|
|
your tabs are accessible in the same way as #GtkStackSwitcher or #GtkNotebook:
|
|
|
|
- Give your tab container the role %GTK_ACCESSIBLE_ROLE_TAB_LIST
|
|
- Give your tab widgets the role %GTK_ACCESSIBLE_ROLE_TAB
|
|
- Set up the %GTK_ACCESSIBLE_RELATION_CONTROLS relation between each
|
|
tab and the #GtkStackPage object for its page
|
|
- Set the %GTK_ACCESSIBLE_PROPERTY_SELECTED property on each tab, with
|
|
the active tab getting the value %TRUE, all others %FALSE
|
|
|
|
To allow changing the active tab via accessible technologies, you can
|
|
export actions. Since the accessibility interfaces only support actions
|
|
without parameters, you can either provide `previous-tab` and `next-tab`
|
|
actions on the tab container that let users step through the tabs one-by-one,
|
|
or add a `activate-tab` action on each tab.
|
|
|
|
### Value controls
|
|
|
|
A value control (ie a widget that controls a one-dimensional quantity
|
|
that can be represented by a #GtkAdjustment) can be represented to
|
|
accessible technologies by setting the %GTK_ACCESSIBLE_PROPERTY_VALUE_MIN,
|
|
%GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, and %GTK_ACCESSIBLE_PROPERTY_VALUE_NOW
|
|
properties.
|
|
|
|
To allow changing the value via accessible technologies, you can export
|
|
actions. Since the accessibility interfaces only support actions
|
|
without parameters, you should provide actions such as `increase-value`
|
|
and `decrease-value`.
|