JavaFX Tip 12: Define Icons in CSS

When you are a UI developer coming from Swing like me then there is a good chance that you are still setting images / icons directly in your code. Most likely something like this:

import javafx.scene.control.Label;
import javafx.scene.image.ImageView;

public class MyLabel extends Label {

    public MyLabel() {
        setGraphic(new ImageView(MyLabel.class.
            getResource("image.gif").toExternalForm()));
    }
}

ImageExample

In this example the image file is looked up via Class.getResource(), the URL is passed to the constructor of the ImageView node and this node is set as the “graphic” property on the label.

This approach works perfectly well but with JavaFX there is a more elegant way. You can put the image definition into a CSS file, making it easy for your and / or others to replace it (once the marketing department has decided to change the corporate identity once again).

The same result as above can be achieved this way:

import javafx.scene.control.Label;

public class CSSLabel extends Label {

    public CSSLabel() {
        getStyleClass().add("folder-icon");
    }
}

Now you obviously need a CSS file as well:

.folder-icon {
	-fx-graphic: url("image.gif");
}

And in your application you need to add the stylesheet to your scene graph. Here we are adding it to the scene.

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MyApplication extends Application {

	public void start(Stage primaryStage) throws 
		                             Exception {
		CSSLabel label = new CSSLabel();
		label.setText("Folder");
		label.setAlignment(Pos.CENTER);
		
		Scene scene = new Scene(label);
		scene.getStylesheets().add(MyApplication.class.
		      getResource("test.css").toExternalForm());
		
		primaryStage.setScene(scene);
		
		primaryStage.setTitle("Image Example");
		primaryStage.setWidth(250);
		primaryStage.setHeight(100);
		primaryStage.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
}

With this approach you have a clean separation of your controls and their apperance and you allow for easy customization as well.

JavaFX Tip 11: Updating Read-Only Properties

Custom controls often feature “read-only” properties. This means that they can not be set from outside the control, not even from their own skin class. It is often the behaviour of a control that leads to a change of the read-only property. In JavaFX this behaviour can be implemented in the control itself and in the skin. So we sometimes end up with a skin wanting to update a read-only property of the control. How can this be done?

Backdoor: Property Map

The solution is quite simple: use the properties map of the control as a backdoor to the control class. The properties map is observable, so if the skin sets a value in the map then the control will be informed and can update the value of the read-only property itself.

The Control Class

The property in the control class might be defined like this:

private final ReadOnlyDoubleWrapper myReadOnly =
   new ReadOnlyDoubleWrapper();

public final ReadOnlyDoubleProperty myReadOnlyProperty() {
    return myReadOnly.getReadOnlyProperty();
}

public final Double getMyReadOnly() {
    return myReadOnly.get();
}

To update the property the control class registers a listener with its own property map and listens for changes to the property called “myReadOnly”:

getProperties().addListener(new MapChangeListener() {
  public void onChanged(Change c) {
    if (c.wasAdded() && "myReadOnly".equals(c.getKey())) {
      if (c.getValueAdded() instanceof Number) {
        myReadOnly.set((Double) c.getValueAdded());
      }
      getProperties().remove("myReadOnly");
    }
  }
});

Important: make sure to use a unique name for the property key or you might end up with naming conflicts. It is good practise to prefix the name with the package name of your control, e.g. com.myframework.myReadOnly.

The Skin Class

Now the skin class can update the property by setting the property value in the control’s property map:

getSkinnable().getProperties().put("myReadOnly", 42);

JavaFX Tip 10: Custom Composite Controls

Writing custom controls in JavaFX is a simple and straight forward process. A control class is needed for controlling the state of the control (hence the name). A skin class is needed for the apperance of the control. And more often than not a CSS file for customizing the apperance.

A common approach for controls is to hide the nodes they are using inside their skin class. The TextField control for example uses two instances of javafx.scene.text.Text. One for the regular text, one for the prompt text. These nodes are not accessible via the TextField API. If you want to get a reference to them you would need to call the lookup(String) method on Node. So far so good. It is actually hard to think of use cases where you would actually need access to the Text nodes.

But…

It becomes a whole different story if you develop complex custom controls. The FlexGanttFX Gantt charting framework is one example. The GanttChart control consists of many other complex controls and following the “separation of concerns” principle these controls carry all those methods and properties that are relevant for them to work properly. If these controls were hidden inside the skin of the Gantt chart then there would be no way to access them and the Gantt chart control would need to implement a whole buch of delegation methods. This would completely clutter the Gantt chart API. For this reason the GanttChart class does provide accessor methods to its child controls and even factory methods for creating the child nodes.

Example

The following screenshot shows a new control I am currently working on for the ControlsFX project. I am calling it ListSelectionView and it features two ListView instances. The user can move items from one list to another by either double clicking on them or by using the buttons in the middle. 

ListSelectionView 

List views are complex controls. They have their own data and selection models, their own cell factories, they fire events, and so on and so on. All of these things we might want to either customize or listen to. Something hard to do if the views are hidden in the skin class. The solution is to create the list views inside the control class via protected factory methods and to provide accessor methods.

The following code fragment shows the pattern that can be used:

public class ListSelectionView<T> extends Control {

    private ListView<T> sourceListView;
    private ListView<T> targetListView;

    public ListSelectionView() {
        sourceListView = createSourceListView();
        targetListView = createTargetListView();
    }

    protected ListView<T> createSourceListView() {
        return new ListView<>();
    }

    protected ListView<T> createTargetListView() {
        return new ListView<>();
    }

    public final ListView<T> getSourceListView() {
        return sourceListView;
    }

    public final ListView<T> getTargetListView() {
        return targetListView;
    }
}

The factory methods can be used to create standard ListView instances and configure them right there or to return already existing ListView specializations. A company called ACME might already provide a standard set of controls (that implement the company’s marketing concept). Then the factory methods might return a control called ACMEListView.

JavaFX Tip 9: Do Not Mix Swing / JavaFX

The JavaFX team has tried very hard to convince us that migrating from Swing to JavaFX is easy because of the option to embed Swing content in a JavaFX UI and vice versa. I must admit that I never tried it myself but based on the feedback I am getting from my customers I can only recommend to not mix Swing and JavaFX. At the time of this writing there were over 200 unresolved issues (120+ bugs) related to Swing integration (registered with the JavaFX issue management system).

Issue Types

The following is a list of issues that you might encounter if you still decide to go with it:

  • Appearance – there will always be a noticeable difference between the parts that were done in Swing and those that were done in JavaFX. Fields will show different font quality, different borders, different focus highlighting, etc….
  • Flickering – you might encounter flickering in your UI
  • Behaviour – controls will behave differently. The user will be able to scroll JavaFX controls with a gesture but not the Swing controls. The columns of a JavaFX TableView control will autosize when you double click the line between two column headers, the Swing JTable does not.
  • Threading – you are constantly dealing with issues related to the use of two different UI threads (the Swing EDT and the JavaFX application thread). You will run into freezing UIs and inconsistent state issues.
  • Window Management - controlling which window will be on top of which other windows and which window is blocking input (modality) for other windows becomes difficult / impossible. Popup windows might no longer hide themselves automatically.
  • Focus Handling- the wrong window might get the focus. Focus traversal between Swing controls and JavaFX controls might not work.
  • Context Menus – you might not be able to close the menu by clicking somewhere else in the UI or you might end up with two context menus open at the same time (one controlled by JavaFX, one controlled by Swing).
  • Cursor – setting different cursors on different controls / components will not work as expected.
  • Drag and Drop – wether within the SwingNode itself or between Swing and JavaFX, exceptions are heading your way.
  • Performance – the performance / rendering speed of JavaFX controls mixed with Swing components will degrade

Conclusion

What does this mean now? Well, it means that in the end you will not save time if you are following the Swing/JavaFX mixing strategy. At least not if quality is important to you. If your focus is only on making features available then maybe, but if you want to ship a commercial grade / professional application, then no.

If you have already decided to migrate to JavaFX, then do the Full Monty and redo your entire application in JavaFX, it is worth the wait.

JavaFX Tip 8: Beauty is Skin Deep

If you are developing a UI framework for JavaFX, then please make it a habit to always split your custom controls into a control class and a skin class. Coming from Swing myself this was not obvious to me right away. Swing also uses an MVC concept and delegates the actual component rendering to a UI delegate, but people extending Swing mostly subclassed one of its controls and added extensions / modifications to the subclass. Only very few frameworks actually worked with the UI delegates (e.g. MacWidgets).

I have the luxury of being able to compare the implementation of the same product / control once done in Swing and once done in JavaFX and I noticed that the JavaFX implementation is so much cleaner, largely because of the splitting in controls and skins (next in row: CSS styling and property binding). In Swing I was exposing a lot of things to the framework user that I personally considered “implementation detail” but that became public API nevertheless. The JavaFX architecture makes it much more obvious where the framework developer draws the line between public and internal API.

The Control

The control class stores the state of the control and provides methods to interact with it. State information can be: the data visualized by the control (e.g. the items in TableView), visual attributes (show this, hide that), factories (e.g. cell factories). Interaction can be: scroll to an item, show a given time, do this, do that. The control class is the contract between your framework code and the application using the framework. It should be well designed, clean, stable, and final.

The Skin

This is the place to go nuts, the Wild West. The skin creates the visual representation of your control by composing already existing controls or by extending the very basic classes, such as Node or Region. Skins are often placed in separate packages with package names that imply that the API contained inside of them is not considered for public use. If somebody does use them then at their own risk because the framework developer (you) might decide to change them from release to release.

 

 

 

JavaFX Tip 7: Use CSS Color Constants / Derive Colors

When working on FlexCalendarFX I got to the point where I had to define a set of colors to visualize the controls for different calendars in different colors. And not just one color per calendar but several: a background and a text color for deselected / selected / hover states.

The  colors were used in several places but for the sake of brevity I only focus on the visual calendar entries in the day view of FlexCalendarFX. The two screenshots below show the same entry, first deselected, then selected.deselected-entry
selected-entry

What is important to notice is that these are not completely different colors but they all have the same base color (green) but with different saturation.

The code below shows the best way I could find to define related colors in JavaFX CSS. I define the base color globally under “.root” and derive all other colors using this constant.

.root {
   -style1-color: rgb(119, 192, 75, .9);
}

.style1-entry {
   -fx-background-color: derive(-style1-color, 50%);
}

.style1-entry:selected {
   -fx-background-color: -style1-color;
}

.style1-entry-time-label, .style1-entry-title-label {
   -fx-text-fill: derive(-style1-color, -50%);
}

Please notice that the base color is using transparency as described in my previous blog about transparent colors. The other background colors in this CSS fragment are all derived from the base color. They are either brighter (positive percentage value in derive function) or darker (negative percentage value).

By using this approach to defining colors you can achieve a consistent and smooth look for your application and it will not look like your child’s coloring book.

JavaFX Tip 6: Use Transparent Colors

Picking the right colors for your user interface elements is always a great challenge, but it is even more challenging when you develop reusable framework controls where you as a developer have no control over the look and feel of the application using them. While you might always add elements on top of the default gray background the developers embedding your controls  might have more of a gothic tendency and use a black background. All of a sudden the nice colors your picked clash with the rest of the application.

To tackle this problem the best way I found while working on FlexGanttFX and FlexCalendarFX was to use semi-transparent colors. When you do the color of your UI elements will always be a mix of their own color and the background color. Your colors will become brighter if the application uses a white background and darker if it is using a black background. The contrast between your element and the background will never be strong, which makes for a smooth appearance.

The following screenshots were taken from FlexCalendarFX (work-in-progress).

opacity1

Same UI now with a darker background. You might not see it at first, but the green and blue are actually different between these two screenshots. These are very subtle differences, but they make a big difference in the overall impression of your application.

opacity2

In JavaFX you can define colors in CSS with an alpha channel value smaller than 1 to achieve transparency:

.my-style {
    -fx-background-color: rgba(255, 255, 255, .7); // transparent white
}

Using opacity also has the nice side-effect that you can still distinguish different elements even when they overlap each other.