JavaFX Show second window after first window - javafx

I want open main window, and, after it, in moment, when it opened, open dialouge window (in which I choice some parameters), whithout clicking or typing anything. Dialouge window must open as such. Where I should write code, which open dialouge window?

You can use the Window.onShown property. The EventHandler is invoked for WINDOW_SHOWN events which, as one would expect, are fired once the Window has been shown. Here's a small example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.Window;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setOnShown(event -> showDialog(primaryStage));
primaryStage.setScene(new Scene(new StackPane(new Label("Hello, World!")), 600, 400));
primaryStage.setTitle("JavaFX Application");
primaryStage.show();
}
private void showDialog(Window owner) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.initOwner(owner);
alert.setContentText("This is a dialog shown immediately after the window was shown.");
alert.show();
}
}

Related

Popup with ScrollPane makes main Stage TextField unresponsive to key events like LEFT, RIGHT, HOME, END

I want to create a Popup for main Stage TextField. Popup contains ScrollPane which holds possible options as Buttons.
After Popup shown TextField key events like left, rigth, home, end have no effect. Key events are received on TextField.
Is there any reasonable solution or workaround for this issue.
To reproduce please type in some text and try to press left arrow.
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Popup;
import javafx.stage.Stage;
public class PopupApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
TextField textField = new TextField();
StackPane stackPane = new StackPane(textField);
stage.setScene(new Scene(stackPane));
stage.show();
ScrollPane scrollPane = new ScrollPane(new VBox(new Button("Option1"), new Button("Option2")));
Popup popup = new Popup();
popup.getContent().add(scrollPane);
Point2D pinPoint = textField.localToScreen(0., textField.getHeight());
popup.show(textField, pinPoint.getX(), pinPoint.getY());
textField.addEventHandler(KeyEvent.KEY_RELEASED, event -> {
System.out.println("KEY_RELEASED " + event);
});
textField.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
System.out.println("KEY_PRESSED " + event);
});
}
}
Actually, the KEY_PRESSED event is not being received by your textField when pressing Keys like Left, Right, ... This is shown by the output from your program.
You are facing this problem because Events are being redirected to Popup. If these events are consumed while dispatching, then the effect of these events are not shown by textField. In your case it is due to the presence of Button on Popup. (Most likely due to the presence of node that can receive focus. Not sure though). To solve this you can set your own EventDispatcher. Something like this before popup.show():
popup.setEventDispatcher((event, tail) -> {
if (event.getEventType() != RedirectedEvent.REDIRECTED) {
tail.dispatchEvent(event);
}
return null;
});
All the redirected events into the Popup will now be discarded and further dispatching does not occur. You can tweak this to suit your requirement.

JavaFX Slider : How to drag the thumb only by increments

I am trying to implement the Slider such that user can drag only by given increments. I tried in different ways by using the Slider API, but didnt get the desired results. Below is a quick demo of what I had tried. I am expecting to drag the thumb only in increments of 10 not with intermediate values. snapToTicks is doing what I required, but only after finishing the drag. I am trying to not move the thumb till the next desired block increment is reached.
Can anyone let me know how can i achieve this. Below is the screenshot while dragging.
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.Slider;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SliderDemo extends Application {
public static void main(String... args){
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Label label = new Label();
label.setStyle("-fx-font-size:30px");
Slider slider = new Slider(5,240,5);
slider.setBlockIncrement(10);
slider.setMajorTickUnit(10);
slider.setMinorTickCount(0);
slider.setShowTickLabels(true);
slider.setShowTickMarks(true);
slider.setSnapToTicks(true);
slider.valueProperty().addListener((obs,old,val)->label.setText((int)Math.round(val.doubleValue())+""));
VBox root = new VBox(slider,label);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
root.setSpacing(20);
Scene scene = new Scene(root,600,200);
stage.setScene(scene);
stage.show();
}
}
The solution is to set the value of the slider directly inside of the listener. The listener will not be called again
final ChangeListener<Number> numberChangeListener = (obs, old, val) -> {
final double roundedValue = Math.floor(val.doubleValue() / 10.0) * 10.0;
slider.valueProperty().set(roundedValue);
label.setText(Double.toString(roundedValue));
};
slider.valueProperty().addListener(numberChangeListener);
If you use Math.floor() instead of round you get a more intuatuive behavior of the thumb.

How to vertically center content of Alert/DialogPane?

I created an Alert that is undecorated and I want the content to be centered in the alert. However, there seems to be some padding at the bottom that I can not get rid of.
Here is an MCVE:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.DialogPane;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Alert alert = new Alert(Alert.AlertType.NONE);
DialogPane pane = new DialogPane();
HBox contentPane = new HBox(10);
contentPane.setPadding(new Insets(10));
contentPane.setAlignment(Pos.CENTER);
// Add progress indicator and label to contentPane
contentPane.getChildren().addAll(
new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS) {{
setPrefSize(30, 30);
}},
new Label("Loading ...")
);
// Add border to demonstate the lower padding
contentPane.setStyle("-fx-border-color: black");
pane.setPadding(new Insets(10));
pane.setStyle("-fx-border-color: black");
pane.setContent(contentPane);
alert.setDialogPane(pane);
alert.initStyle(StageStyle.UNDECORATED);
alert.showAndWait();
}
}
The result is this window:
How do I create an Alert or DialogPane without that gap at the bottom?
Using:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private static Border createBorder(Paint stroke) {
return new Border(new BorderStroke(stroke, BorderStrokeStyle.SOLID,
null, new BorderWidths(2)));
}
#Override
public void start(Stage primaryStage) throws Exception {
ProgressIndicator indicator = new ProgressIndicator();
indicator.setPrefSize(30, 30);
Label label = new Label("Loading...");
label.setMinWidth(Label.USE_PREF_SIZE);
HBox content = new HBox(10, indicator, label);
content.setBorder(createBorder(Color.BLUE));
content.setPadding(new Insets(10));
content.setAlignment(Pos.CENTER);
Alert alert = new Alert(Alert.AlertType.NONE);
alert.initStyle(StageStyle.UNDECORATED);
alert.getDialogPane().getStylesheets()
.add(getClass().getResource("Main.css").toExternalForm());
alert.getDialogPane().setPadding(new Insets(10));
alert.getDialogPane().setContent(content);
alert.getDialogPane().setBorder(createBorder(Color.RED));
alert.show();
}
}
Where this is Main.css:
.dialog-pane .button-bar .container {
-fx-padding: 0px;
}
.dialog-pane:no-header .graphic-container {
-fx-padding: 0px;
}
Resulted in the following:
Some of the needed style-classes I got from here (JavaFX CSS Reference). However, I mostly figured this out from looking at modena.css (where they have the styles for dialog-pane).
If you don't want to use external CSS you can replace alert.getDialogPane().getStylesheets().add(...) with:
alert.getDialogPane().applyCss(); // Seems to stop the lookup calls
// from returning null
alert.getDialogPane()
.lookup(".button-bar")
.lookup(".container")
.setStyle("-fx-padding: 0px;");
alert.getDialogPane().lookup(".graphic-container").setStyle("-fx-padding: 0px;");
alert.show();
Update following your comments.
I initially tried this using Java 10.0.2 but I just tried using Java 8u181 and it still worked for me. I notice in your MCVE you are using a new DialogPane whereas I am using the DialogPane that initially comes with the Alert. Note that the applyCss() method will only work if the Node is a part of a Scene. Apparently the DialogPane is made part of a Scene as soon as it's part of the Alert. So one way you could avoid the NullPointerExceptions is to use the DialogPane that comes with the Alert, as I do.
If you still want to use your own DialogPane you just have to call alert.setDialogPane before calling applyCss(). Note that when I tried this, however, the call lookup(".graphic-container") still returned null. I guess that Node is only present on the original DialogPane. Removing that lookup call fixed it and the other lookup call (for .button-bar > .container) still worked as expected.
All this seems like implementation behavior that should not be relied upon but it works for me on both Java 8 and Java 10. I'd watch this code when changing Java versions just in case though.

Dialog can't be blocked when FileChooser is open

I have question about blocking Dialog window when I'm opening FileChooser. When I open FileChooser from main app window I don't have any problem with blocking main window of app (using fileChooser.showOpenDialog(Main.getPrimaryStage);). But I have problem when I'm opening Filechooser from Dialog. I can't focus on main app window, because Dialog has property dialog.initOwner(Main.getPrimaryStage());, but I still can focus on Dialog and open next FileChooser, over and over again. App image view. What can I do with this ?
You can get a reference to the window used to display a dialog from its dialogPane, via:
Window dialogWindow = dialog.getDialogPane().getScene().getWindow();
Using this, you can make the "owner" of the file chooser the dialog's window:
Window dialogWindow = dialog.getDialogPane().getScene().getWindow();
File file = fileChooser.showOpenDialog(dialogWindow);
Here's a SSCCE:
import java.io.File;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.Window;
public class DialogFileChooser extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Open Dialog...");
button.setOnAction(e -> createDialog(primaryStage).show());
StackPane root = new StackPane(button);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private Dialog<ButtonType> createDialog(Window owner) {
DialogPane dialogPane = new DialogPane();
dialogPane.getButtonTypes().add(ButtonType.CLOSE);
Dialog<ButtonType> dialog = new Dialog<>();
dialog.setDialogPane(dialogPane);
Button openFile = new Button("Open File...");
FileChooser fileChooser = new FileChooser();
openFile.setOnAction(e -> {
Window dialogWindow = dialog.getDialogPane().getScene().getWindow();
File file = fileChooser.showOpenDialog(dialogWindow);
System.out.println(file);
});
dialogPane.setContent(new StackPane(openFile));
dialog.initOwner(owner);
return dialog ;
}
public static void main(String[] args) {
launch(args);
}
}

Why my Stage.close is not Working

Stage.close() is not working for me.
I've checked on:
JavaFX 2.0: Closing a stage (window)
Here is my codes:
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.stage.Modality;
public class MsgBox {
public Stage MessageBox(String Title, String Message){
VBox Pnl = new VBox();
Pnl.setPadding(new Insets(10,10,10,10));
Pnl.setSpacing(10);
Pnl.setAlignment(Pos.CENTER);
Label LblMsg = new Label(Message);
Button CmdOK = new Button("OK");
Pnl.getChildren().addAll(LblMsg, CmdOK);
Scene SCN = new Scene(Pnl);
Stage Window = new Stage();
Window.initModality(Modality.APPLICATION_MODAL);
Window.setTitle(Title);
Window.setScene(SCN);
Window.showAndWait();
CmdOK.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent ev){
Window.close();
}
});
return Window;
}
}
Here is the code that calls the Message Box Class:
CmdUpdate.setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent ev){
new MsgBox().MessageBox("Hello", "Hello World");
}
});
Calling Stage#showAndWait waits until the stage closes before returning, so in fact the next line never gets a chance to run.
Move the line
Window.showAndWait();
to be the last in the method (crucially - after you set the handler to allow closing the stage), or else just use Stage#show, and your problem should be solved.

Resources