I'm trying to make a window with a certain layout using 2 columns, but I can't quite get it to work the way I want.
First, some simplified example code:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class GridTest extends Application {
#Override
public void start(final Stage stage) throws Exception {
final GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10, 10, 10, 10));
grid.add(new Label("Something:"), 0, 0);
final Spinner<?> s1 = new Spinner<>();
grid.add(s1, 1, 0);
grid.add(new Label("Another thing:"), 0, 1);
final Spinner<?> s2 = new Spinner<>();
grid.add(s2, 1, 1);
final Button b = new Button("A button");
grid.add(b, 0, 2, 2, 1);
stage.setScene(new Scene(grid, 400, 150));
stage.show();
}
public static void main(final String... args) {
launch(args);
}
}
Here is what I want:
the spinners and the button should use all available width
the labels should always keep their preferred width, and never change their size
when I make the window wider, the spinners and the button should grow
when I make the window narrower, the spinners and the button should shrink, but not below their preferred width
I tried using all sorts of constraints, but I couldn't get everything to work right. I think the main problem I found was that after getting the spinners to use the available width, they refused to shrink when narrowing the window.
I'm using Oracle JDK 1.8.0_60 in Linux.
This seems to be a Spinner bug, I'm getting much better results if I use TextFields instead.
Example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
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.Priority;
import javafx.stage.Stage;
public class GridTest extends Application {
#Override
public void start(final Stage stage) throws Exception {
final GridPane grid = new GridPane();
grid.setHgap(5);
grid.setVgap(5);
grid.setPadding(new Insets(10, 10, 10, 10));
ColumnConstraints cc = new ColumnConstraints();
cc.setMinWidth(GridPane.USE_PREF_SIZE);
grid.getColumnConstraints().add(cc);
cc = new ColumnConstraints();
cc.setMinWidth(GridPane.USE_PREF_SIZE);
cc.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().add(cc);
grid.add(new Label("Something:"), 0, 0);
final TextField t1 = new TextField();
grid.add(t1, 1, 0);
grid.add(new Label("Another thing:"), 0, 1);
final TextField t2 = new TextField();
grid.add(t2, 1, 1);
final Button b = new Button("A button");
b.setMaxWidth(Double.MAX_VALUE);
grid.add(b, 0, 2, 2, 1);
stage.setScene(new Scene(grid, 400, 150));
stage.show();
}
public static void main(final String... args) {
launch(args);
}
}
But replace the TextFields with Spinners and all hell breaks loose...
Related
I'm new to JavaFX, trying to build a GUI program that displays a bill for a table at a restaurant when you click on that table. The spacing is off between the table buttons and I'm not sure why.
The GUI class for my program:
package restaurantBillingProgram;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
// Label
pane.add(new Label("Generate bill"), 1, 0);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
btT1.setOnAction(e - > Billing.generateT1());
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
btT2.setOnAction(e - > Billing.generateT2());
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
btT3.setOnAction(e - > Billing.generateT3());
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}
From the Javadoc:
Row/Column Sizing
By default, rows and columns will be sized to fit their content; a column will be wide enough to accommodate the widest child, ...
The label in row 0 column 1 forces that column to be wider.
You probably want the label to be centered and span all 3 columns.
While doing you layout, use pane.setGridLinesVisible(true). This should only be used during debugging. It can be very useful for situations like your current situation. As #Jim Garrison pointed out, your Label is causing the issue:
Issue:
One way to fix this is to let the Label span all columns and center the Label's text.
Fix:
Key Code:
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);// Look at the following link to see how this add method works. https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/GridPane.html#add(javafx.scene.Node,int,int,int,int)
Full Code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
pane.setGridLinesVisible(true);//Use for debugging only!!!!
// Label
Label label = new Label("Generate bill");
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}
I am trying to create a start screen in javaFX and add a background to it to go behind the button and the text. It does not seem to work for me however, as the background doe not appear for my app. Would like to know what is wrong. Here is my code:
package calendar;
import java.io.IOException;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Calendar extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("JavaFX Welcome");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Get Started");
scenetitle.setFont(Font.font("Bookman", FontWeight.BOLD, 30));
grid.add(scenetitle, 0, 0, 2, 1);
Button start = new Button("Start");
HBox hbStart = new HBox(10);
hbStart.setAlignment(Pos.CENTER);
hbStart.getChildren().add(start);
grid.add(hbStart, 1, 10);
final Text actiontarget = new Text();
grid.add(actiontarget, 1, 6);
start.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
}
});
Scene scene = new Scene(grid, 800, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add
(Calendar.class.getResource("background.css").toExternalForm());
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The code for the css file is as follows:
.root { -fx-background-image: url("background.jpeg"); }
Any help would be appreciated. I am fairly certain that this is not a problem with me misplacing or misnaming the file.
The code below should move the Label based on the position of the horizontal scroll bar so that the Label appears to remain stationary. This almost works perfectly however when you move the scrollbar to the end the label has moved slightly so it does not look like it is in the same position.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class LblMoves extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
VBox images = new VBox();
images.setPadding(new Insets(0, 0, 0, 0));
Label posLbl = new Label("0");
images.getChildren().add(posLbl);
images.setPrefSize(Integer.MAX_VALUE, 50);
ScrollPane scrollPane = new ScrollPane(images);
scrollPane.setStyle("-fx-background: #FFFFFF;");
scrollPane.hvalueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
double screenPer = scrollPane.getHvalue() * scrollPane.getWidth();
double pos = scrollPane.getHvalue() * images.getWidth();
double marg = pos - screenPer;
posLbl.setPadding(new Insets(0, 0, 0, marg));
}
});
Scene scene = new Scene(scrollPane, 600, 600);
primaryStage.setScene(scene);
primaryStage.setMaximized(true);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
You use the width of the ScrollPane. However the width used in the calculations for ScrollPane use the viewportBounds.
Also since by default the position is rounded to full pixels, which causes some movement of the Label (which could be fixed by using translateX instead of the padding).
InvalidationListener listener = o -> {
double marg = (images.getWidth() - scrollPane.getViewportBounds().getWidth()) * scrollPane.getHvalue();
posLbl.setTranslateX(marg);
// posLbl.setPadding(new Insets(0, 0, 0, marg));
};
scrollPane.hvalueProperty().addListener(listener);
scrollPane.viewportBoundsProperty().addListener(listener);
listener.invalidated(null);
This above picture is result of my code.But I want like the following.
How can I fix it? The following is my code.I read too many sources but they was too complicated. For example,a source that I read, I think this way is very complicated.Maybe there is a easy way to solve this problem.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Test extends Application {
public BorderPane border = new BorderPane();
public Label name = new Label("Name");
public Label surname = new Label("Surname");
public TextField name1 = new TextField();
public TextField surname1 = new TextField();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage arg0) throws Exception {
Scene scene = new Scene(new VBox(), 300, 200);
arg0.setTitle("Header");
arg0.setResizable(false);
scene.setFill(Color.OLDLACE);
StackPane grid = addStackPane();
border.setMargin(grid, new Insets(12,12,12,12));
border.setCenter(grid);
((VBox) scene.getRoot()).getChildren().add(border);
arg0.setScene(scene);
arg0.show();
}
#SuppressWarnings("static-access")
public StackPane addStackPane() {
StackPane pane = new StackPane();
GridPane grid = new GridPane();
Label title = new Label("Border Title");
title.setStyle("-fx-translate-y: -7");
pane.setAlignment(title, Pos.TOP_LEFT);
grid.setStyle("-fx-content-display: top");
grid.setStyle("-fx-border-insets: 20 15 15 15");
grid.setStyle("-fx-background-color: white");
grid.setStyle("-fx-border-color: black");
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 10, 25, 10));
grid.add(name, 1, 0);
grid.add(name1, 2, 0);
grid.add(surname, 1, 1);
grid.add(surname1, 2, 1);
pane.getChildren().addAll(grid, title);
return pane;
}
}
Thank you all that reads this topic.
Try to set -fx-background-color of your title label to the same color as the borderPane's background. And make sure you set in a css file, because it's not possible to set multiple styles via setStyle() unless you concatenate them:
myComponent.setStyle("-fx-text-fill: white;"+
"-fx-background-color: black;");
Furthermore it is bad practice to use an InlineStyleSheets as it always has a higher priority than a rule specified in a CSS StyleSheet.
(If you change pane.setAlignment(title, Pos.TOP_LEFT) to StackPane.setAlignment(title, Pos.TOP_LEFT) you can remove the "static-acces" warning.)
User jewelsea has made a control to perform this.
The related stackoverflow question is: How to add border to panel of javafx?
And on gitHub: https://gist.github.com/jewelsea/2838292
I have used it with minimal modification and it works like a charm.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.OverrunStyle;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class BindTooltipToFocusEvent extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("How to bind tooltip to focus event?");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(25, 25, 25, 25));
grid.add(new Label("Email"), 0, 0);
grid.add(new TextField(), 0, 1);
grid.add(new Label("Password"), 0, 2);
Tooltip tooltip = new Tooltip("Paswords must contain 1-50 characters, etc...");
tooltip.setWrapText(true);
tooltip.setTextOverrun(OverrunStyle.ELLIPSIS);
PasswordField pf = new PasswordField();
pf.setPromptText("Password");
pf.setTooltip(tooltip);
grid.add(pf, 0, 3);
stage.setScene(new Scene(grid, 300, 275));
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) { launch(); }
}
Running the provided example application demonstrates the default tooltip behavior in Java FX to display the tooltip on the mouse hover event. Hovering for a second or two over the password field will cause the tooltip to be displayed. I would rather have the tooltip visible while the user is entering their password rather than while they (might) hover over the field with their mouse.
How can this behavior be changed such that the tooltip will be displayed while the password field has focus?
I know it would be possible to add another label to the user interface and modify its visibility property based on when the password field has focus. This question is not asking for a workaround to the example use case. I would like to know specifically how to make the tooltip visible while the parent field has focus.
To achieve that you should manage the tooltip's visibility and position yourself:
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("How to bind tooltip to focus event?");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(5);
grid.setPadding(new Insets(25, 25, 25, 25));
grid.add(new Label("Email"), 0, 0);
grid.add(new TextField(), 0, 1);
grid.add(new Label("Password"), 0, 2);
final Tooltip tooltip = new Tooltip("Paswords must contain 1-50 characters, etc...");
tooltip.setWrapText(true);
tooltip.setTextOverrun(OverrunStyle.ELLIPSIS);
final PasswordField pf = new PasswordField();
pf.setPromptText("Password");
//pf.setTooltip(tooltip);
pf.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
tooltip.show(pf, //
// popup tooltip on the right, you can adjust these values for different positions
pf.getScene().getWindow().getX() + pf.getLayoutX() + pf.getWidth() + 10, //
pf.getScene().getWindow().getY() + pf.getLayoutY() + pf.getHeight());
} else {
tooltip.hide();
}
}
});
grid.add(pf, 0, 3);
stage.setScene(new Scene(grid, 300, 275));
stage.sizeToScene();
stage.show();
}
One drawback of this approach is that the tooltip will be stayed on the screen even when the window is moved/dragged to the different place.