how to fix custom timeline problem JavaFx? - java

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();
}

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?

Rotate a group of rectangles around their common center

I have a few rectangles that I assign rotation one by one ((javafx.scene.shape.Rectangle) shape).rotateProperty().bind(rotate);
For example, 45 degrees
My rectangle rotates around its center. Please tell me how to make the enemy a few right-angles around their common center.
To get something like this
this.rotate.addListener((obs, old, fresh) -> {
for (VObject vObject : children ) {
vObject.rotate.set(this.rotate.get());
}
});
This is how I add rotation. How can I specify the angle
Update: I used the advice below and now I set the rotation individually for each rectangle. (The selection is still a bit wrong)
this.rotate.addListener((obs, old, fresh) -> {
Rotate groupRotate = new Rotate(rotate.get(),
this.x.getValue().doubleValue() + this.width.getValue().doubleValue() / 2 ,
this.y.getValue().doubleValue() + this.height.getValue().doubleValue() / 2);
for (VObject vObject : children ) {
vObject.getShape().getTransforms().clear();
vObject.getShape().getTransforms().add(groupRotate);
}
});
But now the axis also rotates depending on the rotation.
Can I set the rotation to the rectangles without turning the coordinate axis?
If you put all rectangles into a common Group, you can rotate them at once:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class RotateAllApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
// the common group
Group group = new Group();
group.getChildren().addAll(new Rectangle(10, 10, 80, 40), //
new Rectangle(110, 10, 80, 40), //
new Rectangle(10, 110, 80, 40), //
new Rectangle(110, 110, 80, 40));
// rotate the group instead of each rectangle
group.setRotate(45.0);
root.setCenter(group);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Update: If you don't want to create a parent Group object, you can apply the same rotation transformation to each child instead. While Node#setRotate(double) always rotates around the center of the node, adding a transformation to Node#getTransforms() is more general and not restricted to simple rotations.
The following statement will apply a rotation around the point (100.0/100.0) of the parent coordinate system to all children in the list:
childrenList.forEach(child -> child.getTransforms().add(Transform.rotate(45.0, 100.0, 100.0)));
Use a Rotate transform and specify the appropriate pivot point:
#Override
public void start(Stage primaryStage) throws IOException {
Pane pane = new Pane();
pane.setPrefSize(600, 600);
Rectangle[] rects = new Rectangle[4];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
Rectangle rect = new Rectangle(100 + i * 200, 100 + j * 100, 150, 50);
rects[i * 2 + j] = rect;
pane.getChildren().add(rect);
}
}
Slider slider = new Slider(0, 360, 0);
double minX = Double.POSITIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
double maxY = Double.NEGATIVE_INFINITY;
Rotate rotate = new Rotate();
// find pivot point
for (Rectangle rect : rects) {
double val = rect.getX();
if (minX > val) {
minX = val;
}
val += rect.getWidth();
if (maxX < val) {
maxX = val;
}
val = rect.getY();
if (minY > val) {
minY = val;
}
val += rect.getHeight();
if (maxY < val) {
maxY = val;
}
rect.getTransforms().add(rotate);
}
rotate.setPivotX(0.5 * (maxX + minX));
rotate.setPivotY(0.5 * (maxY + minY));
rotate.angleProperty().bind(slider.valueProperty());
Scene scene = new Scene(new VBox(10, pane, slider));
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
If you're planing to apply multiple transformations, you may need to adjust the code for finding the pivot point to use transforms for calculating the bounds...

Is specifying JavaFX CSS borders on GridPane contents supposed to collapse the sizing?

I was attempting to layout a JavaFX stage using a GridPane when I ran into the following problem. If I setup the grid with the appropriate constraints and add newly instantiated StackPanes to it, the default sizing of the scene, stage, and it's contents ensures that the contents are visible:
However, if I add a JavaFX CSS style specifying a border to the newly instantiated StackPane before adding it to the GridPane, then the default sizing of things seems to collapse complete:
My code is as follows:
public static void main(final String[] args) {
Platform.startup(() -> {});
Platform.runLater(() -> {
final GridPane gridPane = new GridPane();
final Scene scene = new Scene(gridPane);
final Stage stage = new Stage();
stage.setScene(scene);
final List<StackPane> panes = new ArrayList<>();
for (int i = 0; i < 4; i++) {
// Create a new pane with a random background color for
// illustration
final StackPane p = createNewPane();
panes.add(p);
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
}
for (int r = 0; r < 2; r++) {
final RowConstraints rc = new RowConstraints();
rc.setPercentHeight(50);
gridPane.getRowConstraints().add(rc);
}
for (int c = 0; c < 2; c++) {
final ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(50);
gridPane.getColumnConstraints().add(cc);
}
for (int r = 0, i = 0; r < 2; r++) {
for (int c = 0; c < 2; c++) {
gridPane.add(panes.get(i++), c, r);
}
}
stage.show();
});
}
Curiously, if I move the stage.show() to right after I set the Scene, then everything works fine even with the CSS.
Can anyone help me understand, one, whether this is the expected behavior, and two, why the execution order of the stage.show() makes a difference?
Thanks!
What the issue is
Your example is a bit ambiguous. You don't set the preferred size of anything added to the Stage at any time. So, the JavaFX platform can really do whatever it wants in terms of sizing things. Setting a preferred percent size is not the same as setting a preferred absolute size. A percent size is relative, so the question becomes, relative to what? and the answer to that is unclear.
As to why this occurs:
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
I couldn't say. My guess is that the use of CSS is triggering some additional layout logic which effects the resizing in the absence of any size hints.
How to fix it
Anyway, the solution is just to make things more clear and specify preferred sizing for at least something in the application, then the application will initially be sized to that preferred sizing.
Here is an example:
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Starter {
public static void main(String[] args) {
Platform.startup(() -> {});
Platform.runLater(() -> {
final GridPane gridPane = new GridPane();
final Scene scene = new Scene(gridPane);
final Stage stage = new Stage();
stage.setScene(scene);
final List<StackPane> panes = new ArrayList<>();
for (int i = 0; i < 4; i++) {
// Create a new pane with a random background color for
// illustration
final StackPane p = createNewPane();
panes.add(p);
// The addition / removal of the following line affects the
// layout.
p.setStyle("-fx-border-width:2px;-fx-border-color:red");
}
for (int r = 0; r < 2; r++) {
final RowConstraints rc = new RowConstraints();
rc.setPercentHeight(50);
gridPane.getRowConstraints().add(rc);
}
for (int c = 0; c < 2; c++) {
final ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(50);
gridPane.getColumnConstraints().add(cc);
}
for (int r = 0, i = 0; r < 2; r++) {
for (int c = 0; c < 2; c++) {
gridPane.add(panes.get(i++), c, r);
}
}
stage.show();
});
}
private static final Random random = new Random(42);
private static StackPane createNewPane() {
StackPane pane = new StackPane();
pane.setBackground(
new Background(
new BackgroundFill(
randomColor(), null, null
)
)
);
pane.setPrefSize(150, 100);
return pane;
}
private static Color randomColor() {
return Color.rgb(
random.nextInt(256),
random.nextInt(256),
random.nextInt(256)
);
}
}
The key part of the solution is the call:
pane.setPrefSize(150, 100);
which sets the preferred size for the stack panes which have been placed in your layout.
Alternatively, rather than doing the bottom up preferred sizing by setting a preferred size on each of the StackPanes, you could also accomplish a similar thing from a top-down perspective by setting appropriate constraints on the GridPane instead, for example:
gridPane.setPrefSize(300, 200);
Note
I'd advise using a JavaFX Application class instead of Platform.startup() call unless there is a really good reason to use the latter (which there is in this case - interfacing with Swing, as you have noted in your comment).

Drag nodes like in a patience/Klondike card game

I'm doing a Klondike game. The logic is all working. I'm just having trouble with the UI in javafx.
I've been trying to move/drag the cards from the 'tableau pile' arround without the expected result.
My card is a ImageView with an Image inside. The cards are inside a Pane:
Pane tableau = new Pane();
for (int i = 0; i < n; i++) {
Image img = new Image("resources/images/" + (i + 1) + ".png");
ImageView imgView = new ImageView(img);
imgView.setY(i * 20);
//imgView Mouse Events here
tableau.getChildren().add(imgView);
}
I tried:
imgView.setOnMousePressed((MouseEvent mouseEvent) -> {
dragDelta.x = imgView.getLayoutX() - mouseEvent.getSceneX();
dragDelta.y = imgView.getLayoutY() - mouseEvent.getSceneY();
});
imgView.setOnMouseDragged((MouseEvent mouseEvent) -> {
imgView.setLayoutX(mouseEvent.getSceneX() + dragDelta.x);
imgView.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);
});
This solution dont work because I'm setting the positions so when release the card wont return to where it was, and because the card is colliding with other UI objects.
Another attempt:
imgView.setOnDragDetected((MouseEvent event) -> {
ClipboardContent content = new ClipboardContent();
content.putImage(img);
Dragboard db = imgView.startDragAndDrop(TransferMode.ANY);
db.setDragView(img, 35, 50);
db.setContent(content);
event.consume();
});
In this solution the problems are: the card comes semitransparent, like moving a file, the cursor becames no/forbidden but other than that it works well: no collisions and the card goes to his original place if I release the mouse.
Another problem is that I dont know if I can move more than 1 card with this solution?
My final question is, How can I move a node (in this case an ImageView) or a group of nodes, from one pile to another, like in a Solitaire Card Game?
For knowing the original card position you should use the setTranslateX (and Y) instead of setLayoutX in your mouse handler. So when the user releases the card, you can simply invoke a Transition and let the card fly back to the layout position. If the user releases the card on a valid place, you set the translate coordinates to 0 and change the layout position or use relocate.
If you want to make the cards semitransparent, you could e. g. change the opacity or apply CSS.
By the way, I wouldn't use Clipboardcontent, it seems inappropriate for your needs.
You can move multiple objects with your mouse handling code. You simple have to apply the translation to multiple objects simultaneously. When you drag a pile, you determine the cards on top of the selected card and apply the transition to all of them.
Here's a quick example to show you how it could look like.
Card.java, you'll use an ImageView.
public class Card extends Rectangle {
static Random rand = new Random();
public Card() {
setWidth(100);
setHeight(200);
Color color = createRandomColor();
setStroke(color);
setFill( color.deriveColor(1, 1, 1, 0.4));
}
public static Color createRandomColor() {
int max = 200;
Color color = Color.rgb( (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max), (int) (rand.nextDouble() * max));
return color;
}
}
Game.java, the application.
public class Game extends Application {
static List<Card> cardList = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
MouseGestures mg = new MouseGestures();
Group root = new Group();
for( int i=0; i < 10; i++) {
Card card = new Card();
card.relocate( i * 20, i * 10);
mg.makeDraggable(card);
cardList.add( card);
}
root.getChildren().addAll( cardList);
Scene scene = new Scene( root, 1600, 900);
primaryStage.setScene( scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// TODO: don't use a static method, I only added for the example
public static List<Card> getSelectedCards( Card currentCard) {
List<Card> selectedCards = new ArrayList<>();
int i = cardList.indexOf(currentCard);
for( int j=i + 1; j < cardList.size(); j++) {
selectedCards.add( cardList.get( j));
}
return selectedCards;
}
}
MouseGestures.java, the mouse handling mechanism.
public class MouseGestures {
final DragContext dragContext = new DragContext();
public void makeDraggable(final Node node) {
node.setOnMousePressed(onMousePressedEventHandler);
node.setOnMouseDragged(onMouseDraggedEventHandler);
node.setOnMouseReleased(onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragContext.x = event.getSceneX();
dragContext.y = event.getSceneY();
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Node node = (Node) event.getSource();
double offsetX = event.getSceneX() - dragContext.x;
double offsetY = event.getSceneY() - dragContext.y;
node.setTranslateX(offsetX);
node.setTranslateY(offsetY);
// same for the other cards
List<Card> list = Game.getSelectedCards( (Card) node);
for( Card card: list) {
card.setTranslateX(offsetX);
card.setTranslateY(offsetY);
}
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Node node = (Node) event.getSource();
moveToSource(node);
// same for the other cards
List<Card> list = Game.getSelectedCards( (Card) node);
for( Card card: list) {
moveToSource(card);
}
// if you find out that the cards are on a valid position, you need to fix it, ie invoke relocate and set the translation to 0
// fixPosition( node);
}
};
private void moveToSource( Node node) {
double sourceX = node.getLayoutX() + node.getTranslateX();
double sourceY = node.getLayoutY() + node.getTranslateY();
double targetX = node.getLayoutX();
double targetY = node.getLayoutY();
Path path = new Path();
path.getElements().add(new MoveToAbs( node, sourceX, sourceY));
path.getElements().add(new LineToAbs( node, targetX, targetY));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(1000));
pathTransition.setNode(node);
pathTransition.setPath(path);
pathTransition.setCycleCount(1);
pathTransition.setAutoReverse(true);
pathTransition.play();
}
/**
* Relocate card to current position and set translate to 0.
* #param node
*/
private void fixPosition( Node node) {
double x = node.getTranslateX();
double y = node.getTranslateY();
node.relocate(node.getLayoutX() + x, node.getLayoutY() + y);
node.setTranslateX(0);
node.setTranslateY(0);
}
class DragContext {
double x;
double y;
}
// pathtransition works with the center of the node => we need to consider that
public static class MoveToAbs extends MoveTo {
public MoveToAbs( Node node, double x, double y) {
super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
// pathtransition works with the center of the node => we need to consider that
public static class LineToAbs extends LineTo {
public LineToAbs( Node node, double x, double y) {
super( x - node.getLayoutX() + node.getLayoutBounds().getWidth() / 2, y - node.getLayoutY() + node.getLayoutBounds().getHeight() / 2);
}
}
}
When you pick a card or a multiple of cards, they'll transition back to their origin.
Here's a screenshot where I dragged the 3rd card from top.
When you check if a card is on a valid destination, you should invoke the fixPosition method. It simply relocates the card, i. e. calculates the position using the translation values, repositions the node and sets its translation to 0.
The tricky part was the PathTransition. It's working from the center of a node, not from the x/y coordinates. Here's a thread regarding that issue to which I replied in case you wish to know more about it.

How to auto-resize main pane after inserting dynamically GridPane

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.

Categories