I have a BorderPane with Panes for left, right and center elements and HBox for top element. On the right Pane there is a Label. The Label contains some text, which doesn't wrap and therefore gets cut.
public class Main extends Application implements EventHandler<ActionEvent> {
private static BorderPane gamePane;
private static Pane cellsPane;
private static HBox buttonsPane;
private static Pane statsPane;
private static Pane setupsPane;
Label cellsCountLabel;
static int gameWidth= 700;
static int gameHeight=600;
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hello World");
gamePane = new BorderPane();
buttonsPane = new HBox(5);
statsPane = new Pane();
cellsPane = makeGrid(20);
setupsPane = new Pane();
cellsPane.setStyle("-fx-background-color: #ffffff; -fx-border-color: black");
buttonsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
statsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
setupsPane.setStyle("-fx-background-color: #f2f2f2; -fx-border-color: black");
cellsPane.setMaxWidth(400);
statsPane.setMaxWidth((gameWidth-400)/2);
setupsPane.setMaxWidth((gameWidth-400)/2);
createCellButton = new Button();
deleteCellsButton = new Button();
createCellButton.setText("Create a cell");
deleteCellsButton.setText("Delete cells");
...
buttonsPane.setSpacing(10);
buttonsPane.setPadding(new Insets(10, 10, 10, 10));
buttonsPane.getChildren().add(createCellButton);
buttonsPane.getChildren().add(deleteCellsButton);
gamePane.setTop(buttonsPane);
gamePane.setCenter(cellsPane);
...
cellsCountLabel = new Label("Cells Count: " + (cellId + 1));
statsPane.getChildren().add(cellsCountLabel);
gamePane.setLeft(statsPane);
setupsPane.getChildren().add(new Label("Setups Panel1111115552222222133331111"));
gamePane.setRight(setupsPane);
gamePane.setMargin(statsPane, new Insets(0, 0, 0, 0));
gamePane.setMargin(cellsPane, new Insets(0,0,0,0));
gamePane.setMargin(setupsPane, new Insets(0,0,0,0));
statsPane.setPrefWidth(150);
setupsPane.setPrefWidth(150);
cellsCountLabel.setWrapText(true); //doesn't work
Scene scene = new Scene(gamePane, gameWidth, gameHeight);
primaryStage.setScene(scene);
...
primaryStage.show();
}
...
}
However, there exists space between left, center and right Panes. The space gets smaller as the content in those Panes get larger, however, when it gets too large it messes with the center Pane.
I tried using cellsCountLabel.setWrapText(true), but it didn't work.
Try this
Label label = new Label("Setups Panel1111115552222222133331111");
label.setWrapText(true);
label.setMaxWidth((gameWidth-400)/2);
setupsPane.getChildren().add(label);
Also if you go this route you may want to declare something like this
static double sidePaneWidth = (gameWidth-400)/2;
for later use
You can also make this into a function like so if necessary
private Label newSidePaneLabel(String labelString){
Label label = new Label(labelString);
label.setWrapText(true);
label.setMaxWidth(sidePaneWidth);
return label;
}
Related
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);
}
}
I want to render an array of buttons and then a piechart to the screen. I've tried almost every method I could but something doesn't seems to work. either alone array of buttons(usercontrol()) or pie(the graph) can be render but when I try to do both it only render the array of buttons.plz don't worry about return types of function. any help will be really appreciated.
public class Layout {
// returns Windows height and width
private final double width = 600;
private final double height = 400;
private Button[] userControl() { // navigation bar buttons
Button[] buttons = new Button[3];
buttons[0] = new Button("BUY Share!"); // Buy shares buttons
buttons[0].setLayoutX(width - 100);
buttons[0].setLayoutY(10);
buttons[1] = new Button("Sell Shares!"); // Sell shares buttons
buttons[1].setLayoutX(width - 200);
buttons[1].setLayoutY(10);
buttons[2] = new Button("Show Share"); // Show shares buttons
buttons[2].setLayoutX(width - 300);
buttons[2].setLayoutY(10);
return buttons;
}
public void pie() {
ObservableList<PieChart.Data> shareHolders
= FXCollections.observableArrayList(
new PieChart.Data("user1", 13),
new PieChart.Data("user2", 25),
new PieChart.Data("user3", 10),
new PieChart.Data("user4", 22),
new PieChart.Data("user5", 30));
PieChart chart = new PieChart(shareHolders);
chart.setTitle("Share Holders Shares");
VBox pie = new VBox();
pie.setLayoutY(100);
pie.getChildren().addAll(chart);
pane().getChildren().add(pie);
// return pie;
}
private Pane pane() {
Pane pane = new Pane();
pane.getChildren().addAll(userControl());
return pane;
}
public Stage window() {
//pane().getChildren().add();
pie();
Scene scene = new Scene(pane(), 600, 400);
Stage primaryStage = new Stage();
primaryStage.setScene(scene);
primaryStage.setTitle("ShareHolders!");
primaryStage.show();
return primaryStage;
}
}
Your problem is that you are creating a new Pane every time you call the pane method. You need to change it, perhaps by using a global Pane object.
//First, declare a global Pane.
static Pane pane = new Pane();
//Make your pie() method return the pie VBox.
public VBox pie() {
/*Blah blah blah, making the pie...*/
return pie//Remember, pie is a VBox, which is why we are returning the VBox.
}
//Later, when you build your window, add the pie and the buttons to the GLOBAL PANE...
public Stage window() {
pane.getChildren().add(pie()); //...right here.
pane.getChildren().addAll(userControl());
/*Build the primary stage...*/
return primaryStage;
}
This should get you your desired result.
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);
}
}
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.
I'm working on this example which is not working properly:
public class test extends Application
{
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setScene(new Scene(root));
String pillButtonCss = DX57DC.class.getResource("PillButton.css").toExternalForm();
// create 3 toggle buttons and a toogle group for them
ToggleButton tb1 = new ToggleButton("Left Button");
tb1.setId("pill-left");
ToggleButton tb2 = new ToggleButton("Center Button");
tb2.setId("pill-center");
ToggleButton tb3 = new ToggleButton("Right Button");
tb3.setId("pill-right");
final ToggleGroup group = new ToggleGroup();
tb1.setToggleGroup(group);
tb2.setToggleGroup(group);
tb3.setToggleGroup(group);
// select the first button to start with
group.selectToggle(tb1);
//////////////////////////////////////////
final VBox vbox = new VBox();
final Rectangle rect1 = new Rectangle(300, 300);
rect1.setFill(Color.ALICEBLUE);
final Rectangle rect2 = new Rectangle(300, 300);
rect2.setFill(Color.AQUA);
final Rectangle rect3 = new Rectangle(300, 300);
rect3.setFill(Color.AZURE);
tb1.setUserData(rect1);
tb2.setUserData(rect2);
tb3.setUserData(rect3);
group.selectedToggleProperty().addListener(new ChangeListener<Toggle>()
{
#Override
public void changed(ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle)
{
if (new_toggle == null)
{
//rect.setFill(Color.WHITE);
}
else
{
vbox.getChildren().addAll((Node[]) group.getSelectedToggle().getUserData());
//rect.setFill((Color) group.getSelectedToggle().getUserData());
}
}
});
///////////////////////////////////////////
HBox hBox = new HBox();
hBox.getChildren().addAll(tb1, tb2, tb3);
hBox.setPadding(new Insets(20, 20, 260, 20));
hBox.getStylesheets().add(pillButtonCss);
vbox.getChildren().add(hBox);
//vbox.getChildren().add(rect);
root.getChildren().add(vbox);
}
#Override
public void start(Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
I want to create several Rectangles(or object in which or object) in which I want to store data. I want to switch the Rectangles(objects) which are displayed in front of the user using the buttons. The example which I implemented is not working properly. Can you tell me what is the proper way to implement this?
You could create a Stackpane with the Rectangle and a Label with Text on top of it (if thats the data you want to store). Alternatively you can also set the Background of any Pane to have a colored Rectangle.
Than add this Pane as Userdata to the corresponding button and add the buttons userdata to your VBox on toggle.
final StackPane rect1pane = new StackPane();
final Rectangle rect1 = new Rectangle(300, 300);
rect1pane.getChildren().add(rect1);
rect1pane.getChildren().add(new Label("Some text"));
tb1.setUserData(rect1pane);
togglePropertyListener:
...
else{
//Delete rectangles added before ( or check if this one isnt already dispayed)
if(group.getSelectedToggle().getUserData() instanceof Node)
vbox.getChildren().add((Node)group.getSelectedToggle().getUserData());
}
If you just want your example code to work change:
vbox.getChildren().addAll((Node[]) group.getSelectedToggle().getUserData());
to
vbox.getChildren().addAll((Node) group.getSelectedToggle().getUserData());
Because your just adding the Rectangle of the Selected ToggleButton it is only one and not an array.
Make your window bigger after a click to see the rectanlge (the 260px bottom padding doesn't help because even if the space is empty below hbox, it still is part of the hbox and cant get used by your added rectangle) or just move
group.selectToggle(tb1);
to the last line of your init method ;)