MixedState Demo

What Is It

This demos a mixed-state indicator for JCheckBoxes.

Checkboxes traditionally have two states. But sometimes you need a mixed state (or tri-state) option when multiple elements are being represented.

This demo includes two controls: one control for setting this new three-state option, and one control showing the traditional two-state option.

How To Use It

This demo simply calls:

MixedState.set(sampleCheckBox, MixedState.MIXED);

You can also pass in MixedState.SELECTED or MixedState.UNSELECTED. Or you can continue to call myCheckBox.setSelected(b) where you don't require a mixed state.

You can also retrieve the state by calling:

MixedState s = MixedState.get(sampleCheckBox);

How It Works

Setting the MixedState to MIXED does a couple of things:

  1. It replaces the current ButtonModel. The new model intercepts all calls to myButton.setSelected(b) or myButton.getModel().setSelected(b). Those calls are redirected to call MixedState.set(jc, UNSELECTED) or MixedState.set(jc, SELECTED).
  2. It installs a MixedStateUI. (Despite its name this is not a conventional ComponentUI.) This object is responsible for rendering the mixed state. There are separate rendering implementations for Mac and Windows.

Windows Rendering

On Windows (or more specifically: in LookAndFeels where the JCheckBox's UI subclasses BasicRadioButtonUI): the rendering is achieved by replacing the JCheckBox's icon. The new icon paints an unselected checkbox and adds a tick mark on top of it.

Mac Rendering

On Mac the rendering is already taken care of for us if we call this code on a selected JCheckBox:

checkBox.putClientProperty("JButton.selectedState", "indeterminate");

This takes into account the current user's appearance settings. So for example: if all checkboxes for a user render in pink, then the Java component will render in pink too.

Selected State

These two implementations are subtly different. The Windows implementation is rendered when the JCheckBox is unselected, and the Mac implementation is rendered when the JCheckBox is selected. For the most part the user shouldn't notice, but it leads to different behavior when the user clicks a mixed-state checkbox. On Windows a single click will change the state from MIXED to SELECTED. On Mac a single click will change the state from MIXED to UNSELECTED. As of this writing: I don't see an easy way to avoid this inconsistency.

Discussion

Mixed States Overview

Here is a W3C example of this general feature. Their primary use case relates to when the mixed state checkbox acts like a parent/group for several individual checkboxes:

One common use of a tri-state checkbox can be found in software installers where a single tri-state checkbox is used to represent and control the state of an entire group of install options. And, each option in the group can be individually turned on or off with a dual state checkbox.

Apple's guidelines similarly say:

Use a mixed state when it makes sense. A checkbox should accurately reflect its state. If a checkbox is used to globally enable and disable multiple child checkboxes, it should show a mixed state—reflected by a dash—when those children are not all in the same state.

Personally I hope to use this feature for a separate use case: when an inspector needs to represent several elements in a selection. We may need more than a simple boolean representation to signal to the user that some elements use a feature and others do not.

Future Enhancements

I tried creating a subclass of a JCheckBox so I could create my own AccessibleContext that identified a AccesibleState#INDETERMINATE. I was testing on Mac at the time, and I never got VoiceOver to identify the INDETERMINATE property. (I did not test on Windows.) For a fully accessible application this will be a problem, because a visually impaired user will only hear the checkbox described in the traditional terms of "checked" or "unchecked".