Archives for category: Controls

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.

JavaFX has never had a dialogs API, which has always seemed to be a glaring omission – but no more! It has (finally!!!) been decided that JavaFX 8u40 will be the first release that will include a dialogs API. You can follow along at home with the RT-12643 Jira issue. If you have comments as a result of this blog post, you are also encouraged to send them in to RT-12643. Dialogs can mean different things to different people, and with this comes different expectations over how an API should be formed. In this document I hope to outline what dialogs can mean in an attempt to get everyone on the same page, and from there decide on how best to advance JavaFX into the dialogs era! I should note that I am basing this document on my own perspective and understanding, and I am maybe forgetting or misunderstanding concepts. If this is the case, please email me at jonathan.giles@oracle.com and I’ll be sure to update this document.

What is a Dialog?

A dialog is a popup that appears when the user needs to provide some input. Normally they block input to its owner, and normally they are modal, stopping the execution of the calling code until they return. This is beneficial as it means you can show a dialog, wait for the users response, and react accordingly. The alternative approach is to require callbacks to be called once the dialog is closed, but rarely is this the kind of interaction that a developer wants.

Dialogs can either be in their own top-level window (i.e. a JavaFX Stage), or they may be ‘internal’ (i.e. not in a separate window but embedded within the scene of their owner). I’ve always referred to these concepts (incorrectly) as heavyweight and lightweight dialogs, and I will continue to use this as my way of classifying the two types of dialog in this document. My personal preference has been to always use heavyweight dialogs, but in my experience in developing dialogs for ControlsFX (more details below), I have been surprised by how popular and how commonly used the lightweight dialog has been. Some benefits of lightweight dialogs is that they do not need a windowing system and are therefore more friendly on some touch devices.

Dialogs consist of a number of areas, and what the JavaFX dialogs consist of is still up for discussion. However, some common areas include the title area, the masthead area (which is an area beneath the title and above the content for showing bold text and / or graphics), the content area (where the message or other nodes can be placed), and the button area.

There are typically two common use cases for a dialogs API, and they only differ in how much time the developer needs to spend to bring the dialog up. In other words, the most common use case is to simply show a common dialog type (i.e. warning, confirmation, information, etc) with a pre-specified set of buttons, and to just specify the message (and related attributes), before returning the selected button to the developer so that they may react accordingly. The other common use case is only ever considered by developers when the simpler use case fails them: they find the simplest API that allows for them to customise the dialog to suit their needs. I typically refer to these two approaches as ‘high-level dialogs’ and ‘low-level dialogs’. JavaFX should support both approaches.

There are a number of ways a dialog can be customised – but typically it falls down to a desire to change one or more of the the title, masthead, content or buttons areas. From my experience, the most complex aspect to customising dialogs (from an API perspective) is how to specify and configure the buttons to show to the user. In addition to simply specifying the button text, there needs to be consideration over whether to allow for customising the button graphic, or modifying the default action of the button (the alternative is that the button always hides the dialog without question), and finally being able to specify which button is the ‘default’ or ‘cancel’ buttons (i.e. those that are pressed when the Enter or Escape are pressed). I will give extra attention to this area later in this document.

What is the goal for JavaFX dialogs?

At this stage that question does not have a clearly defined answer. JavaFX dialogs could even be a misnomer – I’m calling the feature ‘dialogs’, but in actual fact they are referred to interchangeably as ‘alerts’ as well.

At the most basic end of the spectrum, JavaFX API could consist of little more than API akin to the JavaScript Alert API (where the only parameter is a String of the text to show the user).

A more advanced API might be simply a low-level Dialog class that allows for setting title, masthead, graphic, content and buttons. What the buttons do and how the dialog looks will be largely left to the user.

Stepping further forward, we could include high-level API to make it easier to show common dialogs, and to have more of the implementation details hidden from the user (e.g. default buttons, default graphics, default text, default actions when buttons are pressed, etc).

The next possible step is to include additional pre-built dialogs, for example font choosers, progress dialogs (bound to a worker), ‘command link’ dialogs, login dialogs, etc.

Finally, a dialogs API could conceivable go so far as to include a wizard API, allowing for easy stepping between multiple pages, including branching, reading user input, and generally having more of a lifecycle.

My personal opinion is that if we are to have dialogs in JavaFX, we should go at least to having high-level API. A future release could include pre-built dialogs like those listed in the final step as users demand them. I have developed a prototype Wizard API in ControlsFX (look under the ‘wizard’ branch) to better understand its requirements, and I think it is safe to work on this after an initial release of dialogs – whilst a Wizard API depends a lot on dialogs, it does not require additional API at that level (rather, it is more concerned with pages, and moving between them, which is unrelated to the topic of this document).

What is ControlsFX Dialogs?

ControlsFX is an open source project consisting of a number of UI controls and other useful classes for JavaFX. One of the areas it covers is dialogs, where it has quite a complete implementation. The ControlsFX Dialogs implementation could quite easily form the basis of a JavaFX dialogs implementation. What is more important, and the purpose of this document, is how we see a JavaFX dialogs API taking shape.

The ControlsFX API documentation can be found at http://docs.controlsfx.org. Specific attention should be paid to the org.controlsfx.control.action and org.controlsfx.dialog packages, as well as the org.controlsfx.control.ButtonBar class, which is responsible for platform-specific button placement. Particularly useful documentation can be found on the Dialog and Dialogs classes, although I should note that this documentation is based on ControlsFX 8.0.6 and it has in fact changed (for the better, I hope) quite significantly in the upcoming 8.0.7 release. In other words, take note of the concepts, but not necessarily the specific API.

What are Actions?

As mentioned above, perhaps the most difficult aspect to a dialog API is determining how to specify and configure the buttons that can be displayed to the user in the dialog.

The Swing JOptionPane approach had two separate ways to specify buttons. Firstly, the common case is available via public static final ints (for example, JOptionPane.YES_NO_CANCEL_OPTION would mean that separate buttons for ‘Yes’, ‘No’, and ‘Cancel’ would be shown to the user). When the dialog was dismissed using this approach, the returned value would be a constant that mapped to another int in JOptionPane, for example, JOptionPane.YES_OPTION. Therefore, code would be written as such:

int response = JOptionPane.showConfirmDialog(
                    frame,                              // owner
                    "Hello world",                      // message
                    "Dialog title",                     // title
                    JOptionPane.YES_NO_OPTION);         // optionType

if (response == JOptionPane.YES_OPTION) {
    …
}

In the situation where custom buttons needed to be specified, the developer, if they wanted to use the high-level API, was forced to call the ‘showOptionDialog’ API, as such:

int response = JOptionPane.showOptionDialog(
        frame,                                   // owner
        "Hello world",                           // message
        "Dialog title",                          // title
        JOptionPane.YES_NO_OPTION,               // optionType
        JOptionPane.INFORMATION_MESSAGE,         // messageType
        null,                                    // icon
        new String[] { "Maybe", "Definitely" },  // options
        "Definitely");                           // default option

In this approach, the optionType argument of YES_NO_OPTION appears to be totally ignored – instead the two strings “Maybe” and “Definitely” are the only buttons presented to the user. Because neither “Maybe” or “Definitely” is a pre-specified option in JOptionPane, there is no way to return an int constant to represent this, so instead the response returned to the user will simply be an integer representing the location of the button in the dialog, counting from left to right.

The problem with this approach, in my opinion, is the disconnect between the provided Strings, and the integer return type. I’m left with two concerns:

  1. There is no way to specify the button type. The button type is important as different operating systems order and position buttons differently, based on their type. The ControlsFX approach below has a way of supporting this, but I would imagine that in JOptionPane OS-specific placement is left up to the developer (and that there is no way to left-align certain buttons – e.g. the ‘Help’ button).
  2. What happens in the RTL case? Do “Maybe” and “Definitely” force their orientation such that they are always in the given order, do we reverse the layout but return the original integer, or do we return different integers?

On top of this, there is no way for the buttons to do anything other than hide the dialog. This is unfortunate, as there are often times where a button press should do something whilst retaining the dialog in its current state. Examples include expanding / collapsing a dialog to show more information (common with ‘exception’ dialog types – the user only wants to see the exception after clicking a ‘show exception’ button), showing a new window with help text, or performing some validation on the dialog before dismissing it. Hopefully I am wrong, but I don’t think there is a high-level way of doing this in Swing JOptionPane.

How can we do this better? One option is to introduce the concept of Action. An Action is essentially an encapsulation of relevant properties, as well as an event handler, for zero or more user interface components to depend on (typically used with controls such as Buttons and MenuItems, for example). Properties an Action might include are text, description (for a tooltip, for example), a graphic, disabled (to specify whether the control is disabled or interactive). On top of this, any control with an Action backing it will defer to the Action when it’s ‘onAction’ event is fired.

The nice thing about considering an Action API for dialogs (and for now lets consider it in isolation of the rest of the JavaFX API framework) is that Actions may be passed into a Dialog, and may also be returned from a Dialog once it has been closed (i.e. if the ‘OK’ button has been clicked, the Action that created that button will be returned).

Introducing an Action API into JavaFX does not come without consequences, so it is not possible to say that an Dialog+Action API combination is the best option available. I will try to outline the possible approaches below, including the scenario where JavaFX does not include an Action API.

Possible Approaches for JavaFX

Now that we’ve covered a lot of the background, theory, and a little of how dialogs are handled in Swing, we can start to look forward and work out what we want to do with dialogs in JavaFX. Fortunately, as mentioned, we already have a great playground to experiment with dialogs API – ControlsFX. We can work on API experiments, and test that the API works as expected with the underlying implementation remaining relatively intact. As of now, I have three different high-level API approaches, and one low-level API which I will outline below.

High-Level Approach 1: Using overloaded static methods

The first implementation takes its API inspiration from Swing, with its JOptionPane API. In short, we provide a Dialog class that provides both high-level and low-level API, depending on how the class is instantiated.

The high-level API can be a series of overloaded public static methods, with names such as:

Dialogs.showConfirmDialog(...)
Dialogs.showInformationDialog(...)
Dialogs.showErrorDialog(...)

The permutations for each dialog type will include versions for various combinations, such as:

message,
message, title
message, title, masthead
message, title, masthead, graphic
message, title, masthead, graphic, actions

Note that the actual final set of methods is still to be defined, so this is just a representation and no means final! Also, the last parameter in the last permutation, ‘actions’, could be either an Array (or varargs) of String or Action, depending on whether Action is part of the JavaFX API or not. I discuss this further later.

The benefit of this approach is that it doesn’t bring in any additional API (such as Actions), but it comes at the cost of being either less configurable or more difficult to configure (as the Action API is able to encapsulate the details about each button in the dialog).

High-Level Approach 2: Use constructor + setter methods

Rather than (or in addition to) having static methods as shown in approach 1 above, we could introduce setters (and probably related getter / property methods). This approach would result in code such as the following:

Dialogs dialog = new Dialogs();
dialog.setTitle("Hello world!");
dialog.setMasthead("I can have an optional masthead too");
dialog.setMessage("This is a dialog message");
Action response = dialog.showConfirm();

Of course, the class name is unlikely to be Dialogs, and there is certain to be convenience constructors that would reduce this down somewhat. My concern with this approach is that people will just end up doing the following:

Action response = new Dialogs("Hello world!", 
                              "I can have an optional masthead too", 
                              "This is a dialog message").showConfirm();

The end result is that the static overloaded methods and the setter approach (given the availability of convenience methods on the constructor) are largely the same, and people are essentially ending up with a poor-mans fluent API (i.e. force everything into the constructor and then call show*()).

High-Level Approach 3: Using a fluent API

The third approach is derived from the ControlsFX API, although quite what is and is not included is up for debate. I will firstly present the entire API (in particular, the ControlsFX dialogs API, as well as the Actions API and the ButtonBar control), and then later I will discuss how this could be stripped back.

Here is the first example, using the ControlsFX Dialogs fluent API. It uses the default buttons (as specified for a ‘confirm’ dialog:

Action response = Dialogs.create()
                       .title("Hello world!")
                       .masthead("I can have an optional masthead too")
                       .message("This is a dialog message")
                       .showConfirm();
            
if (Dialog.ACTION_YES == response) {
    System.out.println("Success!");
}

But what about the case where we want custom buttons in our dialog? Here’s another example using the fluent API:

Action response = Dialogs.create()
                       .title("Hello world!")
                       .masthead("I can have an optional masthead too")
                       .message("This is a dialog message")
                       .actions(Dialog.ACTION_CANCEL, 
                                Dialog.ACTION_YES, 
                                Dialog.ACTION_OK, 
                                Dialog.ACTION_NO, 
                                Dialog.ACTION_CLOSE)
                       .showConfirm();
            
System.out.println("Response: " + response);

This approach is a little….odd…..but it shows a dialog with five actions specified: cancel, yes, ok, no, and close. Each of these actions is converted to a Button and shown within the dialog (with their OS-specific placement taken care of automatically). Whichever button is clicked on is returned as the response, which the user may then check and perform the necessary actions.

What about the case where custom actions are needed (i.e. buttons that perform custom functionality)? This can be handled using the following approach:

Action jumpAction = new DialogAction("Jump!", ButtonType.OTHER);
Action dontJumpAction = new DialogAction("Don't Jump!", ButtonType.OTHER);
            
Action response = Dialogs.create()
                       .title("Hello world!")
                       .masthead("I can have an optional masthead too")
                       .message("This is a dialog message")
                       .actions(jumpAction, dontJumpAction)
                       .showConfirm();
            
if (response == jumpAction) {
    System.out.println("You fool!");
} else if (response == dontJumpAction) {
    System.out.println("Phew, that was close!");
}

Of course, rather than just having custom actions being returned, the action could do anything – it will be called as soon as the button is clicked – and that means functionality can be fired before (or even instead of) closing the dialog.

The code examples above have shown the ControlsFX high-level API, and in particular the fluent API. The main benefit of a fluent interface is readability. The Dialogs class is intended for the 95% of cases where you want to show a confirmation dialog, or a warning dialog, or a information dialog, etc, and you want the majority of the dialog preconfigured (e.g. images, default buttons, etc). Using the Dialogs class is significantly more readable than a couple of dozen overloaded public static methods, because the parameter name is directly beside the argument, rather than simply having a long list of arguments. Once you start enumerating all the possible permutations, you can appreciate that we could end up with a lot of overridden methods, multiplied by how many dialog types we offer (error, information, warning, confirmation, input, font, progress, login, etc):

  • Owner
  • Title text
  • Masthead text
  • Graphic
  • Message text
  • Buttons / Actions
  • Style classes

Then, for certain dialog types, you need to provide default values, lists of possible choices, exception text, etc. Pretty much as soon as you write out the full argument list as a user of JOptionPane-esque API you’ve forgotten what each of the arguments is for (and I’ve seen so many times in Swing people putting things in the wrong order to ….interesting…. results).

However, as I note below, there are alternatives to having a fluent API, which we may consider as an alternative approach if a fluent API proves undesirable.

Low-Level Approach

The ControlsFX low-level API is based around the Dialog class (note the naming distinction – Dialogs vs Dialog). The Dialog class has API for allowing the specification of title, masthead, content, actions, and other applicable properties. From this class it is possible to almost entirely customise a dialog, although in reality this class does force certain layout expectations (if the user wants total customisation, they would want to start with Stage and roll their own from there). In other words, masthead will always be at the top, content will always be in the middle, and the buttons will always be at the bottom.

The main difference between the Dialogs and Dialog class is that Dialog does not have any pre-conceived notion of a default dialog. In other words, whereas Dialogs allows a user to show a confirm, information, error, or other type of dialog (with a default graphic and button configuration), Dialog has no such API. In other words, Dialog is great in the use cases where a user wants to show custom content, without the hassle of needing to handle the general layout of a dialog (i.e. button placement, padding, resize support, node positioning, etc).

Here is a custom login dialog created using the low-level API:

final TextField txUserName = new TextField();
final PasswordField txPassword = new PasswordField();
final Action actionLogin = new DialogAction("Login", ButtonType.OK_DONE){
    @Override public void handle(ActionEvent ae) {
        Dialog dlg = (Dialog) ae.getSource();
        // real login code here
        dlg.setResult(this);
    }
};

Dialog dlg = new Dialog(owner, "Login Dialog");
dlg.setMasthead("Login to ControlsFX");

InvalidationListener changeListener = o -> validate();
txUserName.textProperty().addListener(changeListener);
txPassword.textProperty().addListener(changeListener);

final GridPane content = new GridPane();
content.setHgap(10);
content.setVgap(10);

content.add(new Label("User name"), 0, 0);
content.add(txUserName, 1, 0);
GridPane.setHgrow(txUserName, Priority.ALWAYS);
content.add(new Label("Password"), 0, 1);
content.add(txPassword, 1, 1);
GridPane.setHgrow(txPassword, Priority.ALWAYS);

dlg.setResizable(false);
dlg.setIconifiable(false);
dlg.setGraphic(new ImageView(HelloDialog.class.getResource(
        "login.png").toString()));
dlg.setContent(content);
dlg.getActions().addAll(actionLogin, ACTION_CANCEL);
validate();

Action response = dlg.show();
System.out.println("response: " + response);

Reflections on the APIs Presented

Whilst my experience with ControlsFX is that people are happy with the dialogs API, I have never sought feedback from users of the API directly. I would be very interested to hear what people think about it.

However, having discussed the ControlsFX API very, very briefly with my team at Oracle, it is apparent that the ControlsFX API may not immediately be desirable for inclusion in JavaFX (after having been tidied up and repackaged, of course). This is particularly due to its inclusion of the high-level fluent API outlined above, and also due to its reliance on an Action API for specifying which buttons to show.

There are ways to strip back and change the ControlsFX API, with various implications (both positive and negative). In general, what will happen is that approach two will slowly trend towards approach one (i.e. it’ll move closer to having a JOptionPane-esque API). Below I attempt to discuss the most obvious ways I can change the API in two areas: fluentness and actions.

Fluentness

Obviously, if having a fluent API is not palatable (as it is non-standard API from the JavaFX point of view), we could remove the Dialogs class. From the top of my head, the only other fluent API in JavaFX is the Bindings class, and that class benefits from the fluent API due to the order of calls being important, whereas they do not matter for the Dialogs API. If we remove the Dialogs class, we lose the high-level API, and would need to introduce an alternative. There are at least three options:

Option 1: Add static overloaded methods to the Dialog (or another) class (as shown in approach 1 earlier).

Option 2: Add setter (and getter / property) methods to a class other than Dialog (as shown in approach 2 earlier).

Option 3: Replace the Dialogs class with a DialogBuilder class that has a public static create method, but after that it uses setters to modify the DialogBuilder instance. I think this is a non-starter, given the JavaFX move away from builder classes.

Actions

The other (and arguably the more important) area where the ControlsFX API might be pushing the limits of what is acceptable for JavaFX (at least in the 8u40 timeframe) is the actions API.

Based on my own prototyping of this scenario it is still possible to have both high-level and low-level API as discussed above. The change to the API of removing actions essentially results in an API where all Action arguments are changed to String arguments, and all Action return types are changed to integer return types. All other aspects of this document still remain true – we need to choose a high-level API structure, and if we don’t go with actions we need to be comfortable with the implications of this (as noted in the actions section earlier).

Summary

All this document leads us to one big question: what kind of dialogs API do we want in JavaFX? As with any API discussion we need to be careful to ground ourselves in reality – which I hope this document has achieved. Do we want a simple API to just show alerts, or do we want more configurability to customise what we show in dialogs? Do we want to have pre-built dialogs, or would we rather have just the low-level dialog (which, come on, isn’t all that low-level anyway!) :-) Do we want to break the JavaFX pattern and introduce a fluent API? Is it actually any better, or is it just confusing for people trying to wrap their heads around JavaFX? Do people really like long lists of overloaded methods (it makes your dialog instantiations one (long) line), or do they prefer making use of setters and splitting the instantiation and showing of their dialog over multiple lines?

I am not advocating for any particular approach, although I’m aware I’ve not been completely unbiased either. I should state that my primary goal is to define the best dialogs feature set and API we can, which isn’t necessarily to use the ControlsFX API as-is. I know some people who don’t think an Action API should be part of a UI toolkit, and it is not my goal to force API onto people when the feedback is that it isn’t desired. My inclusion of Action in this discussion is more to highlight my perceived shortfalls if we don’t have some Action API. I’m hopeful others may propose alternate solutions that haven’t even been outlined here.

JavaFX dialogs are targeted to 8u40, and we’re right on the verge of making all these calls, so your input in really important now. Not in a months time – now! As noted on the JavaFX 8u40 page, dialogs should be finished by September 15th, so time is definitely of the essence. Please leave comments on this blog post (or even better, at RT-12643), and I’ll do my best to moderate as quickly as possible.

Thanks for reading this far, you’re a champ!

 

 

One of the big features I’ve known people have wanted for a long time (hey, I’ve wanted it too!) is support for returning a TableView back to its original, unsorted state after being sorted by the end user. In general the user interaction goes something like this:

  1. Click on a TableView column header once. Everything sorts in ascending order. Great!
  2. Click on the same column header again. Everything sorts in descending order. We’re on a roll here!
  3. Click on the same column again. The sort arrow disappears, and…….nothing :-(

Of course, what should happen here is that the order of the items in the table should be reset back to their original order, from before the user ever clicked on anything. If you step behind the curtains with me for the briefest of moments, you’ll realise that the only way we can really do this is to of course keep a copy of the list in its original state (or a list of all the changes to the original list, such that we can unwind the changes later on). I never really wanted to do this, as you’re just setting yourself up for failure / pain / bugs / etc. What I always wanted to do was follow the wonderful GlazedLists approach from the Swing days, where the collections themselves became smarter, and the TableView remained mostly* inconsiderate of the type of collection given to it.
(more…)

We have been working really hard on the new Modena theme for JavaFX 8. I think we are finally really close so I wanted to share with you where we got to. I really hope you like the progress and direction. We took a lot of the feedback from the last blog into consideration. Overall though I am really happy and feel that this is going to do as much as we can to make JavaFX applications look great out of the box.

Modena example windows

Retina Mac
For those lucky enough to be running on Retina Mac then we also have support for Retina now in JavaFX 8 and with Modena so enjoy.
retina-pic

Trying for your self
The almost final version of Modena will be available this week in Java 8 Early Access build 81. For instructions for enabling Modena and running the test application see the first Modena blog post.

(more…)

We have been working recently on a new theme for JavaFX 8. The current theme for FX, named Caspian, is showing its age and we wanted to take the opportunity to give JavaFX a face lift for 8. Because folks have created custom controls and designs for their app, we needed to make sure that selecting the theme was something that you could opt-out of. We will be providing both API and command line switches in 8 to allow you to specify caspian specifically. If you do nothing, you’ll get Modena, our new theme, by default. Without further ado this is what it looks like:

Modena-Windows
(more…)

Update: since announcing the JavaFX UI controls sandbox I have announced the ControlsFX project, which is a more convenient way to get access to a number of controls that do not ship with JavaFX. Check out the ControlsFX website for more information.

This is something I’ve been waiting a really, really, really long time to announce, but it has finally happened. Today I am so pleased to announce the opening of the JavaFX UI controls sandbox repository on OpenJFX. This repo is a fork of the JavaFX 8.0 controls repo, but will occasionally sync from there to keep it up to date. This repo is intended for OpenJFX developers to put their ‘toys’ until such time that they get called up to the big leagues for inclusion into OpenJFX itself (although there are no guarantees that this will ever happen). This means that the controls are functional, but most probably not feature complete with a finalised API or any significant documentation.

The reason why I’ve been wanting to open this sandbox up is so that members of the JavaFX community can get super early access to our controls as soon as they reach the most minimal level of maturity, and help guide them along their paths to adulthood. I also wanted to do this as it takes a long time between developing a UI control and having it appear in a JavaFX release. This is something that has frustrated me, and a number of you, to no end.

From the get-go there are a few controls in this repo that you may be interested to play with and give us feedback on. They are TreeTableView (although note this is currently undergoing a total rewrite), Dialogs (ala JOptionPane from Swing), TableView cell span support (look at TableView.spanModel for more info), and a RangeSlider control. These controls will develop over time, but of course we’re always on the lookout for others who want to improve these controls for us. If you’re interested specifically in tending to the new controls in the sandbox, please email me and we can discuss it.

(more…)

Now that JavaOne is over and everyone finally has a chance to exhale, it’s time to start blogging about all the stuff we talked about. For my part, I’m hoping to blog about a bunch of stuff that I covered in my two sessions (primarily TableView cell spanning, RangeSlider, Rating, SegmentedButton and whatever else I can find on my machine). For today, lets talk about adding cell spanning support to TableView. Just a quick pro tip: for those of you who just want a jar and don’t care about how cell spanning was developed, you might want to skip to the end of this post…

So, I need to start this post as per usual – this is not a product of Oracle – there is no support contract or guarantee that this code won’t format your hard drive or do something equally nefarious. Please act responsibly for your personal circumstances. Finally, if you do find something wrong (API, documentation, bug, etc), please email me and lets improve it!

With that out of the way, what exactly is cell spanning (and how does it differ from cell merging)? My understanding (and correct me if I’m wrong) is that cell spanning is when the cell at the ‘spanned index’ takes over the full area of the span, whereas in cell merging you essentially take the content of all cells in the span area and merge them into a single cell taking up the area. In other words, the cell will be the same size regardless of whether spanning or merging is used, and it is simply a question of where does the data come from in either case. My preference is cell spanning over cell merging, so that is what I’ve implemented in this blog post.

To make things even clearer, I guess I should clarify what I mean by the ‘spanned index’. What I mean is that, essentially, we continue to layout our TableView in the same way as we always do (TableRow by TableRow (top to bottom), with each TableRow laying out each TableCell within it (left to right)). As we’re doing this layout, we are basically doing what I show in the code fragment below (although I should note this is pseudocode – things are somewhat more complex internally). The ‘spanned index’ is basically the combination of row/column index as we do the layout. In other words, the value in a spanned cell is the row/column index at the ‘top-left’ of the spanned cell area. This means that all other values that are in row/column indices that are overlapped due to a span will be lost. Here’s the pseudocode:

for (int row = 0; row < maxRows; row++) {
    // start laying out a new TableRow.
    // We do this by laying out each column in the row.
    for (int column = 0; column < maxColumns; column++) {
        // laying out each column
    }
}

Now we’ve covered a bit of theory, I guess we better cover what exactly the end result is. Here’s a few screenshots below – click on them for the full-sized version.


               

Please excuse the actual data used in the screenshots – I paid precisely zero minutes preparing the demo data – I just used what I had on hand. Clearly what I am doing in the demo data makes no sense for cell spanning – please use your imagination (and ideally send me a better sample data set to use for improved screenshots). :-)

To support cell spanning support, the first new class is the CellSpanTableView. It simply extends TableView and adds support for a SpanModel. SpanModel is defined as such:

public interface SpanModel {
    // cell spanning is only run when isCellSpanEnabled() returns true.
    public boolean isCellSpanEnabled();

    // Returns the CellSpan for the given row/column index.
    public CellSpan getCellSpanAt(int rowIndex, int columnIndex);
}

The CellSpan class is (presently) quite simple, it is a final class with two immutable fields representing the amount of spanning that should occur in the horizontal and vertical directions. I should note that a rowSpan (or columnSpan) of one simply means that there is no spanning of multiple rows / columns – it is just the default layout. Here it is in its full glory:

public final class CellSpan {
    private final int rowSpan;
    private final int columnSpan;

    public CellSpan(int rowSpan, int columnSpan) {
        this.rowSpan = rowSpan;
        this.columnSpan = columnSpan;
    }

    public int getRowSpan() {
        return rowSpan;
    }

    public int getColumnSpan() {
        return columnSpan;
    }
}

Therefore, with this API you should do something like the following when creating your own CellSpanTableView instance:

CellSpanTableView cellSpanTableView = ...;

cellSpanTableView.setSpanModel(new SpanModel() {
    private final CellSpan spanTwoRows = new CellSpan(2, 1);

    @Override public CellSpan getCellSpanAt(int rowIndex, int columnIndex) {
        return rowIndex % 3 == 0 && columnIndex == 1 ? spanTwoRows : null;
    }

    @Override public boolean isCellSpanEnabled() {
        return true;
    }
});

Note that all the magic happens in the getCellSpanAt method – for the given row / column indices, you need to tell the SpanModel what the cell span is. If you return null, you are saying there is no spanning required. In the code above, we span two rows whenever the row index is cleanly divisible by three, and we’re looking at the ‘first’ column. This gets you the result shown in the first screenshot above. I have already heard some requests to improve this API – if you have any strong feelings please let me know!

To achieve the new layout, there is a small number of code changes that need to go into the backend of TableView. Because this post is already quite long I won’t bother to talk about that here – if people are interested I will do another post. Needless to say, the code is included in the links below, but it is definitely a proof of concept, not a final implementation!

Another important point to make is that you should definitely join the cell spanning discussion on Jira at RT-24747 – this is where I would appreciate people post feedback on the API. I already know there are a few things to improve (particularly related to what should be passed to the user in the CellSpan.getCellSpanAt(…) method).

Ok, with all that covered, you can download a zip file that contains both the source and a precompiled jar file. Note that these will only work with the JavaFX 8.0 developer preview releases – they use API that is not available in JavaFX 2.2. It is trivial enough to backport though, and if someone does I will happily host the source code and jar file here.

In summary – lets get the discussion around cell spanning started! I look forward to your comments :-)

One question I see occasionally is people asking how to go about using prebuilt cell factories (such as those provided in the DataFX project run by Johan Vos and I, those sitting in the OpenJFX 2.2 repo in the javafx.scene.control.cell package, or just those that they have created internally), and also show a context menu when the user right clicks. More generally, the problem is that cell factories are blackboxes, and there is no support for chaining cell factories together (or even getting hold of the cells as they are being used).

The answer is quite simple: wrap the cell factory inside another cell factory, and set the ContextMenu on the wrapping cell. In other words, you would write code such as this (for ListView):

// The cell factory you actually want to use to render the cell
Callback<ListView<T>, ListCell<T> wrappedCellFactory = ...; 

// The wrapping cell factory that will set the context menu onto the wrapped cell
Callback<ListView<T>, ListCell<T> cellFactory = new Callback<ListView<T>, ListCell<T>>() {
    @Override public ListCell<T> call(ListView<T> listView) {
        ListCell<T> cell = wrappedCellFactory  == null ? new DefaultListCell<T>() : wrappedCellFactory.call(listView);
        cell.setContextMenu(contextMenu);
        return cell;
    }
};

// Creating a ListView and setting the cell factory on it
ListView<T> listView = new ListView<T>();
listView.setCellFactory(cellFactory);

(more…)

One of the missing features of JavaFX in the 2.0 release was a ComboBox control, and I’m very pleased to say that we’ll be filling this gap in JavaFX 2.1. Indeed, it is already in the developer preview builds we’re putting out, and has been sitting in the OpenJFX mercurial repo for some weeks now. I’m fortunate enough to even be getting bug reports filed in our Jira issue tracker, which is justification enough to be getting early developer preview releases out into your hands as early as we have!

Non-editable and editable ComboBox controls of all shapes and sizes!

(more…)

One of Jasper’s favorite websites is called Dribbble, which is a place for designers to post whatever work they’re currently working on for others to view and be inspired from. I got hooked on Dribbble last Thursday and have been looking at a bunch of the mockups and itching to try implementing some of them in JavaFX. Here is my first attempt.

One of the use cases we used for our CSS support and our ToolBar API was that we wanted to support a style of toolbar button which (at least for me) was popularized on the Mac, which is referred to by Cocoa as a “segmented” button. This is essentially nothing more than an HBox of buttons that has been styled such that the first button has rounded left edges, the center buttons are squared up, and the last button has rounded right edges. In the image above by Bady, you can see the segmented button bar in the toolbar area of the application.
(more…)