javajavafx-8

Changing non-editable font of TextField with CSS


A (seemingly) simple task: to make non-editable TextFields have a gray font color. Since other style settings for our application are declared in CSS files, I want it to be declared there as well.

My (unsuccessfull) attempt:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FXTextFieldDemo extends Application {

    static TextField nameField;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setScene(createScene());
        primaryStage.setTitle("TextField Demo");
        primaryStage.show();
    }

    private static Scene createScene() {
        VBox root = createRoot();
        Scene scene = new Scene(root);
        scene.getStylesheets().add("styles/styles.css");
        return scene;
    }

    private static VBox createRoot() {
        VBox root = new VBox(10);
        HBox nameRow = createNameRow();
        root.getChildren().add(nameRow);
        root.paddingProperty().setValue(new Insets(10));
        return root;
    }

    private static HBox createNameRow() {
        HBox nameRow = new HBox(10);
        nameRow.setAlignment(Pos.CENTER_LEFT);
        Label nameLabel = new Label("Name");
        nameRow.getChildren().add(nameLabel);
        nameRow.getChildren().add(createNameField());
        return nameRow;
    }

    private static TextField createNameField() {
        nameField = new TextField();
        nameField.textProperty().setValue("Joseph");
        nameField.editableProperty().setValue(false);
        return nameField;
    }

    public static void main(String[] args) {
        launch(args);
    }
}
/* resources/styles/styles.css */

.text-field:editable {
    -fx-text-fill: black;
}

.text-field:not(:editable) {
    -fx-text-fill: gray;
}

The font is black even though the field is not editable.

enter image description here

Java 8.


Solution

  • Note that JavaFX CSS is not equivalent to Web CSS. The former lacks a decent number of features from the latter, such as not(...). See the JavaFX CSS Reference Guide to know what's available1.

    That said, according to the documentation, the pseudo-class for non-editable text fields is :readonly. So, the following should work:

    .text-field:readonly {
      -fx-text-fill: gray;
    }
    

    But you mention:

    However, if I declare, for example, "Make the font color of text fields in a panel with id X black/red/blue etc.", (which is exactly what's happening in our app), it would be considered more specific and override my readonly selector in that panel, even for non-editable fields. Since JavaFX does not support :not, I'm not sure how I can resolve it (unless I duplicate the setting in each such scenario for each such panel)

    It's not pretty, but you can avoid some duplication by selecting #id:readonly as well when making the text fill gray. For example:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class App extends Application {
    
      private static final String STYLESHEET =
          """
          .root {
            -fx-alignment: center;
            -fx-spacing: 10px;
            -fx-padding: 10px;
          }
    
          .text-field:readonly,
          #fieldOne:readonly,
          #fieldTwo:readonly,
          #fieldThree:readonly,
          #fieldFour:readonly {
            -fx-text-fill: gray;
          }
    
          #fieldOne {
            -fx-text-fill: orange;
          }
    
          #fieldTwo {
            -fx-text-fill: red;
          }
    
          #fieldThree {
            -fx-text-fill: blue;
          }
    
          #fieldFour {
            -fx-text-fill: green;
          }
          """;
    
      @Override
      public void start(Stage primaryStage) {
        var root = new VBox(
            createTextField("fieldOne", "TextField #1 (Editable)", true),
            createTextField("fieldTwo", "TextField #2 (Read-Only)", false),
            createTextField("fieldThree", "TextField #3 (Editable)", true),
            createTextField("fieldFour", "TextField #4 (Read-Only)", false));
    
        var scene = new Scene(root);
        scene.getStylesheets().add("data:text/css," + STYLESHEET);
    
        primaryStage.setScene(scene);
        primaryStage.show();
      }
    
      private TextField createTextField(String id, String text, boolean editable) {
        var field = new TextField(text);
        field.setId(id);
        field.setEditable(editable);
        return field;
      }
    }
    

    That works on JavaFX 25.

    Note I used a text block and a data URI to add the stylesheet to the scene. Text blocks were added to Java in version 15. And the ability to use data URIs to add stylesheets was added in JavaFX 17. And JavaFX 17 requires Java 11+. So, to run the example you need to use at least Java 11 and JavaFX 17. Though you can of course modify the example for earlier versions.


    1. The JavaFX CSS Reference Guide also lists which CSS properties and pseudo-classes are available for the types in the core JavaFX library. Though note for controls, some of the properties and pseudo-classes may be skin-specific, which means if you use a custom skin such properties and pseudo-classes may not work or work differently.