Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFx: How to get the corresponding stylesheet for a given style class of a Node?

I am able to retrive the style class(es) of a JavaFx Node "node" with

List<String> styleClasses = node.getStyleClass();

This gives me for example the style class "chart-plot-background".

How can I retrive the corresponding currently active stylesheet entry from the node?

The Caspian.css file contains the entry

.chart-plot-background {
-fx-background-color: #f5f5f5;
}

and I would expect to be able to retrieve this information from the node with at least on of the following code snippets:

A.

Style style = node.getStyle();
//is empty

B.

Parent parent = (Parent) node;
List<String> styleSheets = parent.getStyleSheets();
//... loop through the list and search for the entry...
//however, the list is empty

C.

List<CssMetaData<? extends Styleable, ?>> cssMetaDataList = node.getCssMetaData();
//... loop through the list and search for the entry...
//however, the property "-fx-background-color" is null

Related old article:

Resolving CSS property for node

points to the bug ticket

http://bugs.openjdk.java.net/browse/JDK-8091202

Motivation

I would like to write a converter from JavaFx charts to scalable vector graphic (*.svg) files. In order to do so I need the active properties of each node, e.g. fill color and stroke-width.

like image 620
Stefan Avatar asked Oct 19 '22 04:10

Stefan


1 Answers

Just a note to start: JavaFX 8 uses modena.css by default, not caspian.css. Since you are using JavaFX 8 (getCssMetaData() was introduced in version 8), you want to refer to modena.css, unless you are using a system property switch to use the old user-agent stylesheet.

This is something of a partial answer; digging into exactly how to do this would take more time than I have. This should get you started though. If someone has already done the work figuring this out, they may be able to provide a more complete answer.

-fx-background-color is actually a sub-property of -fx-region-background, as is hinted in the CSS reference for Region. The code snippet

    Region node = (Region)root.lookup(".chart-plot-background");
    node.getCssMetaData().stream().filter(p -> p.getProperty().equals("-fx-region-background"))
        .findFirst()
        .ifPresent(System.out::println);

produces the output

CSSProperty {property: -fx-region-background, converter: javafx.scene.layout.BackgroundConverter@6f457c5c, initalValue: null, inherits: false, subProperties: [CSSProperty {property: -fx-background-color, converter: Paint.SequenceConverter, initalValue: [Ljavafx.scene.paint.Paint;@737bd041, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-insets, converter: InsetsSequenceConverter, initalValue: [Ljavafx.geometry.Insets;@22e4912, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-radius, converter: javafx.scene.layout.CornerRadiiConverter@3d63caf3, initalValue: [Ljavafx.scene.layout.CornerRadii;@7980e69f, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-image, converter: URLSeqType, initalValue: null, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-repeat, converter: RepeatStructConverter, initalValue: [Lcom.sun.javafx.scene.layout.region.RepeatStruct;@54d4d836, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-position, converter: LayeredBackgroundPositionConverter, initalValue: [Ljavafx.scene.layout.BackgroundPosition;@24c26d67, inherits: false, subProperties: []}, CSSProperty {property: -fx-background-size, converter: com.sun.javafx.scene.layout.region.LayeredBackgroundSizeConverter@7550f5e, initalValue: [Ljavafx.scene.layout.BackgroundSize;@791fb535, inherits: false, subProperties: []}]}

which, if you dig into a little, you will see contains

subProperties: [CSSProperty {property: -fx-background-color, converter: Paint.SequenceConverter, initalValue: [Ljavafx.scene.paint.Paint;@737bd041, inherits: false, subProperties: []}, ...]

So the -fx-background-color is in there.

The way this works, to the best of my knowledge, is that the CssMetaData objects describe how to map the CSS into an actual JavaFX property (an instance of StyleableProperty, which is a subclass of WritableValue. So if you dig through this, you should be able to figure out that -fx-region-background maps to Region.backgroundProperty(), and -fx-background-color maps to the fill property of each element of Region.getBackground().getFills(). The code snippet

    Background bg = node.getBackground();
    bg.getFills().forEach(fill -> System.out.println(fill.getFill()));

produces the output

0xf4f4f4ff

which I believe is the background color defined in modena.css for the chart-plot-background.

So if you want to automate this, it seems pretty tricky, albeit possible. You'll need to account for "nested" css meta data, which you can access via getSubProperties(). You should then be able to call getStyleableProperty().getValue(), and (possibly by reflection) dig into the resulting object to get the values set by the "subProperties".

Good luck...

Complete code I used for testing:

import java.util.Random;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.layout.Background;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;


public class CssPropertyLookupTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
        chart.setAnimated(false);
        Random rng = new Random();

        Series<Number, Number> series = new Series<>();
        for (int x = 1; x <= 10; x++) {
            Data<Number, Number> data = new Data<>(x, rng.nextDouble());
            series.getData().add(data);
        }
        chart.getData().add(series);

        Rectangle rect = new Rectangle(400, 20);

        BorderPane root = new BorderPane(chart, null, null, rect, null);
        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();

        Region node = (Region)root.lookup(".chart-plot-background");
        node.getCssMetaData().stream()
            .filter(p -> p.getProperty().equals("-fx-region-background"))
            .findFirst()
            .ifPresent(System.out::println);

        Background bg = node.getBackground();
        bg.getFills().forEach(fill -> System.out.println(fill.getFill()));
    }

    public static void main(String[] args) {
        launch(args);
    }
}
like image 85
James_D Avatar answered Oct 21 '22 22:10

James_D