'Selection Border in JavaFX. Wanting to change default behavior with CSS

I am using validation that puts a red border around my TextFields

nameTF.setStyle("-fx-text-box-border: red;");

And set it back to default using

nameTF.setStyle("-fx-text-box-border: #D3D3D3;");

I am trying to use a CSS: Hoping to increase the width of the border so that the focused border is inside/or outside of my red validation border (so both show). Now when I click on a cell the selection border replaces the current border color and width...?

.text-input {
 -fx-border-color: #D3D3D3;
-fx-border-width: 3 ;
 }

.text-field:focused{
 -fx-border-color: blue ;
 -fx-border-width: 1 ;
}

Also, I have noticed that by using

.text-input {
 -fx-border-color: #D3D3D3;
-fx-border-width: 3 ;
 }

The red nameTF.setStyle("-fx-text-box-border: red;"); doesn't show up at all?

What's Going on and what I am hoping for:

image of current behavior vs desired behavior

Thanks!



Solution 1:[1]

Suggested Approach

I suggest that you look at how controlsfx accomplishes this and either use that library or learn from their approach by studying their source code (if appropriate).

I checked the controlsfx validation code which applies style classes for validation feedback. The sample usage provides feedback via css drop shadow effects. This is a straightforward solution and easier than customizing backgrounds.

I tried this using a drop shadow effect to mark an invalid text field and it appeared to work well for me.

For complex controls other than text fields (like range sliders where the range tick text is also shadowed), the drop shadow approach looks a little weird, so perhaps for those, you may want to do something more sophisticated.

Example application

This sets or unsets an invalid style class on a field as appropriate.

.invalid {
    -fx-effect: dropshadow(three-pass-box, tomato, 3, 0.8, 0, 0);
}

A drop shadow effect is triggered when the invalid style class is active, to provide the user with an indication that the field is invalid.

Refer to the CSS effect documentation to understand the settings for the dropshadow effect.

States

Invalid value with focus:

Invalid value with focus

Invalid value without focus:

Invalid value without focus

Valid value with focus:

Valid value with focus

Valid value without focus:

Valid value without focus

This sample is not meant as a robust validation framework, it is just an example to show how you might provide validation feedback styles for a text field.

The example operates based on a standard style class, but you could use a psuedo-class instead if you wanted to look into that. For example, similar to the :focus psuedo-class, you could implement the CSS :invalid psuedo-class.

import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ValidatorApp extends Application {

    public static final String CSS = "data:text/css," + // language=CSS
            """
            .invalid {
                -fx-effect: dropshadow(three-pass-box, tomato, 3, 0.8, 0, 0);
            }
            """;

    @Override
    public void start(Stage stage) throws Exception {
        TextField integerTextField = new TextField();

        IntegerTextFieldValidator validator = new IntegerTextFieldValidator();

        validator.validate(integerTextField);
        integerTextField.textProperty().addListener(o -> 
                validator.validate(integerTextField)
        );

        VBox layout = new VBox(10,
                labelField("Enter an integer: ", integerTextField),
                labelField("Enter any value:  ", new TextField("foobar"))
        );
        layout.setPadding(new Insets(10));

        Scene scene = new Scene(layout);
        scene.getStylesheets().add(CSS);

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

    HBox labelField(String labelText, Node field) {
        HBox box = new HBox(10, new Label(labelText), field);
        box.setAlignment(Pos.BASELINE_LEFT);

        return box;
    }

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

import javafx.scene.Node;
import javafx.scene.control.TextField;

public class IntegerTextFieldValidator {
    private static final String INVALID_STYLECLASS = "invalid";

    public void validate(TextField textField) {
        String text = textField.getText();

        if (text == null) {
            setInvalid(textField);
            return;
        }

        try {
            Integer.parseInt(text);
            setValid(textField);
        } catch (NumberFormatException e) {
            setInvalid(textField);
        }
    }

    private void setInvalid(Node node) {
        if (!node.getStyleClass().contains(INVALID_STYLECLASS)) {
            node.getStyleClass().add(INVALID_STYLECLASS);
        }
    }

    private void setValid(Node node) {
        node.getStyleClass().removeAll(INVALID_STYLECLASS);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1