New Release: FlexGanttFX 1.0.0

Gantt Chart
I finally managed to put together a 1.0.0 production-ready release of FlexGanttFX. After two early access releases at the beginning of the year I put in a lot of hours to get the framework to a maturity level that I deem to be high enough for real-world application development. The release can be downloaded from the product website at http://www.flexganttfx.com. I would recommend to run the three standalone demos in this distribution to get an idea of FlexGanttFX’s capabilities.

FlexGanttFX is much bigger than a regular JavaFX control. The view consists of 83 and the model of 58 classes. It could have been even bigger but during development I decided to contribute some of the custom controls to the ControlsFX project (e.g. StatusBar, PopOver, PlusMinusSlider, HiddenSidesPane, MasterDetailPane).

emirates

There were two events in the past that delayed the release: the first one was a requirement from an early adopter to use the graphics area of the Gantt chart standalone, without the tree table view on the left-hand side. They wanted to use it as an editor for creating classroom schedules. Even though this requirement delayed the release it turned out to be very beneficial for the overall design of the framework. In the end I came up with four different ways the graphics can be presented: single row, using a VBox, using a SplitPane, or using a ListView.

The second event was the release of JDK 8u20. Changes were made to the way CSS was applied / processed which caused many warning messages to be shown in the console (“…unable to resolve -fx-background-color …”). While most people only get these warnings I could actually see that the layout of the controls in the Gantt chart was messed up. I had to find work-arounds for these issues which took a long time. I still see these messages but they seem to be harmless now. If anyone can point me to the root of this problem I would very much appreciate it.

msproject

I would like to say a special thank you to Gilberto Gomez, Werner Lehmann, and Christian Hollmann of MINT Media who provided very valuable input during the development of FlexGanttFX. I would also like to say thank you to the entire JavaFX developer community, especially the guys from Oracle and ControlsFX (Jonathan Giles, Eugene Ryzhikov).

JavaFX Tip 14: StackPane Children – Hidden But Not Gone

Another short tip: Swing provides a layout manager called CardLayout, which manages a set of components (cards) inside a container but always only shows one of them. The method CardLayout.show(Container, String) allows to switch between the components / the cards.

The same behaviour can be accomplished in JavaFX by using the StackPane, adding several children (each using the entire width and height of the pane) and calling the Node.toFront() method to switch between the children. However, there is one big difference: the StackPane will always layout all of its children, independent of wether they are currently showing or not. This might result in bad performance of your application and can be noticed when resizing the window that contains the pane.

My advice: manage your “cards” by adding them to or removing them from the scene graph. These operations are fast and flicker-free (this is JavaFX in Java 8, not Swing before Java 6).

JavaFX Tip 13: Study Modena CSS File

This is the easiest and shortest tip so far. If you want to do any of the following things:

  • learn how to use CSS
  • make your custom controls look like the standard controls
  • reuse an SVG path graphic used by a standard control (e.g. scrollbar arrows)
  • figure out how to navigate the structure of the standard controls
  • determine the color used for a specific item
  • consistently modify several standard controls

then simply take a look at the default CSS stylesheet that ships with JavaFX. The file is called modena.css and can be found in the jfxrt.jar file in this location: com/sun/javafx/scene/control/skin/modena/

e(fx)clipse 1.0 released

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.