FX Experience Has Gone Read-Only

I've been maintaining FX Experience for a really long time now, and I love hearing from people who enjoy my weekly links roundup. One thing I've noticed recently is that maintaining two sites (FX Experience and JonathanGiles.net) takes more time than ideal, and splits the audience up. Therefore, FX Experience will become read-only for new blog posts, but weekly posts will continue to be published on JonathanGiles.net. If you follow @FXExperience on Twitter, I suggest you also follow @JonathanGiles. This is not the end - just a consolidation of my online presence to make my life a little easier!

tl;dr: Follow me on Twitter and check for the latest news on JonathanGiles.net.

Since the initial JavaFX dialogs post here on FXExperience, the JavaFX dialogs discussion has been going gangbusters, most of it at the Jira issue over at RT-12643. Now that things have settled down a little, I wanted to take a step back and summarise where things are, and what has been discussed in the Jira issue and in comments to the previous blog post. I also want to discuss the evolution of the API (and implementation) that Eugene Ryzhikov and I have been working on (derived from ControlsFX). I apologise in advance for not covering all the points people have raised in the Jira issue – partly it is because I want to focus on what I perceive to be the most important points, but also it is because I’ve just returned from vacation, so things take a while to page back into memory.

RT-12643 has been really useful at discussing both peoples use cases and API details. There have been multiple implementations developed and discussed, and I think in general the members of the community involved in the discussion are in violent agreement over what is necessary at the core, with some differing opinions on exactly how to approach aspects of the API. My impression is that most people are happy with the direction of the discussion, but I’m always happy to be proven wrong! 🙂 There have been a few alternative implementations provided (which are all attached in RT-12643), and I’ll do my best to summarise their differences (or important ideas) briefly below:

  • Mikael Grev proposed a fluent API that uses a ButtonType enumeration to individual buttons available, and then has API to pass in a varargs argument consisting of the buttons to show in the dialog (although custom buttons are also supported). The ButtonType is also used as the return type for dialogs that don’t require user input (dialogs that require user input return the user input as the result). I like the use of Optional (it is something ControlsFX uses too) and the fluent API (which is something I’ve already removed from my API due to feedback that it doesn’t fit the JavaFX API style). Things get a little complex if the same ButtonType is used multiple times, but other than that it seems to be a fine API.
  • Scott Palmer proposed a more low-level API – it relies on a DialogType enumeration to specify which buttons to show (e.g. OK, OK_CANCEL, YES_NO_CANCEL, etc). The result back from the Dialog is a String representing the button, which you can compare against a constant (e.g. if (result == Dialog.OK_BUTTON_ID)). If users want a button that is not part of the DialogType enumeration, they can do so by creating a Dialog instance and overriding a createButtonBar() method, at which point it becomes their responsibility to create and layout all buttons. If users want to customise a Button, they can do so by getting the dialog pane from the dialog, and calling lookup with the button ID. This returns the Button instance that they can customise to their needs.
  • Graham Matthews proposed yet another approach which relied on dialogs being more easily broken down and recomposed. To achieve this his implementation introduce a DialogModel to represent properties like title, blocking, modality and the result of the dialog. It then has a DialogPane class that accepts the model, as well as the display content, and is able to show the dialog when requested. There are subclasses of DialogPane for common use cases like OkCancelDialog, which handles the creation and placement of the OK and Cancel buttons. The DialogPane can be completely customised by the user overriding the buildContent() method with their own code.

As feedback and alternate implementations have been received, Eugene and I have been revising our dialog API and implementation (as noted, a fork from ControlsFX). It is hoped that as we revise our API we will slowly take into account the feedback and ideas people have been proposing. I’ve been posting javadocs of each revision on my website, so for those of you interested in seeing the evolution and giving your thoughts, here are the links of the already posted APIs:

The changes between revisions is relatively minor:

  • Version 1 is the ControlsFX API. It has a fluent Dialogs API for the ‘high-level dialogs’, and a Dialog API for ‘low-level dialogs’.
  • Version 2 is the first API based on community discussion. This version removes the fluent Dialogs API, instead making the Dialog class support high- and low-level dialogs by adding additional setters and show*() methods. We also improved the Action API to have id / style / styleClass properties so that controls generated from the Action can be more easily styled via CSS. Even though it doesn’t appear in the API docs, version 2 did include the concept of a ButtonBar / ButtonType, as shown in the version 3 API docs.
  • Version 3 refined the Action API slightly – it removed the id property added in version 2, and added a lot more complexity around the graphic property to account for the fact that a single graphic Node in an Action could not be used in multiple controls (the scenegraph does not support a node being in multiple places).

My gut feeling remains the same as it did at the beginning: a JavaFX dialogs API should provide a simple way for users to create and show a modal and blocking dialog to a user. In my mind, this is the 90% use case. The extent of customisation in this API is enough to change title, header (our new name for masthead), graphic, content and buttons. Of course, by being able to change content, the entire visuals of the Dialog can also be changed. What has become more obvious to me in the discussions is the other use cases people have, and their objections to the API I was proposing, including:

  • Dialogs should ideally support non-blocking and non-modal modes.
  • Dialogs should ideally support further customisation by being able to access the root pane of the dialog.
  • Some people feel that the requirement to use Action is overkill, and would rather create their buttons in an alternative manner.

Version 3 was given a slightly more comprehensive internal API review, and I posted the feedback from this in Jira. This API review, along with the miles of community feedback, has helped to inform V4, which is now available for review. I need to stress immediately that V4 does not imply that this version is better than V3, it is just a variation on API based on feedback. It may very well be that we decide that V4 is taking things too far, and that the added complexity is not worth the cost. I am very keen to hear your thoughts on this – please post them over at RT-12643.

What are the changes in V4? Here’s a high-level summary:

  • We’ve made the root node of the dialog more accessible – you can now call dialog.getDialogPane() to get a DialogPaneBase instance. This allows for you to completely modify the dialog (it’s a GridPane, so go wild). It also has helper API to access the common sections of the default dialog.
  • We’ve created a DialogBase class that has two generic parameters – one for specifying the actions type (note that this is small ‘a’ action – there is no Action API dependency in DialogBase), and one for specifying the result type). We have also added a Dialog subclass of DialogBase, which uses the Action class for both the actions type and the result type.
  • Action has been simplified – we now don’t have graphicFactory to deal with the limitations of the scenegraph. Now the graphic property is just an Image, which can be reused multiple times in the scenegraph (as each time it will be placed in its own ImageView node).

We also made a bunch of small tweaks:

  • Masthead has been renamed header
  • There is API to specify modality
  • There is API to get the dialog owner object
  • Some methods have been renamed to be more Scene Builder friendly
  • Events have been added for onShowing, onShown, onHiding, and onHidden.

The result of the V4 API is that we have more classes, and more complexity (although most of it is hidden to users who just want to show a dialog). The way 95% of people interact with the API remains the same – they create a Dialog instance, set the title/header/content/actions and then call one of the show methods. The rest of the API is for people who want more power to customise their dialog. The big question I have for the community is, how do you feel about this? Is the additional functionality worth the extra complexity? What use cases do you have to have alternative action and return types, and is the API suitable for you? Is there more that needs to be done to enable your use case?

So, that’s where things are today. How can you help? Get into RT-12643 and offer your thoughts and use cases. It would be especially great if you could relate your use cases back to the APIs that are highlighted in the V3 and V4 links above. I’m really keen to hear from people who have taken a good long look at the V4 JavaDoc and are concerned about some aspect not meeting their requirements.