I placed an HBox into the center of a BorderPane, that is the only direct Node on a DialogPane of my Dialog by using this code:
public GameDialog(Player[] players) {
setTitle("Spiel eintragen");
setWidth(WIDTH);
setHeight(HEIGHT);
createLeftBox();
createRightBox(players);
// Center
this.center = new HBox(leftBox, rightBox);
center.setStyle("-fx-border-color: #00ff00;");
center.getChildren()
.forEach(b -> HBox.setMargin(b, INSETS));
// Bottom
this.btnOk = new Button("ok");
this.btnCancel = new Button("abbrechen");
this.bottom = new HBox(btnOk, btnCancel);
bottom.getChildren()
.forEach(b -> HBox.setMargin(b, INSETS));
// Pane
this.pane = new BorderPane(center);
pane.setBottom(bottom);
pane.setPrefSize(WIDTH, HEIGHT);
getDialogPane().setPrefSize(WIDTH, HEIGHT);
getDialogPane().getChildren().add(pane);
...
}
private void createLeftBox() {
// Points spinner
List<Integer> points = Stream.iterate(0, Util::increment)
.limit(MAX_POINTS)
.collect(Collectors.toList());
this.spPoints = new Spinner<>(observableArrayList(points));
// Solo combobox
List<Solotype> soli = Arrays.asList(Solotype.values());
this.cbSolo = new ComboBox<>(observableArrayList(soli));
cbSolo.setOnAction(e -> changed());
// Bock checkbox
this.chBock = new CheckBox("Bock");
// Left box
leftBox = new VBox(spPoints, cbSolo, chBock);
leftBox.getChildren()
.forEach(n -> VBox.setMargin(n, INSETS));
leftBox.setStyle("-fx-border-color: #ff0000");
}
private void createRightBox(Player[] players) {
List<Player> playerlist = Arrays.asList(players);
// Right box
rightBox = new VBox();
List<Node> rightChildren = rightBox.getChildren();
this.playerBoxes = playerlist.stream()
.collect(toMap(p -> p,
p -> new HBox(4)));
this.players = playerlist.stream()
.collect(toMap(p -> p,
this::createToggleGroup));
playerBoxes.values()
.stream()
.peek(rightChildren::add)
.forEach(b -> VBox.setMargin(b, INSETS));
rightBox.setStyle("-fx-border-color: #ff0000");
}
My class GameDialog extends the javafx-class Dialog<R>. However the HBox in the center of the BorderPane doesn't fill all of the available space. It just looks like this:
I tried to setMinWidth, setMinHeight, setPrefSize, setFillWidth(true) and setFillHeight(true) on several of the H- and VBoxes. Nothing Changed. I just don't understand this behaviour. Any ideas?
I couldn't fully recreate your example (since some code is missing, I used TextField instead of those toggle groups), but had some success with this solution.
Instead of using:
getDialogPane().getChildren().add(pane);
use:
getDialogPane().setContent(pane);
And the controls should use all the available space.
(I also removed the setting of the preferred width and height, so the image was a little smaller. Setting the preferred size of the pane doesn't change anything any ways.)
Related
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);
}
}
}
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.
I am using a logarithmic scale for a graph and I want to be able to make the individual curves a little wider when hovered over and on top of all the other curves as well as the color key's value too. Below is a picture better illustrating what I want to do (with sensitive data redacted)
Is something like this even possible? And if so what direction should I move in to achieve this?
The first of your cases, which is applying the visual changes when the mouse hovers over the curve, is possible by modifying the Node that represents the Series on the chart, which is a Path. You can apply the changes to the stroke of the Path making it darker, wider and bringing in to the front when the mouse enters and reverting them when the mouse leaves
The second, which is applying the visual changes when hovering over the legend items is still possible but it's not as clean a solution, at least it's not in my implementation below. With a Node lookup you can get the items and cast them to either a Label or a LegendItem which expose the graphic and text. I chose Label to avoid using the internal API
See more here: It is a bad practice to use Sun's proprietary Java classes?
With String comparisons between the legend text and series names you can associate the two assuming each series has a name and that it is unique. If it isn't unique you could compare the stroke fills as well as the names
Important: These assumptions limit this approach and so if possible I'd avoid it
SSCCE:
public class DarkerSeriesOnHoverExample extends Application {
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) throws Exception {
//LogarithmicNumberAxis source: https://stackoverflow.com/a/22424519/5556314
LineChart lineChart = new LineChart(new LogarithmicNumberAxis(1, 1000000), new NumberAxis(0, 2.25, 0.25));
lineChart.setCreateSymbols(false);
//Values guessed from the screen shot
ObservableList<XYChart.Data> seriesData = FXCollections.observableArrayList(new XYChart.Data(1, 2),
new XYChart.Data(10, 2), new XYChart.Data(100, 2), new XYChart.Data(1000, 1.85),
new XYChart.Data(10000, 1.50), new XYChart.Data(100000, 1.20), new XYChart.Data(1000000, 0.9));
ObservableList<XYChart.Data> series2Data = FXCollections.observableArrayList(new XYChart.Data(1, 2),
new XYChart.Data(10, 2), new XYChart.Data(100, 2), new XYChart.Data(1000, 1.60),
new XYChart.Data(10000, 1.25), new XYChart.Data(100000, 0.95), new XYChart.Data(1000000, 0.65));
ObservableList<XYChart.Data> series3Data = FXCollections.observableArrayList(new XYChart.Data(1, 2),
new XYChart.Data(10, 1.85), new XYChart.Data(100, 1.55), new XYChart.Data(1000, 1),
new XYChart.Data(10000, 0.65), new XYChart.Data(100000, 0.5), new XYChart.Data(1000000, 0.45));
ObservableList<XYChart.Series> displayedSeries = FXCollections.observableArrayList(
new XYChart.Series("Series 1", seriesData), new XYChart.Series("Series 2", series2Data),
new XYChart.Series("Series 3", series3Data));
lineChart.getData().addAll(displayedSeries);
Scene scene = new Scene(lineChart, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
darkenSeriesOnHover(displayedSeries); //Setup for hovering on series (cleaner)
darkenSeriesOnLegendHover(lineChart); //Setup both hovering on series and legend (messier)
}
private void darkenSeriesOnHover(List<XYChart.Series> seriesList){
for(XYChart.Series series : seriesList){
Node seriesNode = series.getNode();
//seriesNode will be null if this method is called before the scene CSS has been applied
if(seriesNode != null && seriesNode instanceof Path){
Path seriesPath = (Path) seriesNode;
Color initialStrokeColor = (Color)seriesPath.getStroke();
double initialStrokeWidth = seriesPath.getStrokeWidth();
seriesPath.setOnMouseEntered(event -> {
updatePath(seriesPath, initialStrokeColor.darker(), initialStrokeWidth*2, true);
});
seriesPath.setOnMouseExited(event -> {
//Reset
updatePath(seriesPath, initialStrokeColor, initialStrokeWidth, false);
});
}
}
}
private void darkenSeriesOnLegendHover(LineChart lineChart){
Set<Node> legendItems = lineChart.lookupAll("Label.chart-legend-item");
List<XYChart.Series> seriesList = lineChart.getData();
//Will be empty if this method is called before the scene CSS has been applied
if(legendItems.isEmpty()){ return; }
for(Node legendItem : legendItems){
Label legend = (Label) legendItem;
XYChart.Series matchingSeries = getMatchingSeriesByName(seriesList, legend.getText());
if(matchingSeries == null){ return; }
Node seriesNode = matchingSeries.getNode();
//seriesNode will be null if this method is called before the scene CSS has been applied
if(seriesNode != null && seriesNode instanceof Path){
Path seriesPath = (Path) seriesNode;
Color initialStrokeColor = (Color)seriesPath.getStroke();
double initialStrokeWidth = seriesPath.getStrokeWidth();
legendItem.setOnMouseEntered(event -> {
updatePath(seriesPath, initialStrokeColor.darker(), initialStrokeWidth*2, true);
});
legendItem.setOnMouseExited(event -> {
//Reset
updatePath(seriesPath, initialStrokeColor, initialStrokeWidth, false);
});
seriesPath.setOnMouseEntered(event -> {
updatePath(seriesPath, initialStrokeColor.darker(), initialStrokeWidth*2, true);
});
seriesPath.setOnMouseExited(event -> {
//Reset
updatePath(seriesPath, initialStrokeColor, initialStrokeWidth, false);
});
}
}
}
private void updatePath(Path seriesPath, Paint strokeColor, double strokeWidth, boolean toFront){
seriesPath.setStroke(strokeColor);
seriesPath.setStrokeWidth(strokeWidth);
if(!toFront){ return; }
seriesPath.toFront();
}
private XYChart.Series getMatchingSeriesByName(List<XYChart.Series> seriesList, String searchParam){
for (XYChart.Series series : seriesList){
if(series.getName().equals(searchParam)){
return series;
}
}
return null;
}
}
Output:
Before vs Hover
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
});