Alert box is not showing in the center of the application using JavaFX [duplicate] - javafx

This question already has answers here:
Center stage on parent stage
(2 answers)
Closed 4 years ago.
I am trying to pop-up the box in the center of the application when I resize the application or move it.
I tried with css alignment and also using Java. Is it possible if I don't use pane and directly add box in the scene?
Here is my code:
public Boolean call(String question) {
final Stage dialogStage = new Stage(StageStyle.UNDECORATED);
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(owner);
dialogStage.setTitle("ConfirmTitle"); // WIP, waiting for the strings&trans
final Button ok = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.ok")); // WIP,
// waiting
// for
// the
// strings&trans
ok.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Button cancel = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.cancel")); // WIP,
// waiting
// for the
// strings&trans
cancel.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Text text = new Text(question);
text.getStyleClass().add(HTML_POPUP_STYLE);
final Insets ins = new Insets(10);
final VBox box = new VBox();
box.setAlignment(Pos.BOTTOM_CENTER);
box.setSpacing(10);
box.setPadding(ins);
final HBox buttons = new HBox(10);
buttons.getChildren().addAll(ok, cancel);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(ins);
box.getChildren().addAll(text, buttons);
box.getStyleClass().add(HTML_POPUP_STYLE);
StackPane pane = new StackPane();
pane.setAlignment(box, Pos.CENTER);
pane.getChildren().add(box);
Scene scene = new Scene(pane);
try {
URL javafxCss = nmsGuiContainer.getBundleContext().getBundle()
.getResource(NmsGuiContainer.JAVAFX_CSS_URL);
scene.getStylesheets().add(javafxCss.toExternalForm());
} catch (Exception e) {
LOGGER.error("Cannot load the CSS file for JavaFX components ", e);
}
dialogStage.setScene(scene);
ok.setCancelButton(false);
final boolean[] res = new boolean[1];
ok.setOnAction(new CloseDialogHandler(dialogStage, res));
cancel.setCancelButton(true);
cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
dialogStage.centerOnScreen();
nmsGuiContainer.fadeContainer();
dialogStage.showAndWait();
nmsGuiContainer.unfadeContainer();
return res[0];
}
Here is a screenshot of the alertbox:

The Stage.initOwner() method does exactly what you need. While you do call it in your example code, I do not know what owner you are passing to it.
Here is a sample that demonstrates how to do this.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button btnShowAlert = new Button("Show Alert!");
// Set the action to show the alert
btnShowAlert.setOnAction(e -> {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setHeaderText("This is centered over the main window!");
alert.setContentText("Move the main window and show the alert again!");
alert.initOwner(primaryStage);
alert.showAndWait();
});
root.getChildren().add(btnShowAlert);
primaryStage.setTitle("Centered Alerts");
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(500);
primaryStage.setHeight(300);
primaryStage.show();
}
}

Related

How to use modal dialog in this case?

I have a question. I need to make a GridPane with a directory choose that will then lead me to a modal dialog showing photos. I cannot figure how to do the modal dialog that also has to be a GridPane or a HBox...so the question is , how do I get to show a Modal Dialog after selecting the Folder and pressing the "Show" Button... Thanks a lot!
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class FotoView extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf = new TextField();
Button b1 = new Button("Search");
Button b2 = new Button("Show");
DirectoryChooser dc = new DirectoryChooser();
GridPane gp = new GridPane();
gp.add(tf, 0 , 0);
gp.add(b1, 1, 0);
gp.add(b2, 0, 1);
b1.setOnAction(e-> dc.showDialog(primaryStage));
primaryStage.setScene(new Scene(gp)) ;
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
} ```
Below is a quick example where a first window has a button that opens up a DirectoryChooser. Once a directory has been selected a second smaller window opens up with the Modality set to APPLICATION_MODAL. In this second window you could add the image(s) that you load and add them to the GridPane.
import java.io.File;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage aStage) throws Exception {
final HBox root = new HBox();
final Button browseBtn = new Button("Click to open a Directory chooser");
root.getChildren().add(browseBtn);
browseBtn.setOnAction(e -> {
final DirectoryChooser chooser = new DirectoryChooser();
final File dir = chooser.showDialog(aStage);
openNewModalStage(aStage, dir);
});
final Scene scene = new Scene(root, 500, 500);
aStage.setScene(scene);
aStage.show();
}
private void openNewModalStage(final Stage aStage, final File aDirectory) {
final Stage stage = new Stage();
final GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
grid.setStyle("-fx-background-color:black");
grid.setPrefWidth(400);
grid.setPrefHeight(400);
// get your images from 'aDirectory' and add them to your grid pane.
stage.setScene(scene);
// set the new windows Modality.
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
}
}
This way you would only need the one button and the dialog would show as soon as you've selected a directory. However, if you would still want a Search and Show button then just store the directory as a variable and add a listener on the 'show' button and move the openNewModalStage call to that one and remove the second argument.
Edit:
Also, depending on how many images and exactly what you want to display in the modal window, you might want to reconsider the GridPane and use a TilePane, or an hbox/vbox inside of a scroll pane. It's just a thought but I don't know what you will be doing with the GridPane.

JavaFX : Mouse events for a PopOver Window (ControlsFX)

I am having the following code to display a PopOver (Custom PopUp by ControlsFX - mvn repo)
public class JavaFXApplication35 extends Application {
#Override
public void start(Stage primaryStage) {
try {
Label lblName = new Label("Tetsing name");
Label lblStreet = new Label("Some street name");
Label lblCityStateZip = new Label("Some city, 111111");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
PopOver popOver = new PopOver(vBox);
Label label = new Label("Mouse mouse over me");
label.setOnMouseEntered(mouseEvent -> {
popOver.show(label, -3);
});
label.setOnMouseExited(mouseEvent -> {
if (popOver.isShowing()) {
popOver.hide();
}
});
StackPane root = new StackPane();
root.getChildren().add(label);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setOnCloseRequest((WindowEvent event) -> {
System.exit(0);
});
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The problem is ,
I want the pop-up to be displayed when mouse entered the Label - works fine.
I want the pop-up to be hidden when user exits mouse from Label but not if he enters mouse in to the pop-up window.
I have added MouseEntered and MouseExited actions on Label but how can i handle the another scenario where i don't want to hide the pop-up if user enters mouse in to pop-up.
I ran into the same problem. Here is my solution. Just pass your label (or other node) and PopOver's content node as arguments to this method.
public static void addAutoHidingPopOver(Node hoverableNode, Node contentNode) {
//Creating PopOver
PopOver popOver = new PopOver(hoverableNode);
popOver.setContentNode(contentNode);
//Here you can set custom parameters of your PopOver
//...
//Mouse Actions handling
final Timeline timeline = new Timeline();
timeline.getKeyFrames().add(new KeyFrame(Duration.millis(1000)));
timeline.setOnFinished(finishEvent -> {
if (hoverableNode.isHover() || contentNode.isHover()) timeline.play();
else popOver.hide();
});
hoverableNode.setOnMouseEntered(mouseEvent -> {if (!popOver.isShowing()) popOver.show(hoverableNode);});
hoverableNode.setOnMouseExited(mouseEvent -> timeline.play());
}
PopOver will be hidden after 1 sec after mouse leave hoverableNode or contentNode. Use it like this:
addAutoHidingPopOver(someLabel, someContentNode);
Note, that your content node should take all visible space of PopOver for comfort use.
That could be expected behavior. I am not sure, but here is a workaround. You can use a ToggleButton.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.PopOver;
public class App extends Application
{
#Override
public void start(Stage primaryStage)
{
//Build PopOver look and feel
Label lblName = new Label("John Doe");
Label lblStreet = new Label("123 Hello Street");
Button lblCityStateZip = new Button("MadeUpCity, XX 55555");
VBox vBox = new VBox(lblName, lblStreet, lblCityStateZip);
//Create PopOver and add look and feel
PopOver popOver = new PopOver(vBox);
ToggleButton toggleButton = new ToggleButton("Click me!");
toggleButton.selectedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
popOver.show(toggleButton);
}
else {
popOver.hide();
}
});
;
StackPane root = new StackPane();
root.getChildren().add(toggleButton);
var scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

How to Fix 'JavaFX Dialogbox taking user input' problem

I am creating a JavaFx dialog box and I have written to a large extent the code. My problem is how to display the error message if a user enters the invalid input. I know I have to use a while loop somewhere but not sure where because of the structure of JavaFx dialog box. Second problem is if the user enters the right input, say 1 for yes, I would want to call a function to carry out a task.
The code I have written brings up the pop up box and prints the consequence of the user input to the console.
public static void AnotherMatch() {
//creates a popUp window
Stage popUp = new Stage();
// makes sure no changes are made in the Main window while this window is open
popUp.initModality(Modality.APPLICATION_MODAL);
popUp.setTitle("New Game");
popUp.setMinWidth(400);
popUp.setHeight(200);
TextPanel textPanel2 = new TextPanel();
TextField nameInput = new TextField();
Button button = new Button("Enter");
//label explains how the game works
Label displayLabel = new Label();
displayLabel.setText("Do you want to play another match: Yes: 1 -- No: 2");
button.setOnAction(e -> isChoice(nameInput, nameInput.getText()));
//vbox stores label and is set in centre
VBox windowDisplay = new VBox();
windowDisplay.setStyle("-fx-background-color:Wheat"); //background colour is set
windowDisplay.getChildren().addAll(displayLabel,nameInput, button);
windowDisplay.setAlignment(Pos.CENTER);
Scene scene = new Scene(windowDisplay);
popUp.setScene(scene);
popUp.showAndWait(); }
Code for isChoice function
private static boolean isChoice(TextField nameInput, String message) {
// TODO Auto-generated method stub
try {
int choice = Integer.parseInt(nameInput.getText());
if(choice == 1) {
System.out.println("I want to play game again");
return true;
}
else if (choice == 2){
System.out.println("I want to stop playing");
return false;
}
else {
System.out.println("Invalid entry");
return false;
}
}
catch(NumberFormatException e){
System.out.println(message + " Invalid .Enter 1 for yes and 2 for no");
return false;
}
}
The user should be asked to enter yes or no. If the user invalid input, an error message should be displayed to the user and the answer asked again until they answer yes or no.
One way you can do is using Bindings to disable the Button unless the TextField contains Yes or No(ignore case).
Demo App using Bindings.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication357 extends Application
{
#Override
public void start(Stage primaryStage)
{
TextField textField = new TextField();
Button btn = new Button();
btn.disableProperty().bind(Bindings.notEqualIgnoreCase("yes", textField.textProperty()).and(Bindings.notEqualIgnoreCase("no", textField.textProperty())));
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane(new VBox(textField, btn));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}

Automatically resizing the window based on dynamic content

I am checking for a feature to automatically resize the window based on the content. I am already aware of Window.sizeToScene() method. But this is so cumbersome that I need to take care in every place to call the sizeToScene(). For example, when I add/remove nodes, when I expanded the titlePanes, etc...
Can someone let me know if it is possible to automatically adjust the window based on the content. I am looking this feature for both normal and transparent windows.
Any hints/suggestion is highly appreciated.
Please find below a quick demo of what I am looking for. Everything works as expected if I consider calling sizeToScene(). I am targeting for a way to get the same behavior without calling sizeToScene for every scenario.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentWindowResizeDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setSpacing(15);
root.setAlignment(Pos.CENTER);
Scene sc = new Scene(root, 300, 300);
stage.setScene(sc);
stage.show();
Button showNormal = new Button("Show Normal Window");
showNormal.setOnAction(e -> showWindow(false));
Button showTransparent = new Button("Show Transparent Window");
showTransparent.setOnAction(e -> showWindow(true));
root.getChildren().addAll(showNormal, showTransparent);
}
private void showWindow(boolean isTransparent) {
Stage window = new Stage();
VBox root = new VBox();
root.setStyle("-fx-background-color: grey; -fx-border-width:2px;-fx-border-color:black;");
Scene scene = new Scene(root, Color.TRANSPARENT);
window.setScene(scene);
VBox layout = new VBox();
layout.setSpacing(5);
layout.setPadding(new Insets(5));
CheckBox sizeToScene = new CheckBox("sizeToScene");
sizeToScene.setSelected(true);
StackPane box = new StackPane();
box.setStyle("-fx-background-color:yellow; -fx-border-width:1px;-fx-border-color:black;");
box.setMinSize(200, 100);
box.setMaxSize(200, 100);
Button add = new Button("Add Pane");
Button remove = new Button("Remove Pane");
remove.setDisable(true);
add.setOnAction(e -> {
layout.getChildren().add(box);
add.setDisable(true);
remove.setDisable(false);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
remove.setOnAction(e -> {
layout.getChildren().remove(box);
add.setDisable(false);
remove.setDisable(true);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
HBox btnLayout = new HBox();
btnLayout.setSpacing(5);
btnLayout.getChildren().addAll(add, remove);
StackPane tpContent = new StackPane();
tpContent.setStyle("-fx-background-color:pink; -fx-border-width:1px;-fx-border-color:black;");
tpContent.setMinSize(200, 100);
tpContent.setMaxSize(200, 100);
TitledPane tp = new TitledPane("Titled Pane", tpContent);
tp.setAnimated(false);
tp.expandedProperty().addListener((obs, oldVal, newVal) -> {
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
if (isTransparent) {
window.initStyle(StageStyle.TRANSPARENT);
StackPane close = new StackPane();
close.setMaxWidth(30);
close.setStyle("-fx-background-color:red;");
close.getChildren().add(new Label("X"));
close.setOnMouseClicked(e -> window.hide());
DoubleProperty x = new SimpleDoubleProperty();
DoubleProperty y = new SimpleDoubleProperty();
StackPane header = new StackPane();
header.setOnMousePressed(e -> {
x.set(e.getSceneX());
y.set(e.getSceneY());
});
header.setOnMouseDragged(e -> {
window.setX(e.getScreenX() - x.get());
window.setY(e.getScreenY() - y.get());
});
header.setStyle("-fx-border-width:0px 0px 2px 0px;-fx-border-color:black;");
header.setMinHeight(30);
header.setAlignment(Pos.CENTER_RIGHT);
Label lbl = new Label("I am draggable !!");
lbl.setStyle("-fx-text-fill:white;-fx-font-weight:bold;");
StackPane.setAlignment(lbl,Pos.CENTER_LEFT);
header.getChildren().addAll(lbl,close);
root.getChildren().add(header);
}
layout.getChildren().addAll(sizeToScene, btnLayout, tp);
root.getChildren().add(layout);
window.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Edit :: This is different from what I already found in other questions or with the link in the comment. The solution that is specified is what I am aware of and I already mentioned that in the question. I have a heavy screen where it contain many nodes in differnt hirerchy. I am checking for any generic solution to include in one place rather that calling from every node add/remove scenario.
I would say this is not a graceful solution (it's more like a hack), but at least it has worked using your example:
VBox root = new VBox() {
private boolean needsResize = false;
#Override
protected void layoutChildren()
{
super.layoutChildren();
if (!needsResize) {
needsResize = true;
Platform.runLater(() -> {
if (needsResize) {
window.sizeToScene();
needsResize = false;
}
});
}
}
};
Of course, you should add in the sizeToScene.isSelected() part, and you could also make this an actual subclass.

JavaFX: ProgressBar with timeline; reset both if completed

I am building an application which is an exam. All the questions are shown on different screens, all with a progressbar showing how much time the user has left.
When the progressbar is full, the screen with another question is shown and the progressbar and timeline should be resetted.
When the time is up, the next screen is shown and the timeline resets, but the progressbar (shown in code as 'PRGB') remains full and it will not reset to an empty progressbar. This is my code:
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
new KeyFrame(Duration.ONE, new KeyValue(PRGB.progressProperty(), 0)),
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
timeline.play();
//in here the progressbar has to be resetted
});
}
Update:
I have deleted my ProgressBar from SceneBuilder, and made a new one on which I then run the code below (with a few alterations taking by your suggestions). But now the progressbar does reset it self, but it does not show the progress: it stays empty.
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
//I only use two keyframes instead of three
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
//The setProgress is added
PRGB.setProgress(0.0F);
timeline.play();
});
}
This can probably be done better. In this example, 3 seconds is given for each question with 1 second at the end of each question to load the next question. Code from here.
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
Label lblCurrentQuestion = new Label();
ProgressBar prgb = new ProgressBar(0);
int numberOfQuestions = 5;//Number of questions
int timeForEachQuestion = 3;//3 second to do each questions
AtomicInteger currentProgressCounter = new AtomicInteger(0);
AtomicInteger currentQuestionCounter = new AtomicInteger(1);
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), (event) -> {
if (currentProgressCounter.get() == timeForEachQuestion + 1) {
//Go to next question!
prgb.setProgress(0);
currentProgressCounter.set(0);
currentQuestionCounter.incrementAndGet();
}
lblCurrentQuestion.setText("Question " + currentQuestionCounter.get());
prgb.setProgress(currentProgressCounter.getAndIncrement() / (double) timeForEachQuestion);
}));
fiveSecondsWonder.setCycleCount(numberOfQuestions * (timeForEachQuestion + 1));
fiveSecondsWonder.play();
fiveSecondsWonder.setOnFinished(event -> {
PauseTransition wait = new PauseTransition(Duration.seconds(1));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
lblCurrentQuestion.setText("Quiz done!");
prgb.setProgress(0);
wait.playFromStart();
});
wait.play();
});
Scene scene = new Scene(new StackPane(new VBox(lblCurrentQuestion, prgb)), 500, 500);
stage.setScene(scene);
stage.show();
}
}
You have a call to timeline.play() in your setOnFinished() call. Remove that call so that the progress bar doesn't get reset to full.
Also, you should reset your progress bar by doing progressBar.setProgress(0.0F); in the setOnFinished()

Resources