Mark Meyers
Mark Meyers

Reputation: 563

JavaFX Panes: Making a 1-pixel Border

I am struggling and will no doubt have to buy a manual to understand JavaFX CSS, or the JavaFX CSS Reference Guide...

But what I want to do is make a 1 pixel border around some of my nodes, such as a TableView or ScrollPane on one side, and a GridPane or ScrollPane on the other side of a Scene I'm working on. I say "or", because I'll take either one. Ha ha! (And regardless of whether it's filled up with controls or not) Cheers.

Upvotes: 1

Views: 5181

Answers (1)

James_D
James_D

Reputation: 209418

Here's a test sample for demoing some ways to make borders in CSS:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class TableViewBorderTest extends Application {

    @Override
    public void start(Stage primaryStage) {

        HBox root = new HBox(5);
        TableView<String> table = new TableView<>();
        table.getColumns().add(new TableColumn<String, String>("Data"));

        ScrollPane scroller = new ScrollPane();
        scroller.setMinWidth(200);

        GridPane grid = new GridPane();
        grid.setMinWidth(200);
        grid.getStyleClass().add("grid");

        root.getChildren().addAll(table, scroller, grid);
        // padding so we can easily see borders:
        root.setPadding(new Insets(10));

        Scene scene = new Scene(root);
        scene.getStylesheets().add("border-table.css");
        primaryStage.setScene(scene);
        primaryStage.show();

        // remove focus from tables
        root.requestFocus();

    }

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

You can run this with just an empty border-table.css file. The first thing to notice is that the table view and scroll pane already have a 1-pixel border in a medium gray (slightly darker than the default background):

enter image description here

This border is defined (see how later) in the default stylesheet, modena.css, and is set to a "looked-up color" called -fx-box-border. Just for demo purposes, to make it easier to see the border, you can reassign this color with the following in border-table.css:

.root {
    -fx-box-border: red ;
}

This gives

enter image description here

Notice that the table header and column headers use the same color as a border. If you compare to the first image, you can probably see the borders more clearly in it too.

To replace the border in the table view and scroll pane, you can define borders in the css file. The simplest, but not necessarily the best, way is to define a -fx-border-color. Replace the border-table.css with the following:

.table-view, .scroll-pane {
    -fx-border-color: green ;
}

The default value of -fx-border-width is 1, so this gives a one-pixel green border:

enter image description here

For the GridPane, note that it has no default border and also has no style class (see CSS docs). In the Java code, I defined a style class for it:

grid.getStyleClass().add("grid");

so we can add the same border just by adding this style class to the selector:

.table-view, .scroll-pane, .grid {
    -fx-border-color: green ;
}

enter image description here

It's interesting to note that the default stylesheet, modena.css doesn't use -fx-border-... properties at all. Instead, it creates borders by creating two (or more) "nested backgrounds". For example, it has:

.scroll-pane,
.split-pane,
.list-view,
.tree-view,
.table-view, 
.tree-table-view,
.html-editor {
    -fx-background-color: -fx-box-border, -fx-control-inner-background;
    -fx-background-insets: 0, 1;
    -fx-padding: 1;
}

This defines, for TableView, ScrollPane, and other similar controls, two background colors. The first (so painted first, i.e. underneath) is a solid background fill in the looked-up color -fx-box-border, and the second (painted on top) is a solid background fill in the looked-up color -fx-control-inner-background. The first background fill has 0 insets, and the second has a 1-pixel inset, meaning that the bottom background fill will be visible for 1 pixel width around the edge of the control. (The padding ensures nothing is placed over this effective 1-pixel border.)

I haven't tested this at all, but it's claimed that the nested background approach is more efficient than drawing borders (I guess the native graphics is vey fast at rectangular background fills).

So you could use the same approach and replace border-table.css with

.table-view, .scroll-pane {
    -fx-background-color: blue, -fx-control-inner-background ;
    -fx-background-insets: 0, 1 ;
}

.grid {
    -fx-background-color: blue, -fx-background ;
    -fx-background-insets: 0, 1 ;
    -fx-padding : 1 ;
}

enter image description here

And you could even introduce a looked-up color to make it easier to modify the style of the app:

.root {
    -my-border: blue ;
}

.table-view, .scroll-pane {
    -fx-background-color: -my-border, -fx-control-inner-background ;
    -fx-background-insets: 0, 1 ;
}

.grid {
    -fx-background-color: -my-border, -fx-background ;
    -fx-background-insets: 0, 1 ;
    -fx-padding : 1 ;
}

(this has exactly the same effect as the previous, but there is just one place to change the color definition instead of two).

Note these last two versions override the default focus border, which is implemented in the default style sheet by defining a different set of background colors when the controls are focused. You can restore these with:

.root {
    -my-border: blue ;
}

.table-view, .scroll-pane {
    -fx-background-color: -my-border, -fx-control-inner-background ;
    -fx-background-insets: 0, 1 ;
}

.table-view:focused, .scroll-pane:focused {
    -fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background; 
    -fx-background-insets: -1.4, -0.3, 1;
    -fx-background-radius: 2, 0, 0;
}

.grid {
    -fx-background-color: -my-border, -fx-background ;
    -fx-background-insets: 0, 1 ;
    -fx-padding : 1 ;
}

which references two more looked-up colors, -fx-faint-focus-color and -fx-focus-color (the first is just a partially-transparent version of the second); of course you could redefine these for your own focus colors if you chose.

Upvotes: 4

Related Questions