I'm not sure if this is possible and I couldn't find anything on it, but could I make a scrollpane scroll forever?
I am constantly adding new buttons and labels into the window once a button is pressed, and eventually is gets to the bottom.
I may be setting it up wrong but here is code:
BorderPane bp = new BorderPane();
GridPane gp = new GridPane();
Button b = new Button("click");
gp.add(b, 1, 1);
ScrollPane sp = new ScrollPane(gp);
bp.setTop(sp);
b.setOnAction(e -> createLabel());
Your'e nearly there, all you now need to do is add the scroll pane as a child of whatever the container is
GridPane gp = new GridPane();
Button b = new Button("click");
gp.add(b, 1, 1);
b.setOnAction(e -> createLabel());
ScrollPane sp = new ScrollPane(gp);
container.add(sp); // where container is whatever node that'll contain the gridpane.
Play with this code
public class Controller {
#FXML private VBox topLevelContainer; // root fxml element
#FXML
void initialize(){
GridPane gridPane = new GridPane();
ScrollPane sp = new ScrollPane(gridPane);
topLevelContainer.getChildren().add(sp);
// add a 100 buttons to 0th column
for (int i = 0; i < 100; i++) {
gridPane.add(new Button("button"),0,i);
}
}
}
I have a JavaFX dialog, and it seems the content of the grid don't span the entire width of the dialog. Right now it spans to a MAXIMUM of the the left side of the dialog, to the width of the OK button. I'd like to either remove the white space to the right of my input fields. Or span the entire width.
I've tried:
Setting the min-width of the gridpane
Setting the min-width of the textfields
Setting the width of the stage
I'm pretty much at a loss here.
Here's my code!
// Create the custom dialog.
Dialog dialog = new Dialog<>();
dialog.initOwner(mainStage);
// Set Custom Icon
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
stage.getIcons().add(new Image(Constants.kApplicationIcon));
dialog.getDialogPane().getStylesheets().add(Constants.kRootStylesheet);
dialog.getDialogPane().getStyleClass().add("accountDialog");
dialog.setTitle("New User Detected");
dialog.setHeaderText("Please complete user registration!");
// Set the button types.
ButtonType loginButtonType = new ButtonType("Create Account", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);
// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));
grid.setAlignment(Pos.CENTER);
grid.setId("accountGrid");
TextField firstName = new TextField("First Name");
TextField lastName = new TextField("Last Name");
TextField email = new TextField("Email");
TextField gender = new TextField("Gender");
firstName.setId("textField");
lastName.setId("textField");
firstName.setPromptText("");
lastName.setPromptText("");
gender.setPromptText("");
email.setPromptText("");
email.setId("textField");
gender.setId("textField");
ToggleGroup studentRadioGroup = new ToggleGroup();
RadioButton mentorRadio = new RadioButton("Mentor");
RadioButton studentRadio = new RadioButton("Student");
studentRadio.fire();
mentorRadio.setToggleGroup(studentRadioGroup);
studentRadio.setToggleGroup(studentRadioGroup);
grid.add(new Label("First Name:"), 0, 0);
grid.add(firstName, 1, 0);
grid.add(new Label("Last Name:"), 0, 1);
grid.add(lastName, 1, 1);
grid.add(new Label("Email:"), 0, 2);
grid.add(email, 1, 2);
grid.add(new Label("Gender:"), 0, 3);
grid.add(gender, 1, 3);
GridPane.setHalignment(grid, HPos.CENTER);
GridPane.setHalignment(studentRadio, HPos.CENTER);
GridPane.setHalignment(studentRadio, HPos.CENTER);
grid.add(studentRadio, 0, 4);
grid.add(mentorRadio, 1, 4);
grid.setGridLinesVisible(true);
// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);
// Do some validation (using the Java 8 lambda syntax).
firstName.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty());
});
dialog.getDialogPane().setContent(grid);
// Request focus on the firstname field by default.
Platform.runLater(firstName::requestFocus);
Optional<ButtonType> result = dialog.showAndWait();
ArrayList<String> data = new ArrayList<>();
System.out.println(result.get().toString());
if (result.get().getButtonData() == ButtonBar.ButtonData.OK_DONE) {
data.add("TRUE");
data.add(firstName.getText());
data.add(lastName.getText());
data.add(email.getText());
data.add(gender.getText());
data.add(mentorRadio.isSelected() ? "TRUE" : "FALSE");
} else {
data.add("FALSE");
}
return data;
Here's an image of the result. I want to again; either remove all the whitespace to the right of my grid, or span my grid to fit the whole width.
Image of Result
You are creating that extra space with grid.setPadding(new Insets(20, 150, 10, 10));. That 150 is the right padding. See the Insets documentation.
The GridPane is resized to it's preferred size. The padding on the right of 150 prevents the TextFields from growing larger in this direction.
grid.setPadding(new Insets(20, 150, 10, 10)); // sets right padding to 150
If you want to grow the TextFields to the maximum size the Labels/RadioButton to the left leaves, you should use ColumnConstraints to specify, that the second column should always grow:
ColumnConstraints constraints = new ColumnConstraints();
constraints.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().addAll(new ColumnConstraints(), constraints);
It's also possible to specify the preferred width of the second column via the ColumnConstraints object.
To test the behaviour when the window size changes, you could make the dialog resizeable:
dialog.setResizable(true);
To see the area the GridPane covers it could be helpful to assign a background:
grid.setStyle("-fx-background-color: red;");
Have you tried the setExpandableContent() method from the dialog's pane? You can pass in whatever node you like, and then it should be able to resize to fit the entire Alert.
An example of setting a GridPane as the Alert's content.
GridPane() grid = new GridPane();
Alert alert = new Alert(AlertType.INFORMATION);
alert.getDialogPane().setExpandableContent(grid);
Left side of picture: It is when it is run directly from intellij
Right side of picture: Created fat jar (which is created by the feature called "Jar with dependencies") is run as double click from mouse
As you can see, Checkboxes are not aligned .Every component is created by code not from fxml...What can be the cause of this?
Edit:
First of all, width and height are fixed. Thus they will never change. I disabled Them Below you can find the code.
HBox row1 = new HBox(10);
//row1.setPadding();
Label nameLbl = new Label("Login Email");
nameLbl.setPrefWidth(DefaultValues.LABEL_WIDTH);
nameLbl.setPadding(new Insets(4,0,0,0));
txtEmail = new TextField();
txtEmail.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
txtEmail.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue)
checkLicence();
});
row1.getChildren().addAll(nameLbl,txtEmail);
HBox row2 = new HBox(10);
Label passwordLbl = new Label("Password");
passwordLbl.setPrefWidth(DefaultValues.LABEL_WIDTH);
passwordLbl.setPadding(new Insets(4,0,0,0));
txtPassword = new PasswordField();
txtPassword.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
row2.getChildren().add(passwordLbl);
row2.getChildren().add(txtPassword);
HBox row3 = new HBox(10);
//row1.setPadding();
Label refreshTime = new Label("Refresh Time");
refreshTime.setPrefWidth(DefaultValues.LABEL_WIDTH);
refreshTime.setPadding(new Insets(4,0,0,0));
txtRefreshTime = new TextField();
txtRefreshTime.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
txtRefreshTime.setPromptText("Seconds");
txtRefreshTime.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
txtRefreshTime.setText(newValue.replaceAll("[^\\d]", ""));
}
});
row3.getChildren().add(refreshTime);
row3.getChildren().add(txtRefreshTime);
HBox row3_1 = new HBox(10);
//row1.setPadding();
Label userCountLbl = new Label("User Count(for point calc.)");
userCountLbl.setPrefWidth(DefaultValues.LABEL_WIDTH);
userCountLbl.setPadding(new Insets(4,0,0,0));
txtUserCountForPointCalc = new TextField();
txtUserCountForPointCalc.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
txtUserCountForPointCalc.setPromptText("Not very important");
txtUserCountForPointCalc.textProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
txtUserCountForPointCalc.setText(newValue.replaceAll("[^\\d]", ""));
}
});
row3_1.getChildren().add(userCountLbl);
row3_1.getChildren().add(txtUserCountForPointCalc);
HBox row4 = new HBox(10);
//row1.setPadding();
Label showNotifications = new Label("Show Notifications");
showNotifications.setPrefWidth(DefaultValues.LABEL_WIDTH - 10);
showNotifications.setPadding(new Insets(4,0,0,0));
cbShowNotifications = new CheckBox();
cbShowNotifications.setPrefWidth(180);
Button btnClearNotificationCache = new Button("Clear Notification Cache");
btnClearNotificationCache.setOnAction(e -> {
notifiedAssignedToMeTickets.clear();
notifiedUnassignedTickets.clear();
});
row4.setAlignment(Pos.CENTER_LEFT);
row4.getChildren().addAll(showNotifications,cbShowNotifications,btnClearNotificationCache);
HBox row5 = new HBox(10);
//row1.setPadding();
Label autoReplyCompanies = new Label("Auto-Reply Companies");
autoReplyCompanies.setPrefWidth(DefaultValues.LABEL_WIDTH);
autoReplyCompanies.setPadding(new Insets(4,0,0,0));
txtAutoReplyCompanies = new TextField();
txtAutoReplyCompanies.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
txtAutoReplyCompanies.setPromptText("(For Unassigned Tickets..)Seperate with ';' for multiple companies");
row5.getChildren().add(autoReplyCompanies);
row5.getChildren().add(txtAutoReplyCompanies);
//txtAutoReplyModules
HBox row5_2 = new HBox(10);
//row1.setPadding();
Label autoReplyModules = new Label("Auto-Reply Modules");
autoReplyModules.setPrefWidth(DefaultValues.LABEL_WIDTH);
autoReplyModules.setPadding(new Insets(4,0,0,0));
txtAutoReplyModules = new TextField();
txtAutoReplyModules.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
txtAutoReplyModules.setPromptText("(For Unassigned Tickets..)Seperate with ';' for multiple modules");
row5_2.getChildren().add(autoReplyModules);
row5_2.getChildren().add(txtAutoReplyModules);
HBox row6 = new HBox(10);
//row1.setPadding();
Label autoReplyMessage = new Label("Auto-Reply Message");
autoReplyMessage.setPrefWidth(DefaultValues.LABEL_WIDTH);
autoReplyMessage.setPadding(new Insets(4,0,0,0));
txtAutoReplyMessage = new TextArea();
txtAutoReplyMessage.setPrefSize(DefaultValues.TEXTAREA_WIDTH,65);
row6.getChildren().add(autoReplyMessage);
row6.getChildren().add(txtAutoReplyMessage);
//cbStatistics
HBox row6_1 = new HBox(10);
Label searchStatistics = new Label("Process Statistics");
searchStatistics.setPrefWidth(DefaultValues.LABEL_WIDTH - 10);
searchStatistics.setPadding(new Insets(4,0,0,0));
cbStatistics = new CheckBox();
cbStatistics.setSelected(true);
cbStatistics.setPrefWidth(180);
row6_1.getChildren().addAll(searchStatistics,cbStatistics);
HBox row7 = new HBox(10);
//row1.setPadding();
Label searchUnassignedsLbl = new Label("Search Unassigned Tickets");
searchUnassignedsLbl.setPrefWidth(DefaultValues.LABEL_WIDTH - 10);
searchUnassignedsLbl.setPadding(new Insets(4,0,0,0));
cbSearchUnassigneds = new CheckBox();
cbSearchUnassigneds.setSelected(true);
cbSearchUnassigneds.setPrefWidth(180);
//row7.setAlignment(Pos.CENTER_LEFT);
row7.getChildren().addAll(searchUnassignedsLbl,cbSearchUnassigneds);
HBox row8 = new HBox(10);
//row1.setPadding();
Label searchAssignedToMe = new Label("Search Replied to u");
searchAssignedToMe.setPrefWidth(DefaultValues.LABEL_WIDTH);
searchAssignedToMe.setPadding(new Insets(4,0,0,0));
cbSearchAssignedToMeTickets = new CheckBox();
cbSearchAssignedToMeTickets.setSelected(true);
cbSearchAssignedToMeTickets.setPrefSize(DefaultValues.TEXTAREA_WIDTH,20);
row8.getChildren().add(searchAssignedToMe);
row8.getChildren().add(cbSearchAssignedToMeTickets);
HBox row9 = new HBox(10);
Label checkUpdateLbl = new Label("Check Updates");
checkUpdateLbl.setPrefWidth(DefaultValues.LABEL_WIDTH - 10);
checkUpdateLbl.setPadding(new Insets(4,0,0,0));
cbCheckUpdates = new CheckBox();
cbCheckUpdates.setSelected(checkUpdatesSetting);
cbCheckUpdates.setPrefWidth(180);
Button btnUpdateUpdater = new Button("Update Updater");
btnUpdateUpdater.setOnAction(event -> downloadUpdaterUpdate());
//btnUpdateUpdater.setPadding(new Insets(5));
row9.setAlignment(Pos.CENTER_LEFT);
row9.getChildren().addAll(checkUpdateLbl,cbCheckUpdates,btnUpdateUpdater);
HBox row10 = new HBox();
Label dummy = new Label("");
dummy.setPrefWidth(DefaultValues.LABEL_WIDTH);
Button btnSaveSettings = new Button("Save Settings");
btnSaveSettings.setOnAction(e -> {
if(txtEmail.getLength() == 0 || txtPassword.getLength() == 0 || txtRefreshTime.getLength() == 0)
showAlert(Alert.AlertType.ERROR,"","ilk 3 alan boş olamaz");
else{
Task<Void> task = new Task<Void>() {
#Override
protected Void call(){
shutDownCalled = true;
waitExecutorShutDown();
checkLicence();
Settings st = new Settings();
st.setEmail(txtEmail.getText().trim());
st.setPassword(txtPassword.getText().trim());
st.setRefreshTime(Integer.parseInt(txtRefreshTime.getText().trim()));
st.setUserCountForPointCalculation(txtUserCountForPointCalc.getLength() == 0 ? DefaultValues.userCountForPointCalculation : Integer.parseInt(txtUserCountForPointCalc.getText()));
st.setShowNotifications(cbShowNotifications.isSelected());
st.setAutoReplyCompanies(txtAutoReplyCompanies.getText().trim());
st.setAutoReplyModules(txtAutoReplyModules.getText().trim());
st.setAutoReplyMessage(txtAutoReplyMessage.getText().trim());
st.setSearchUnassignedTickets(cbSearchUnassigneds.isSelected());
st.setSearchAssignedToMeTickets(cbSearchAssignedToMeTickets.isSelected());
st.setCheckUpdates(cbCheckUpdates.isSelected());
st.setProcessStatistics(cbStatistics.isSelected());
Settings.saveNormalBotSettingsToFile(st);
settings = st;
needLogin = true;
initData(false);
return null;
}
};
new Thread(task).start();
mainTabs.getSelectionModel().select(0);
}
});
row10.getChildren().addAll(dummy,btnSaveSettings);
VBox vb = new VBox(9);
vb.setPadding(new Insets(10,10,0,10));
vb.getChildren().addAll(row1,row2,row3,row3_1,row5,row5_2,row6,row6_1,row4,row7,row8,row9,row10);
return vb;
An HBox makes no guarantee about the amount of space it actually assigns to any child node that is contained inside it. It merely guarantees to place them in order, with a minimum gap if you specify a spacing, and makes a best effort to size each child node to its preferred size. Many factors which are beyond your control will affect the actual size of each node, including font sizes (which depend on the available fonts), total size available to the HBox, etc etc. All these may change depending on the platform the application is running on, including depending on the JDK version.
So trying to line things up vertically by placing them in a collection of HBoxs and setting the preferred sizes of the child nodes is simply not a reliable way to approach this (and is not designed as such). The problem is there is no real way to connect the layout of one HBox to the layout of another HBox: they are all laid out independently. If you want to lay components out so they are aligned relative to each other both horizontally and vertically, you should use a GridPane, which is specifically designed for that purpose.
It is generally a very bad idea (not just in JavaFX; this applies to most UI toolkits) to hard-code sizes of anything, so anytime you are using this as a solution, there is almost certainly a better approach.
The basic idea behind using a GridPane would look like:
GridPane grid = new GridPane();
// padding around entire grid:
grid.setPadding(new Insets(4);
grid.setHgap(10);
grid.setVgap(9);
Label nameLbl = new Label("Login Email");
// column 0, row 0:
grid.add(nameLbl, 0, 0);
txtEmail = new TextField();
txtEmail.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue)
checkLicence();
});
// column 1, row 0, span 2 columns:
grid.add(txtEmail, 1, 0, 2, 1);
// ...
Label searchAssignedToMe = new Label("Search Replied to u");
// column 0, row 7:
grid.add(searchAssignedToMe, 0, 7);
cbSearchAssignedToMeTickets = new CheckBox();
cbSearchAssignedToMeTickets.setSelected(true);
// column 1, row 7, span two columns:
grid.add(cbSearchAssignedToMeTickets, 1, 7, 2, 1);
Label checkUpdateLbl = new Label("Check Updates");
// column 0, row 8:
grid.add(checkUpdateLbl, 0, 8);
cbCheckUpdates = new CheckBox();
cbCheckUpdates.setSelected(checkUpdatesSetting);
// column 1, row 8:
grid.add(cbCheckUpdates, 1, 8);
Button btnUpdateUpdater = new Button("Update Updater");
btnUpdateUpdater.setOnAction(event -> downloadUpdaterUpdate());
// column 2, row 8:
grid.add(btnUpdateUpdater, 2, 8);
// ...
Button btnSaveSettings = new Button("Save Settings");
btnSaveSettings.setOnAction(...);
// center button horizontally in its cells (it spans the whole row):
GridPane.setHalignment(btnSaveSettings, HPos.CENTER);
// column 0, row 9, span 3 columns:
grid.add(btnSaveSettings, 0, 9, 3, 1);
You can completely configure how any potential extra space is allocated among the columns (using ColumnConstraints instances), among the rows (using RowConstraints instances), and how the controls are aligned within their individual cell(s). You can also specify these on a node-by-node basis if you need.
You probably want, for example, hgrow of the three columns to be SOMETIMES, SOMETIMES, and ALWAYS; you may need to set the fillWidth of the TextInputControls to true.
See the GridPane documentation, which explains this all completely.
FeathersPerSec fps = new FeathersPerSec();
public void start(Stage primaryStage)
{
double getFeathers = fps.getNumOfFeathers();
//double setFeathers = fps.setNumOfFeathers(1);
double click = 1;
GridPane pane = new GridPane();
Image image = new Image("dancingChicken.gif");
Button chickenClick = new Button();
Label feathers = new Label("FPS " + click);
chickenClick.setGraphic(new ImageView(image));
chickenClick.setOnAction((e) -> {
click++;
feathers = new Label("FPS" + click);
});
heres a pieceof my program, im trying to make a cookie clicker type game. except with a chicken and feathers. Im trying to make it so when the user clicks the image button the number of feathers will increment by one. Im not sure what I should put in the setOnAction. with my current set up I keep getting a "Local Variable feathers defined in an enclosing scope must be final or effectively final."
You need to update the text of the current label, instead of creating a new label (which isn't part of your scene graph).
Additionally, you can't change a local variable inside a lambda expression (local variables accessed in a lambda expression must be final or effectively final (meaning they can only be assigned a value once)). The simplest fix for this is to move click to an instance variable:
private FeathersPerSec fps = new FeathersPerSec();
private double click ;
public void start(Stage primaryStage)
{
double getFeathers = fps.getNumOfFeathers();
//double setFeathers = fps.setNumOfFeathers(1);
click = 1;
GridPane pane = new GridPane();
Image image = new Image("dancingChicken.gif");
Button chickenClick = new Button();
Label feathers = new Label("FPS " + click);
chickenClick.setGraphic(new ImageView(image));
chickenClick.setOnAction((e) -> {
click++;
feathers.setText("FPS" + click);
});
}
I've got a little problem with my JavaFX code. I'm sure you all know that you can get the input from a TextInputDialog with an Optional< String > and .showAndWait(). But what should I do when I have a custom dialog with multiple TextFields and a ChoiceBox? How do I get the results from all of them when clicking OK? I thought about a List<String> but I didn't manage to do it..
Code (Custom Dialog):
public class ImageEffectInputDialog extends Dialog {
private ButtonType apply = new ButtonType("Apply", ButtonBar.ButtonData.OK_DONE);
private ButtonType cancel = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
public ImageEffectInputDialog(String title) {
setTitle(title);
setHeaderText(null);
GridPane dPane = new GridPane();
Label offsetX = new Label("Offset X: ");
Label offsetY = new Label("Offset Y: ");
Label color = new Label("Shadow Color: ");
TextField offsetXText = new TextField();
TextField offsetYText = new TextField();
ChoiceBox<String> shadowColors = new ChoiceBox<>();
shadowColors.getItems().add(0, "Black");
shadowColors.getItems().add(1, "White");
dPane.setHgap(7D);
dPane.setVgap(8D);
GridPane.setConstraints(offsetX, 0, 0);
GridPane.setConstraints(offsetY, 0, 1);
GridPane.setConstraints(offsetXText, 1, 0);
GridPane.setConstraints(offsetYText, 1, 1);
GridPane.setConstraints(color, 0, 2);
GridPane.setConstraints(shadowColors, 1, 2);
dPane.getChildren().addAll(offsetX, offsetY, color, offsetXText, offsetYText, shadowColors);
getDialogPane().getButtonTypes().addAll(apply, cancel);
getDialogPane().setContent(dPane);
}
}
Code (where I want the results)
if(scrollPane.getContent() != null && scrollPane.getContent() instanceof ImageView) {
// ImageEffectUtil.addDropShadow((ImageView) scrollPane.getContent());
ImageEffectInputDialog drop = new ImageEffectInputDialog("Drop Shadow");
//Want the Results here..
}
I hope someone might be able to help.
First of all, in order to obtain different values of different types (generic solution) just define a new data structure, say Result, which contains fields like offsetX, offsetY and whatever else you need. Next, extend Dialog<Result> instead of just Dialog. Finally, in the constructor of your ImageEffectInputDialog you need to set result converter, as follows:
setResultConverter(button -> {
// here you can also check what button was pressed
// and return things accordingly
return new Result(offsetXText.getText(), offsetYText.getText());
});
Now wherever you need to use the dialog, you can do:
ImageEffectInputDialog dialog = new ImageEffectInputDialog("Title");
dialog.showAndWait().ifPresent(result -> {
// do something with result object, which is of type Result
});