jLuger.de - Lessons learned from making a swing application accessible

What is this article about
Lets start with accessible. Here it means that people with a disability can use your program. The disability may be that they are blind, have a visual impairment (varying degree), are color blind or at least red-green color blind, have problems focusing a target via mouse, or can't hear.

Swing is a GUI framework for the Java programing language. The special thing about it is, that it paints all the widgets itself instead of using the ones of the operating system. This means that the operating system will not know anything about the buttons and textfields of a Java application that uses swing. Well, when the operating system knows nothing about them then the accessibility tools can neither. To solve this issue there is an accessibility bridge for Java. I was told that this bridge doesn't offer all the features that you get when you have a native application.

This article will highlight some of the problems I've solved for making an existing swing application accessible. It will not be a guide on how to make applications accessible in general nor will it cover topics that weren't a problem for me (e.g. The application uses no sounds. So people that can't hear weren't an issue.)

The tool settings
Depending of the disability people may use assistive technology. For people with a visual impairment there is screen magnifier software. The concrete type that the application had to support was LunarPlus. It works in that way that the whole screen will just show a part of the desktop. The size of the desktop that is shown decreases when the magnification increases. Typical magnification rates are 4 to 5 but some people will go up to 12. With this rates you will need to scroll a lot even on small 19" monitors with 1024*768 resolution. As it is pretty easy to lose orientation with this setting LunarPlus has the ability to follow the focus. When you press tab to get to the next component it will set the screen to there.
For people with slight visual impairment LunarPlus offers the possibility to change colors in order to increase contrast. This goes from some descend color inverting up to some hard black and white mode.

Blind people have the option to use a screen reading software or a braille display (or both of course). A braille display is a very expensive hardware and pretty useless if you have never learned braille alphabet. For this reasons developers that could see, like me, didn't get a braille display. Fortunately JAWS, the screen reading software to be used, had a braille display emulator.

Starting the application so that JAWS could read its content wasn't easy. Nor starting it from eclipse neither from command line had helped. It turned out that you have to use Webstart (javaws + launch.jnlp). I don't know if it was a problem on my computer or if it is a bug in the accessibility bridge of Java.

Lessons learned
Grouping JRadioButtons
JAWS will tell you which position in the group a radio button has and how many the radio buttons the group has (e.g. one of three). This will work fine as long as you add all radio buttons to a ButtonGroup object. Now imagine that you have a framework that forbids the usage of ButtonGroup. That is what happened to me. The solution was to search where the accessibility bridge get its information from. It turned out that it is the method AccessibleRelationSet getAccessibleRelationSet() in the class AccessibleAbstractButton. If you want to override the method you have not only to extend the AccessibleJRadioButton (the descendant of AccessibleAbstractButton for JRadioButton) but also JRadioButton as it is an inner class that is protected and as you need to override the creation of an instance (public AccessibleContext getAccessibleContext()) in JRadioButton .

Increasing contrast for widget selection
All kinds of buttons (Buttons, Checkboxes, RadioButtons) and the JCombobox have a small border when they are selected. Thats a good thing as it shows you if the component is selected when you work with LunarPlus. I had to work with FocusListener and CompoundBorders  to get something like this for textfields. It just turned ugly when I wanted to set a different color to increase contrast. To set a new color you use UIManager:
UIManager.put(COLOR_KEY,NEW_COLOR);

You may think one color for all borders or at least one color for the buttons but that is wrong. Each type of button has its own color. And thats the easy part. JCombobox has no color you could set but uses the focus color of the theme (at least for Metal based L&F). Read this as extend your theme and override the method that returns the focus color. If you're just asking why you have to set the color with keys when there is a focus color wait a moment. There is even more crazy stuff. While Buttons take the new color directly, Checkboxes and RadioButtons need a kind of restart:
metalCheckBoxUI.uninstallUI(sampleCheckBox);
metalCheckBoxUI.installUI(sampleCheckBox);

The same code will work for RadioButtons. sampleCheckBox is an instance of a JCheckBox but the changes will be for all existing and future CheckBoxes.

How to get out of this hell? The SUN/Oracle JDK deliver sample applications with several themes. Use one of these to get a theme with high contrast and use or extend it. Don't start to do little extensions to an existing theme. The time you need will allow you to create a new one. Then let the user choose which one he needs.

BTW, here is the code to update the Look and Feel after the user selected another theme:
UIManager.setLookAndFeel(newLookAndFeel);
SwingUtilities.updateComponentTreeUI(mainFrame);

Contrast in Black White mode
In the previous section I've recommended to create a new theme with high contrast. Even when you do that there is a small pitfall with LunarPlus. LunarPlus offers a black and white mode where you really have only those two colors. As one is needed for background you have only one for the foreground. When your application needs tow foreground colors to distinguish two states, e.g. disabled/enabled, you will have a logical problem. One of the foreground color will either get the background color or merge with the other foreground color into one.

Check your application for all those possible conflict points and decide whether the information is irrelevant or if it needs to be displayed.

Accessing inactive elements

JAWS will read out all the content of a dialog/window when it is opened. When you want to reread the contents of an element you have to put focus on it. Per default this is not possible. This means that disabled elements aren't existing for a blind person.

How did this affect me? Well, the application needed some read only views on the data someone has entered. This was done via disabling all input components. For textfield I could have changed the setEnabled(false) to setEditable(false) but for the Comboboxes and the various button types this was not possible. Creating a special read-only view would have been a very cumbersome way.

While thinking about another way I started wondering whether it is possible to put the focus on disabled components and if JAWS will then be able to read out the content. For the first question a little search on the web got me this link: http://www.coderanch.com/t/342205/GUI/java/Tab-order-swing-components. The post of Michael Dunn (20.01.2006 12:07) showed a possible solution. A quick check with JAWS revealed that it would work. Sounds like the problem is solved. This is true for the KeyEventDispatcher but a closer look on the implementation of FocusTraversalPolicy showed that it knows about all the components used. Not pretty good for a general purpose implementation.

In order get a good general purpose implementation I've started with DefaultFocusTraversalPolicy. The accept method contains a lot of code but the most is about not going to disabled components. After taking out all that stuff I've stayed with a query that asked if the component is visible, displayable, and focusable. With this policy you are not just able to focus on disabled components but also on JLabel (good thing) and panels, scrollpanes, .... (bad thing). I've added some if statements that queried via instanceof for unwanted focus targets. This list got quickly very large. You better use a list to keep track of components that shouldn't get a focus.

Getting the content of JOptionPane accessible
In the last paragraphs I've showed how to access disabled elements and Labels. However the solution requires that you set the FocusTraversalPolicy for each dialog where it should be used. That shouldn't be a problem for your own dialogs but when it comes to JOptionPane you have no chance to set it. The only way to get around this is to build custom message dialogs. For a start I recommend to look at the showOptionDialog method of JOptionPane.

When you do use JOptionPane for your custom message dialogs you will face a problem with the message icon. It has no alternative text and as it is a label you can't easily exclude it from the focus traversal. To make things worse, the icon, the text and even the buttons aren't part of the component but of the UI. Depending on the theme you use you may try to extend it but this wasn't possible in my case. I had to use the brute force method that searched all components of the pane for a label with an icon and no text. There I could set an accessible name.

It turned out that this was just half of the way. JAWS recognizes that there is an icon and wanted to get more information from the AccessibleIconDescription. Somehow there were no information but only a null value that caused a NullPointerException. I had to set manually a description text. Then it worked and I've got some text for the icon.

Defining Shortcuts
Shortcuts are a good thing. They ease the usage of the application not only for people with disabilities but for all your users. Well, if you do them right. Doing it right means that they are ease to get and that they are consistent. Easy to get means that you can see them or even better guess them. Most user will guess that ALT+S is for save. Seeing means that the action key is highlighted. E.g. the u in Turn when ALT+U triggers the key. Consistent means that the keys are the same on all screens. While this should be a no-brainer for common actions like Save and Close but could be difficult for less common actions when your set of action keys is limited to one character. Then you will be quickly forced to give the same key different meanings on different screens. I haven't found a solution for this but when you start choosing a framework to create your GUI you should take care that they support shortcuts with multiple keys.

Another thing with shortcuts is the processing by JAWS. Sometimes it will cause shortcuts to be fired twice. So take care that no security question is confirmed with the same shortcut as with which the action was initially triggered. Should be a great issue. Well, except the great ALT+F4. When you use this to close a modal dialog it may close your application too. Depending on how difficult this situation will be for your users you may think about reintroducing one of those old and nasty "Are you sure to quit?" dialogs.

AccessibilityName
When you change the accessibility name of a visible component JAWS will read out the new text. Sounds like a great way to get status messages to the user? Rethink it again. There may be users that only use the braille display. Those will not get notified.

What really brought trouble to the project was the fact that the accessible name was even read out when there was another dialog in front of the window with that component. That may be not that bad but the covering dialog had a textfield that showed error messages via a tooltip and setting these error message as text in the hidden status bar. That caused wondering why the tooltip gets read out but won't be accessible by the braille display. It seems that the tooltip wasn't accessible all the way. It was the status bar whose accessible name just got the same message.

Accessing information outside the focus cycle
The focus cycle includes all components that you can reach from a starting position via (Ctrl-)Tab. All those component that didn't receive the focus after you've reached the starting position again are out of the focus cycle. Normally you don't want to have any components outside as these aren't "visible" for the blind users.

What to do when you can't get a component in the focus cycle? You may want to define a shortcut (when you've got some characters left) that starts an action that lets the component request the focus. Yes even labels can request the focus. Just put that code in an Action class and register an instance of the class at the input map of the parent dialog.

Screen resolution
When designing the GUI of your application keep in mind that the time of 800x600 screen resolution isn't gone. People will try to use the lowest possible screen resolution to get a kind of magnification effect. When the graphic card and the monitor allow to go down to 800x600 they will try it. To cope with this, always use layout manager and don't add to many elements on a single screen. Use tab panes or a card layout instead.

HTML and Labels
You may have heard that Swing supports HTML. A common use case is to use HTML to display the text in multiple lines within one label. The hidden disadvantage is that JAWS doesn't extract the text from the HTML automatically. Instead it will read the HTML tags too. The simple solution is to explicitly set accessible name of the label.

When the providing of two texts for one label seems to be cumbersome and the HTML stays simple one can use the build in HTML-Parser of the JDK. Just override the method handleText(char[],int) in the class HTMLEditorKit.ParserCallback. There put the char[] content together with a blank to a string buffer and give the arguments of handleText to the overridden method. Then use the parse method of ParserDelegator to start parsing. The pure text of the label will be then string buffer and could be assigned to the accessible name of the component.

Read-Only document for HTML in JEditorPane
JEditorPane is able to display simple HTML documents and with this component JAWS is able to extract the text. In order to repeat the read out of a piece of the document the user has to place the cursor to the start of the line/table cell. This is a problem as there will be no cursor when the document is not editable. So the user will get only the document once as a complete text. Thats not pretty nice when you only want to get a piece of information or when you need some kind of navigation.

The solution seems to turn on the cursor for read only documents. The code for turning on and off the cursor is hidden pretty good. So it was easier to create a read only document and tell the editor that it is writable.

In order to make a document read only override the methods insertString and remove. As it is pretty difficult to fill a HTMLDocument on creation you may add a flag that will toggle between write mode (here you just call the methods of the super class) and read only mode (do nothing).

Focus for JLabel
While working on another project I've found out that you can call setFocusable(true) on JLabel. The text of the label is then accessible to the screen reader and the braille display. While it makes the text accessible it has the problem that you don't see whether it is focused or not. So I've replaced them most times with a textfield that was formatted to look like a label. The advantage of the textfield is that you can still select the text. The disadvantage for you will be that it isn't a standard widget of Swing but was part of an internal Framework based on JGoodies (there is a good chance that is now part of JGoodies but you have to search yourself).

JComboBox
JAWS seems to have a problem with JComboBox. When the first entry of a combobox is selected it won't read the text, the position (1 in that case), and the total number of entries. When the users selects another value it will read these three information. Even when switching back to the first entry they will be read. When selecting another entry than the first one using the method setSelectedItem(Object) of JComboBox it will read the text, index, and total count of entries. So far so bad. Things get worse when you use data binding and don't use the setSelectedItem(Object) method of JComboBox but instead call setSelectedItem(Object) method of ComboBoxModel. JAWS won't recognize the new selected value. I guess it thinks that the first value is still selected. Anyway it won't read the text and position of the selected entry or the total number of entries. So when you use data binding ensure that setSelectedItem(Object) method of JComboBox is called.