Issue
I'm trying to show walls on the GUI (fxml) with CSS borders using .getStyleClass().add() but they're not showing up.
I'm using a switch that adds the corresponding style classes to the cells.
The Java code:
private StackPane createCell(int row, int col) {
var cell = new StackPane();
cell.getStyleClass().add("cell");
cell.getStyleClass().add((row + col) % 2 == 0 ? "light": "dark");
for (var i = 0; i < 2; i++) {
var pieceView = new ImageView(pieceImages[i]);
pieceView.visibleProperty().bind(createBindingForPieceAtPosition(i, row, col));
cell.getChildren().add(pieceView);
}
var wallsInDirections = state.checkAllWalls(new Position(row, col));
for (var direction : wallsInDirections) {
switch (direction) {
case UP -> cell.getStyleClass().add("topwall");
case RIGHT -> cell.getStyleClass().add("rightwall");
case DOWN -> cell.getStyleClass().add("bottomwall");
case LEFT -> cell.getStyleClass().add("leftwall");
}
}
return cell;
}
The CSS code:
.cell.light {
-fx-background-color: white;
}
.cell.dark {
-fx-background-color: #F6F6F6;
}
.cell.light:hover, .cell.dark:hover {
-fx-background-color: #FAFA33;
}
.topwall {
border-top: solid black;
}
.rightwall {
border-right: solid black;
}
.bottomwall {
border-bottom: solid black;
}
.leftwall {
border-left: solid black;
}
Pic of the GUI afterwards: result
Solution
The properties border-top
, border-right
, etc. are not valid JavaFX CSS properties.
Referring to the documentation, you set the border using the properties
selector {
-fx-border-color: color ;
-fx-border-width: width ;
}
where color
and width
are either a single value (of type paint and size, respectively) or four values separated by spaces, representing the top, right, bottom, and left borders.
It should also be noted that borders are not typically used this way in JavaFX. The more usual approach is to use "nested backgrounds", where you would do something like this to define a border of width 5 around all four sides:
.cell.light {
-fx-background: white;
}
.cell.dark {
-fx-background: #f6f6f6;
}
.cell {
-fx-background-color: black, -fx-background;
-fx-background-insets: 0, 5;
}
This works by painting two backgrounds, one over the other. The first is black, with no insets; the second is defined by the "looked-up color" -fx-background
, with an inset of 5 pixels on all sides.
My understanding (though I have no direct evidence of this) is that the nested background approach performs better than using explicit borders.
Unfortunately, doing this entirely in an external CSS sheet is very verbose, as you have to handle all 16 possibilities individually (unless I am missing a clever trick):
.cell.light {
-fx-background: white;
}
.cell.dark {
-fx-background: #f6f6f6;
}
.cell {
-fx-background-color: black, -fx-background;
-fx-background-insets: 0, 0;
}
.cell.topwall {
-fx-background-insets: 0, 5 0 0 0;
}
.cell.rightwall {
-fx-background-insets: 0, 0 5 0 0;
}
.cell.bottomwall {
-fx-background-insets: 0, 0 0 5 0;
}
.cell.leftwall {
-fx-background-insets: 0, 0 0 0 5;
}
.cell.topwall.rightwall {
-fx-background-insets: 0, 5 5 0 0;
}
.cell.topwall.bottomwall {
-fx-background-insets: 0, 5 0 5 0;
}
.cell.topwall.leftwall {
-fx-background-insets: 0, 5 0 0 5;
}
.cell.rightwall.bottomwall {
-fx-background-insets: 0, 0 5 5 0;
}
.cell.rightwall.leftwall {
-fx-background-insets: 0, 0 5 0 5;
}
.cell.bottomwall.leftwall {
-fx-background-insets: 0, 0 0 5 5;
}
.cell.topwall.rightwall.bottomwall {
-fx-background-insets: 0, 5 5 5 0;
}
.cell.topwall.bottomwall.leftwall {
-fx-background-insets: 0, 5 0 5 5;
}
.cell.topwall.rightwall.leftwall {
-fx-background-insets: 0, 5 5 0 5;
}
.cell.rightwall.bottomwall.leftwall {
-fx-background-insets: 0, 0 5 5 5;
}
.cell.topwall.rightwall.bottomwall.leftwall {
-fx-background-insets: 0, 5;
}
(using -fx-border-*
is no better).
It may be easier to set the insets programmatically as inline styles:
.cell.light {
-fx-background: white;
}
.cell.dark {
-fx-background: #f6f6f6;
}
.cell {
-fx-background-color: black, -fx-background;
-fx-background-insets: 0, 0;
}
and then
int top, right, bottom, left ;
top=right=bottom=left=0 ;
var wallsInDirections = state.checkAllWalls(new Position(row, col));
for (var direction : wallsInDirections) {
switch (direction) {
case UP -> top=5;
case RIGHT -> right=5;
case DOWN -> bottom=5;
case LEFT -> left=5;
}
}
cell.setStyle(String.format("-fx-background-insets: %d %d %d %d;", top, right, bottom, left));
Answered By - James_D
Answer Checked By - Pedro (JavaFixing Volunteer)