Accessibility in wxWidgets |
by Julian Smart, February 2003
On Microsoft Windows, this essentially means support for the Active Accessibility API (MSAA). Although a framework cannot do everything that is needed for an application to be AA conformant, it can provide the extra support to make this task relatively straightforward.
An interesting potential additional benefit of providing accessibility support is the ability to use the API for automation of wxWidgets applications.
I am starting to add AA features to wxWidgets (in CVS head, 2.5.x series). The rest of this document details the approach, progress made, and what has to be done to complete it.
I am being funded to start the AA support by Dartmouth Medical School. However I will need input from others to solve implementation problems. So if you're interested in this aspect, please join in! You can discuss it on the wx-dev mailing list, submit patches via SourceForge, add comments to the wxWidgets Wiki, or mail me.
Since I am working on Windows 2000, I am addressing MSAA version 1.3. We can extend it to version 2.0 later, which means supporting dynamic annotation and text services. MSAA 2.0 can only be used on XP, Windows 98, and Windows NT 4.
The main new class of interest is wxAccessible, which is the platform-independent equivalent of MSAA's IAccessible. wxAccessible for each platform derives from wxAccessibleBase, which contains virtual functions such as GetChild, GetChildCount, Navigate which the framework will call and convert to data that the underlying system (such as MSAA on WIN32) can process. There will eventually be a wxAccessible-derived class for every wxWidgets window class or group of window classes, and an application writing a custom window should provide their own wxAccessible-derived class.
wxWindowBase now has a virtual function called CreateAccessible which each window can override to create a new wxAccessible-derived object. GetOrCreateAccessible gets or creates an accessible, and you can call SetAccessible to set the accessible for a window, though this is normally done automatically if you pass a wxWindow argument to the wxAccessible constructor.
When MSAA requires an accessible object, a WM_GETOBJECT message is sent to the window, and wxWidgets handles this by passing the wxAccessible object associated with the window (creating one if necessary).
For functions that the wxWidgets application declines to implement, and if the object represents a standard WIN32 control, the behaviour is handled instead by a standard IAccessible object obtained using CreateStdAccessibleObject.
These are the relevant source files:
include/wx/access.h ; The accessibility classes and definitions include/wx/window.h ; Declares wxWindowAccessible include/wx/msw/ole/access.h ; wxAccessible declaration src/common/accesscmn.cpp ; Currently empty src/common/wincmn.cpp ; Implementation of wxWindowAccessible src/msw/window.cpp ; WM_GETOBJECT implementation src/msw/access.cpp ; Implements IAccessible, wxAccessible etc. samples/access ; A sample to demonstrate accessibility support
const long wxDateTime::TIME_T_FACTOR = 1000l;is always compiled, otherwise I got wxDateTime-related link errors. This issue needs to be resolved.
If you need to temporarily disable accessibility for a wxWidgets app, simply comment out the code that handles WM_GETOBJECT in window.cpp and recompile this file. This will make MSAA handle accessibility for all windows itself.
You can use tools from the MSAA development kit to look at the accessibility hierarchy. Unfortunately, they produce rather different results. Accessible Explorer fails to find the splitter window child of the frame's client area when you show the hierarchy starting from the frame. However, when you select the controls in AccessTest, it shows information about them; but it fails to navigate up the hierarchy properly when you click on 'Go to parent'.
On the other hand, using the Active Accessibility Object Inspector, you can navigate to the splitter window from the frame's client, and from there to the child controls. Similar, the MSAAVeri application shows the full tree, and the Narrator application (Programs/Accessories/Accessibility/Narrator) also seems to work fine.
The following images show problems with the current implementation that will need to be resolved.
Above, Accessible Explorer exploring AccessTest compiled without accessibility features. Note how the full hierarchy is shown. |
Above, Accessible Explorer exploring AccessTest compiled with accessibility features. Note how the splitter window and windows under it are not found. |
Above, AccessTest showing that it can traverse its own accessibility information using client-side code. |
If source for Accessible Explorer is available, it would help us know what it's expecting to find. Unfortunately only the source for Inspector seems to be provided, and that has less trouble with our implementation.
A fundamental question I have about AA is the relationship between windows and clients. If you explore, say, the version of AccessTest without explicit accessibility support, you find that some windows have a single child, 'client'. Are we supposed to model this relationship explicitly? In which case, our GetParent/GetChild assumptions are wrong, since they return wxAccessible objects which represents a whole window, not the window/client parts separately. Should a window's role always be 'client', or 'window'?
All help with this will be very gratefully received!
We need to write appropriate wxAccessible classes for wxWidgets windows that are not already supported by MSAA. For example, the splitter window should allow the sash to be accessible, as well as its one or two visible children.
We need to document the API and write a topic overview.
We should also look at supporting AA 2.0 features, and investigate GTK+/Gnome and Mac accessibility. We can perhaps provide a generic client/server implementation of accessibility for platforms that don't support a standard: for example we can have a TCP/IP server responding to client requests to access the wxAccessible hierarchy.
Some observations gleaned from the web:
"Active Accessibility weirdness note: You will probably want to use AccessibleChildren() instead of get_accChildCount() and get_accChild(). AccessibleChildren probably calls those and then improves the results. But, AccessibleChildren frequently returns fewer children than get_accChildCount says it should.
Active Accessibility weirdness note: Some objects report 1 child with AccessibleChildren, yet accNavigate reveals more children."