Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Edit JavaFX CSS at runtime

Tags:

java

javafx

I'm creating a CSS stylesheet for a JavaFX 8 application.

I'm repeating these steps several times:

  1. Edit the CSS
  2. Run the application and see the changes
  3. Stop the application

I found this approach very time-consuming and looking for something smarter.

Is it possibile to edit the CSS of my JavaFX application while running and see the result in real-time?

I'm looking for something like the Inspect tool of Google Chrome.

This can be partially reached by using Oracle Scene Builder. In fact I can load a CSS file and run the preview. Any change to the CSS file is correctly shown in the preview without the need to restart.

Unfortunately that's just a preview: many components are empty/uninitialized. Sure this is anyway very helpful, but many refinements require the real application running.

like image 701
Paolo Fulgoni Avatar asked Oct 20 '22 05:10

Paolo Fulgoni


1 Answers

You will have to change using getStylesheets() / setStyle() etc.

In the example below I had to use a temp file as I am changing the CSS of the scene, when using into single components this is not necessary as you can change directly in the components.

enter image description here

You may download this example on gist.

Test.java

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

public class Test extends Application{

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("doc.fxml"));
        Parent root = (Parent)loader.load();
        CSSController myController = loader.getController();
        Scene scene = new Scene(root);
        myController.setScene(scene);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }

}

CSSController.java

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;

public class CSSController implements Initializable{
    private Scene scene;
    @FXML TextArea cssContent;
    @FXML Button defineCSSButton;
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        defineCSSButton.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent event) {
                String css = cssContent.getText();
                try{
                    File temp = new File("tempfile.css"); 
                    temp.delete();
                    temp.createNewFile();
                    BufferedWriter bw = new BufferedWriter(new FileWriter(temp));
                    bw.write(css);
                    bw.close();
                    String url = "tempfile.css";
                    scene.getStylesheets().add(temp.toURI().toString());
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        });    
    }
    public void setScene(Scene scene) { this.scene = scene; }
}

doc.fxml

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

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity"
    minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
    prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="CSSController">
    <children>
        <TextArea fx:id="cssContent" layoutX="14.0" layoutY="26.0"
            prefHeight="292.0" prefWidth="319.0" />
        <Label layoutX="22.0" layoutY="6.0" text="Define your CSS here:" />
        <Button fx:id="defineCSSButton" layoutX="14.1875" layoutY="333.5"
            mnemonicParsing="false" text="Define CSS" />
        <Line endY="400.0" layoutX="332.0" />
        <Label layoutX="418.0" layoutY="15.0" text="Components:" />
        <ChoiceBox layoutX="343.0" layoutY="48.0" prefWidth="150.0" />
        <ColorPicker layoutX="343.3759765625" layoutY="86.0" />
        <CheckBox layoutX="347.6650390625" layoutY="123.0"
            mnemonicParsing="false" text="CheckBox" />
        <MenuButton layoutX="343.37109375" layoutY="159.5"
            mnemonicParsing="false" text="MenuButton">
            <items>
                <MenuItem mnemonicParsing="false" text="Action 1" />
                <MenuItem mnemonicParsing="false" text="Unspecified Action" />
                <MenuItem mnemonicParsing="false" text="Unspecified Action" />
                <CheckMenuItem mnemonicParsing="false" text="Unspecified Action" />
            </items>
        </MenuButton>
        <ProgressIndicator layoutX="347.9091796875" layoutY="200.01953125"
            progress="0.0" />
        <Slider layoutX="410.0" layoutY="213.01953125" />
        <ToggleButton layoutX="348.7421875" layoutY="254.0"
            mnemonicParsing="false" text="ToggleButton" />
    </children>
</AnchorPane>

Css in the screenshot:

.label{
-fx-font: 15px "System Bold";
-fx-text-fill: #FF0000;
}

.button{
    -fx-background-color:
        #c3c4c4,
        linear-gradient(#d6d6d6 50%, white 100%),
        radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,1;
    -fx-text-fill: black;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
}
like image 106
Mansueli Avatar answered Oct 23 '22 09:10

Mansueli