 |
Home | Changes | Index | Search | Go
Series on core Swing inconsistencies: the center of interest here are the mechanisms (aka: event notification) around the themes of action, selection and bindable properties and how the diversness hurts SwingX.
Intro
Since a while "ease-of-use" - for desktop developers writing rich desktop applications with the help of the Swing/X framework - is in the midth of turbulent public debate. The paths to reach that goal range from efforts to revamp it into Swing 2.0 to dump it in favour of a new babe in town. Whatever the solution, first step would be to analyse the problem. Swing as a highly flexible can-do-it-all ui framework comes with a high complexity and consequently a steep (and ever-upward-sloping) learning curve. Remove unnecessary complexity (aka: inconsistency and redundancy).
Core Swing is Inconsistent
One aspect of complexity is the myriads of different event notification mechanisms, different even if they serve a very similar requirement, different to an extent that borders on inconsistency. Following is a table of Input-Actors with
- Input: goal is to get hold of a single value as passed into a component. Could be a toggled boolean, a selection un-/contained in a list, free-style and formatted typed
- Actor: goal is to do-something (after a user-gesture?)
| | act | select | bind |
| Core Swing |
| Button | X onKeyBinding, onMouse, onDoClick | (-) | (-) |
| ToggleButton | X onKeyBinding, onMouse, onDoClick, onDoSelect? | X (same as act) | - (could use ItemEvent) |
| ComboBox | X onSelectChange, onEdited, onEnter (only if editable) | X (same as act) | - (could use ItemEvent) |
| Spinner | - | - | - (could use ChangeEvent) |
| TextField | X onEnter (always if has ActionListener) | - | - (could use DocumentEvent) |
| FormattedTF | X onEnter (only if edited) | - | X (value) onChange |
| SwingX |
| DatePicker | X onCommit (enter), onCancel (esc) | (-) | X (date) onChange |
| RadioGroup | | | |
| AutoComplete | | | |
The relative amount of both roles differs as might be expected if different components have any right-of-existance at all. What's not expected are
- the (seemingly) arbitrary mapping of whatever triggers an actionEvent
- the wide variety of event types to get notified of inputChange: Item-, Change-, Document-, PropertyChange
Semantics of ActionEvent Notification
Nowhere documented, just guessing: user gesture to convey some kind of "readiness". Like "ready with"
- current input: Formatted/TextFields
- current navigation, selection (?)
- current form: triggering DefaultButton's Action
Mostly (not necessarily?) if input happens via keyboard, the gesture is the ENTER key (or equivalent), the input component is focused (or a focused popup's owner). The one end of the variance is the FormattedTextFields which fires only on receiving an enter while it has a pending change. The other end is a JComboBox which multiple actionEvents (with different commands) on a single (even programmatic) change. This indecent verbosity makes it hard to use: the whole point of having an actionEvent notification path orthogonally to the "dataEvent" notification path is to allow developers to differentiate between both.
Further complication arises from the notion of a per-form default action (as implemented JRootPane's DefaultButton). Typically, per-component readiness is signalled by the same user gesture (ENTER) as the per-form readiness. This is a problem if the readiness gesture happens on a ready-aware-component inside of a ready-aware-form. A reasonable approach might be to use it on the component level if needed (aka: there had been a change which requires a ready signal) and let it pass to the (next higher) form level if not. Again the actual implementation vary widely, not only across component types but also across jdk versions
- simple TextField: always fires and consumes if has actionListener, always triggers default if there are no actionListeners
- FormattedTextField: fires and consumes if has a pending edit, passes on if not
- editable ComboBox (jdk1.5): fires and consumes if the selected item had been edited, passes on if not
- editable ComboBox (jdk1.6): fires on component level and passes on always
- SwingX DatePicker: always fires and consumes on component level
Semantics of ItemSelectable
Deducting from how ItemSelectable is implemented in Swing, I was rather sure that it belongs into the "DataEvent" category. After that, the addItemListener api doc comes at a surprise:
/**
* Adds a listener to receive item events when the state of an item is
* changed by the user. Item events are not sent when an item's
* state is set programmatically. If ...
*/
Semantics of "DataEvent" Notification
Unification
Actually, these thoughts were triggered by a discussion in Issue 974-swingx which was raised because the (obviously rarely used but useful if used, citing Karl) JXRadioGroup doesn't fire (any or no propertyChange?) a per-group notification on setting the its selected value. The original suggestion was to fire a propertyChange event (instead of following the ToggleButton/ComboBox itemSelectable path). Which could well be viewed a higher-level semantic event of what we are after at the end of the day (which here is the input): a value to do something with. Doing that for all those Input-Actors (except for the button with a 100% Actor) could hide the lower-level details most of the time - we often don't really care if the value was selected or input, do we?.
Open question: Text-Input elements (and their clients like ComboBox, DatePicker) have the notion of "commit/cancel" That's a hook for user gestures to denote input-finalization or revert. While the finalization is (mostly) transported via an ActionEvent, the mapping of a user gesture to that finalization is varying, and the cancel mostly missing.
Links
|