Simple Example of JavaFx Control Skinning

screenshot-from-2016-11-24-08-41-58JavaFx provides a few ways to change a control’s appearance. You can change the css or replace the FXML. However, there is a problem with adhering to the Model-View-Controller pattern with those approaches. The view and the controller are not well separated. The addition of SkinBase to JavaFx’s public API promises to improve the separation of the view and controller.  Extending SkinBase may be the best way to preserve the Model-View-Controller pattern.

To test the usefulness of extending SkinBase I made a simple example of skinnable  JavaFx control. I choose to make a login control, which is a commonly needed control (One I happen to need too).

I’m going to walk you through my development process.  Following an Agile approach, I starting with making test code.  As a core of the test code, I want a control that will takes an initial username and a callback to be run on clicking “sign in.”  The below statement defines creating the control:
Login root = new Login(initialUsername, loginCallback);

Login Class

Next, I defined the Login class:

public class Login extends Control
{
    private String initialUsername;
    private Callback<Pair<String, String>, Void> loginCallback;
    
    public Login(String initialUsername, Callback<Pair<String, String>, Void> loginCallback)
    {
        this.initialUsername = initialUsername;
        this.loginCallback = loginCallback;
    }

    @Override
    public Skin<?> createDefaultSkin() {
        return new LoginSkin(
                this,
                initialUsername,
                loginCallback);
    }
}

Notice that the only required steps are to create a constructor that assigns the arguments to fields and override the createDefaultSkin method. Now I need to create a class for the default skin, that is used by default.

Default Skin

public class LoginSkin extends SkinBase<Login>
{
    public LoginSkin(
            Login control,
            String initialUsername,
            Callback<Pair<String, String>, Void> loginCallback)
    {
        super(control);
        LoginBorderPane borderPane = new LoginBorderPane(initialUsername, loginCallback);
        // the border pane is the same size as the whole skin
        borderPane.prefWidthProperty().bind(getSkinnable().widthProperty());
        borderPane.prefHeightProperty().bind(getSkinnable().heightProperty());
        getChildren().add(borderPane);
        // always add self as style class, because CSS should relate to the skin not the control
        getSkinnable().getStyleClass().add(getClass().getSimpleName());
    }
}

The LoginSkin must extend SkinBase in order to serve as a control’s skin. Some interesting things about the LoginSkin include binding the preferred width and height to the same properties of the skinnable. That is necessary to cause the border pane to be the same size as the whole skin. Also, the style class should relate to the skin and not the control to enable different css clases on changing skins.

LoginBorderPane

Finally, I set up the view portion.  The view’s elements are set up in the LoginBorderPane.  This is done with a controller class and FXML.  First is the controller:

public class LoginBorderPane extends BorderPane
{
    @FXML private Label organizationNameLabel;
    @FXML private TextField usernameTextField;
    @FXML private PasswordField passwordField;
    @FXML private Button signInButton;
    
    public LoginBorderPane(String initialUsername, Callback<Pair<String, String>, Void> loginCallback)
    {
        super();
        loadFxml(LoginBorderPane.class.getResource("Login.fxml"), this);
        usernameTextField.setText(initialUsername);
        signInButton.setOnMouseClicked((EventHandler<? super MouseEvent>) (event) -> 
        {
            loginCallback.call(new Pair<>(usernameTextField.getText(), passwordField.getText()));
        });
    }
}

Notice I use a static FXML loader named loadFxml.  This method allows the FXML to omit specifying a controller. This isn’t necessary.  It’s just my preference, as I think it could make FXML more reusable. Alternatively, the FXML could specify the controller. Notice, I set the initial username and assign a event handler that calls the loginCallback that the client provided.

I built the FXML with SceneBuilder, but I’m omitting it for brevity (don’t worry, it’s in the source available below)

Test Code for Default Control

public class LoginDemo3 extends Application
{
    public static void main(String[] args) {
        launch(args);       
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Login root = new Login(null, (c) -> null);
        Scene scene = new Scene(root, 300, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

The default test above has a stub as a callback, so there’s no functionality after “sign in” is clicked.  If you want some functionality then replace the stub with some real code.

Customized Control

Adding a real callback can dictate what happens when the “sign in” button is pressed.  This code separation allows the control to be versatile enough to be used for any kind of login system; be it a database, LDAP or stand-alone application.

Also, the css can be changed to replace the image and the styling.

Download

You can download the above example with the link below.  A few additions are added.  For example, I added a ResourceBundle to allow changing of the organization name and all the fields (for internationalization), but it’s essentially identical.

Any comments are welcomed!

https://github.com/daviddbal/javafx-skinned-login

Combo Quicksort and Insertion Sort

Insertion sort is very efficient for sorting a small number of elements. It is so fast, that many divide-and-conquer sort algorithms (e.g. merge and quick sort) switch to insertion sort when the group size is small. However, one important, but not obvious question, is what’s the right group size to convert when the algorithm should switch insertion sort?

quicksort vs insertion sort
The above chart shows the performance of quicksort and insertion sort on sorting random 32-bit real numbers (on Fortran 95). The data suggests the algorithm should switch to insertion sort when the number of elements is less that 190. However, the combined effect of the two sort methods running together may produce different results.

optimal insertion size limit
The above chart shows the optimal insertion size is far less than 190. The algorithm should switch to insertion sort only when the number of elements is below about 50. This result is surprising and shows the synergy between the two sort methods. Also, these results are for arrays of 32-bit values. If the sorted elements are larger, then the CPU cache could store less of them, the insertion size limit will shrink. Study into individual cases will be necessary to determine the ideal size for special data elements.

The chart also shows the magnitude of improvement the insertion-quicksort combo algorithm provides over plain quicksort. For small number of elements (~100 elements) the improvement is more than halving the execution time. For one million elements the improvement is still a meaningful 30% execution time reduction.

The following examples were run in using Fortran 95 and single-threaded.