Commit on focus loss in ListView, TreeView, TableView, and TreeTableView

Jonathan Giles jonathan.giles at
Thu May 4 23:19:29 UTC 2017

Hi all,

In the next week I have two topics to discuss, both with similar names 
but related to quite different functionality. The topic for today is 
adding the necessary API to the JavaFX UI control Cell class to enable 
support for 'commit on focus loss', that is, the ability to 
automatically commit a user-input value (into, say, a TextField inside a 
Cell) back into the backing data structure when the cell (or its 
content) looses focus. The topic for _next_ week relates to a new focus 
traversal API I plan to propose, related to focus traversal over all 
nodes in the scenegraph.

At present, the cells used in ListView, TreeView, TableView, and 
TreeTableView all support the concept of switching between a normal 
state and an editing state. Due to the way cells were built, when the 
editing state was enabled, the developer was simply entitled to change 
the controls displayed to the user. The cell is never made aware of what 
the editing controls were, and as such has no way to know what the user 
has entered. Today, when focus is lost, this results in a cancel edit 
event being fired, the user input is dismissed, and the cell returns to 
its non-editing state displaying the previous data. This is less than 
ideal, and is what we are trying to fix here.

Unfortunately, there is no way to enable this 'for free'. If we used 
heuristics and looked at the nodes in the cell when in the editing 
state, it would be error-prone. For one, what if the cell has, say, 
multiple TextFields? Even in the simple cases, what if the data model 
was not String-based, and required some conversion from String to T? 
What if the user had a custom cell for editing that could not be 
understood by the Cell? We'd have no choice but to offer some form of 
fallback API to support this.

Therefore, my proposal is to flip this around - we introduce a single 
new method on the Cell class, as shown below, and then retrofit our 
custom cell factories (most notably the TextField*Cell classes) to 
implement this method, providing the commit-on-focus-loss behavior that 
is expected.

      * Developers of custom cell implementations should override this 
method when
      * the cell provides editing functionality, with this method 
returning the
      * user input after the user has interacted with the editing 
      * The form of the returned data (wrapped in an {@link Optional}) 
      * be the same as the form of the underlying data model, hence the
      * use of the {@code T} generic type. If no value is available, or 
if the
      * value to be returned is invalid, {@code Optional.empty()} should 
be returned
      * to indicate that the commit should not proceed..
      * @return The value provided by the user into this cell when it 
was in its
      *      editing state, in the form of the underlying data model. If 
the value
      *      is invalid or unable to be determined, {@code 
Optional.empty()} should
      *      be returned.
      * @since 10
     protected Optional<T> getEditorValue() {
         return Optional.empty();

As noted, this method is already overridden by the pre-built 
TextField*Cell classes (so by default, users of TextFieldListCell, 
TextFieldTreeCell, TextFieldTableCell, and TextFieldTreeTableCell get 
commit on focus loss by default). Developers of custom cells who want 
commit on focus loss to be enabled will have to override this method. I 
will work to ensure that this new method is well-understood and widely 
known, and that all relevant documentation I can change will be changed 
to mention this.

As part of this work, I have also developed a test app - 
HelloCommitOnFocusLoss - that tests this new functionality on ListView, 
TreeView, TableView, and TreeTableView. In all tests that I have 
implemented so far, I can see that commit on focus loss works in cases 
where the user clicks on a new row, and when they give focus to another 
node (by both mouse and tab key). I am happy to extend this test 
application with any test cases people fear may not be supported, and 
then we can find gaps in this implementation (or, hopefully, be 
pleasantly surprised that these cases are already being met).

Discussion can take place in the Jira issue at [1], and the webrev with 
my proposed changes is at [2], although note that I have yet to develop 
enough unit tests for this change - as I wanted to discuss the API first.

Your feedback now is much appreciated.


-- Jonathan

More information about the openjfx-dev mailing list