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);
Related
I have a JavaFX application and I need simple to avoid user from opening the same window inside the application more than once.
I tried to find some solution, but nothing get applicable.
As a sample... I have a window that give me payments options, its not a modal, it's a new stage. While I click the button to open that window, it's open, doesn't matter if there is another instance of this same stage running, simple open new windows every click. I want to avoid this. Like switch to the already opened stage window when click the button, or simply miss the click if that window is already opened.
You just need to keep track of the stage and only open a new one if its not already shown. You could also choose to disable the Button if the new window is showing, but I prefer to have the new window simply brought back in front so the user knows it's there.
You can do this by creating a reference to your Stage and then checking if it is null or showing within the button's event handler.
Here is an MCVE to demonstrate:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
/**
* Reference to the new Window that will allow only one instance at a time.
*/
private Stage newWindowStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button openWindow = new Button("Open Window");
// **********************************************************************************************
// Set the button to open the new Window Stage
// **********************************************************************************************
openWindow.setOnAction(event -> {
// **********************************************************************************************
// Check if the Stage is already showing.
// **********************************************************************************************
if (newWindowStage == null || !newWindowStage.isShowing()) {
// **********************************************************************************************
// The new window is not currently open, so create/show it
// **********************************************************************************************
newWindowStage = new Stage();
newWindowStage.setWidth(300);
newWindowStage.setHeight(300);
newWindowStage.setScene(new Scene(
new VBox(
new Label("New Window!")
)
));
newWindowStage.show();
} else {
// **********************************************************************************************
// The window is already open, so bring it to the front of focus
// **********************************************************************************************
newWindowStage.toFront();
}
});
root.getChildren().add(openWindow);
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(200);
primaryStage.setHeight(200);
primaryStage.setTitle("Test Application");
primaryStage.show();
}
}
package example;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Text text = new Text("This is a Text");
VBox box = new VBox();
box.setAlignment(Pos.CENTER);
box.setStyle("-fx-background-color: yellow;");
box.getChildren().add(text);
StackPane container = new StackPane();
container.getChildren().add(box);
BorderPane bp = new BorderPane();
bp.setCenter(container);
Scene scene = new Scene(bp, 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);
}
}
Here's the output:
Question: Can someone explain to me why the Vbox fill the whole screen? Is there a method that is similar to Android's wrap_content? I want the image below to be the output:
Solution
Wrap the VBox in a Group; e.g. use:
container.getChildren().add(new Group(box));
instead of:
container.getChildren().add(box);
Why it works
From the Group javadoc:
By default, a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass.
This means that the VBox won't grow past the preferred size of it's content (which is just enough area to display the label inside it).
Alternate implementation
Set the maximum size of the VBox to the preferred size. Then the VBox will only ever grow large enough to fit the preferred size of the content inside it and will never grow any larger.
box.setMaxSize(VBox.USE_PREF_SIZE, VBox.USE_PREF_SIZE);
Why VBox grows by default
It is a resizable container which will stretch to fill available area.
Note
I don't know that the effect is exactly the same as an Android wrap_content method as I have never developed for Android, however the effect does seem to exactly match the second image you provided in your question, which appears to be what you want.
VBox automatically resizes itself to the size of the Parent, so it is better not to set background color to it. Instead, you can use a Label in place of a Text and then add background color to the Label instead of the VBox.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Label text = new Label("This is a Text");
VBox box = new VBox();
box.setAlignment(Pos.CENTER);
text.setStyle("-fx-background-color: yellow;");
box.getChildren().add(text);
StackPane container = new StackPane();
container.getChildren().add(box);
BorderPane bp = new BorderPane();
bp.setCenter(container);
Scene scene = new Scene(bp, 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);
}
}
This will give you an output like an image below:
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();
}
}
In one of my recent projects I want to implement a hidden page. I want to be able to reach it by just typing the password without anything showing on the screen. I tried to just set a PasswordField as visible(false). However that didn't work. Also I would like the hidden page to pop up without having to press enter after typing the password. Is there a way for a simple javafx application to behave like that?
You could use a KeyListener. Though you need to press the screen once for the keypresses to register. And make sure to add the keylistener to the JFrame, I always forget that. This will look for keys, but requires a window to be shown, this can be empty though.
If you don't want a window at all, you can use the external library jnativehook it looks for keypresses globally.
You can add an event filter to the scene that keeps track of what has been typed.
Here is a simple example (type "secret" with the main window focussed to show the popup window; press enter if you mistype to clear the hidden text):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class OpenSecretWindow extends Application {
#Override
public void start(Stage primaryStage) {
Label label = new Label("Type the secret password\nto open the secret window");
label.setTooltip(new Tooltip("The secret password is \"secret\""));
StackPane root = new StackPane(label);
Scene scene = new Scene(root, 400, 400);
StringBuilder typedText = new StringBuilder();
scene.addEventFilter(KeyEvent.KEY_TYPED, e -> {
switch(e.getCharacter()) {
case "\n":
case "\r":
typedText.delete(0, typedText.length());
break ;
default:
typedText.append(e.getCharacter());
}
if ("secret".equals(typedText.toString())) {
openSecretWindow(primaryStage);
typedText.delete(0, typedText.length());
}
});
// handle backspace and delete:
scene.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode() == KeyCode.BACK_SPACE || e.getCode() == KeyCode.DELETE) {
if (typedText.length() > 0) {
typedText.delete(typedText.length()-1, typedText.length());
}
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void openSecretWindow(Stage owner) {
Stage stage = new Stage();
StackPane root = new StackPane(new Label("You have found\nthe secret window!"));
Scene scene = new Scene(root, 300, 180);
stage.setScene(scene);
stage.initOwner(owner);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I believe you could set the foreground of the JPasswordField to be the same as the background colour but don't quote me on that. Something like:
JPasswordField.SetForeground(Color.RED);
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);
}
}