Skip to content

ComboBoxTableCell

kleopatra edited this page Feb 24, 2016 · 11 revisions

ComboBoxTableCell

Note
the problems (and solutions) are similar for all collection views

Reported Issues

Created

2015-10-01 13:48

Status

Resolved

The issue was that navigating the popup by keyboard committed the new selection, making navigation impossible and thus useless for any interaction by keyboard. The technical problem was that the edit was committed on value/selection change of the combo.

The fix is to use the combo’s showing property as commit trigger: commit on hiding. This would be correct only if there ware a strict correlation between the property change and a user’s intention. Use-cases where there’s no correlation:

  • no change: the popup might not be open during the edit - the property is always false, thus a commit is never triggered

  • change without commit-intention: an open popup might be closed (f.i. by alt-arrow on Win) for other reasons, thus triggering an unwanted commit

In fact, it might have introduced several new issues, f.i.:

Stumbling Blocks

Just as in ol' Swing, combo’s high-level notification isn’t fit for being used as a trigger in cell editing. At any time the equation holds:

Action == Value == SelectedItem

Navigation inside the popup changes the selectedItem which changes the value which fires the action. So we can’t hook into any of this to trigger a cell commit.

Solution

Basic Idea

The idea is inspired by ol' Swing: Don’t use high-level notification, instead hook into low-level events such that we effectively ignore the internal coupling.

Implementation

XComboBoxTableCell is a prototype that implements the basic idea.

It includes:

  • listen to keyStrokes on the combo and the list that indicate the cancel/commit intention of the current value

  • listen to mouse events on the list that indicate a commit intention

  • make sure the selection is adjusted on text changes

KeyHandler

A crucial requirement is to reliably react on esc/enter as the usual(?) keys for cancel/commit. Not entirely obvious (to me, at least) that the popup as an internal consumer comes into the way of normal interception (in an eventFilter). Below is a table of which keys are received by which handler on a stand-alone editable combo, depending on whether the popup is visible or not. Having modified the textfield or not doesn’t make a difference, nor does navigation in the open popup.

Key / Showing Filter:Pressed Filter:Released Handler:Pressed Handler:Released

Enter / false

yes

yes

no

no

Esc / false

yes

yes

yes

yes

Enter / true

no

yes

no

no

Esc / true

no

yes

no

yes

Looks like a adding an eventFilter and looking for released meets the requirement always.

Note
unrelated aside - Esc has no semantics other than closing the popup, text is committed on focusLost

Deselect on Text Change

When navigating the list, the combo’s value is updated - independent of whether it the text had been modified or not. The other way round: starting with a selection, then modifying the text has no direct effect on the selection (only after the change is committed on the combo level).

A "natural" commit (on cell level) implementation is to commit the combo’s value. As this is not yet updated, the text edit is effectively dismissed and the old selection committed. To overcome this obstacle, the prototye commits-on-textChange (on combo level). Doing so, clears the old selection and allows the "natural" cell commit to work both for hidden and visible popup.

Testing

There’s a rudimentary driver application, that has columns with core/x comboTableCell for comparison. No formal testing yet: need to manually walk through the bugs as noted in its api doc.

TBD ;)

Fix

All bugs fixed. The fix commits/cancels on Enter/Esc in a filter on the comboBox. For an editable combo, the text is converted into the value immediately before committing. No need to commit-on-typing (on the combo/textfield level) nor for a filter on the list.

What’s missing is the mouse handler on the list: without, the cell edit is not committed on clicking into the popup.