How to auto-resize main pane after inserting dynamically GridPane - java

I have AnchorPane which is the Main that has all other panes.
Inside it I have the board area which is AnchorPane and area for the dice which is also AnchorPane.
This is how it looks on scene builder:
inside the board Area which is AnchorPane I am creating dynamically (according to the user request) the size of the board (5x5, 6x6, 7x7 or 8x8):
GridPane boardGame;
#FXML
Button dice;
#FXML
AnchorPane boardArea;
#FXML
AnchorPane boardGameAnchorPane;
#FXML
AnchorPane dicePane;
public void CreateBoard()
{
int boardSize = m_Engine.GetBoard().GetBoardSize();
int num = 1;
int maxColumns = m_Engine.GetNumOfCols();
int maxRows = m_Engine.GetNumOfRows();
boardGame = new GridPane();
//boardGame.setAlignment(Pos.CENTER);
for(int row = maxRows - 1; row >= 0 ; row--)
{
for(int col = 0; col < maxColumns ; col++)
{
StackPane stackPane = new StackPane();
stackPane.setMaxSize(SIZE_OF_CELL, SIZE_OF_CELL);
stackPane.setMinSize(SIZE_OF_CELL, SIZE_OF_CELL);
if((col + row) % 2 != 0)
{
stackPane.getStyleClass().add("oddCellBorder");
}
else
{
stackPane.getStyleClass().add("evenCellBorder");
}
Label label = new Label(String.valueOf(num));
StackPane.setAlignment(label, Pos.BOTTOM_LEFT);
stackPane.getChildren().add(label);
boardGame.add(stackPane, col, row);
num++;
}
}
this.fixBoardGameSize();
boardGame.setGridLinesVisible(true);
// boardGame.autosize();
boardArea.getChildren().add(boardGame);
//ImageView imageView = ImageUtils.getImageView("diceTransprntBack3D.png");
//dice.setGraphic(imageView);
// Image img1 = new Image(getClass().getResourceAsStream("about.png"));
}
My problem starts from board size of 7x7 or 8x8.
The board area spread to the dice area:
I tried to fix the sizes manually with this function:
private void fixBoardGameSize()
{
boardArea.setMinHeight(boardGame.getHeight());
boardArea.setMaxHeight(boardGame.getHeight());
boardArea.setMinWidth(boardGame.getWidth());
boardArea.setMaxWidth(boardGame.getWidth());
boardGameAnchorPane.setPrefWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
boardGameAnchorPane.setMinWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
boardGameAnchorPane.setMaxWidth(boardArea.getWidth() + dicePane.getWidth() + 500 );
dicePane.setLayoutX(boardArea.getLayoutX() + boardArea.getWidth() + 1000);
}
But with no success.
I tried also to play with the anchor pain constraint with no success.
I searched but can't find something that can help me so I am asking here if someone knows how I can keep the two anchor panes separated.

Based on your description, using the same anchor panes, and given a fixed size of your application, what you can do is:
First, set a maximum size for the boardArea, a margin for the grid to be correctly displayed on that pane
// Maximum fixed size you give to boardArea
public static final double MAX_SIZE = 600;
// Margin to border
public static final double MARGIN = 25;
And now, in your fixBoardGameSize() method, resize properly the grid:
private void fixBoardGameSize(){
// Listener, since boardGame dimensions are determined after the stage is shown
ChangeListener<Number> resize = (ov, v, v1) -> {
double scale = Math.min((MAX_SIZE - 2d*MARGIN) / boardGame.getWidth(),
(MAX_SIZE - 2d*MARGIN) / boardGame.getHeight());
boardGame.setScaleX(scale);
boardGame.setScaleY(scale);
boardGame.setTranslateX((MAX_SIZE - boardGame.getWidth()) / 2d);
boardGame.setTranslateY((MAX_SIZE - boardGame.getHeight()) / 2d);
};
boardGame.widthProperty().addListener(resize);
boardGame.heightProperty().addListener(resize);
}
Now you can display games with any number of rows and columns. It will always fit in the board, and it will be properly centered.

Related

BorderPane not showing other pane except for center Javafx

I have this piece of code here that has a borderpane as a parent and 2 other panes, one is a minesweeper game and the other is an empty pane with a black background, the minesweeper game is set to be in the center of the borderpane and the empty pane is set to be in the bottom. Right now when I run the code, it will only show the minesweeper game and not the empty pane (can be seen in the image). How do I make it show the empty pane at the bottom of the borderpane?
(Layout as of right now)
package project;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MinesweeperApp extends Application {
private static final int TILE_SIZE = 40;
private static final int W = 800;
private static final int H = 800;
private static final int X_TILES = W / TILE_SIZE;
private static final int Y_TILES = H / TILE_SIZE;
private Tile[][] grid = new Tile[X_TILES][Y_TILES];
// private ArrayList[][] grid = new ArrayList[X_TILES][Y_TILES];
private Scene scene;
private Parent createContent() {
BorderPane root = new BorderPane();
Pane middlepane = new Pane();
Pane lowerPane = new Pane();
lowerPane.setStyle("-fx-background-color: black;");
root.setTop(lowerPane);
root.setBottom(middlepane);
middlepane.setPrefSize(W, H);
for (int y = 0; y < Y_TILES; y++) {
for (int x = 0; x < X_TILES; x++) {
Tile tile = new Tile(x, y, Math.random() < 0.2);
grid[x][y] = tile;
middlepane.getChildren().add(tile);
}
}
for (int y = 0; y < Y_TILES; y++) {
for (int x = 0; x < X_TILES; x++) {
Tile tile = grid[x][y];
if (tile.hasBomb)
continue;
long bombs = getNeighbors(tile).stream().filter(t -> t.hasBomb).count();
if (bombs > 0)
tile.text.setText(String.valueOf(bombs));
}
}
return root;
}
private List<Tile> getNeighbors(Tile tile) {
List<Tile> neighbors = new ArrayList<>();
// ttt
// tXt
// ttt
int[] points = new int[] {
-1, -1,
-1, 0,
-1, 1,
0, -1,
0, 1,
1, -1,
1, 0,
1, 1
};
for (int i = 0; i < points.length; i++) {
int dx = points[i];
int dy = points[++i];
int newX = tile.x + dx;
int newY = tile.y + dy;
if (newX >= 0 && newX < X_TILES
&& newY >= 0 && newY < Y_TILES) {
neighbors.add(grid[newX][newY]);
}
}
return neighbors;
}
private class Tile extends StackPane {
private int x, y;
private boolean hasBomb;
private boolean isOpen = false;
private Rectangle border = new Rectangle(TILE_SIZE - 2, TILE_SIZE - 2);
private Text text = new Text();
public Tile(int x, int y, boolean hasBomb) {
this.x = x;
this.y = y;
this.hasBomb = hasBomb;
border.setStroke(null);
border.setFill(Color.NAVY);
text.setFont(Font.font(18));
text.setText(hasBomb ? "X" : "");
text.setVisible(false);
getChildren().addAll(border, text);
setTranslateX(x * TILE_SIZE);
setTranslateY(y * TILE_SIZE);
setOnMouseClicked(e -> open());
}
public void open() {
System.out.println("clicked");
System.out.println("x: " + this.x + " " + "y: " + this.y);
if (isOpen){
return;
}
if (hasBomb) {
System.out.println("Game Over");
scene.setRoot(createContent());
return;
}
isOpen = true;
text.setVisible(true);
border.setFill(Color.RED);
if (text.getText().isEmpty()) {
getNeighbors(this).forEach(Tile::open);
}
}
}
#Override
public void start(Stage stage) throws Exception {
scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Absent children, an empty Pane has zero width and height. Try adding non-empty content to the top and bottom to see the effect:
root.setTop(new Label("top"));
root.setCenter(middlepane);
root.setBottom(new Label("bottom"));
As an aside, your original root.setBottom(middlepane) may be misleading.
Setting Constraints
Set size constraints on a pane to “see” a Pane which has no content.
This will make the pane take up space in the layout.
To make the app behave as you desire, configure the pane's:
min width or height, OR
pref width or height, OR
any combination of those.
For example, to set a min height for the top pane in a border pane:
BorderPane borderPane = new BorderPane();
Pane top = new Pane();
top.setMinHeight(15);
borderPane.setTop(top);
Hiding Content
Or you could put some content in the pane and make it invisible until it is needed, while still reserving the space required to display it.
For example:
Label header = new Label("header");
header.setVisible(false);
Pane top = new Pane(header);
borderPane.setTop(top);
This works because things in the scene graph which are not visible still take up layout space, even though they are not displayed, unless you also call setManaged(false).
For the specific case of a label, you wouldn’t need the visibility setting, you could just set the label to an empty string and it would still take up room, even though nothing would be shown.
For myself, I try to avoid sizing hints where I can, so I would use an empty label. Or, set the visibility, for a more complex pane. That way I let the layout engine calculate the appropriate amount of space to reserve.
Spacing Content
This doesn't apply in your case with border pane, but for other layouts like HBox and VBox you might want to push a node all the way right or to the bottom of the layout. You can do that by adding an empty pane before the last node and setting a layout constraint on the pane like this:
HBox.setHgrow(spacer, Priority.ALWAYS);
This technique is explained more in the answer to:
How to align a button right in JavaFX?

How can I scale list of rectangles or GridPane in JavaFX to window Width?

Im trying to make lists of rectangles in matrix like manner, I would want them to scale depending on amount of rectangles in row to always fit to the fixed size of window. End of row is little cut or couple of them are outside of border, depending on amount of rectangles.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final int size = 80;
stage.setWidth(1040);
stage.setHeight(920);
final double cellDimension = (stage.getWidth() / size) - 1;
stage.setScene(new Scene(render(20, size, cellDimension), Color.WHITE));
stage.show();
}
public static Region render(int generations, int size, double cellDimension) {
VBox results = new VBox(0);
Random random = new Random();
for (int y = 0; y < generations; y++) {
HBox hBox = new HBox(0);
hBox.getChildren().addAll(IntStream.range(0, size).
mapToObj(idx -> random.nextInt(2)).map(item -> createAppropriateRectangle(item == 1, cellDimension)).
collect(Collectors.toList()));
results.getChildren().add(hBox);
}
results.setSnapToPixel(true);
return results;
}
private static Rectangle createAppropriateRectangle(boolean state, double cellDimension) {
Rectangle rectangle = new Rectangle(cellDimension, cellDimension);
rectangle.setStroke(Color.GRAY);
if (state) {
rectangle.setFill(Color.WHITE);
}
return rectangle;
}
}
What am I doing wrong ? If something is unclear please let me know, thanks :)
for list of 90 values, up is original, bottom shows how much was outside of border
EDIT:
Made mwe using #DaveB stripped example
Building a MRE would probably have shown you the answer fairly quickly. It looks like using a GridPane was just an attempt to make the rectangles fit, but GridPane potentially adds complexity that may obscure the answer. So I went with a VBox holding a bunch of HBoxes. That handles the relative positioning of the elements without any fuss.
You didn't include the Generator class, meaning your code was un-runnable, so I've stripped it down to just the mechanics of putting some random black and white squares on the screen, and dumped everything else:
public class Squares extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final int size = 80;
stage.setWidth(1040);
stage.setHeight(920);
final double cellDimension = (stage.getWidth() / size) - 1;
stage.setScene(new Scene(render(20, size, cellDimension), Color.WHITE));
stage.show();
}
public static Region render(int generations, int size, double cellDimension) {
VBox results = new VBox(0);
Random random = new Random();
for (int y = 0; y < generations; y++) {
HBox hBox = new HBox(0);
hBox.getChildren().addAll(IntStream.range(0, size).mapToObj(idx -> random.nextInt(2)).map(item -> createAppropriateRectangle(item == 1, cellDimension)).collect(Collectors.toList()));
results.getChildren().add(hBox);
}
return results;
}
private static Rectangle createAppropriateRectangle(boolean state, double cellDimension) {
Rectangle rectangle = new Rectangle(cellDimension, cellDimension);
rectangle.setStroke(Color.GRAY);
if (state) {
rectangle.setFill(Color.WHITE);
}
return rectangle;
}
}
It looks like you need to allow 1 extra pixel per rectangle to make the math work properly. Also, it seems to work only when the stage width divides nicely by the number of rectangles per row - there's probably some mechanics of partial pixels that comes into play here. You could probably find a rounding policy that would work when the row size is not an even divisor of the stage width.
You can bind it to stage or parent width, height property.
Binding is one of the most powerful concepts of JavaFX.

how to fix custom timeline problem JavaFx?

I have seen this timeline build here then I have tried to make I timeline using hbox and sperators and labels, but I fall in a representation errors
the following controller allows me to build this timeline this above nodes
public class GuiMainController implements Initializable {
#FXML
private BorderPane borderPane;
public void getUnit1(){
//This will fill the Time Line Unit in your Gui
VBox vbox = new VBox();
HBox hbox = new HBox();
hbox.setManaged(false);
for (int i =0; i < 24; i++) {
//For each unit create a new instance
AnchorPane anchorPane = new AnchorPane();
anchorPane.setPrefHeight(30);
anchorPane.setPrefWidth(66);
Separator separatorR = new Separator();
separatorR.setLayoutX(66);
separatorR.setLayoutY(14);
separatorR.setOrientation(Orientation.VERTICAL);
separatorR.setPrefHeight(15);
Separator separatorM = new Separator();
separatorM.setLayoutY(14);
separatorM.setOrientation(Orientation.VERTICAL);
separatorM.setPrefHeight(15);
Separator separatorL = new Separator();
separatorL.setLayoutX(33);
separatorL.setLayoutY(20);
separatorL.setOrientation(Orientation.VERTICAL);
separatorL.setPrefHeight(10);
separatorL.setPrefWidth(6);
Label lblT = new Label();
lblT.setText(i+"h");
Label lblTT = new Label();
lblTT.setLayoutX(66);
lblTT.setText((i+1)+"h");
anchorPane.getChildren().addAll(separatorL,lblT,separatorM,separatorR,lblTT);
hbox.getChildren().add(anchorPane);
}
borderPane.getChildren().add(hbox);
}
public void initialize(URL arg0, ResourceBundle arg1) {
getUnit1();
}
}
How can I made this timeline, any help are appreciated to get some like this
Imho you shouldn't get any layouts involved here, since this way you rely on the exact implementation of those layouts. Furthermore I'm not sure Seperator should be involved here. You could use a Path though and calculate the positions of all the lines on the axis.
private static Label createLabel(String text, double layoutY, double majorTickDistance, int index) {
Label label = new Label(text);
// label width should be exactly the distance between ticks
label.setMaxWidth(Region.USE_PREF_SIZE);
label.setMinWidth(Region.USE_PREF_SIZE);
label.setPrefWidth(majorTickDistance);
// center text
label.setAlignment(Pos.BASELINE_CENTER);
label.setLayoutY(layoutY);
// align center of the label with major tick
label.setLayoutX((index + 0.5) * majorTickDistance);
return label;
}
/**
* #param majorTickDistance the width between major ticks
* #param minorTicks the number of minor ticks between major ticks
* #param majorTickHeight the height of major tick markers
* #param minorTickHeight the height of minor tick markers
* #param firstText the first text for the first major tick
* #param text the text for other ticks
*/
private static Pane createAxis(double majorTickDistance, int minorTicks,
double majorTickHeight, double minorTickHeight, String firstText, String... text) {
final double labelY = majorTickHeight + 3;
final double minorTickDistance = majorTickDistance / minorTicks;
// initialize path with first major tick and horizontal line
Path path = new Path(
new MoveTo(0, 0), new HLineTo(majorTickDistance * text.length),
new MoveTo(0, 0), new VLineTo(majorTickHeight)
);
// add path and first label
Pane pane = new Pane(path, createLabel(firstText, labelY, majorTickDistance, -1));
for (int i = 0; i < text.length; i++) {
double offset = i * majorTickDistance;
// add minor ticks
for (int j = 1; j < minorTicks; j++) {
double x = offset + j * minorTickDistance;
path.getElements().addAll(new MoveTo(x, 0), new VLineTo(minorTickHeight));
}
offset += majorTickDistance;
// add major tick at the end of the current interval
path.getElements().addAll(new MoveTo(offset, 0), new VLineTo(majorTickHeight));
// add label below major tick
pane.getChildren().add(createLabel(text[i], labelY, majorTickDistance, i));
}
pane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return pane;
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(new StackPane(createAxis(30, 5, 10, 5, "1", "2", "3", "4", "5", "6")), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}

Display a 2D grid of images or shapes in java

I made a 2D grid of ASCII characters before in which i was able to move an ascii character around. Now i want to take it to the next level by making a "visualised" version of it in a javafx window. I have tried making 2 image objects, one with a black square inside of it, and one with a white one and then putting those 2 objects multiple times inside a 2D grid like this:
Image[][] Grid = {
{B,W,B,B,B,B,B,B,B,B,B},
{B,B,B,B,W,W,W,B,B,B,B},
{B,B,B,B,B,B,B,B,B,B,B}
};
The problem is that the only way i know how to display them, is by making an imageview object for each index and if i were to say, want a 25X25 grid, that would mean i would have to make 625 imageview objects which would obviously be ridiculous.
I also tried simply putting the grid indexes one by one into the pane like this:
HBox gameLayout = new HBox(Grid[1][1], Grid[1][2], Grid[1][3]);
but that gives me a "invocationTargetException".
My goal is to be able to make snake by specifically targeting and manipulating grid elements. I want the square-color/imageView/rectangle/whatever to change when i change the value of a "B"array element to "W" (white) but the things i'v tried are either very inefficient or just don't work.
It's not at all clear what your objection is to creating multiple ImageViews. Since they can refer to the same Image instance, this should be fairly efficient (the image data doesn't need to be replicated).
This seems to work just fine:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class TiledBoard extends Application {
private final int tileSize = 30 ;
#Override
public void start(Stage primaryStage) {
Image b = createImage(Color.BLACK);
Image w = createImage(Color.WHITE);
Image[][] grid = {
{b,w,b,b,b,b,b,b,b,b,b},
{b,b,b,b,w,w,w,b,b,b,b},
{b,b,b,b,b,b,b,b,b,b,b}
};
GridPane gridPane = new GridPane();
// for visualizing the different squares:
gridPane.setHgap(2);
gridPane.setVgap(2);
gridPane.setStyle("-fx-background-color: grey;");
for (int y = 0 ; y < grid.length ; y++) {
for (int x = 0 ; x < grid[y].length ; x++) {
ImageView imageView = new ImageView(grid[y][x]);
imageView.setFitWidth(tileSize);
imageView.setFitHeight(tileSize);
gridPane.add(imageView, x, y);
}
}
Scene scene = new Scene(gridPane);
primaryStage.setScene(scene);
primaryStage.show();
}
private Image createImage(Color color) {
WritableImage image = new WritableImage(1, 1);
image.getPixelWriter().setColor(0, 0, color);
return image ;
}
public static void main(String[] args) {
launch(args);
}
}
You could do this with some kind of Shape (e.g. Rectangle), if you prefer:
#Override
public void start(Stage primaryStage) {
Color b = Color.BLACK;
Color w = Color.WHITE;
Color[][] grid = {
{b,w,b,b,b,b,b,b,b,b,b},
{b,b,b,b,w,w,w,b,b,b,b},
{b,b,b,b,b,b,b,b,b,b,b}
};
GridPane gridPane = new GridPane();
// for visualizing the different squares:
gridPane.setHgap(2);
gridPane.setVgap(2);
gridPane.setStyle("-fx-background-color: grey;");
for (int y = 0 ; y < grid.length ; y++) {
for (int x = 0 ; x < grid[y].length ; x++) {
Rectangle rect = new Rectangle(tileSize, tileSize, grid[y][x]);
gridPane.add(rect, x, y);
}
}
Scene scene = new Scene(gridPane);
primaryStage.setScene(scene);
primaryStage.show();
}
or with a Region:
#Override
public void start(Stage primaryStage) {
Color b = Color.BLACK;
Color w = Color.WHITE;
Color[][] grid = {
{b,w,b,b,b,b,b,b,b,b,b},
{b,b,b,b,w,w,w,b,b,b,b},
{b,b,b,b,b,b,b,b,b,b,b}
};
GridPane gridPane = new GridPane();
// for visualizing the different squares:
gridPane.setHgap(2);
gridPane.setVgap(2);
gridPane.setStyle("-fx-background-color: grey;");
for (int y = 0 ; y < grid.length ; y++) {
for (int x = 0 ; x < grid[y].length ; x++) {
Region rect = new Region();
rect.setMinSize(tileSize, tileSize);
rect.setPrefSize(tileSize, tileSize);
rect.setMaxSize(tileSize, tileSize);
rect.setBackground(new Background(new BackgroundFill(grid[y][x], CornerRadii.EMPTY, Insets.EMPTY)));
gridPane.add(rect, x, y);
}
}
Scene scene = new Scene(gridPane);
primaryStage.setScene(scene);
primaryStage.show();
}
Which of these is better really just depends on what else you want to do with them.

How to keep dragged Node in front of others (JavaFX 8)?

I'm trying to implement a chess board with draggable pieces as seen below. However, I'm unable to keep the piece being dragged to stay in front of other nodes to below or right to it. Left and up seems to work fine.
I tried to solve this by declaring the StackPanes forming the checkered background first and all the pieces only after that, as I read Java assigns the z-index based on the order in which the Nodes are added to their Parents. This approach is reflected below. I also tried creating a Group and adding both StackPanes and ImageViews to it in order to be able to use toFront(). Resulted in only the coordinate labels being shown.
How can I achieve the functionality I'm after?
This method creates the board:
public Parent chessBoard() {
GridPane board = new GridPane();
StackPane[][] cells = new StackPane[8][8];
// Create the board first
// (For dragging pieces to work correctly, draggable pieces must be
// added after the whole board, since z-index cannot be set explicitly
// in JavaFX.
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
// x and y in chess coordinate system (0-indexed)
int[] invertedY = {-1,7,6,5,4,3,2,1,0,-1};
int x = col - 1;
int y = invertedY[row];
// Coordinate labels
String[] abcLabels = {"A","B","C","D","E","F","G","H"};
if (row == 9 || row == 0) {
if (col == 0 || col == 9) continue;
Label label = new Label(abcLabels[x]);
label.setTextAlignment(TextAlignment.CENTER);
board.add(label, col, row);
continue;
} else if (col == 0 || col == 9) {
Label label = new Label(Integer.toString(y + 1));
board.add(label, col, row);
continue;
}
// Cell background color
Square square = game.getBoard().getSquare(x, y);
Color color = square.getColor() == ChessColor.BLACK
? Color.PERU : Color.BLANCHEDALMOND;
StackPane cell = cells[y][x] = new StackPane();
cell.setMaxSize(60, 60);
cell.setMinSize(60, 60);
cell.setBackground(new Background(
new BackgroundFill(color, null, null)));
board.add(cell, col, row);
}
}
// Finally, add pieces to their respective cells
for (int y = 0; y < 8; y++) {
for (int x = 0; x < 8; x++) {
Square square = game.getBoard().getSquare(x, y);
Piece occupant = square.getOccupant();
if (occupant != null) {
String path = "/resources/" + occupant + ".png";
Image image =
new Image(getClass().getResourceAsStream(path));
DraggablePieceIcon imageView =
new DraggablePieceIcon(image);
imageView.setManaged(false);
cells[y][x].getChildren().add(imageView);
}
}
}
return board;
}
This class makes the draggable icons:
public class DraggablePieceIcon extends ImageView {
private double mouseX;
private double mouseY;
public DraggablePieceIcon(Image image) {
super(image);
setOnMousePressed(event -> {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
});
setOnMouseDragged(event -> {
double deltaX = event.getSceneX() - mouseX;
double deltaY = event.getSceneY() - mouseY;
relocate(getLayoutX() + deltaX, getLayoutY() + deltaY);
mouseX = event.getSceneX();
mouseY = event.getSceneY();
});
}
}
And here's what I'm seeing:
You're adding the cells row by row from left to right. Since you add the pieces to the cells, the descendants of a cell cover the contents of cells in rows above or in the same row and left to the cell and will be covered by contents of all other cells.
To fix this you could make the parent of a dragged item the topmost node in the GridPane:
public DraggablePieceIcon(Image image) {
super(image);
setOnMousePressed(event -> {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
// make cell containing this piece the top-most cell
this.getParent().toFront()
});
...
}
Note that this solution will require you to implement some logic that makes the pieces children of the cells they are moved to and move the pieces to the center of those cells. Otherwise a piece could be covered by other cells, if you drag the piece inside such a cell later...
An alternative would be to make the pieces children of the GridPane itself. You allow the pieces to be dragged around independent of the cells anyways; the association between cell and piece is important for the model (i.e. in this case the implementation of the chess rules) not for the view and usually these parts are kept seperate.

Categories