I would like to display a NotificationPane after certain user actions. My application has multiple scenes and the NotificationPane should be showed up in the currently active scene.
The whole thing works with Notification, it pops up when I need it.
But I can't figure out how to make this work for NotificationPane.
Steps I made so far:
I tryed to put NotificationPane directly to my scene and call
show() - it works.
Now the Idea is to get the current pane by calling
stage.getScene().getRoot(), wrap it to NotificationPane and then call
show() - it doesn't work and I have no idea why.
((BorderPane) pane).setCenter(new Label("TEST")); this line is replacing buttons with text label, so stage.getScene().getRoot() is returning the right object
I made a simple program to test the behaviour. One button to call NotificationPane.
Any suggestions?
Here is my test program:
package application;
import org.controlsfx.control.NotificationPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
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) {
Button notificationPaneButton = new Button("NotificationPane");
notificationPaneButton.setOnAction(e -> showNotificationPane(primaryStage, "Notification text"));
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(notificationPaneButton);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(vbox);
primaryStage.setTitle("Notifications test");
primaryStage.setScene(new Scene(borderPane, 300, 200));
primaryStage.show();
}
public void showNotificationPane(Stage stage, String message) {
Parent pane = stage.getScene().getRoot();
// ((BorderPane) pane).setCenter(new Label("TEST"));
NotificationPane notificationPane = new NotificationPane(pane);
notificationPane.setText(message);
if (notificationPane.showingProperty().get()) {
notificationPane.hide();
System.err.println("hide");
} else {
notificationPane.show();
System.err.println("show");
}
}
}
Ok, I see the problem now. Wrapping current pane is not enough, I also have to add the NotificationPane to the scene. Right?
Anyway my current solution is following:
get current scene
get current pane
wrap pane
replace current scene with the new one
To avoid wrapping NotificationPane multiple times I check if current pane is already a NotificationPane and then call show().
public void showNotificationPane(Stage stage) {
Scene scene = stage.getScene();
Parent pane = scene.getRoot();
if (!(pane instanceof NotificationPane)){
NotificationPane notificationPane = new NotificationPane(pane);
scene = new Scene(notificationPane, scene.getWidth(), scene.getHeight());
stage.setScene(scene);
notificationPane.show();
} else {
((NotificationPane)pane).show();
}
}
Related
I am trying to figure out how to center and bind a label perfectly at the bottom of a scene. I have a simple test application here to show what I am working with and what my issue is.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class LabelTest extends Application {
#Override
public void start(Stage stage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 400, 400);
Label label = new Label("Testing testing 1 2 3");
label.layoutXProperty().bind(scene.widthProperty().divide(2).subtract(label.getWidth() / 2)); //Should align label to horizontal center, but it is off
label.layoutYProperty().bind(scene.heightProperty().subtract(label.getHeight() + 35)); //Aligns the label to bottom of scene
root.getChildren().add(label);
stage.setScene(scene);
stage.show();
}
}
The logic behind my positioning makes sense to me, so I am not sure why it is not horizontally centered. I have included a screenshot below to show what the output looks like:
And below is more of what I am wanting it to look like (still off by a bit, but you get the point)
Thanks in advance to anyone who helps me out!
Let layout managers do the layout for you:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LabelTest extends Application {
#Override
public void start(Stage stage) throws Exception {
Label label = new Label("Testing testing 1 2 3");
BorderPane root = new BorderPane();
//center label by
//BorderPane.setAlignment(label, Pos.CENTER);
//root.setBottom(label);
//OR
root.setBottom(new StackPane(label));
Scene scene = new Scene(root, 400, 400);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The issue is you are taking the values of width/height at the time of binding. And at this instance it will be 0 as they are not yet rendered. You need bind those properties as well for computing.
label.layoutXProperty().bind(scene.widthProperty().divide(2).subtract(label.widthProperty().divide(2)));
label.layoutYProperty().bind(scene.heightProperty().subtract(label.heightProperty().add(35)));
So this is the code i have that launches the GUI i read on a previous post that it maybe has to do with the fact that my root is of type Group but i wasn't able to figure out how to implement any other way. The content inside the GUI gets eaten up when i try to resize the main frame manually.I want it to stay center and resize with the frame.
package view;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import singleton.MainModel;
public class MainView extends Application {
#Override
public void start(Stage primaryStage) {
// Initialize mainController.
MainController mainController = new MainController();
// Add the controller to the singleton.
MainModel.getModel().getMainData().setMainController(mainController);
// Initialize display components.
Group root = new Group();
Scene scene = new Scene(root, 1280, 720);
// Add mainController.
root.getChildren().addAll(mainController);
// Pin the root to scene and display it.
primaryStage.setScene(scene);
primaryStage.show();
// Properly terminate the application if the user presses the "X" window button.
primaryStage.setOnCloseRequest(event -> {
mainController.closeApplication();
stop();
});
// Set the title and make the application a fixed size.
primaryStage.setTitle("Visual Earth Modelling System");
primaryStage.setResizable(true);
primaryStage.sizeToScene();
// Add the stage to the singleton.
MainModel.getModel().getMainData().setMainStage(primaryStage);
// Go to the first screen.
mainController.goToLoginScreen();
}
/**
* To destroy resources upon application close. Should be called in all instances of a properly closed JavaFX application.
*/
#Override
public void stop() {
if (MainModel.getModel().getNetworkData().isHandlerSet())
MainModel.getModel().getNetworkData().closeHandler();
}
/**
* This method is actually not used in a correctly deployed JavaFX application. Instead, the start method above is called. This main serves as a fallback in case of improper configuration.
*/
public static void main(String[] args) {
launch(args);
}
}
Replace Group with StackPane. StackPane by default sets alignment of children to center. Just to be sure that alignment is correct you can set it explicity by StackPane.setAlignment(Pos value).
StackPane root = new StackPane();
root.setAlignment(Pos.CENTER);
root.getChildren().add(mainController);
Scene scene = new Scene(root, 1280, 720);
very new to JavaFX I'm following a simple tutorial here
I created a new JavaFX project but it has a BorderPane as a default rather than a StackPane as the tutorial says, so I left it there.
The application only has a button on it and if I use the BorderPane the button isn't displayed.
If I change it to StackPane the button shows up.
Thinking that for some reason the BorderPane was clipping something off, I made the application windows full size, but I still couldn't see the button.
Here is the code with the BorderPane the one that doesn't display the button:
package application;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setTitle("This is a test!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
root.getChildren().add(btn);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Any idea?
Take a look at the docs about BorderPane:
BorderPane lays out children in top, left, right, bottom, and center
positions.
Therefore you need to use stuff like:
borderPane.setTop(toolbar);
borderPane.setCenter(appContent);
borderPane.setBottom(statusbar);
In your case root.getChildren().add(btn); should be for example root.setCenter(btn);.
My configuration:
I need to switch between scenes via the same stage
I need to keep a maximized stage that fills the whole screen
My issue:
although I set my stage to be maximized primaryStage.setMaximized(true);it adopts its size to the size of the scenes afterwards.
What I tried until now:
I tried using primaryStage.getScene().setRoot(<the root node of scene>). While it worked to keep the stage maximized, yet after each change of scene the focus on the previously focused gui control is lost (after switch only the first gui control in the scene hierarchy is focused). I really need scenes, so that any gui control that was focused still be will focused after the stage changes its scene.
I need your assistance:
I really need your assistance in keeping the stage maximized during changing scenes.
Here is my example code:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
// init buttons
Button btn1 = new Button("switch to next scene >>");
Button btn2 = new Button("<< switch to previous scene");
// first scene
StackPane root1 = new StackPane();
root1.getChildren().add(btn1);
Scene scene1 = new Scene(root1, 300, 250);
// second scene
StackPane root2 = new StackPane();
root2.getChildren().add(btn2);
Scene scene2 = new Scene(root2, 500, 400);
// button actions
btn1.setOnAction((ActionEvent event) -> {
primaryStage.setScene(scene2);
});
btn2.setOnAction((ActionEvent event) -> {
primaryStage.setScene(scene1);
});
primaryStage.setMaximized(true);
primaryStage.setScene(scene1);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
SOLUTION
create scenes depending on the screen size of your monitor
after several attempts I finally figured how to easily solve this problem to keep the maximized screen while retaining the focused node on each Scene. Hope it helps the community:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
// get screensize of monitor
Rectangle2D screenSize = Screen.getPrimary().getVisualBounds();
// init buttons
Button btn1 = new Button("switch to next scene >>");
Button btn2 = new Button("<< switch to previous scene");
// first rootNode
StackPane root1 = new StackPane();
root1.getChildren().add(btn1);
Scene scene1 = new Scene(root1, screenSize.getWidth(), screenSize.getHeight());
// second rootNode
StackPane root2 = new StackPane();
root2.getChildren().add(btn2);
Scene scene2 = new Scene(root2, screenSize.getWidth(), screenSize.getHeight());
// button actions
btn1.setOnAction((ActionEvent event) -> {
primaryStage.setScene(scene2);
});
btn2.setOnAction((ActionEvent event) -> {
primaryStage.setScene(scene1);
});
primaryStage.setMaximized(true); // keep this since otherwise the titlebar is bit overlapped
primaryStage.setScene(scene1);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I think it's a bug. You can report it at: http://bugreport.java.com.
In the meantime, as a workaround, you probably need to just set the pane for a shared scene, replacing its content rather than replacing the scene.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class NewFXMainFixed extends Application {
#Override
public void start(Stage primaryStage) {
// init buttons
Button btn1 = new Button("switch to next scene >>");
Button btn2 = new Button("<< switch to previous scene");
// first scene
StackPane root1 = new StackPane();
root1.getChildren().add(btn1);
// second scene
StackPane root2 = new StackPane();
root2.getChildren().add(btn2);
// button actions
btn1.setOnAction((ActionEvent event) ->
primaryStage.getScene().setRoot(root2)
);
btn2.setOnAction((ActionEvent event) ->
primaryStage.getScene().setRoot(root1)
);
Scene scene = new Scene(root1);
primaryStage.setMaximized(true);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is there any possible way to wait for the scene to repaint?
My problem is, that i want to add a Note to a Pane with getChildren().add() and then to fire an event on this Node with Node.fireEvent(event).
But the event is not performed. I think the problem is, that the scene was not repainted at the point of the fireevent and so the Node is not a part of the new Scene at this time.
So the best way would be to wait for the scene to repaint and then fire the event i think.
I do not know which is the UI Component you has been used here, but, try to find out the invalidate() method (or something like that) of your component to make it update the screen after all.
Can you post code? This works fine for me:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class NewNodeEventTest extends Application {
#Override
public void start(Stage primaryStage) {
HBox root = new HBox(5);
Button newNodeButton = new Button("Add button");
newNodeButton.setOnAction(event -> {
Button newButton = new Button("Button");
newButton.setOnAction(e -> System.out.println("New button pressed"));
root.getChildren().add(newButton);
ActionEvent evt = new ActionEvent(newButton, newButton);
newButton.fireEvent(evt);
});
root.getChildren().add(newNodeButton);
Scene scene = new Scene(root, 250, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}