JavaFX Pop Up window throws exception when called again - java

I am currently trying to write a simple application in Java using JavaFX.
In the application I want to have a pop up window that prompts the user for input. This works fine, as long as the user doesn't try to open the pop up window again.
If he does that the following error occurs:
Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Grid hgap=5.0, vgap=0.0, alignment=TOP_LEFTis already set as root of another scene
And here is the code that opens the window:
In the main view controller:
btAddChat.setOnAction(e -> lvItems.add(PopUpVC.display()));
And in the view controller for the pop up:
public class PopUpVC
{
private static final GridPane root = new GridPane();
private static final TextField tfInput = new TextField();
private static final Button btOK = new Button("OK");
private static String result;
private static Stage primaryStage = new Stage();
public static String display()
{
Scene scene = new Scene(root, 200, 50);
MainVC mvc = new MainVC();
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.setPrefWidth(scene.getWidth()*0.65);
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e ->
{
if(!tfInput.getText().equals(""))
{
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.showAndWait();
return tfInput.getText();
}
I only copied the most important part, the actual error is a lot longer. I know what is causing the error (trying to open a window with the same root throws an error because the root is already in use), I just can't figure out how to resolve it.
Here is a picture of the application:
The top left button opens the pop up.
The pop up:
If there are any suggestions on how to resolve this issue I would be very glad to hear them, if there should be any another way to open a pop up window that prompts the user for text input, I would also be glad to hear about those!
Thanks a lot in advance!

The OP's solution:
I figured it out, easy thing really, I added a parameter to the start function so when I call it I just give it a new GridPane() and it works perfectly fine.
Is really the wrong approach. As #James_D pointed out, static is not a good idea for anything like this. Trying to keep with the original design as much as possible, I'd suggest this, which builds the PopUp just once, and re-displays it:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
PopUpVC popUpVC = new PopUpVC();
button.setOnAction(evt -> {
popUpVC.display();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
public class PopUpVC {
private final TextField tfInput = new TextField();
private Stage primaryStage = new Stage();
public PopUpVC() {
GridPane root = new GridPane();
Scene scene = new Scene(root, 200, 50);
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
Button btOK = new Button("OK");
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e -> {
if (!tfInput.getText().equals("")) {
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
}
public String display() {
primaryStage.showAndWait();
return tfInput.getText();
}
}
}
When you have something that's completely static, like PopUpVC, that's a hint that it's just code that you've moved out of somewhere else. This is useful, and a good practice, if you are calling the methods from a variety of other classes, when it saves code duplication. However, you shouldn't have JavaFX elements as static fields in such a class.
In this case, you can do away with PopUpVC and move all of the code into a local method:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
button.setOnAction(evt -> {
displayPopUpVC();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
public String displayPopUpVC() {
TextField tfInput = new TextField();
Stage primaryStage = new Stage();
GridPane root = new GridPane();
Scene scene = new Scene(root, 200, 50);
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
Button btOK = new Button("OK");
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e -> {
if (!tfInput.getText().equals("")) {
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.showAndWait();
return tfInput.getText();
}
}
This behaves slightly different from the first solution because it doesn't retain the text in the TextField between calls. But the default text could be passed in a parameter if you wanted.
But, also as #James_D has pointed out, TextInputDialog does exactly what you want. Plus it does it with about 4 lines of code:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
TextInputDialog dialog = new TextInputDialog();
dialog.setHeaderText(null);
dialog.setTitle("Chat");
dialog.setGraphic(null);
button.setOnAction(evt -> {
dialog.showAndWait();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
}

Related

JavaFx: ImageView disappears after moving through the scene

I have a code written using javafx and java 11. As I showed in the below code snippet, when I move my ImageView to right of the scene, The ImageView disappears.
At first:
and then after moving it using arrow keys with the help of scene event listener:
then:
and finally:
I don't use fxml. Here is my demo which has the problem too.
public class HelloApplication extends Application {
public String fetchResource(String path) {
return Objects.requireNonNull(getClass().getResource(path)).toString();
}
#Override
public void start(Stage stage) throws IOException {
Rectangle r = new Rectangle(100, 100);
ImageView spaceShip = new ImageView(fetchResource("spaceShip.png"));
spaceShip.setFitHeight(100);
spaceShip.setFitWidth(100);
spaceShip.setX(0);
spaceShip.setY(0);
EventHandler<KeyEvent> keyListener = event -> {
if (event.getCode() == KeyCode.RIGHT) {
spaceShip.setX(spaceShip.getX() + 20);
}
};
Group game = new Group(spaceShip);
Scene scene = new Scene(game, 1840, 1080);
scene.setOnKeyPressed(keyListener);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I should mention that I create a rectangle and check the scenario with that, And there was not any problem. I think there is some problem with ImageView.
I couldn't reproduce the issue with your code, but I could with SceneBuilder. You may get the desired result if you wrap the Group in a Pane (at least it works in SceneBuilder).
Group game = new Group(spaceShip);
Pane pane = new Pane(game);
Scene scene = new Scene(pane, 1840, 1080);
stage.setScene(scene);
stage.show();

displaying a tooltip in javafx brings its stage into the foreground

I am trying to work around this bug in the jdk: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8088624
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button btn = new Button("Click");
btn.setTooltip(new Tooltip("Blubb"));
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
}
}
If the button on the primary stage is hovered, it will come in front of the second stage. I found that calling initOwner() on a Stage will eliminate this behavior.
Now my problem is following: I have multiple "popups" that have a common owner (the primary stage). Hovering over controls on the primary stage doesn't cause any unexpected behavior after the initOwner() workaround. If you however hover over controls in a popup while another popup was in focus, the hovered popup will steal focus.
Is there a way I can work around this bug for not only the primary stage but also the popups?
UPDATE: turns out my workaround has undesired side-effects. Javadocs for Stage state following:
A stage will always be on top of its parent window.
So additionally, what would be a workaround that makes the popup not "always on top" and minimizable?
There is a way to get around it by overlaying StackPanes. Create your Scene with a StackPane so that you can add another StackPane when the stage has lost its focus. The overlayed pane will prevent Tooltips or anything else happening on mouse-over while the pane is not in focus. You may also minimize any of your stages and they won't be always-on-top.
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Button button_1 = new Button("Button #1");
button_1.setTooltip(new Tooltip("Blubb #1"));
StackPane primary = new StackPane(new BorderPane(button_1));
primaryStage.setScene(new Scene(primary, 320, 240));
addStageFocusListener(primaryStage, primary);
primaryStage.show();
Button button_2 = new Button("Button #2");
button_2.setTooltip(new Tooltip("Blubb #2"));
StackPane second = new StackPane(new BorderPane(button_2));
Stage secondStage = new Stage();
addStageFocusListener(secondStage, second);
secondStage.setScene(new Scene(second, 320, 240));
secondStage.show();
}
public void addStageFocusListener(Stage stage, StackPane stackPane) {
stage.focusedProperty().addListener(new ChangeListener<Boolean>(){
public final StackPane preventTooltip = new StackPane();
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(stage.isFocused()) {
if(stackPane.getChildren().contains(preventTooltip)) {
stackPane.getChildren().remove(preventTooltip);
}
} else {
stackPane.getChildren().add(preventTooltip);
}
}
});
}
}
You can try this:
public static final disableMouseEventOnUnfocus(final Stage stage)
{
if (stage == null
|| stage.getScene() == null
|| stage.getScene().getRoot() == null)
return;
else
{
stage.getScene().getRoot().mouseTransparentProperty().bind(stage.focusedProperty().not());
}
}
I didn't try it though, but if it works, this should be a good alternative. There is no need to restructure your layout, and you can leave all your layout in FXML, without specifying fx:id for the tooltips.
I've come up with this alternative solution, as I've found it easier in my case to subclass Tooltip and apply a fix there. I just overload the show() method to only show if the owning window is focused. It's working like a charm for me...
public class FixedTooltip extends Tooltip {
public FixedTooltip(String string) {
super(string);
}
#Override
protected void show() {
Window owner = getOwnerWindow();
if (owner.isFocused())
super.show();
}
}
You could try to unset the tooltip whenever the node's window loses focus. Such as below:
public class Blubb extends Application {
public static void main(String[] args) {
launch(args);
}
public static void installTooltip(Node n, Tooltip tp)
{
Window w = n.getScene().getWindow();
w.focusedProperty().addListener((val, before, after) -> {
if (after)
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
});
if (w.isFocused())
Tooltip.install(n, tp);
else
Tooltip.uninstall(n, tp);
}
#Override
public void start(Stage primaryStage) throws Exception {
Tooltip tp = new Tooltip("Blubb");
Button btn = new Button("Click");
Scene scene = new Scene(new BorderPane(btn), 320, 240);
primaryStage.setScene(scene);
//primaryStage.show();
Stage secondStage = new Stage();
secondStage.setScene(new Scene(new BorderPane(new Button("Click")), 320, 240));
//secondStage.initOwner(primaryStage);
secondStage.show();
primaryStage.show();
installTooltip(btn, tp);
}
}
Of course, you would have to call installTooltip after the node is added to the component.

JavaFX switching scenes

I've been trying to make my application to switch between scenes. Here is a copy of part of the code. The credits scene simply has a back button which should return me to the main scene.
When I try to click on credits button on main scene it is becoming white a white screen. I believe that there is a better way to solve this problem could you give me some advices ?
public class Application {
public static void main(String[] args) {
javafx.application.Application.launch(GUI.class);
}
}
public class GUI extends Application {
#Override
public void start(Stage primaryStage) {
Scene mainScene, creditsScene = null;
mainScene = getMainScene(primaryStage, creditsScene);
creditsScene = getCreditsScene(primaryStage, mainScene);
primaryStage.setTitle("Test application");
primaryStage.setScene(mainScene);
primaryStage.show();
}
private Scene getMainScene(Stage primaryStage, Scene creditsScene) {
final Button credits = new Button("Credits");
credits.setOnAction((ActionEvent e) -> {
primaryStage.close();
primaryStage.setScene(creditsScene);
primaryStage.show();
});
VBox x = new VBox(50);
x.setAlignment(Pos.CENTER);
x.getChildren().addAll( run, displayInfo,
label1, displayInfo, textField, submitName, credits, exit);
//scene size
Scene scene = new Scene(x, 650, 900);
return scene;
}
private Scene getCreditsScene(Stage primaryStage, Scene main) {
final Button back = new Button("Back");
back.setOnAction((ActionEvent e) -> {
primaryStage.setScene(main);
});
VBox x = new VBox(50);
x.getChildren().addAll(back);
Scene credits = new Scene(x, 650, 900);
return credits;
}
Try to switch order of strings:
mainScene = getMainScene(primaryStage, creditsScene);
creditsScene = getCreditsScene(primaryStage, mainScene);
here you pass to getMainScene null.

Stage will not stay maximized when changing scenes

I'm extremely new to programming. I'm using JavaFX and NetBeans IDE 8.0.2 to write a simple math program. I'm trying to change scenes in the same stage, and my code works but the stage will not stay maximized once the scenes have changed. I've tried everything i could think of to keep it maximized or to restore it to maximized i.e. stage.setMaximized(true); after the next scene is switched, but none of the code is working. I created a simple example of my problem. Does anyone have any tips for me in anyway? Thank you.
public class ProblemExample extends Application
{
final double WIDTH = 600;
final double HEIGHT = 600;
Stage stage;
Scene scene1, scene2;
Pane pane1, pane2;
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage primaryStage)
{
stage = primaryStage;
pane1 = new Pane();
pane2 = new Pane();
getuiPane1();
getuiPane2();
scene1 = new Scene(pane1, WIDTH, HEIGHT);
scene2 = new Scene(pane2, WIDTH, HEIGHT);
stage.setTitle("Example");
stage.setScene(scene1);
stage.setMaximized(true);
stage.show();
}
public void getuiPane1()
{
Text nextText = new Text(300, 300, "Next >>");
pane1.getChildren().add(nextText);
nextText.setOnMouseClicked(e ->
{
if (e.getSource() == nextText)
{
stage.setScene(scene2);
} else
{
stage.setScene(scene1);
}
}
);
}
public void getuiPane2()
{
Text backText = new Text(300, 300, "<< Back");
pane2.getChildren().add(backText);
backText.setOnMouseClicked(e ->
{
if (e.getSource() == backText)
{
stage.setScene(scene1);
} else
{
stage.setScene(scene2);
}
}
);
}
}

New Stage in Javafx is coming to center

We are using more than one stage(window) in JavaFX for a desktop standalone application. So when we drag or move any of the stage(window) form one point to another and clicking a certain button to navigate to the other page means the new stage is coming to center again(default position). Is there any methods to set the future stage(future windows) also in the previous stage location or point(previous windows). Please help.
You can control stage location before showing it. Just remember last stage coordinates and create new one in the same point:
public class FXStages extends Application {
private int counter = 0;
#Override
public void start(final Stage stage) {
Button btn = new Button();
btn.setText("Reopen");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
stage.close();
Stage newStage = new Stage();
newStage.setX(stage.getX());
newStage.setY(stage.getY());
start(newStage);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
stage.setTitle("Stage " + counter++);
stage.setScene(scene);
stage.show();
}
}

Categories

Resources