javajavafxscenebuilder

Is this mistake of JavaFX SceneBuilder?


In JavaFX SceneBuilder I wanted to style border of Pane to be only on the right side, so I put in the JavaFX CSS style box this: -fx-border-style: none solid none none , but it did whole border visible. Then I find out, that if I put "none" as a first value, the border will be allways whole.

Can someone tell me please , how to fix it, or if there's some other way to do that?

JavaFX CSS view


Solution

  • This is a bug in JavaFX 21 CSS border rule processing

    This appears to be a bug in CSS border handling in JavaFX 21. It is not an issue in SceneBuilder or FXML, or the core border logic of JavaFX.

    As noted by James in comments:

    Both the implementation (doesn't work correctly if the top border is set to "none') and the documentation (does not describe sets of four values for four different sides for border-style) appear to have bugs.

    You might wish to file a bug report on this. If you do so, you can link back to this issue and add a comment to this answer (or edit it) to add a reference to the related bug report.

    The issue is that, if you specify all four border styles in a CSS rule and the first border style is set to none, then the border styles do not take effect -> all borders show.

    Replicating the Issue

    The code below replicates the issue (tested on JavaFX 21).

    1. The first box, is rendered incorrectly, showing all four borders when only the right border should show.

      -fx-border-style: none solid none none;
      
    2. The second box renders correctly, the top and right borders show and the other borders do not.

      -fx-border-style: solid solid none none;
      
    3. The third box renders correctly, only the right border shows. However, this was implemented in code rather than CSS. As a work-around for your issue you can write your border styles in code rather than using CSS.

      Border codedBorder = new Border(
              new BorderStroke(
                      Color.BLACK, Color.RED, Color.GREEN, Color.BLUE,
                      BorderStrokeStyle.NONE, BorderStrokeStyle.SOLID, BorderStrokeStyle.NONE, BorderStrokeStyle.NONE,
                      CornerRadii.EMPTY,
                      new BorderWidths(10, 8, 6, 4),
                      Insets.EMPTY
              )
      );
      

    demos

    Example Code: BrokenBorders.java

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.layout.*;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    public class BrokenBorders extends Application {
        private static final String CSS = "data:text/css,";
        private static final String CUSTOM_BORDER_CSS = CSS + // language=CSS
                """            
                .broken-border {
                    -fx-border-style: none solid none none;
                    -fx-border-color: black red green blue;
                    -fx-border-width: 10 8 6 4;
                    -fx-background-color: lightgreen;
                }
    
                .working-border {
                    -fx-border-style: solid solid none none;
                    -fx-border-color: black red green blue;
                    -fx-border-width: 10 8 6 4;
                    -fx-background-color: lightblue;
                }
                
                .root {
                    -fx-font-size: 40px;
                }
                """;
    
        @Override
        public void start(Stage stage) {
            stage.setScene(new Scene(createLayout()));
            stage.show();
        }
    
        private static VBox createLayout() {
            StackPane brokenBorderedPane = new StackPane(new Label("1"));
            brokenBorderedPane.getStyleClass().add("broken-border");
            brokenBorderedPane.setPrefSize(100, 100);
    
            StackPane workingBorderedPane = new StackPane(new Label("2"));
            workingBorderedPane.getStyleClass().add("working-border");
            workingBorderedPane.setPrefSize(100, 100);
    
            StackPane codedBorderPane = new StackPane(new Label("3"));
            Border codedBorder = new Border(
                    new BorderStroke(
                            Color.BLACK, Color.RED, Color.GREEN, Color.BLUE,
                            BorderStrokeStyle.NONE, BorderStrokeStyle.SOLID, BorderStrokeStyle.NONE, BorderStrokeStyle.NONE,
                            CornerRadii.EMPTY,
                            new BorderWidths(10, 8, 6, 4),
                            Insets.EMPTY
                    )
            );
            codedBorderPane.setBackground(Background.fill(Color.LIGHTPINK));
            codedBorderPane.setBorder(codedBorder);
            codedBorderPane.setPrefSize(100, 100);
    
            VBox layout = new VBox(
                    10,
                    brokenBorderedPane,
                    workingBorderedPane,
                    codedBorderPane
            );
            layout.getStylesheets().add(CUSTOM_BORDER_CSS);
            layout.setPadding(new Insets(10));
    
            return layout;
        }
    
        public static void main(String[] args) {
            launch();
        }
    }