CopperSpice API  1.7.2
Accessibility

Classes

class  QAccessible
 Enums and static functions relating to accessibility More...
 
class  QAccessible::State
 Structure which defines bit flags that indicate the state of an accessible object More...
 
class  QAccessibleActionInterface
 Implements support for invocable actions in the interface More...
 
class  QAccessibleBridge
 Base class for accessibility back-ends More...
 
class  QAccessibleBridgePlugin
 Abstract base for accessibility bridge plugins More...
 
class  QAccessibleEvent
 Base class for accessibility notifications More...
 
class  QAccessibleInterface
 Defines an interface that exposes information about accessible objects More...
 
class  QAccessibleObject
 Implements parts of the QAccessibleInterface for QObjects More...
 
class  QAccessiblePlugin
 Abstract base for accessibility plugins More...
 
class  QAccessibleTableCellInterface
 
class  QAccessibleTableInterface
 
class  QAccessibleTextInterface
 Implements support for text handling More...
 
class  QAccessibleValueInterface
 
class  QAccessibleWidget
 Implements the QAccessibleInterface for QWidgets More...
 
class  QPlatformAccessibility
 Base class for integrating accessibility backends More...
 

Detailed Description

Accessibility in computer software is making applications usable for people with different abilities. It is important to take different people's needs into account, for example, in case of low vision, hearing, dexterity, or cognitive problems. Some examples of accessibility measures are keyboard shortcuts, a high-contrast user interface that uses specially selected colors and fonts, or support for assistive tools such as screen readers and braille displays.

An application should consider the following items to provide accessibility.

Usability
Usability and user centric design generally lead to more usable applications, including improvements for people with various abilities.
Fonts
Font settings should follow the system/platform. This allows users to select fonts for readability and increasing the font size.
Colors
Provide enough contrast and consider the most common cases of low vision and color blindness. Make sure that the application is usable, for example, for people with red/green blindness, and do not depend on colors only.
Scalable User Interface
A user interface that works in various sizes and properly supports different fonts and accommodates size changes.
Sounds
Do not exclusively rely on sound notifications, provide a visual alternative when a sound signal is imperative to using the application.
Spelling
Offer spell checking wherever it makes sense, even when only a single word is expected.
Assistive Technology
Support the use of assistive tools (AT). Either use standard widgets/controls which support ATs out of the box, or make sure that your custom widgets and controls support accessibility properly. In order to learn more about this read on below.

This remainder of this documentation assumes the foundation for accessibility have been implemented in your application and focuses more specifically on supporting assistive technology.

There are a number of different Assistive Tools which can be used to provide a solution for users who require special accessibility features. The information needed by the specific tool will differ depending upon what it does. CopperSpice applications should provide a generic API to the Assistive Tool so it can query information about what is on the screen and then provide the appropriate accessibility to the end user.

Applications do not usually communicate directly with the assistive tools, but rather through a platform specific API. Accessibility support to your application may only require a few changes to provide accessibility information.

Generally the communication with the ATs works though an IPC mechanism. Semantic information about user interface elements, such as buttons and scroll bars, is exposed to the assistive technologies. CopperSpice supports Microsoft Active Accessibility (MSAA) and IAccessible2 on Windows, macOS Accessibility on macOS, and AT-SPI via DBus on Unix/X11. The platform specific technologies are abstracted by CopperSpice so applications do not need any platform specific changes to work with the different native APIs.

In this overview document, we will examine the overall accessibility architecture and how to implement accessibility for custom widgets and elements.

Accessibility

When we communicate with the assistive technologies, we need to describe the CopperSpice user interface in a way that they can understand. CopperSpice applications use QAccessibleInterface to expose information about the individual UI elements. Currently, CopperSpice provides support for its widgets and widget parts, e.g., slider handles, but the interface could also be implemented for any QObject if necessary. QAccessible contains enums that describe the UI. The description is mainly based on MSAA and is independent of CopperSpice.

The structure of the UI is represented as a tree of QAccessibleInterface subclasses. You can think of this as a representation of a UI like the QObject tree built by CopperSpice. Objects can be widgets or widget parts (such as scroll bar handles).

Servers notify clients through QAccessible::updateAccessibility() about changes in objects by sending events and the clients register to receive the events. The available events are defined by the QAccessible::Event enum. The clients may then query for the object that generated the event through QAccessible::queryAccessibleInterface().

The members and enums in QAccessible are used to describe accessible objects.

Role
Describes the role the object fills in the user interface. For example if it is a window, a text edit, or a cell in a table.
Relation
Describes the relationship between objects in the object hierarchy.
State
The objects can be in a number of different states. Examples of states are whether the object is disabled, if it has focus, or if it provides a pop-up menu.

A client can also retrieve the content of objects. For example, to retrieve a button's text, use the QAccessible::Text enum.

Accessible Object Tree

As mentioned, a tree structure is built from the accessible objects of an application. By navigating through the tree, the clients can access all elements in the UI. Object relations give clients information about the UI. For instance, a slider handle is a child of the slider to which it belongs. QAccessible::Relation describes the various relationships the clients can ask objects about.

There is no direct mapping between a QObject tree and the accessible object tree. For instance, scroll bar handles are accessible objects but are not widgets or objects in CopperSpice.

AT-Clients have access to the accessibility object tree through the root object in the tree, which is the QApplication. They can navigate the tree with the QAccessibleInterface::parent(), QAccessibleInterface::childCount() and QAccessibleInterface::child() methods.

CopperSpice provides accessible interfaces for its widgets. Interfaces for any QObject subclass can be requested through QAccessible::queryAccessibleInterface(). A default implementation is provided if a more specialized interface is not defined. An AT-Client can not acquire an interface for accessible objects that do not have an equivalent QObject, e.g., scroll bar handles, but they appear as normal objects through interfaces of parent accessible objects, e.g., you can query their relationships with QAccessibleInterface::relations().

To illustrate, the following is an image of an accessible object tree. Beneath the tree is a table with examples of object relationships.

The labels in top-down order are: the QAccessibleInterface class name, the widget for which an interface is provided, and the Role of the object. The PageLeft, Position, and PageRight correspond to the slider groove left, slider handle, and the slider groove right, respectively. These accessible objects do not have an equivalent QObject.

Source Object Target ObjectRelation
SliderIndicatorController
IndicatorSliderControlled
SliderApplicationAncestor
ApplicationSliderChild
PushButtonIndicatorSibling

Static QAccessible Methods

Accessibility is managed by static methods in the QAccessible class, These methods are responsible for building the object tree and initiating the connection with MSAA or the other platform specific technologies. If your interest is in making an application accessible, you can skip over this section to Implementing Accessibility.

Communication between clients and the server is set up when QAccessible::setRootObject() is called. This will automatically happen when a QApplication is instantiated in your program. When a QObject calls QAccessible::updateAccessibility(), clients that are listening to events are notified of the changes and events are posted to the assistive technology.

The method QAccessible::queryAccessibleInterface() returns accessible interfaces for any given QObject. Since all of the widgets in CopperSpice provide interfaces, if you need interfaces to control the behavior of other QObject subclasses you will need to implement these interfaces. The QAccessibleObject convenience class can be used to implement parts of this functionality.

The factory that produces accessibility interfaces for QObjects is a function of type QAccessible::InterfaceFactory. It is possible to have several factories installed. The last factory installed will be the first one invoked. Normally you will not need to implement a factory since implementing a plugin will provide an interface.

Implementing Accessibility

To provide accessibility support for a widget or another user interface element, you need to implement the QAccessibleInterface and distribute it in a QAccessiblePlugin. It is also possible to compile the interface into the application and provide a QAccessible::InterfaceFactory. The factory can be used if you link statically or do not want the added complexity of plugins.

If you want your application to support accessibility you will need to consider the following items.

You should be familiar with MSAA, which the CopperSpice accessibility architecture is based on. You should also study the enum values of QAccessible as these describe the roles, actions, relationships, and events you need to consider.

One major problem with the MSAA standard is that some interfaces are implemented in an inconsistent way. This can make things difficult for clients and often leads to guesswork on object functionality.

It is possible to implement interfaces by inheriting from QAccessibleInterface and overriding its pure virtual methods. In practice it is better to inherit from QAccessibleObject or QAccessibleWidget, which implement part of the functionality for you. In the next section, we will see an example of implementing accessibility for a widget by inheriting the QAccessibleWidget class.

QAccessibleObject and QAccessibleWidget Convenience Classes

When implementing an accessibility interface for widgets you normally inherit from QAccessibleWidget. Another option is to inherit directly from the parent QAccessibleObject class, which implements part of the interface for QObject.

The QAccessibleWidget provides the following functionality.

QAccessibleWidget Example

Instead of creating a custom widget and implementing an interface, we will show how accessibility is implemented for one of the CopperSpice standard widgets, namely QSlider. The accessible interface class QAccessibleSlider, inherits from QAccessibleAbstractSlider, which in turn inherits from QAccessibleWidget.

The following code is the constructor for QAccessibleSlider. The slider is a complex control that functions as a Controller for its accessible children. This relationship must be known by the interface for QAccessibleInterface::parent(), QAccessibleInterface::child() and QAccessibleInterface::relations(). This can be done using a controlling signal, which is a mechanism provided by QAccessibleWidget.

QAccessibleSlider::QAccessibleSlider(QWidget *w)
: QAccessibleAbstractSlider(w)
{
Q_ASSERT(slider());
addControllingSignal("valueChanged(int)");
}

When an accessible object is changed in a way that users need to know about, it notifies clients of the change by sending them an event via the accessible interface. This is how QSlider calls updateAccessibility() to indicate that its value has changed.

The call is made after the value of the slider has changed because clients may query the new value immediately after receiving the event.

...
QAccessibleValueChangeEvent event(this, d->value);
...
}

The interface must be able to calculate bounding rectangles of itself and any children that do not provide an interface of their own. The QAccessibleSlider> has three such children identified by the private enum, SliderElements, which has the following values: PageLeft (the rectangle on the left hand side of the slider handle), PageRight (the rectangle on the right hand side of the handle), and Position, (the slider handle).

The first part of the method which we have omitted, uses the current style to calculate the slider handle's bounding rectangle and is stored in srect. If child is 0 the default case is used and we return the QSlider bounding rectangle obtained from the superclass. This is effectively the value obtained from QAccessibleWidget::rect(). Before the rectangle is returned it must be mapped to screen coordinates.

The following is an implementation of the rect() method.

QRect QAccessibleSlider::rect(int child) const {
...
switch (child) {
case PageLeft:
if (slider()->orientation() == Qt::Vertical) {
rect = QRect(0, 0, slider()->width(), srect.y());
} else }
rect = QRect(0, 0, srect.x(), slider()->height());
}
break;
case Position:
rect = srect;
break;
case PageRight:
if (slider()->orientation() == Qt::Vertical) {
rect = QRect(0, srect.y() + srect.height(), slider()->width(), slider()->height()- srect.y() - srect.height());
} else {
rect = QRect(srect.x() + srect.width(), 0, slider()->width() - srect.x() - srect.width(), slider()->height());
}
break;
default:
}
QPoint tp = slider()->mapToGlobal(QPoint(0,0));
return QRect(tp.x() + rect.x(), tp.y() + rect.y(), rect.width(), rect.height());
}

The QAccessibleSlider must reimplement QAccessibleInterface::childCount() since it manages children without interfaces. The text() method returns the QAccessible::Text strings for the slider.

The call to the slider() method returns a pointer to the interface's QSlider. Some values are left for the superclass implementation. Not all values are appropriate for all accessible objects, as you can see for the QAccessible::Value case. You should just return an empty string for those values where no relevant text can be provided.

QString QAccessibleSlider::text(Text t, int child) const
{
if (! slider()->isVisible()) {
return QString();
}
switch (t) {
case Value:
if (! child || child == 2) {
return QString::number(slider()->value());
}
return QString();
case Name:
switch (child) {
case PageLeft:
return slider()->orientation() == Qt::Horizontal ?
QSlider::tr("Page left") : QSlider::tr("Page up");
case Position:
return QSlider::tr("Position");
case PageRight:
return slider()->orientation() == Qt::Horizontal ?
QSlider::tr("Page right") : QSlider::tr("Page down");
}
break;
default:
break;
}
}

The implementation of the role() method is pretty straightforward. It should reimplement all objects and describe the role of the slider and any children which do not provide accessible interfaces of their own.

{
switch (child) {
case PageLeft:
case PageRight:
return PushButton;
case Position:
return Indicator;
default:
return Slider;
}
}

Next, the accessible interface needs to return the QAccessible::State that the slider can be in. The following is a look at a few parts of the implementation to show they are handled.

{
const State parentState = QAccessibleAbstractSlider::state(0);
...
switch (child) {
case PageLeft:
if (slider->value() <= slider->minimum()) {
state |= Unavailable;
}
break;
case PageRight:
if (slider->value() >= slider->maximum()) {
state |= Unavailable;
}
break;
case Position:
default:
break;
}
return state;
}

The superclass implementation of state() uses the QAccessibleInterface::state() implementation. We simply need to disable the buttons if the slider is at its minimum or maximum.

We have now exposed the information about the slider to the clients. For the clients to be able to alter the slider and change its value, we must provide information about the actions that can be performed and perform them upon request.

Handling Action Requests from Clients

Applications can expose actions which can be invoked by the client. In order to support actions in an object inherit from the QAccessibleActionInterface class. Interactive elements should expose and functionality invoked by user input. For example a push button should expose a click action.

Setting the focus is another action that should be implemented for widgets that accept receive the focus. You will need to reimplement QAccessibleActionInterface::actionNames() to return a list of all actions that the object supports. This list should not be localized.

There are two methods that give information about the actions that must return localized strings: QAccessibleActionInterface::localizedActionName() and QAccessibleActionInterface::localizedActionDescription(). These methods can be used by the client to present the actions to the user. In general, the name should be concise and only consist of a single word, such as "press".

There is a list of standard action names and localizations available that should be used when the action fits. This makes it easier for clients to understand the semantics and CopperSpice will try to expose them correctly on the different platforms. Of course the action also needs a way to be triggered. For example, QAccessibleActionInterface::doAction() should invoke the action as advertised by name and description.

Implementing Accessible Plugins

This section covers the steps required to implement accessible plugins. A plugin is a class stored in a shared library which is loaded at run time. It can be convenient to distribute interfaces as plugins since they will only be loaded when required.

Creating an accessible plugin is achieved by subclassing QAccessiblePlugin, reimplementing the pure virtual method create() and then exporting the class with the CS_PLUGIN_REGISTER() macro.

In the header file you will need the following class.

class SliderPlugin : public QAccessiblePlugin
{
CS_OBJECT(SliderPlugin)
CS_PLUGIN_IID("com.copperspice.CS.Accessibility.SliderPlugin")
CS_PLUGIN_KEY("QSlider")
public:
QStyle *create(const QString &key);
};

This code shows an implementation of the create() method. We check whether the interface requested is for QSlider. If it is, then you instantiate an accessible interface and return it. The object will always be an instance of classname. You must return a nullptr if the className is not supported

QAccessibleInterface *SliderPlugin::create(const QString &className, QObject *object) {
QAccessibleInterface *interface = nullptr;
if (className == "QSlider" && object && object->isWidgetType()) {
interface = new QAccessibleSlider(static_cast<QWidget *>(object));
}
return interface;
}

The last step is to include the following macro in the cpp file. The CS_PLUGIN_REGISTER macro exports the plugin in the SliderPlugin class. The argument is the name of the plugin class.

CS_PLUGIN_REGISTER(SliderPlugin)

Implementing Interface Factories

If you do not want to provide plugins for your accessibility interfaces, you can use an interface factory such as QAccessible::InterfaceFactory. This is the recommended way to provide accessible interfaces in a statically linked application.

A factory is a function pointer for a function that takes the same parameters as QAccessiblePlugin's create(), a QString and a QObject. It works the same way, you simply install the factory with the installFactory() method. The following is an example showing how to create a factory for the QAccessibleSlider interface,

QAccessibleInterface *sliderFactory(const QString &className, QObject *object)
{
QAccessibleInterface *interface = nullptr;
if (className == "QSlider" && object && object->isWidgetType()) {
interface = new QAccessibleSlider(static_cast<QWidget *>(object));
}
return interface;
}
int main(int argv, char **args)
{
QApplication app(argv, args);
...
}