Wednesday, May 28, 2014

Styling Cells in JavaFX

When working with Cells, in a ListView or TableView for example, it is best to use CSS to style the look of the cell. While it is possible to set the value of a property directly, the way cells are reused means that you have to be careful to undo or redo the value when the cell is reused.

Here is an example of a ListView that sets the text fill to red for prime numbers. Notice the use of the pseudo-class state. Adding and removing a style-class is also an option. But a psuedo-class state change has much less overhead.


package fxtest;

import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

import java.util.Arrays;
import java.util.function.IntFunction;
import java.util.stream.IntStream;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }
 
    final static int MAX_N = 100;
    final static boolean isPrime[] = new boolean[MAX_N];

    static {
        Arrays.fill(isPrime, true);
        for (int i = 2; i*i < MAX_N; i++) {
            if (isPrime[i]) {
                for (int j = i*i; j < MAX_N; j += i) {
                    isPrime[j] = false;
                }
            }
        }
    }

    final static PseudoClass prime = PseudoClass.getPseudoClass("prime");

    final Integer[] items = IntStream.range(0, MAX_N).boxed().toArray(n -> { return new Integer[n]; });

    @Override
    public void start(final Stage stage) {

        ListView<Integer> listView = new ListView<>();
        listView.getItems().addAll(items);

        listView.setCellFactory(lv -> {
            return new ListCell<Integer>() {

                @Override
                public void updateItem(Integer item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty || item == null) {
                        setText(null);
                        pseudoClassStateChanged(prime, false);
                    } else {
                        setText(item.toString());
                        int n = item.intValue();
                        boolean bool = ((1 < n) && (n < isPrime.length) && isPrime[n]);
                        pseudoClassStateChanged(prime, bool);
                    }
                }
            };
        });

        Scene scene = new Scene(new Group(listView));
        scene.getStylesheets().add("/fxtest/test.css");
        stage.setWidth(400);
        stage.setHeight(400);
        stage.setScene(scene);
        stage.show();

    }

}

The file fxtest/test.css:

.list-cell:prime {
    -fx-text-fill: red;
}

Friday, May 16, 2014

Styling the progress color a ProgressIndicator along a gradient

I thought it would be fun to have ProgressIndicator that went from red, to yellow, to green as the progress value went from zero to one. So I cooked this up. The color transitions nicely along a gradient from red at 0%, yellow at 50%, and green at 100%. Note the use of Bindings.createStringBinding, too.  Oh, and a lambda thrown in for good measure!

        // styled ProgressIndicator
        final ProgressIndicator progressIndicator = new ProgressIndicator();
        progressIndicator.setPrefSize(100, 100);
        progressIndicator.styleProperty().bind(Bindings.createStringBinding(
                () -> {
                    final double percent = progressIndicator.getProgress();
                    if (percent < 0) return null; // progress bar went indeterminate
                    //
                    // poor man's gradient for stops: red, yellow 50%, green
                    // Based on http://en.wikibooks.org/wiki/Color_Theory/Color_gradient#Linear_RGB_gradient_with_6_segments
                    //
                    final double m = (2d * percent);
                    final int n = (int) m;
                    final double f = m - n;
                    final int t = (int) (255 * f);
                    int r = 0, g = 0, b = 0;
                    switch (n) {
                        case 0:
                            r = 255;
                            g = t;
                            b = 0;
                            break;
                        case 1:
                            r = 255 - t;
                            g = 255;
                            b = 0;
                            break;
                        case 2:
                            r = 0;
                            g = 255;
                            b = 0;
                            break;

                    }
                    final String style = String.format("-fx-progress-color: rgb(%d,%d,%d)", r, g, b);
                    return style;
                },
                progressIndicator.progressProperty()
        ));

Thursday, March 6, 2014

lambda to the rescue

I'm a big fan of lambda expressions in Java 8, but it was only recently that I was able to push some lambda code to OpenJFX.  The thing of it is, lambda expressions helped me in two ways. First, it got me looking at the problem I was trying to solve from a different angle. Second, it helped me eliminate a lot of duplicate code.

The problem I was trying to solve was one where I had two identical code paths. The only difference between the two was the data type of one of the parameters. Basically, I had

    private List<StylesheetContainer> gatherParentStylesheets(Parent parent)
and 
    private List<StylesheetContainer> gatherSceneStylesheets(Scene parent) 

The main difference between these two methods was a call to add the Parent to one list, and a call to add the Scene to a different list (the idea is that there is one list of Parents that refer to the stylesheet and another list of Scenes that refer to the stylesheet). I tried different ways of refactoring these methods into one and kept coming back to the same thought; that if I could pass both the parent or scene and the list as parameters, I'd be set.

I started to think about whether or not lambdas could help me. What if I passed a function that did that for me? What I really wanted to do was say that this Scene or Parent refers to the StylesheetContainer and I came up with this interface. 

    @FunctionalInterface
    static interface Referent {void addReferenceTo(StylesheetContainer c) } 

Now I could pass the "addReferenceTo" function to a method and could refactor the code into a single method.

    private List<StylesheetContainer> processStylesheets(List<String> stylesheets, StylesheetContainer.Referent referent)

In this method, I just do:

    referent.addReferenceTo(container);
 
And call it like so:

    final List<StylesheetContainer> list = processStylesheets(sceneStylesheets, (c) -> c.sceneUsers.add(scene));
or
    final List<StylesheetContainer> list = processStylesheets(parentStylesheets, (c) -> c.parentUsers.add(parent));

In retrospect, I could have done the same thing with an Interface and an anonymous inner class. Or I could have just used 'instance of'.  But I think the lambda solution is rather elegant.

You can see the change here, in the diffs to StyleManager.