3
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        ScrollPane scrollPane = new ScrollPane();
        VBox vbox = new VBox();

        for (int i = 0; i < 50; i++) {
            Label label = new Label("Label " + i);
            label.getStyleClass().add("test");
            vbox.getChildren().add(label);
        }

        scrollPane.setContent(vbox);

        Scene scene = new Scene(scrollPane, 500, 500);
        primaryStage.setScene(scene);
        scene.getStylesheets().add("/style.css");
        primaryStage.show();
    }

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

style.css:

.test.label .text {
    -fx-fill: white;
    -fx-stroke: red;
    -fx-stroke-type: outside;
}

JDK and JavaFX 13

The labels render just fine but when I try to scroll it lags really much.

I expect it to be as smooth as it is when used without -fx-stroke-type: outside;, for example, replacing the contents of style.css with

.test.label .text {
    -fx-fill: white;
    -fx-stroke: red;
    /* -fx-stroke-type: outside; */
}

fixes the scrolling lag but the stroke overlaps the fill as it is drawn inside of the text.

I've tried running with Java and JavaFX 11 as well but there was no difference.

Nand
  • 568
  • 3
  • 18
  • 1
    What is ScrollType.OUTSIDE? Did you mean to write “stroke-type: outside” in the title? – VGR Jan 02 '20 at 20:50
  • Yes, that's correct. I will fix it now. – Nand Jan 02 '20 at 21:14
  • 3
    That performance is terrible. I recommend [filing a bug report](https://wiki.openjdk.java.net/display/OpenJFX/Submitting+a+Bug+Report). – jewelsea Jan 02 '20 at 22:38
  • @jewelsea Thanks for the suggestion, I submitted a bug and I hope it gets fixed soon but meanwhile I will use the workaround I posted below. – Nand Jan 03 '20 at 15:10

2 Answers2

2

I worked out how to get better performance on the scrolling without using Nand's work-around. I used some of the advice from this question:

Specifically, the advice to cache nodes:

node.setCache(true);
node.setCacheShape(true);
node.setCacheHint(CacheHint.SPEED);

Once I added these function calls to the labels generated in Nand's test program, scrolling performance was perfectly fine (rather than visibly incredibly laggy).

The initial drawing of the scene, before the rendering is cached is a bit slow (it takes a second or so). The slowness of the initial drawing can be best observed if you increase the number of labels from 50 to 500. But, once the scene has been drawn once and the nodes cached, scrolling no longer slows down the frames per second performance of the application.

In context, this solution is:

public void start(Stage primaryStage) {
    ScrollPane scrollPane = new ScrollPane();
    VBox vbox = new VBox();

    for (int i = 0; i < 500; i++) {
        Label label = new Label("Label " + i);
        label.getStyleClass().add("test");
        vbox.getChildren().add(label);

        label.setCache(true);
        label.setCacheShape(true);
        label.setCacheHint(CacheHint.SPEED);
    }

    scrollPane.setContent(vbox);

    Scene scene = new Scene(scrollPane, 500, 500);
    primaryStage.setScene(scene);
    scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
    primaryStage.show();
}

where style.css is as supplied in the question.

To speed up the initial drawing of the scene you can use a drop shadow effect, which simulates the outline, providing a similar look. Different settings for the effect (e.g. using one-pass-box rather than gaussian), will provide slightly different rendering, so it is a bit customizable.

.shadow-outline.label .text {
    -fx-fill: white;
    -fx-effect: dropshadow(gaussian, red, 1, 1.0, 0, 0);
}
jewelsea
  • 150,031
  • 14
  • 366
  • 406
0

I created a workaround so that I can use this feature until this bug is solved. (I've reported it to Oracle).

    public static Canvas outlined(Font font, Color fill, Color stroke, String text) {
        Text temp = new Text(text);
        temp.setFont(font);
        Bounds bounds = temp.getBoundsInLocal();
        Canvas canvas = new Canvas(bounds.getWidth(), bounds.getHeight());
        GraphicsContext ctx = canvas.getGraphicsContext2D();
        ctx.setFont(font);
        ctx.setTextBaseline(VPos.TOP);
        ctx.setFill(stroke);
        ctx.fillText(text, -1, -1);
        ctx.fillText(text, 1, -1);
        ctx.fillText(text, -1, 0);
        ctx.fillText(text, 1, 0);
        ctx.fillText(text, -1, 1);
        ctx.fillText(text, 1, 1);
        ctx.fillText(text, 0, 0);
        ctx.setFill(fill);
        ctx.fillText(text, 0, 0);
        return canvas;
    }

I know it can probably be improved to support stroke-width higher than 1 but I do not need that feature at the moment so this is enough for me.

Nand
  • 568
  • 3
  • 18