This site has been retired. For up to date information, see handbook.gnome.org or gitlab.gnome.org.


[Home] [TitleIndex] [WordIndex

Building Custom Accessible Widgets

This section is an introduction to building custom accessible widgets for a Linux GNOME application using ATK. There are basically two different ways to create a custom widget for a GNOME application:

  1. Start with a GTK widget class and override gtk_widget_get_accessible in your widget's AtkObject implementation. Assess which ATK interfaces (including attributes and events) can be inherited versus overridden from the parent widget class.

  2. Create an AtkObjectFactoryClass which overrides create_accessible and get_accessible_type. Register the AtkObject factory type usingatk_registry_set_factory_type. Determine which ATK interfaces a custom widget should implement based on the widget's features and functionality.

Note: The GAIL library is an ATK implementation libary for GTK widgets. Its source code may be helpful in understanding how to create custom ATK object implementations for custom widgets. However, if developing proprietary code, do not review GAIL or any other open source code protected by open source licenses. Violating this rule could compromise the originality of proprietary code.

Start with a GTK widget class

If creating a custom GTK widget, create an AtkObject subclass which overrides gtk_widget_get_accessible and inherits from a "default" ATK implementation for a similar GTK widget. Decide which ATK interface methods the new widget should override and if there are additional ATK interfaces that should be supported. For example, if the custom widget is like a GTK notebook widget, determine which AtkObject, AtkSelection, AtkComponent, AtkStateSet, and AtkRelationSet method implementations in the basic widget and notebook widget classes that the new custom widget ATK implementation should inherit versus override. If additional ATK interfaces (i.e. AtkText, AtkHypertext, AtkTable, etc), methods, role, states, or events should be supported, implement those.

Create and register an ''AtkObject'' factory

If a custom widget or set of custom widgets does not have an accessible object (AtkObject) implementation in an ATK implementation library, then the developer must provide custom AtkObject implementations in a GTK module. (Note that libgail.so is a GTK module containing all the ATK implementation classes for GTK widgets.) This module should be included in the GTK_MODULES environment variable so it is loaded at runtime. Each AtkObject implementation has a corresponding accessible object factory class (AtkObjectFactoryClass). A central ATK registry links object types (GTypes) to these accessible object factories.

Each factory must be implemented as a child of class type ATK_TYPE_OBJECT_FACTORY and must implement the function create_accessible. This function must create an appropriate AtkObject. A factory can be used to create more than one type of object, in which case its create_accessible function will need to include logic to build and return the correct AtkObject.

AtkObject* atk_object_factory_create_accessible
           (AtkObjectFactory *factory, GObject *obj);

Create an AtkObjectFactoryClass with an initialization function that overrides the default create_accessible and get_accessible_type methods for an AtkObjectFactory:

static void
newatklib_new_widget_factory_class_init (AtkLibNewWidgetFactoryClass *klass)
{
  AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass);
  class->create_accessible = newatklib_new_widget_factory_create_accessible;
  class->get_accessible_type = newatklib_new_widget_factory_get_accessible_type;
}

Then implement those methods in the factory class.

In the base ATK implementation class, register an ATK object factory type for each ATK object in the ATK module initializaion method:

atk_registry_set_factory_type (atk_get_default_registry(), SOME_TYPE_NEW_WIDGET,
                               newatklib_new_widget_factory_get_accessible_type());

Macros could be defined in the factory class and then used by the accessibility_module_init function in the base widget class to create and register the ATK object factory types for new widgets.

Creating an ATK object implementation class

After ensuring that every custom widget in your application has an AtkObjectFactoryClass which is registered, developers must create an ATK object implementation class for each custom widget that either overrides ATK interfaces in the parent widget class or implements all the ATK interfaces required to support the features and functionality of the custom widget.

Implement get_type plus class and instance initializer functions

As a first step, all AtkObject implementation classes (which are GObjects) must implement a get_type() function to set up class and instance initializer functions, and to indicate which ATK interfaces the ATK object implements. The class_init() function can redefine any ATK functions calls defined by the object's parent class, and can define the object class' own custom init(), notify_gtk(), and finalize() functions. A custom init() function caches data or listens to signals retrieved from a backing GTK widget. If the ATK object needs to listen to property notifications from a backing GTK widget, a notify_gtk() function is needed. A finalize() function is needed to free data when the ATK object is destroyed. Examples 5 - 12 in the "GNOME Accessibility for Developers" guide show sample implementations of all these functions.

Implement base ''AtkObject'' methods and properties

If creating a base AtkObject class for a whole set of custom widgets, or if overriding base ATK object property values (such as name, description, relations, role, and states), in a more specific AtkObject class, you need to implement functions which override the methods or set properties found in the ''AtkObject'' interface. For example, a base AtkObject class for widgets should implement AtkObject methods such as atk_object_get_description, atk_object_get_parent, atk_object_ref_relation_set, atk_object_ref_state_set, atk_object_get_index_in_parent, and atk_object_initialize. Specific ATK widget classes, such as a widget class for a radio button, which inherit from a base AtkObject class may need to implement only atk_object_initialize to set the specific widget's role property.

Parent objects should identify their child objects in the logical order in which they should be navigated via the keyboard, and child objects should report who their parent is and where they are located within the parent object. To make it possible for assistive technologies to properly navigate all objects in the correct order, it is critical for application developers to implement the following ATK interfaces and methods:

When it is not possible to indicate parent/child relationships between objects that match relationships seen visually, implement ATK_RELATION_FLOWS_TO and ATK_RELATION_FLOWS_FROM to let the AT know the logical navigation order of those objects.

The AtkObject interface provides a significant number of built-in role enumerations.

The atk_object_ref_state_set returns information about object states, for which enumerations are defined in the AtkState interface. Associate states with custom objects using AtkStateSet.

The atk_object_ref_relation_set returns an object's relationship types, which are enumerated in the ''AtkRelation'' interface. [bmgapguidegtkatk.html#_Toc412398981 Establish relationships between objects] for custom objects using the same AtkRelation, AtkRelationSet, and AtkObject methods used for defining relationships between GTK+ widgets.

Flyweight objects are AtkObjects created on the behalf of objects that aren't full-blown widgets, like cells in a treeview or table, and icons in an icon list. Their state is ATK_STATE_TRANSIENT, and they need to be generic enough to be useful in other widgets.

Two derived implementations of AtkObject are:

  1. AtkNoOpObject, which is the type of AtkObject created if an accessible object is requested for an object type for which no factory type is specified. When implementing this type of accessible object, all ATK interfaces should also be implemented. I think this just happens but application developers shouldn't do anything with this interface.

  2. ''AtkGObjectAccessible'', which should be implemented for GObjects which are not derived from GtkWidget, such as a canvas item.

Implementing ATK Subinterfaces

Custom widget developers must also decide which of the following ATK subinterfaces should be implemented for each widget to further define for an AT the specialized type of object and its features, functions, and behaviors.

AtkAction: Interactive AtkObjects (such as buttons, checkboxes, menu items, etc) should implement this interface to execute and describe an object's activations (click, press, toggle, expand, etc) and expose its key bindings, except when the user interaction is already covered by another appropriate interface. Exceptions include ''AtkEditableText'', which handles text insertion and deletion, and AtkValue, which handles the setting of values. When mouse and keyboard actions are redundant, expose only one action.

AtkComponent: All graphical user interface (GUI) objects which have screen coordinates should implement this interface to expose information about an object's size, position, and focus. A possible exception may be a text object with a transparent background that implements ''AtkText'' instead. Be sure to implement atk_component_ref_accessible_at_point and atk_component_get_extents to assist an AT in restricting their review of objects to what's visible in order to achieve better performance. A base ATK object class could implement AtkComponent, and ATK object classes which subclass the base class would inherit that implementation.

''AtkImage:'' Objects which display image or pixmap content on the screen, such as icons, buttons with icons, toolbar elements, and image viewing panes, should implement this interface to expose image descriptions (such as alternative text), size, and position.

''AtkSelection:'' UI components whose children can be selected, such as selectable lists and tree views, should implement this interface. For text selection, use the AtkText interface instead. If multiple children can be selected at the same time, set the ATK_STATE_MULTISELECTABLE state.

''AtkTable'': UI components which order child cell objects in rows and columns or present tree-structured information should implement this interface to expose row and column information (descriptions, headers, number of, selection, content at specific index) and the table caption and summary. The child objects (cells, headers, captions, summaries) implement other interfaces, such as AtkText and AtkImage, as required.

AtkValue: UI objects, such as sliders or dials, which either display and/or allow the user to specify a value from a bounded range should implement this interface for setting the current value and getting the minimum, maximum, or current value for a control. Values may be read-only.

AtkStreamableContent: UI text objects should implement this interface to provide access to a flat, streamable content view of a document. This interface is primarily useful for saving, printing, or post-processing entire documents, or for persisting alternate views of a document.

AtkText and AtkEditableText: Non-trivial objects which have non-editable text content with text attributes, are multi-line, and/or are selectable should implement the AtkText interface to expose text, character, caret, selection, range, and attribute content and information. If the text content is editable, the AtkEditableText interface should be implemented instead to expose all AtkText information as well as content which is copied, cut, pasted, inserted, and deleted. If the text is short and simple with no attributes, implement atk_object_get_name in the AtkObject interface instead.

If the text object is a document object, also implement AtkDocument (which is being updated) to indicate the document boundaries, locale, attributes, and events, plus implement all the ATK interfaces required to [GAP/AtkGuide/Atk#_Toc412398982 expose the accessibility information for each embedded object in the document]. Parent objects in a document should identify their child embedded objects in the logical order in which they should be navigated via the keyboard, and child objects in a document should report who their parent is and where they are located within the parent object using the parent and child methods in AtkObject as described above when implementing any accessible object. For example, a single paragraph may be a parent object for multiple child objects, such as images.

Remember to [bmgapguidegtkatk.html#_Toc412398981 establish relationships between objects] for form controls embedded in a document using the same AtkRelation, AtkRelationSet, and AtkObject methods used for defining relationships between GTK+ widgets.

Use the AtkSelection interface on the top-level document object to show which paragraphs have selected text, especially if selections span paragraphs or if there are multiple text selections in a document.

The AtkText interface provides a set of enumerated text attributes, but when necessary, new name-value pairs can be defined as text attributes to describe new object characteristics using atk_text_attribute_register. To set attributes for text objects, implement atk_editable_text_set_run_attributes in the AtkEditableText interface, even if the text object is read-only.

To assist an AT in restricting their review of objects to what's visible in order to achieve better performance, be sure to implement atk_text_get_character_extents, atk_text_get_offset_at_point, and atk_text_ get_bounded_ranges.

Note: To improve the efficiency of complex document navigation by assistive technologies, a new AtkCollection interface may be defined to match the new AT-SPI ''Collection'' interface sometime in the near future. For now, the AT-SPI Collection interface will be implemented in the ATK bridge to use the ATK interfaces implemented for objects embedded in complex, rich text documents. Complete implementation of ATK interfaces for embedded objects is critically important for the success of the Collection interface implementation.

''AtkHypertext''and '''AtkHyperlink''': Each UI object containing links should implement the AtkHypertext interface to provide a way to get links, their offsets, and the number of links. Each object which contains a link or set of links should also implement AtkHyperlink to provide a way to get URIs and find out information about the anchors (location, validity, is inline, is selected, how many), but the information is returned via calls to AtkHypertext. AtkHyperlink implements AtkAction for activating links.

For recommendations about which ATK interfaces, states, role, events, actions, and other accessible information and behaviors should be implemented for common types of user interface objects, refer to section [GAP/AtkGuide/UI Recommended ATK Implementations for Common UI Components].

Managing Children

There are two types of children you can have in any GUI application. One is a "live" child, created by adding a component to a container. These live children operate as separate entities and respond to events propagated by the component hierarchy.

The second type of child is drawn and managed by the parent in an effort to save system resources. Here, the parent is a "proxy" for its child. This lightweight, proxied child is usually a transient object. A typical proxied child would be a list box entry. A list box could have 100 items in it and you would not want to have the overhead of creating a live child for each item. So, proxied children are manufactured and drawn by their parent from the list boxes data as needed.

An example of this is implementation of GTK cell renderers for a list box, which is created using a GtkTreeView widget. In a GTK list box, each list entry is created through the use of a single GtkCellRenderer. The GtkTreeView widget for the list box tells the renderer where to draw, when to "appear" selected, and so on. The GtkTreeView component does not care what the cell renderer draws in it as long as the renderer does what it is told. The cell renderer does not receive focus from the windowing system, even though GtkTreeView may ask it to appear focused.

The GtkNotebook widget presents a different parent/child relationship that might best be described as a limited proxy. In the case of notebook page tabs, you know you will be dealing with a limited number of children. Here, the tabs are not actually components, but merely visual page tab elements with a reference to the component to be shown, based on the page tab selected. Because the list of elements is small, and generally persistent, it is easier to maintain a vector of accessible objects representing each page tab. When assistive technology requests an accessible child, the ATK implementation for GtkNotebook, merely goes to the internal vector of accessible page tab objects and returns the corresponding object.

The parent component should report its accessible children using atk_object_get_n_accessible_children andatk_object_ref_accessible_child.

Here are some basic rules to follow when managing children:

Managing Events

The main purpose of the class AtkUtil is to provide methods for adding and removing event listeners and for inquiring about the root AtkObject of the application and the name and version of the GUI toolkit. The adding and removing of the listeners must be done in the same thread. The following event listeners can be added:

An application or toolkit that bridges to ATK needs to subclass AtkUtil and to re-implement these methods. For event notification it is necessary to manually notify each of the event listeners.

Since live children need to be available to receive events, they are are also available to fire events, such as accessible state change events, needed by an assistive technology. However, unlike live children, proxied children are usually transient and therefore an assistive technology would not be able to receive any events from them. Therefore, all accessibility events pertaining to the proxied children should be generated from the proxying parent.

Refer to section [GAP/AtkGuide/Atk2Msaa#msaaevents Object Level Events aka Signals] for a list of ATK events.

To set up event listeners for ATK objects and which functions should be called when a specified event type occurs, use AtkEventListener(), atk_add_global_event_listener, atk_add_focus_tracker, and related methods in the AtkUtil class. Object events are also known as signals in GNOME and ATK. Some new events/signals are being proposed as a result of AT-SPI 1.7 interface changes.

Examples:

ATK implementation examples for documents

Below is a table containing HTML document samples with recommend ATK implementations.

Note: All accessible objects implement AtkObject and AtkComponent. Key: * = Embedded object character (0xfffc), used when no text from the object will be inserted in the parent AtkHypertext

Example: Paragraph with an Image in it

HTML source

Example: Header with a break in it

HTML source

HTML source

Example: Two paragraphs with a horizontal line

HTML source

Example: Pagragraph with an emphasis

HTML source

HTML source

HTML source

Example: BLAH with a break in it

HTML source

Example: image map

HTML source

Example: bulleted list

HTML source

Example: numbered list

HTML source

(parent AtkObject, role=ATK_ROLE_LIST,
   attr="html:role=ol, css:list-style-type:decimal")
  (child AtkText, role=ATK_ROLE_LIST_ITEM,
   text="1. This is a list item.",
   attribute run "static" first 3 chars)
  (child AtkText, role=ATK_ROLE_LIST_ITEM,
   text="2. This is another list item.",
    attribute run "static" first 3 chars)
[we should clearly define a text attribute which means "this text is
not explicit in the content, but was added by the presentation/user agent. I
believe MSAA/IE uses "static" ?]

Example: nested bulleted lists

HTML source

Example: form with a text area

HTML source

Example: form with checkbox

HTML source

Example: form with a selection

HTML source

Example: form with a multi line selection

HTML source


2024-10-23 10:57