I want to write code, that can do following:
Create 9*9 GridPane board that filled with 81 StackPanes.
When I click on cell (StackPane) I want to change color of background StackPane to blue if it is white. If I have alreday had blue cell on board, than old cell becomes white and new one becomes blue. So, In anytime I can only have 0 or 1 colored cell.
I tried to use Array[9][9] of StackPanes but I failed because even if I declare this array before "public void start(Stage stage)" I fail to use each StackPane in lambda expressions or innerclasses.
I tried to use variable that tells if I have 1 colored cell or not and coordinates of colored cell in 2 variables. When I click on cells I see that coordinates works, but I can't change color of Panes.
package com.example.testingcolorswitching;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
boolean isAnyPaneSelected = true; // true if 1 of cells is selected
int xColored = 1; // holds X coordinate of Selected cell;
int yColored = 1; // holds Y coordinate of Selected cell;
int columnSize = 50;
int rowSize = 50;
@Override
public void start(Stage stage) throws IOException {
GridPane grid = new GridPane();
for (int x = 0; x < 9; x++)
for (int y = 0; y < 9; y++){
StackPane cell = new StackPane();
cell.setMinSize(columnSize, rowSize);
// I would like that this was checked every time I click on GridPane
if (isAnyPaneSelected && x == xColored && y == yColored)
cell.setStyle("-fx-background-color:BLUE");
final int thisX = x;
final int thisY = y;
cell.setOnMouseClicked((event) -> {
if(isAnyPaneSelected) {
if (thisX == xColored & thisY == yColored)
isAnyPaneSelected = false;
else {
xColored = thisX;
yColored = thisY;
}
} else {
isAnyPaneSelected = true;
xColored = thisX;
yColored = thisY;
}
System.out.println(isAnyPaneSelected + " " + xColored + " " + yColored);
});
grid.add(cell, x, y);
}
// code below is not important. This code adds lines on screen.
for (int i = 0; i <= 9; i++){
Line line = new Line(0.0, rowSize * i, rowSize * 9, rowSize * i);
line.setManaged(false);
if (i % 3 == 0)
line.setStrokeWidth(3);
else
line.setStrokeWidth(1);
grid.getChildren().add(line);
line = new Line(columnSize * i, 0.0, columnSize * i, columnSize * 9);
line.setManaged(false);
if (i % 3 == 0)
line.setStrokeWidth(3);
else
line.setStrokeWidth(1);
grid.getChildren().add(line);
}
Scene scene = new Scene(grid, 600, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
One way to fix this is by including the cell to an ObjectProperty and then you can add listener to add/remove the style of the cell.
Something like:
// Create an object property to hold the selected cell.
private ObjectProperty<StackPane> selectedCell = new SimpleObjectProperty<>();
// Add listener to the object property to toggle the style
selectedCell.addListener((obs, old, val) -> {
if (old != null) {
old.setStyle(null);
}
if (val != null) {
val.setStyle("-fx-background-color:BLUE");
}
});
// On click, set the cell to the objectproperty.
cell.setOnMouseClicked((event) -> {
// If already selected, then remove it.
if (cell.equals(selectedCell.get())) {
selectedCell.set(null);
} else {
selectedCell.set(cell);
}
});
Having said that, I also suggest to alter the logic of showing the lines. It really doesnt seem right adding unmanaged lines and positioning them in GridPane. I included a logic to add borders to each cell to get the final result (below image).
Below is a complete demo of both the changes:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.io.IOException;
public class CellColouringDemo extends Application {
public static final String CSS = "data:text/css," + // language=CSS
"""
.outer-box{
-fx-border-style:solid;
-fx-border-color:black;
-fx-border-width:3px 0px 0px 3px;
}
.cell{
-fx-border-style:solid;
-fx-border-color:black;
-fx-border-width:0px 1px 1px 0px;
}
.cell-right{
-fx-border-width:0px 3px 1px 0px;
}
.cell-bottom{
-fx-border-width:0px 1px 3px 0px;
}
.cell-corner{
-fx-border-width:0px 3px 3px 0px;
}
.selected-cell{
-fx-background-color: blue;
}
""";
private int columnSize = 50;
private int rowSize = 50;
private ObjectProperty<StackPane> selectedCell = new SimpleObjectProperty<>();
record Pos(int col, int row){}
@Override
public void start(Stage stage) throws IOException {
selectedCell.addListener((obs, old, val) -> {
if (old != null) {
old.getStyleClass().remove("selected-cell");
Pos pos = (Pos) old.getUserData();
// Do further logic with previous cell position
}
if (val != null) {
val.getStyleClass().add("selected-cell");
Pos pos = (Pos) val.getUserData();
// Do further logic with current cell position
}
});
GridPane grid = new GridPane();
grid.getStyleClass().add("outer-box");
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
StackPane cell = new StackPane();
cell.setUserData(new Pos(col, row));
cell.getStyleClass().add("cell");
setCellStyle(cell, row, col);
cell.setMinSize(columnSize, rowSize);
cell.setOnMouseClicked((event) -> {
if (cell.equals(selectedCell.get())) {
selectedCell.set(null);
} else {
selectedCell.set(cell);
}
});
grid.add(cell, col, row);
}
}
StackPane root = new StackPane(new Group(grid));
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
private void setCellStyle(StackPane cell, int row, int col) {
boolean isBottom = (row + 1) % 3 == 0;
boolean isRight = (col + 1) % 3 == 0;
if (isBottom && isRight) {
cell.getStyleClass().add("cell-corner");
} else if (isBottom) {
cell.getStyleClass().add("cell-bottom");
} else if (isRight) {
cell.getStyleClass().add("cell-right");
}
}
public static void main(String[] args) {
launch();
}
}