I have a fxml dialog in SceneBuilder, which contains a Gridpane on the right side of a Splitpane contained in a BorderPane. I have two issues with this dialog that relate to each other.
Problem #1 is the width of the last column doesn't behave as
expected, given I set the max width to "USE_COMPUTED_SIZE", Hgrow = "Never" and Fill Width = false.
Problem #2, is that the preview in SceneBuilder differs
from the layout when I run the application.
The layout of the overall dialog is supposed to look like this (I highlighted where the different columns are located):
Regarding problem 1: In order to achieve this layout of the GridPane I have to constraint the max width of the last column to a value of ~60, despite that all of its Nodes, in the different rows, are limited in width. I would expect that this column stays narrow and that column 2 would take all the available space. But if I don't use this explicit max width value, the remaining space is not given to the Combobox but to the last column, which looks like this:
Regarding problem #2: In SceneBuilder the dialog looks like the images above. And if I resize the Splitpane the layout works such that I can continuesly see the buttons in the bottom and the exercise break and total duration information in the top right as long as possible. The Combobox and the table resize accordingly.Nice. However, if I run the application the right side of the Gridpane is cut off and I need to move the Splitpane to the left in order to see the missing nodes:
Here, the fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.media.MediaView?>
<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="1000.0" stylesheets="#application.css" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ctrl.MainCtrl">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" onAction="#exit" text="Exit" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Sessions">
<items>
<MenuItem mnemonicParsing="false" onAction="#openSessionEditor" text="Open..." />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<SplitPane fx:id="splitPane" dividerPositions="0.5" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" BorderPane.alignment="CENTER">
<items>
<StackPane fx:id="mediaPane" prefHeight="150.0" prefWidth="200.0">
<children>
<MediaView fx:id="mediaView" fitHeight="200.0" fitWidth="200.0" />
<ImageView fx:id="imageView" pickOnBounds="true" preserveRatio="true" StackPane.alignment="CENTER_RIGHT">
<StackPane.margin>
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
</StackPane.margin></ImageView>
</children>
</StackPane>
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="0.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="0.0" />
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="0.0" />
<ColumnConstraints fillWidth="false" hgrow="NEVER" minWidth="0.0" />
<ColumnConstraints fillWidth="false" hgrow="NEVER" minWidth="0.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints fillHeight="false" vgrow="NEVER" />
<RowConstraints fillHeight="false" maxHeight="-Infinity" minHeight="10.0" vgrow="NEVER" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="-Infinity" vgrow="ALWAYS" />
<RowConstraints vgrow="NEVER" />
<RowConstraints minHeight="-Infinity" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<ComboBox fx:id="sessionComboBox" maxWidth="1.7976931348623157E308" minWidth="0.0" onAction="#sessionSelected" promptText="Select session..." GridPane.columnSpan="3" GridPane.hgrow="ALWAYS">
<GridPane.margin>
<Insets bottom="12.0" left="12.0" right="24.0" top="12.0" />
</GridPane.margin>
</ComboBox>
<TableView fx:id="exerciseTable" editable="true" maxHeight="1.7976931348623157E308" minWidth="0.0" tableMenuButtonVisible="true" GridPane.columnSpan="2147483647" GridPane.hgrow="ALWAYS" GridPane.rowIndex="2" GridPane.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="selectedColumn" maxWidth="-1.0" minWidth="30.0" prefWidth="30.0" text="Sel" />
<TableColumn fx:id="excerciseColumn" maxWidth="300.0" minWidth="80.0" prefWidth="160.0" sortable="false" text="Exercise" />
<TableColumn fx:id="setsColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Sets" />
<TableColumn fx:id="repsColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Reps" />
<TableColumn fx:id="repBreakColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Break" />
<TableColumn fx:id="introColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Intro" />
<TableColumn fx:id="startSpeedColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="Start Speed" />
<TableColumn fx:id="endSpeedColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="End Speed" />
<TableColumn fx:id="metronomColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="Metronom" />
<TableColumn fx:id="durationColumn" prefWidth="75.0" text="Duration" />
</columns>
<GridPane.margin>
<Insets bottom="6.0" left="12.0" right="24.0" />
</GridPane.margin>
</TableView>
<TextField fx:id="exerciseBreakTextField" maxWidth="40.0" minWidth="0.0" GridPane.columnIndex="4" GridPane.hgrow="NEVER">
<GridPane.margin>
<Insets bottom="12.0" right="30.0" top="12.0" />
</GridPane.margin>
</TextField>
<Label minWidth="0.0" text="Exercise break (sec)" GridPane.columnIndex="3" GridPane.hgrow="NEVER">
<GridPane.margin>
<Insets right="6.0" />
</GridPane.margin>
</Label>
<Label minWidth="0.0" text="Total duration (min)" GridPane.columnIndex="3" GridPane.rowIndex="1">
<GridPane.margin>
<Insets />
</GridPane.margin>
</Label>
<Label fx:id="durationLabel" maxWidth="40.0" text="0" GridPane.columnIndex="4" GridPane.hgrow="NEVER" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="6.0" right="30.0" />
</GridPane.margin>
</Label>
<Button fx:id="tableUpButton" maxHeight="-Infinity" mnemonicParsing="false" onAction="#exerciseUp" prefHeight="20.0" GridPane.hgrow="NEVER" GridPane.rowIndex="1" GridPane.vgrow="NEVER">
<GridPane.margin>
<Insets bottom="6.0" left="12.0" />
</GridPane.margin>
</Button>
<Button fx:id="tableDownButton" maxHeight="-Infinity" mnemonicParsing="false" onAction="#exerciseDown" prefHeight="20.0" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="1">
<GridPane.margin>
<Insets bottom="6.0" left="6.0" />
</GridPane.margin>
</Button>
<HBox nodeOrientation="RIGHT_TO_LEFT" spacing="6.0" GridPane.columnSpan="2147483647" GridPane.hgrow="NEVER" GridPane.rowIndex="3">
<children>
<Button mnemonicParsing="false" onAction="#saveSession" text="Save">
<HBox.margin>
<Insets right="24.0" />
</HBox.margin>
</Button>
<Button minWidth="0.0" mnemonicParsing="false" onAction="#editExercise" prefWidth="50.0" text="Edit" />
<Button minWidth="0.0" mnemonicParsing="false" onAction="#deleteExercise" text="Delete" />
<Button minWidth="0.0" mnemonicParsing="false" onAction="#addNewExercise" prefWidth="50.0" text="Add" />
</children>
<GridPane.margin>
<Insets bottom="12.0" left="12.0" right="24.0" top="6.0" />
</GridPane.margin>
</HBox>
</children>
</GridPane>
</items>
</SplitPane>
</center>
<bottom>
<GridPane BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="NEVER" minWidth="10.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" vgrow="ALWAYS" />
<RowConstraints minHeight="10.0" vgrow="ALWAYS" />
</rowConstraints>
<children>
<Label text="Set:" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="30.0" top="12.0" />
</GridPane.margin>
</Label>
<Label fx:id="setLabel" text="0" GridPane.columnIndex="1" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="12.0" top="12.0" />
</GridPane.margin>
</Label>
<Label text="Repetition:" GridPane.columnIndex="2" GridPane.rowIndex="1">
<GridPane.margin>
<Insets top="12.0" />
</GridPane.margin></Label>
<Label fx:id="repLabel" text="0" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="12.0" top="12.0" />
</GridPane.margin>
</Label>
<Label fx:id="exerciseLabel" styleClass="exercise-header" text="Exercise" GridPane.columnSpan="5">
<GridPane.margin>
<Insets left="24.0" top="12.0" />
</GridPane.margin>
</Label>
<Button fx:id="startStopContinueButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#startStopContinuePressed" styleClass="start-button" text="Start" GridPane.columnSpan="5" GridPane.hgrow="ALWAYS" GridPane.rowIndex="2" GridPane.rowSpan="2" GridPane.vgrow="ALWAYS">
<GridPane.margin>
<Insets bottom="24.0" left="24.0" right="12.0" top="12.0" />
</GridPane.margin>
</Button>
<Slider fx:id="speedSlider" blockIncrement="0.5" majorTickUnit="1.0" max="8.0" showTickLabels="true" showTickMarks="true" GridPane.columnIndex="6" GridPane.hgrow="SOMETIMES" GridPane.rowIndex="2">
<GridPane.margin>
<Insets right="24.0" top="12.0" />
</GridPane.margin>
</Slider>
<Slider fx:id="volumeSlider" majorTickUnit="0.2" max="1.0" showTickMarks="true" value="1.0" GridPane.columnIndex="6" GridPane.hgrow="SOMETIMES" GridPane.rowIndex="3">
<GridPane.margin>
<Insets bottom="24.0" right="24.0" top="12.0" />
</GridPane.margin>
</Slider>
<Label text="Speed" GridPane.columnIndex="5" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="12.0" right="6.0" top="12.0" />
</GridPane.margin>
</Label>
<Label text="Volume" GridPane.columnIndex="5" GridPane.rowIndex="3">
<GridPane.margin>
<Insets bottom="24.0" left="12.0" right="6.0" top="12.0" />
</GridPane.margin>
</Label>
</children>
</GridPane>
</bottom>
</BorderPane>
There is no magic in loading the fxml:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
URL url = getClass().getClassLoader().getResource("ui/MainScene.fxml");
Parent root = FXMLLoader.load(url);
Scene scene = new Scene(root);
primaryStage.setTitle("Sifu says...");
// primaryStage.setFullScreen(true);
// primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
EDIT:
If I don't set the last column width manually, then the application version does not cut the right side but looks like the 2nd picture.
END_EDIT
What am I doing wrong? Thank you in advance.
Unfortunately, could not find a solution, just a minimal reproducible example for debugging/analysing: I think the behavior might be a bug.
The example below is part of the upper right gridpane in the question. The buttons in its second line are wired to increment/decrement the preferred width of the first column and log the table's pref width (along with its current width).
When run as-is, the initial layout is as expected, that is the last grid column the same as the restricted max of the upper left textField.
incr/dec the pref column width a bit and see that the the table's pref width is at its lower boundary (400 * golden-ratio), so doesn't change
increment further to see the misbehavior (the last grid column increasing): it starts as soon as the table's pref is increasing.
output (the numbers might depend on screen size/resolution):
pref: 247.2135948 actual: 315.3333333333333 // pref at lower boundary
pref: 247.2135948 actual: 315.3333333333333
pref: 247.66666666666669 actual: 315.3333333333333 // incr pref -> last grid column increasing
pref: 248.66666666666669 actual: 316.0
Example code
public class GridPaneTableExtract extends Application {
private Parent createContent() {
GridPane grid = new GridPane();
ObservableList<ColumnConstraints> columnConstraints = grid.getColumnConstraints();
for (int i = 0; i < 5; i++) {
if (i == 2) {
columnConstraints.add(createGrowingColumn());
} else {
columnConstraints.add(createFixedColumn());
}
}
ComboBox<String> combo = new ComboBox<>();
combo.setPromptText("some prompt");
combo.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(combo, Priority.ALWAYS);
GridPane.setFillWidth(combo, true);
Label fieldLabel = new Label("break in seconds: ");
TextField field = new TextField();
field.setPrefColumnCount(2);
field.setMaxWidth(Region.USE_PREF_SIZE);
// first row
grid.add(combo, 0, 0, 3, 1);
grid.add(fieldLabel, 3, 0);
grid.add(field, 4, 0);
Button up = new Button("+");
Button down = new Button("-");
Label durationLabel = new Label("Total Duration");
Label duration = new Label("000");
// second row
grid.add(up, 0, 1);
grid.add(down, 1, 1);
grid.add(durationLabel, 3, 1);
grid.add(duration, 4, 1);
// third row
TableView table = createTable(3);
grid.add(table, 0, 2, 5, 1);
GridPane.setHgrow(table, Priority.ALWAYS);
GridPane.setFillWidth(table, true);
up.setOnAction(e -> {
updateColumnPref(table, 1);
});
down.setOnAction(e -> {
updateColumnPref(table, -1);
});
grid.setGridLinesVisible(true);
BorderPane content = new BorderPane(grid);
return content;
}
private void updateColumnPref(TableView table, double delta) {
TableColumn last = (TableColumn) table.getColumns().get(0);
last.setPrefWidth(last.getPrefWidth() + delta);
System.out.println(" pref: " + table.prefWidth(-1) + " actual: " + table.getWidth());
}
private ColumnConstraints createFixedColumn() {
ColumnConstraints constraint = new ColumnConstraints();
constraint.setHgrow(Priority.NEVER);
constraint.setFillWidth(false);
return constraint;
}
private ColumnConstraints createGrowingColumn() {
ColumnConstraints constraint = new ColumnConstraints();
constraint.setHgrow(Priority.ALWAYS);
constraint.setFillWidth(true);
return constraint;
}
private TableView createTable(int colCount) {
TableView table = new TableView();
for (int i = 0; i < colCount; i++) {
TableColumn column = new TableColumn("column " + i);
table.getColumns().add(column);
}
if (colCount == 4) {
((TableColumn) table.getColumns().get(1)).setPrefWidth(40);
}
return table;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setX(20);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This is an opinionated answer.
I'd get rid of a lot of the hard coding of constraints and use the GridPane minimally, only where it makes sense to align things in a Grid. Otherwise, I would use other layout panes (e.g., VBox, HBox, ButtonBar) with appropriate padding, spacing and alignment values to get the layout required.
Don't use node orientation for layout purposes. The purpose of that setting is described in the documentation:
Node orientation describes the flow of visual data within a node. In the English speaking world, visual data normally flows from left-to-right. In an Arabic or Hebrew world, visual data flows from right-to-left. This is consistent with the reading order of text in both worlds. The default value is left-to-right.
But your app is in English, so you shouldn't change the default node orientation.
The sample below demonstrates the approach I advocate. It is not supposed to be a complete layout for your app, nor to be exactly the layout you want to create for the provided elements (you will need to make adjustments). Nor does it try to solve all issues you raise in your question. Hopefully, it will provide you with a start on how to fix some of your issues by applying the recommended approach.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<children>
<GridPane hgap="10.0" vgap="10.0">
<columnConstraints>
<ColumnConstraints hgrow="ALWAYS" />
<ColumnConstraints hgrow="SOMETIMES" />
<ColumnConstraints hgrow="SOMETIMES" />
</columnConstraints>
<rowConstraints>
<RowConstraints vgrow="SOMETIMES" />
<RowConstraints vgrow="SOMETIMES" />
</rowConstraints>
<children>
<ComboBox fx:id="sessionComboBox" maxWidth="1.7976931348623157E308" promptText="Select session..." />
<Label text="Exercise break (sec)" GridPane.columnIndex="1" />
<TextField fx:id="exerciseBreakTextField" prefColumnCount="2" GridPane.columnIndex="2" />
<HBox spacing="10.0" GridPane.rowIndex="1">
<children>
<Button fx:id="tableUpButton" maxHeight="-Infinity" mnemonicParsing="false" prefHeight="20.0" />
<Button fx:id="tableDownButton" maxHeight="-Infinity" mnemonicParsing="false" prefHeight="20.0" />
</children>
</HBox>
<Label text="Total duration (min)" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label fx:id="durationLabel" text="0" GridPane.columnIndex="2" GridPane.rowIndex="1" />
</children>
</GridPane>
<TableView fx:id="exerciseTable" editable="true" prefWidth="690.0" tableMenuButtonVisible="true" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="selectedColumn" maxWidth="-1.0" minWidth="30.0" prefWidth="30.0" text="Sel" />
<TableColumn fx:id="excerciseColumn" maxWidth="300.0" minWidth="80.0" prefWidth="160.0" sortable="false" text="Exercise" />
<TableColumn fx:id="setsColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Sets" />
<TableColumn fx:id="repsColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Reps" />
<TableColumn fx:id="repBreakColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Break" />
<TableColumn fx:id="introColumn" maxWidth="-Infinity" minWidth="40.0" prefWidth="40.0" sortable="false" text="Intro" />
<TableColumn fx:id="startSpeedColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="Start Speed" />
<TableColumn fx:id="endSpeedColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="End Speed" />
<TableColumn fx:id="metronomColumn" maxWidth="-Infinity" minWidth="80.0" sortable="false" text="Metronom" />
<TableColumn fx:id="durationColumn" prefWidth="75.0" text="Duration" />
</columns>
</TableView>
<ButtonBar buttonOrder="+U_R">
<buttons>
<Button mnemonicParsing="false" text="Add" />
<Button mnemonicParsing="false" text="Edit" />
<Button mnemonicParsing="false" text="Delete" />
<Button mnemonicParsing="false" text="Save" ButtonBar.buttonData="RIGHT" />
</buttons>
</ButtonBar>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
In scene builder preview button is on exact spot i.e bottom right corner without any gap but when i run my application there is gap.
Button is on a pane and pane is placed on top of anchor pane.
How can i fix this issue?
same thing happening with other components when i placed them on far right or far bottom.
Please examin the sample. Pay attention to min_width, min_height, pref_width, pref_height, max_width, max_height, vgrow, hgrow attributues in SceneBuiler.
The main concept is that type of pane you use sholud give you desired layout. It should not be hardcoded.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<VBox style="-fx-background-color: yellow;">
<children>
<StackPane VBox.vgrow="ALWAYS">
<children>
<Button mnemonicParsing="false" text="Menu Button" />
</children>
</StackPane>
<StackPane VBox.vgrow="ALWAYS">
<children>
<Button mnemonicParsing="false" text="Menu Button" />
</children>
</StackPane>
<StackPane layoutX="10.0" layoutY="210.0" VBox.vgrow="ALWAYS">
<children>
<Button mnemonicParsing="false" text="Menu Button" />
</children>
</StackPane>
<StackPane layoutX="10.0" layoutY="276.0" VBox.vgrow="ALWAYS">
<children>
<Button mnemonicParsing="false" text="Menu Button" />
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</StackPane>
<StackPane layoutX="10.0" layoutY="310.0" VBox.vgrow="ALWAYS">
<children>
<Button mnemonicParsing="false" text="Menu Button" />
</children>
</StackPane>
</children>
</VBox>
<VBox style="-fx-background-color: red;" HBox.hgrow="ALWAYS">
<children>
<StackPane prefHeight="150.0" prefWidth="200.0" style="-fx-background-color: green;" VBox.vgrow="ALWAYS">
<children>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" style="-fx-background-color: purple;">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Label" />
<TextField GridPane.columnIndex="1" />
<TextField GridPane.columnIndex="1" GridPane.rowIndex="1" />
<TextField GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Label" GridPane.rowIndex="1" />
<Label text="Label" GridPane.rowIndex="2" />
</children>
</GridPane>
</children>
</StackPane>
<HBox alignment="TOP_RIGHT">
<children>
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</children>
</VBox>
</children>
</HBox>
Have you put constraints in the layout section of the button? It could be related to that. Check layout x and layout of the button and the pref size x and y of AnchorPane, if that is same, the button must be on the left-bottom corner of the pane.
I want to create three pop-up windows in JavaFX. These windows are almost the same, but they differ in styles. For example, the user deletion window has a black title field, green to activate the user, and red to block. I want to create only one such window in FXML, and then pass the styles as a parameter. How can I do this and is this even possible?
you can either create a custom dialog window or use javafx native dialog window
---------------here i i have created a custom dialog window and i change the colors and icons dynamically as i need
-------FXML-------------------
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<GridPane xmlns:fx="http://javafx.com/fxml/1" style="-fx-border-color: red;" xmlns="http://javafx.com/javafx/8.0.141">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="194.0" minHeight="10.0" prefHeight="117.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="113.0" minHeight="10.0" prefHeight="113.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="113.0" prefWidth="400.0" style="-fx-background-color: e34c5e;">
<children>
<ImageView fitHeight="82.0" fitWidth="104.0" layoutX="153.0" layoutY="18.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="17.0" AnchorPane.leftAnchor="153.0" AnchorPane.rightAnchor="165.0" AnchorPane.topAnchor="18.0">
<image>
<Image url="#../Images/eror_icon.png" />
</image>
</ImageView>
</children></AnchorPane>
<AnchorPane layoutX="10.0" layoutY="10.0" prefHeight="129.0" prefWidth="400.0" style="-fx-background-color: white;" GridPane.rowIndex="1">
<children>
<GridPane layoutX="84.0" layoutY="-5.0" prefHeight="99.0" prefWidth="400.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="43.0" minHeight="10.0" prefHeight="39.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="27.0" minHeight="10.0" prefHeight="22.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="36.0" minHeight="10.0" prefHeight="36.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label alignment="CENTER" contentDisplay="CENTER" prefHeight="50.0" prefWidth="401.0" text="Item Not Selected !">
<font>
<Font name="System Bold" size="19.0" />
</font>
</Label>
<Label alignment="CENTER" layoutX="10.0" layoutY="43.0" prefHeight="50.0" prefWidth="401.0" text="Item to Purchase Is Not Selected..Select an Item" GridPane.rowIndex="1" />
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="2">
<children>
<JFXButton fx:id="btnOk" buttonType="RAISED" onMouseClicked="#btnOkClicked" prefHeight="32.0" prefWidth="121.0" style="-fx-background-color: e34c5e; -fx-background-radius: 80;" text="Ok" textFill="WHITE">
<HBox.margin>
<Insets bottom="2.0" top="2.0" />
</HBox.margin>
<font>
<Font name="System Bold" size="15.0" />
</font>
</JFXButton>
</children>
</HBox>
</children>
</GridPane>
</children>
</AnchorPane>
</children>
</GridPane>
--------------------Controller--------
package Controllers;
import javafx.scene.control.Dialog;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class CustomAlertDialogBox {
private Dialog currentDialog;
public CustomAlertDialogBox(Dialog dialog) {
this.currentDialog = dialog;
}
public void btnOkClicked(MouseEvent mouseEvent) {
Stage stage = (Stage) currentDialog.getDialogPane().getScene().getWindow();
stage.close();
}
}
check this for native dialog box
http://code.makery.ch/blog/javafx-dialogs-official/
I have GridPane in my JavaFX window and I span two columns.
When I click on grid lines visible in scenebuilder there is line in the middle of merged cells.
How can I remove or not show this line?
Here is one approach: In this approach, I make the GridPane's background black. Then I stretch each Node to fill their Cell and make the Node's background white. Also on the GridPane, I set all the Insets to 5 and the VGap and HGap to 5. I suggest you use CSS and take #James_D's approach here.
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane hgap="5.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: black;" vgap="5.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: white;" text="Label" GridPane.columnSpan="2147483647" />
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: white;" text="Label" GridPane.rowIndex="1" />
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: white;" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: white;" text="Label" GridPane.rowIndex="2" />
<Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: white;" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="2" />
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</GridPane>
But I would suggest you use CSS and follow #James_D's approach here.
So I have this code here:
// import statements are ommitted
public class HomePage implements Initializable{
#FXML
private volatile MenuButton fullUserName;
public void initialize(URL url, ResourceBundle resourceBundle)
{
}
public void loadHomePage(Stage primaryStage) throws IOException {
Parent HomePage = FXMLLoader.load(LoginScreen.class.getResource("HomePage.fxml"));
Timer animTimer = new Timer();
animTimer.scheduleAtFixedRate(new TimerTask() {
int i=0;
#Override
public void run() {
if (i<100) {
primaryStage.setX(primaryStage.getX() - 4);
primaryStage.setY(primaryStage.getY() - 1);
primaryStage.setWidth(primaryStage.getWidth()+8.11);
primaryStage.setHeight(primaryStage.getHeight()+3.19);
} else {
this.cancel();
}
i++;
}
}, 0, 10);
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10 * 101), ev -> {
primaryStage.setResizable(true);
primaryStage.setScene(new Scene(HomePage));
fullUserName.setText("User"); // Got NullPointerException at this line
}));
timeline.play();
}
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
loadHomePage(primaryStage);
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
HomePage.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="9.0" minWidth="16.0" prefHeight="720.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.HomePage">
<top>
<GridPane maxHeight="-Infinity" minHeight="-Infinity" prefHeight="32.5" style="-fx-background-color: #aeccc9;" BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="11.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="27.5" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="11.5" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" />
<ColumnConstraints hgrow="ALWAYS" maxWidth="-Infinity" minWidth="-Infinity" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="15.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<MenuButton fx:id="fullUserName" mnemonicParsing="false" GridPane.columnIndex="4">
<items>
<MenuItem mnemonicParsing="false" text="Logout" />
<MenuItem mnemonicParsing="false" text="Exit" />
</items>
<graphic>
<ImageView fitHeight="22.0" fitWidth="22.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#Cool-50.png" />
</image>
</ImageView>
</graphic>
<padding>
<Insets bottom="-1.5" left="-1.5" right="-1.5" top="-1.5" />
</padding>
</MenuButton>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Title" GridPane.columnIndex="3">
<font>
<Font name="Arial Rounded MT Bold" size="22.0" />
</font>
</Text>
</children>
</GridPane>
</top>
</BorderPane>
So as you can see, I got a NullPointerException when executing fullUserName.setText("User"); which means that the "fullUserName" menu button is not initialized.
Is it possible for the program to wait until fullUserName (or any other FXML variables for that matter) has been initialized so that I can execute the statement?