How to align a GridPane based on position of a single node? - java

Say I have a scene. I have a grid pane in it which contains 2 x 2 buttons. I can align this whole grid pane exactly in the center by simply doing gridPane.setAlignment(Pos.CENTER); however I want it the whole grid pane to be positioned on the position of one node.
Here is an illustration:
The whole grid pane is aligned in the center. What I want is to set the position of r2 c1 exactly in the center and I want the other three nodes positioned above and besides it respectively.
I can position a single button but I do not know how to make the whole grid pane positioned based on the position of one node.
Here is the code I wrote for the illustration:
private BorderPane root = new BorderPane();
private Scene scene = new Scene(root, 1366, 768);
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
gridPane.addRow(0, new Button("r1 c1"), new Button("r1 c2"));
gridPane.addRow(1, new Button("r2 c1"), new Button("r2 c2"));
gridPane.setVgap(20);
gridPane.setHgap(30);
gridPane.setAlignment(Pos.CENTER);
root.setCenter(gridPane);
primaryStage.setTitle("Test");
primaryStage.setScene(scene);
primaryStage.show();
}
The r2c1 node should exactly be in the middle and the rest of the buttons positioned based on its position. Here is the desired view:
r2c1 starts exactly from the center of the screen and the other nodes are moved corresponding to it.
Any help would be greatly appreciated.

"moving the whole GridPane as a whole" requires changing the way GridPane is laid out with in it's parent (and not the internal layout of the GridPane itself).
There are a few alternatives to achieve it. Manipulating the parent layout is one.
Setting translation to the GridPane is another:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class FxMain extends Application {
private static final int VGAP = 20, HGAP = 30;
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
Button button1 = new Button("r1 c1");
gridPane.addRow(1, button1, new Button("r1 c2"));
gridPane.addRow(2, new Button("r2 c1"), new Button("r2 c2"));
gridPane.setVgap(VGAP); gridPane.setHgap(HGAP);
gridPane.setAlignment(Pos.CENTER);
//apply row-height + vgap down translate (row height represented by
//button height)
gridPane.translateYProperty().bind(button1.heightProperty().add(VGAP));
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 200, 200);
root.setCenter(gridPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
EDIT: after clarifying the desired layout:
The technique is similar. Enclose the GridPane in an AnchorPane and apply the desired translation:
public class FxMain extends Application {
private static final int VGAP = 20, HGAP = 30;
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
AnchorPane root = new AnchorPane(gridPane); //enclose grid in an AnchorPane
gridPane.addRow(1, new Button("r1 c1"), new Button("r1 c2"));
gridPane.addRow(2, new Button("r2 c1"), new Button("r2 c2"));
gridPane.setVgap(VGAP); gridPane.setHgap(HGAP);
//apply y translation: (root height/2) minus grid pane height
gridPane.translateYProperty().bind(root.heightProperty().divide(2).subtract(gridPane.heightProperty()));
//apply x translation of root widt / 2
gridPane.translateXProperty().bind(root.widthProperty().divide(2));
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}

Related

How to use shape position with label

If I show a circle with specific x and y coordinates it works fine:
public class FxApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Group group = new Group();
Circle circle = new Circle(100, 100, 2);
group.getChildren().add(circle);
Pane pane = new Pane(group);
ScrollPane scrollPane = new ScrollPane(pane);
BorderPane borderPane = new BorderPane(scrollPane);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
But if I add a label to the circle the position of the circle is ignored.
public class FxApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Group group = new Group();
Circle circle = new Circle(100, 100, 2);
Label label = new Label("test", circle);
group.getChildren().add(label);
Pane pane = new Pane(group);
ScrollPane scrollPane = new ScrollPane(pane);
BorderPane borderPane = new BorderPane(scrollPane);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
How to keep the position of the circle and only add the label or how to set the correct circle position inclusive label?
For example what works is:
Circle circle = new Circle(circle_center_x, circle_center_y, 3);
Text label = new Text("test");
double halfLabelHeight = label.getLayoutBounds().getHeight() / 2;
label.relocate(circle_center_x + 10, circle_center_y - halfLabelHeight);
this.getChildren().addAll(circle , label);
But I'm looking for a more integrated solution. I thought the Label object could be somewhat smart and do this on it's own but instead it's taking the circle x and y position and applies that to it's own space and not the parent space.
You actually need to relocate the label now instead of just telling the circle where to be displayed. When you specify the new Circle(100,100,2) you telling the Circle Object to be located at the x=100 and y=100 of its parent. In the first case its parent is the group but in the second case, its parent is now the Label. In order to locate the Label to x,y = 100,100 inside the Group you will need to call :
label.relocate(100, 100);
The Circle initialization is now not necessary. Even if you put the Circle at 0,0 it's still going to be displayed next to the Label because the label will manage the Node location.
PS. You can either change the NodeOrientation from LEFT_TO_RIGHT to RIGHT_TO_LEFT by label.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); or in case you want to change the "shape" location you can do label.setContentDisplay(ContentDisplay.TOP); ( or BOTTOM etc )
I am not sure I understand correct what are you trying to achieve here but I guess you want to have the Circle and the Label next to each other. In addition you want to label to be centered on height depending the circle location. If the previous assumption is correct then here is the code to achieve that :
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class FxApplication extends Application {
private Group group;
#Override
public void start(Stage primaryStage) throws Exception {
group = new Group();
addCustomNode(100, 100, new Circle(2), new Label("Test"));
Pane pane = new Pane(group);
ScrollPane scrollPane = new ScrollPane(pane);
BorderPane borderPane = new BorderPane(scrollPane);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
private void addCustomNode(int x, int y, Circle circle, Label label) {
double labelDimensions[] = getLabelDimensions(label);
circle.setCenterX(100);
circle.setCenterY(100);
label.relocate(circle.getCenterX() + labelDimensions[0] / 2.0, circle.getCenterY() - labelDimensions[1] / 2.0);
group.getChildren().addAll(circle, label);
}
// find the height and width before we
// add the label to the stage
private double[] getLabelDimensions(Label label) {
HBox h = new HBox();
Label l = new Label("Hello");
h.getChildren().add(l);
Scene s = new Scene(h);
l.impl_processCSS(true);
return new double[] { l.prefWidth(-1), l.prefHeight(-1) };
}
public static void main(String[] args) {
launch(args);
}
}

Insert a pane in Javafx [duplicate]

This question already has answers here:
How to insert an object in an ArrayList at a specific position
(6 answers)
Closed 7 years ago.
In a VBox I already have two Grid Panes. Now I want to insert a new anchor pane between them. If I use the below code,
vBoxPane.getChildren().add(anchorPane);
it will insert anchor pane at last, but I want it inbetween the gridpanes. Is there any way?
Since you're using a VBox as main container, the index of its children determine their vertical position.
So, if you want to place a child node in the middle, just insert it in the middle of the list returned by the getChildren() method.
Here is a complete runnable example:
public class Example extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
GridPane gridTop = new GridPane();
GridPane gridBottom = new GridPane();
VBox mainPanel = new VBox(gridTop, gridBottom);
Label topLabel = new Label("Top");
gridTop.add(topLabel, 0, 0);
Button createAnchorPane = new Button("Create AnchorPane");
gridBottom.add(createAnchorPane, 0, 0);
createAnchorPane.setOnAction(event -> {
Label centerLabel = new Label("Center");
AnchorPane newPane = new AnchorPane();
newPane.getChildren().add(centerLabel);
// add the anchor pane in the middle
mainPanel.getChildren().add(1, newPane);
});
Scene scene = new Scene(mainPanel, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
}

How do I set up two vertical split panes so that dragging each will not affect the other?

I am trying to set up a UI with three split panes. The first two are vertical panes, on the left and right side of the screen. One side of each split has a title pane. The user can select items from these panes to include in fields in the central pane. There is also a horizontal pane at the bottom that is not relevant to this question.
The user can open these side panes either by dragging the vertical dividers, or by clicking on the relevant toggle button (Films, Books etc.) to show that pane.
The issue I have is that I want to make it so that dragging one vertical divider does not move the other. However, since I cannot find a way to set this up without putting one of the vertical split panes into the other vertical pane, this always results in a situation where moving one of the dividers also moves the other. In the case of the below code for instance, moving the vertical divider for the left-hand (Films) split pane moves the right-hand vertical divider.
Can anyone help with this?
package pane2;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.application.*;
import javafx.beans.property.DoubleProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TitledPane;
import javafx.scene.input.*;
import javafx.stage.Stage;
public class Pane2 extends Application {
SplitPane rightSplit;
DoubleProperty rightSplitDividerPos;
TitledPane books;
ToggleButton selectBooks;
VBox booksBox;
VBox centre;
SplitPane leftSplit;
DoubleProperty leftSplitDividerPos;
TitledPane films;
ToggleButton selectFilms;
VBox filmsBox;
VBox centreLeft;
SplitPane mainSplit;
DoubleProperty mainSplitDividerPos;
TitledPane arts;
ToggleButton selectArts;
VBox artsBox;
BorderPane root;
public static void main(String[] args)
{
launch( args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test");
//Create right-hand titled pane for the books list and centre it in Vbox
books = new TitledPane();
books.setText("Books");
books.setMinWidth(0);
booksBox = new VBox(0,books);
//Create central pane and add toggle buttons to open hidden panes on the
//left, right, and bottom (films, books, and arts respectively)
selectBooks = new ToggleButton("Books");
selectFilms = new ToggleButton("Films");
selectArts = new ToggleButton("Arts");
centre = new VBox(100,selectBooks,selectFilms,selectArts);
centre.setPrefWidth(1300);
centre.setPrefHeight(750);
//Create split pane to divide the central pane and books list
rightSplit = new SplitPane();
rightSplit.getItems().addAll(centre,booksBox);
//Create left-hand titled pane for the films list and centre it in VBox
films = new TitledPane();
films.setText("Films");
films.setMinWidth(0);
filmsBox = new VBox(0,films);
//Create split pane to divide the films list and the central pane
leftSplit = new SplitPane();
leftSplit.getItems().addAll(filmsBox,rightSplit);
//Create mainSplit pane
arts = new TitledPane();
arts.setText("arts");
arts.setMinHeight(0);
artsBox = new VBox(0,arts);
mainSplit = new SplitPane();
mainSplit.setOrientation(Orientation.VERTICAL);
mainSplit.getItems().addAll(leftSplit,artsBox);
root = new BorderPane();
root.setCenter(mainSplit);
//Set divider positions for the three dividers
rightSplitDividerPos = rightSplit.getDividers().get(0).positionProperty();
rightSplitDividerPos.set(1.0);
leftSplitDividerPos = leftSplit.getDividers().get(0).positionProperty();
leftSplitDividerPos.set(0.0);
mainSplitDividerPos = mainSplit.getDividers().get(0).positionProperty();
mainSplitDividerPos.set(1.0);
//Start up scene and stage
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setMaximized(true);
primaryStage.show();
//Event - if the books toggle button is selected, the left divider will
//move to the right to show the books selection pane
selectBooks.setOnAction(event -> {
if(selectBooks.isSelected()){
leftSplitDividerPos.set(0.15);
}
if(!selectBooks.isSelected()){
leftSplitDividerPos.set(0.0);
}else{
}
});
//Event - if the films toggle button is selected, the right divider will
//move to the left to show the films selection pane
selectFilms.setOnAction(event -> {
if(selectFilms.isSelected()){
rightSplitDividerPos.set(0.8);
}
if(!selectFilms.isSelected()){
rightSplitDividerPos.set(1.0);
}else{
}
});
//Event - if the arts toggle button is selected, the bottom divider will
//move up to show the arts selection pane
selectArts.setOnAction(event -> {
if(selectArts.isSelected()){
mainSplitDividerPos.set(0.75);
}
if(!selectArts.isSelected()){
mainSplitDividerPos.set(1.0);
}else{
}
});
}
}
do you really need 3 SplitPane in your layout? because i think you can achieve pretty much the same result with just 1 pane:
SplitPane split = new SplitPane();
VBox left = new VBox(new Label("left"));
left.setStyle("-fx-background-color: cadetblue");
VBox right = new VBox(new Label("right"));
right.setStyle("-fx-background-color: darkorange");
VBox center = new VBox(new Label("center"));
center.setStyle("-fx-background-color: darkgreen");
split.getItems().addAll(left, center, right);
split.setDividerPosition(0,1/(double)3);
split.setDividerPosition(1,2/(double)3);
Scene scene = new Scene(split, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
Here is your code realated Example:
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test");
//Create central pane and add toggle buttons to open hidden panes on the
//left, right, and bottom (films, books, and arts respectively)
ToggleButton selectBooks = new ToggleButton("Books");
ToggleButton selectFilms = new ToggleButton("Films");
ToggleButton selectArts = new ToggleButton("Arts");
VBox centre = new VBox(100,selectBooks,selectFilms,selectArts);
//Create left-hand titled pane for the films list and centre it in VBox
TitledPane films = new TitledPane();
films.setText("Films");
VBox filmsBox = new VBox(films);
//Create right-hand titled pane for the books list and centre it in Vbox
TitledPane books = new TitledPane();
books.setText("Books");
VBox booksBox = new VBox(books);
//Create mainSplit pane
TitledPane arts = new TitledPane();
arts.setText("arts");
VBox artsBox = new VBox(arts);
SplitPane mainSplit = new SplitPane();
mainSplit.getItems().addAll(filmsBox, centre, booksBox);
mainSplit.setDividerPosition(0,1/(double)12);
mainSplit.setDividerPosition(1,11/(double)12);
SplitPane root = new SplitPane();
root.setOrientation(Orientation.VERTICAL);
root.getItems().addAll(mainSplit, artsBox);
root.setDividerPosition(0,0.9);
root.setPrefWidth(1300);
root.setPrefHeight(750);
//Start up scene and stage
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setMaximized(true);
primaryStage.show();
}

JavaFX - Updating size of BorderPane after modifying size of child node

I have a BorderPane with a Canvas in its center and I want the BorderPane to always wrap around the canvas when I change the canvas's size. Take for example this code:
public class Test extends Application {
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Canvas canvas = new Canvas(10, 10);
root.setCenter(canvas);
Scene s = new Scene(root);
primaryStage.setScene(s);
primaryStage.show();
canvas.setWidth(100);
canvas.setHeight(100);
}
public static void main(String[] args) {launch(args);}
I want the BorderPane to change its size after I call the setWidth and setHeight methods on canvas, but it just stays the same size as if the canvas was still (10,10) big. How do I do this?
The problem is that your BorderPane is the root of your applications Scene. The Scenes root container will not (at least not automatically) grow larger that its containing Scene.
Look at this example application:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(0, 0);
Button button = new Button("test");
button.setOnAction(ev -> {
canvas.setWidth(canvas.getWidth() + 10);
canvas.setHeight(canvas.getHeight() + 10);
canvas.getGraphicsContext2D().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
canvas.getGraphicsContext2D().fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
});
BorderPane canvasBorderPane = new BorderPane(canvas);
canvasBorderPane.setPadding(new Insets(10));
canvasBorderPane.setBackground(new Background(new BackgroundFill(Color.RED, new CornerRadii(0), Insets.EMPTY)));
BorderPane root = new BorderPane(canvasBorderPane);
root.setPadding(new Insets(10));
root.setBackground(new Background(new BackgroundFill(Color.BLUE, new CornerRadii(0), Insets.EMPTY)));
root.setBottom(button);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I stacked two BorderPanes together and put the Canvas on the innermost, applied some background colors so you can see what is happening.

JavaFX 2.0 SplitPane no longer working as expected

Since updating to JavaFX 2.0 b36 (SDK for Windows (32Bit) + Netbeans Plugin) from a previous JavaFX 2.0 version the SplitPane control does not work as expected any longer.
The divider can't be moved
The divider position is not as expected
The sizing of the contained sides is not as expected
Here my example code for a SplitPane .
public class FxTest extends Application {
public static void main(String[] args) {
Application.launch(FxTest.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SplitPane Test");
Group root = new Group();
Scene scene = new Scene(root, 200, 200, Color.WHITE);
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
SplitPane splitPane = new SplitPane();
splitPane.setPrefSize(200, 200);
splitPane.setOrientation(Orientation.HORIZONTAL);
splitPane.setDividerPosition(0, 0.7);
splitPane.getItems().addAll(button1, button2);
root.getChildren().add(splitPane);
primaryStage.setScene(scene);
primaryStage.setVisible(true);
}
}
As you can (hopefully) see the left side is clearly smaller than the right side.
Another funny fact is, when you change orientation to VERTICAL
splitPane.setOrientation(Orientation.VERTICAL);
and try to move the divider up or down you get some console output saying 'HERE'.
Looks like some test output.
What's the issue with this?
To get the SplitPane working as expected add a layout (e.g. BorderPane) to each side. Add the controls to display to each of these layouts. I think this should be made more clear in API documentation!
public class FxTest extends Application {
public static void main(String[] args) {
Application.launch(FxTest.class, args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("SplitPane Test");
Group root = new Group();
Scene scene = new Scene(root, 200, 200, Color.WHITE);
//CREATE THE SPLITPANE
SplitPane splitPane = new SplitPane();
splitPane.setPrefSize(200, 200);
splitPane.setOrientation(Orientation.HORIZONTAL);
splitPane.setDividerPosition(0, 0.7);
//ADD LAYOUTS AND ASSIGN CONTAINED CONTROLS
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
BorderPane leftPane = new BorderPane();
leftPane.getChildren().add(button1);
BorderPane rightPane = new BorderPane();
rightPane.getChildren().add(button2);
splitPane.getItems().addAll(leftPane, rightPane);
//ADD SPLITPANE TO ROOT
root.getChildren().add(splitPane);
primaryStage.setScene(scene);
primaryStage.setVisible(true);
}
}

Categories

Resources