How to Set a Javafx Static Property in CSS

Is it possible to set a JavaFX static property in CSS?

For JavaFX versions 2.0 to 2.2 => no you cannot set the static layout properties via css.

You can create a feature request in the JavaFX jira to ask this functionality be implemented in a future JavaFX version.

StyleableProperty in JavaFX CSS

We found out the answer by deconstructing the meta classes - css properties are not set until after the constrcutor has been called. Therefore you need to set the skin up to listen to changes in the css and then do the drawing etc.

Dynamically change JavaFX css property

The method getStyleClass() returns a list of CSS class names associated with the node. These class names are used to determine which rules in an external CSS file are applied to the node. You cannot pass CSS selectors and rules in here.

Instead, write an external CSS file which contains a rule for the background color for the track. You can use a "looked-up color" here, which basically works like a "CSS variable". Define the "looked-up color" for the slider, and use it in a rule for the track to set the background color:

style.css:

.slider {
-track-color: white ;
}
.slider .track {
-fx-background-color: -track-color ;
}

Now, in the Java code, you can update the value for the looked-up color by calling setStyle("-track-color: /* someColor */") on the slider. Here is a quick example:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class SliderTest extends Application {

@Override
public void start(Stage primaryStage) {
Slider slider = new Slider();
slider.valueProperty().addListener((obs, oldValue, newValue) -> {
double percentage = 100.0 * (newValue.doubleValue() - slider.getMin()) / (slider.getMax() - slider.getMin());
slider.setStyle("-track-color: linear-gradient(to right, #90C7E0 " + percentage+"%, white "+percentage+("%);"));
});
StackPane root = new StackPane(slider);
root.setPadding(new Insets(20));
Scene scene = new Scene(root);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}

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

In your application, you can do the same using the current time of the media player (or just register a listener on the slider's value property, as that is changing too).

JavaFx: CSS element unable to inherit properties of it's ID

There is no need to set non-JavaFX CSS attributes like font rather than -fx-font, those non-JavaFX CSS attributes will just be ignored (or potentially cause issues).

If you set the font in the FXML (not recommended), then I think that would override the value you set in CSS usually.

Generally, for styles like titles, it is probably best to use a style class rather than an id.

I don't think using the * universal selector is a good idea. Maybe it is OK, maybe it is not. The standard JavaFX modena.css style sheet uses a syntax of .root for the defaults, perhaps that would be better for you.

A description of the root style class in the Scene JavaFX CSS:

The Scene object has no settable CSS properties, nor does it have any pseudo‑classes. However, the root node of the scene is assigned the style class "root" (in addition to style classes already assigned to the node). This is useful because the root node of Scene is the root container for all active scene‑graph nodes. Thus, it can serve as a container for properties that are inherited or looked up.

Also, you tagged your question javafx-8. JavaFX 8 is obsolete, and a more modern version of JavaFX is recommended.

Changing default colors

If you want to change the default colors for the application, then you can do that by overriding the looked-up colors defined in the .root section of the default modena.css stylesheet.

The main looked-up color which you might override is -fx-base which sets the default color style for the entire application.

Study the JavaFX CSS reference if you would like to learn more about looked-up colors.

For example, the following style sheet rule sets the default font size to 20 and the default color of the text in labels to dark green.

.root {
-fx-font-size: 20;
-fx-text-background-color: darkgreen;
}

Example App

The example defines a default size for text in the app within the .root section. The default style size is overridden in the .title section so that titles can be larger. The example application requires JavaFX 17+.

The example application doesn't set default colors, but, if you want to do that, you can experiment by changing the supplied CSS to match the CSS in the default colors section of the answer.

earthlings

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class RootFontSizeApp extends Application {

public static final String CSS = "data:text/css," + // language=CSS
"""
.root {
-fx-font-size: 20;
}

.title {
-fx-font-size: 30;
}
""";

@Override
public void start(Stage stage) throws Exception {
Label title = new Label("Greetings earthlings");
title.getStyleClass().add("title");

Scene scene = new Scene(
new VBox(20,
title,
new Label("hello, world")
)
);
scene.getStylesheets().setAll(CSS);

stage.setScene(scene);
stage.show();
}

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

Declaring Variable In JavaFX CSS File

Unfortunately it seems to work only for colors. But you need to ensure that your variable is "visible" from rule which uses it:

* {
-fx-base2: #e00;
}
.track {-fx-background-color: -fx-base2;}

How to set several default CSS stylesheet for an application with JavaFX 9 (a replace for StyleManager)?

It's just another lack of JavaFX - simply use the JVM parameter below to make the StyleManager accessible.

--add-exports=javafx.graphics/com.sun.javafx.css=ALL-UNNAMED

JavaFX Custom Control CSS PseudoClass state change stroke color

You only ever apply the color/stoke width at the time you create the skin.

You need to listen/bind to the stylable properties instead:

strokeColorProperty = control.strokeColorProperty();
strokeWidthProperty = control.strokeWidthProperty();

How to change the style of components by their attributes?

If I would change the text it would also automatically change the
style

Option 1: control style by id

You can achieve it by using a custom label that changes style when changing text. I'll demonstrat it by changing the label's id. This simplified example uses the text as id :

import javafx.geometry.Pos;
import javafx.scene.control.Label;

public class CustomLabel extends Label{

public CustomLabel() {
setAlignment(Pos.CENTER);
setPrefSize(50, 25);
}

void setTextAndId(String s){
super.setText(s);
/*To keep this demo simple and clear id is changed.
If used, care must be taken to keep id unique.
Using setStyle() or PseudoClass should be preferred
*/
setId(s);
}
}

The custom label can be used in an fxml (Root.fxml):

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<?import tests.CustomLabel?>

<StackPane fx:id="root" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="tests.Controller">
<children>
<CustomLabel fx:id="cLabel" text="""" />
</children>
</StackPane>

A simple css that changes background color based on id (Root.css):

#1{
-fx-background-color: red;
-fx-text-fill: white;
}
#2{
-fx-background-color: yellow;
-fx-text-fill: red;
}
#3{
-fx-background-color: green;
-fx-text-fill: yellow;
}

Test class:

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class LabelCssTest extends Application {

@Override public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
stage.setScene(new Scene(root));
stage.show();
}

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

And test controller :

import javafx.animation.PauseTransition;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.util.Duration;

public class Controller {

@FXML
CustomLabel cLabel;
@FXML Parent root;
private static final int MIN_VALUE = 1, MAX_VALUE = 3;
private int counter = MIN_VALUE;

@FXML
private void initialize() {

root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
cLabel.setTextAndId(String.valueOf(counter++));

PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
cLabel.setTextAndId(String.valueOf(counter++));
if(counter > MAX_VALUE) {
counter = MIN_VALUE;
}
pause.play();
});
pause.play();
}
}

Option 2: control style by changing style-class

Use the same test class as option 1.

Root.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>

<StackPane fx:id="root" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tests.Controller">
<children>
<Label fx:id="label" alignment="CENTER" contentDisplay="CENTER" prefHeight="20.0" prefWidth="70.0" text="" "" />
</children>
</StackPane>

Root.css:

.style1{
-fx-background-color: red;
-fx-text-fill: white;
}
.style2{
-fx-background-color: yellow;
-fx-text-fill: red;
}
.style3{
-fx-background-color: green;
-fx-text-fill: yellow;
}

And controller:

import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.util.Duration;

public class Controller {

@FXML Label label;
@FXML Parent root;

private static final int MIN_VALUE = 1, MAX_VALUE = 3;
private SimpleIntegerProperty counter = new SimpleIntegerProperty();

@FXML
private void initialize() {

root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
counter = new SimpleIntegerProperty();
counter.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
label.getStyleClass().clear();
label.getStyleClass().add("style"+counter.get());
});
label.textProperty().bind(Bindings.createStringBinding(()->String.valueOf(counter.get()), counter));
counter.set(1);

PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
counter.set(counter.get() >= MAX_VALUE ? MIN_VALUE : counter.get()+1);
pause.play();
});
pause.play();
}
}

Option 3: control style by using PseudoClass:

Changes from option 2:

Root.css:

.root:style1 #label{
-fx-background-color: red;
-fx-text-fill: white;
}
.root:style2 #label{
-fx-background-color: yellow;
-fx-text-fill: red;
}
.root:style3 #label{
-fx-background-color: green;
-fx-text-fill: yellow;
}

And controller:

import javafx.animation.PauseTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.util.Duration;

public class Controller {

@FXML Label label;
@FXML Parent root;

private static final int MAX_VALUE = 3;
private SimpleIntegerProperty counter = new SimpleIntegerProperty(1);

@FXML
private void initialize() {

root.getStylesheets().add(getClass().getResource("Root.css").toExternalForm());
counter = new SimpleIntegerProperty();
counter.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
updateStates();
});

label.textProperty().bind(Bindings.createStringBinding(()->String.valueOf(counter.get()), counter));
counter.set(1);

PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(event ->{
counter.set(counter.get() >= MAX_VALUE ? 1 : counter.get()+1);
pause.play();
});
pause.play();
}

private void updateStates() {
for( int index = 1; index <= MAX_VALUE; index++){
PseudoClass pc = PseudoClass.getPseudoClass("style"+String.valueOf(index));
root.pseudoClassStateChanged(pc, index == counter.get() ? true : false);
}
}
}

Javafx - change Theme (CSS) on active window

Css file can be changed on the run without reloading or setting a new stage

replacing the styleSheet with set() method for observableList

css file javafx

App.java

public class App extends Application {

@Override
public void start(Stage stage) {

Button button = new Button("change");
button.setOnAction(e ->button.getScene().getStylesheets().set(0, "1.css"));

Button button1 = new Button("default");
button1.setOnAction(e ->button.getScene().getStylesheets().set(0, "0.css"));
HBox hBox = new HBox(button,button1);


AnchorPane anchorPane =new AnchorPane(hBox);
anchorPane.getStyleClass().add("pane");

var scene = new Scene(anchorPane, 640, 480);
scene.getStylesheets().add("0.css");
stage.setScene(scene);
stage.show();
}

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

}

0.css

.pane{
-fx-background-color:cyan;

}

1.css

.pane{
-fx-background-color:orange;

}

JavaFX CSS root property reference not resolved

It appears the issue here is that the stylesheet (caspian.css) is lazily loaded. Therefore, unless a component is constructed (not even necessarily added) the stylesheet is not resolved. Layout containers (like AnchorPane) do not trigger loading of the stylesheet. On the other hand, Controls (like Button/CheckBox/Label etc.) will trigger the stylesheet to be loaded.

Here is an updated version of the test case above to illustrate this behavior:

public class Main extends Application {

@Override
public void start(Stage stage) throws Exception {
AnchorPane pane = new AnchorPane();

new Button(); // Trigger loading of default stylesheet

Scene s = new Scene(pane, 800, 600);
pane.getStylesheets().add("test.css");
pane.getStyleClass().add("test");

System.out.println("Root pane style classes:");
for (String clazz : pane.getStyleClass())
System.out.println(clazz);

stage.setScene(s);
stage.show();
}

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


Related Topics



Leave a reply



Submit