switch/quit fxml - javafx

My test app's flow uses multiple screens :
start(Stage stage) -> Screen 1
-> Screen 2
-> ...
I want to implement some of my screens in fxml, but can't figure what's the best practise way to switch between them.
How can i implement some quit-event mechanism in the screen 1 controller, when screen 1 reached its final state, and connect it to the "main loop" to delete screen 1 and update the scene with screen 2 ?

In my opinion, the best way to do it is loading your screens "on demand" whenever they are to be used, or even load them for just certain regions of your main screen (like a tab). To load a screen with FXML and then assign it to your main stage you would do something like:
Parent root = FXMLLoader.load(me.getClass().getResource("Scene2.fxml"));
Scene scene = new Scene( root );
stage.setScene(scene);
Another alternative is to use multiple stages, launch a stage whenever you need to do a specific action. This stage can be modal, so when it is closed, the main window stays behind:
final Stage stage = new Stage();
stage.initStyle(StageStyle.UNDECORATED);
stage.initOwner(owner_stage);
stage.initModality(Modality.APPLICATION_MODAL);
In this later case, the "quit mechanism" is just hiding the scene:
// from a label of your controller class
label.getScene().getWindow().hide();
In the first case, you would just load the main scene in your stage. Using multiple stages is the most common and straightforward way.

Related

Switching To Primary Scene When Secondary Scene Is Closed

I am using JavaFX 11 and a newbie.
I have a single stage with two scenes: a primary scene that shows on start and a secondary scene that is switched to and shown when I press a certain button on the main scene. On the secondary scene, I want to be able to switch back to the main scene when I click the close X button on the top right of the window instead of having the entire application close.
I currently have a method for the cancel button that looks like this:
public void cancelButtonPushed(ActionEvent event) throws IOException {
Parent parent = FXMLLoader.load(getClass().getResource("ExampleMainScreen.fxml"));
Scene scene = new Scene(parent);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
This method allows me to switch back to the main scene when the Cancel button is pushed. However, I am lost trying to find something that can be used any time the user clicks the close X on the secondary scene.
First, get the terminology right, my guess is that you have two Stages. Scenes can be displayed inside those.
Second, Scene Builder (and the FXML it produces) does not manage stages, it only constructs nodes (and event handling for those nodes) that are placed inside scenes. So, you won't find the hooks you need to integrate with the window close functions in SceneBuilder or FXML.
Third, when a user wants to close a window (a stage is a kind of window), then an event will be emitted, which you can action onCloseRequest.
Fourth, somehow you have already managed to create a second stage, probably by calling new Stage(). This will provide you with a reference to the stage which you can set your close request on:
Stage secondaryStage = new Stage();
Stage setScene(secondaryScene);
secondaryStage.setOnCloseRequest(e -> primaryStage.show());
This will show your primary stage (which I guess you hid earlier), when the secondary stage is being closed, but before it has actually closed.
Next, read up on the Application lifecycle, specifically see the section which references Platform.setImplicitExit(boolean implicitExit):
If this attribute is true, the JavaFX runtime will implicitly shutdown when the last window is closed; the JavaFX launcher will call the Application.stop() method and terminate the JavaFX application thread. If this attribute is false, the application will continue to run normally even after the last window is closed, until the application calls exit(). The default value is true.
Note, that, you probably don't need to explicitly set the implicit exit flag if you handle the stage switching as outlined previously, but I provide the info for you in case you need to understand it.
Finally, consider whether you really should be creating new stages for your application and this particular task or just replacing the content in a single stage (similar to how a web browser works).

How to disable dragndrop for tabpane in Javafx/Tornadofx?

My Scene already haves a dragndrop feature but now that I have made the root a TabPane, the TabPane creates a new tab if I drag files into my Program (my dragndrop feature still works). How can I disable this behaivior of the TabPane? I tried to consume the events in the setOnDragDropped and setOnDragOver of the scene but it seems like the events reaches the TabPane before it reaches my Scene.
Update:
I think its not possible for me to close the question (correct me if Im wrong), but I misinterpreted what was going on or what was the mistake, the whole Question was wrong. I solved the real problem with a workaround (by putting tabpane into vbox, so now vbox is the root).
The real problem is this: accesing nodes adds them into other nodes.
fun justManipulateNodes(){
someNode.add(...) //This line doesnt only manipulate someNode but it Adds someNode itself to where ever justManipulateNodes() is called. To be concrete, someNode is another vbox in my example and Im adding labels into it
}
override fun onBeforeShow() {
super.onBeforeShow()
this.currentStage?.scene?.setOnDragDropped {
//do other stuff
justManipulateNodes() //this call ads someNode into the Tabpane as a new Tab if it is the root
it.consume()
}

JavaFX - How to load a specific AnchorPane's contents from an FXML file?

I'm trying to make an application which switches between scenes back and forth however I need to load a specific AnchorPane's contents into another AnchorPane when the scene switches back. For Example:
In my FXML1, I have a hierarchy that looks like this:
AnchorPane0
----SplitPane
--------AnchorPane1
--------AnchorPane2
In FXML2 the hierarchy is just this:
AnchorPane0
So I load FXML1, then I have a button that switches scenes loading FXML2.AnchorPane0 into FXML1.AnchorPane2. I have a back button in FXML2.AnchorPane0 that needs to load the original scene of FXML1.AnchorPane2 into FXML1.AnchorPane2. Right now my back button loads all 4 containers of FXML1 into FXML1.AnchorPane2. So my questions is, how do I load a specific container's contents preferably without making FXML1.AnchorPane2 its own FXML? Do I need to write a get method for the FXML1.AnchorPane2 to access its contents or is there a way to return an AnchorPane with all of its contents in place already?
I found the solution as shown below:
AnchorPane loader = FXMLLoader.load(getClass().getResource("myFXML.fxml"));
SplitPane spane = (SplitPane) loader.getChildren().get(0);
AnchorPane pane = (AnchorPane) spane.getItems().get(1);
foregroundAnchorPane.getChildren().setAll(pane);

setScene() method, JavaFX

I have a program with few fxml files so at different points of program different scene and layout is shown.
some point in a program:
mainStage.setScene(FXMLScene1);
...
later in a program:
mainStage.setScene(FXMLScene2);
...
later in a program:
mainStage.setScene(FXMLScene2);
I wonder what happens to old scene when I use setScene() several times?
There are very complicated methods to change scene(like this https://blogs.oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1) and my solution is just to make static refference to main stage at MainApplication class so I can manage it everywhere.
public class MainApplication extends Application {
public static Stage parentWindow;
#Override
public void start(Stage stage) throws Exception {
parentWindow = stage;
so it made me wonder if everything is allright with my concept...
You don't have to create a scene to flip screens. You can directly set the root node on the present scene, using setRoot() of the Scene.
This will save you the pain of creating a scene instance every time to want to change the content of your application.
You can use it:
Parent root = FXMLLoader.load(getclass.getResource("some-fxml.fxml"));
scene.setRoot(root);
Just keep in mind the Parent element being used in the FXML that you want to set as the Root Element. From the docs
The application must specify the root Node for the scene graph by setting the root property. If a Group is used as the root, the contents of the scene graph will be clipped by the scene's width and height and changes to the scene's size (if user resizes the stage) will not alter the layout of the scene graph. If a resizable node (layout Region or Control is set as the root, then the root's size will track the scene's size, causing the contents to be relayed out as necessary.
N.B. Please read through the EXAMPLE that you have provided, it uses setScreen( ) instead of setScene( ). The whole example has just one Scene and many Screens, where screens can be considered as any child of the scene graph
Additional data as per comments
If you go through the scene javadoc, you will find that Scene resizes itself to the root size, if no predefined size is present
The scene's size may be initialized by the application during construction. If no size is specified, the scene will automatically compute its initial size based on the preferred size of its content. If only one dimension is specified, the other dimension is computed using the specified dimension, respecting content bias of a root.
Different FXML have different size
In case you have different FXML that you want to set as ROOT nodes and each of them have different sizes.Futhermore, you want to re-size your stage in accordance to every FXML that you load, then you will have to re-initialize the Scene, there is no other way.
Parent root = FXMLLoader.load(getclass.getResource("some-fxml.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
If you set a new scene on your stage and keep no reference to the old scene or objects in it, the old scene will be garbage collected and all resources associated with it will be thrown away (whenever the garbage collector in the Java Virtual Machine decides to do so). If you keep a reference to the old scene (e.g. assign it to a static final variable in your application), then the old scene resources will remain in memory until your application terminates.
If I change the root, how to make Stage change to the new size(size of Layout)?
Use stage.sizeToScene(), which will: "Set the width and height of this Window to match the size of the content of this Window's Scene." The stage sizing process when you invoke this call is similar to when you initially show a stage, but updated for the current scene's content and layout constraints.
The algorithm used is documented in the Scene javadoc: "The scene's size may be initialized by the application during construction. If no size is specified, the scene will automatically compute its initial size based on the preferred size of its content. If only one dimension is specified, the other dimension is computed using the specified dimension, respecting content bias of a root."
what is better, to change the whole scene, or just a root?
I don't think it makes much difference, choose whichever strategy makes the most sense to you.

Make a javafx stage the owner of a JDialog?

Display a swings JDialog containing a JRViewer for Jasper Report, from within a javafx application menu item click. BUT the JDialog is not MODAL even after setModal(true) as it is not owned by javafx stage. How to make a javafx stage the owner of a JDialog? Alternatively how to display a Jasper report inside a javafx stage, scene?
Don't use a JDialog. In JavaFX you use Stages. Put your Jasper report inside a Scene, put the Scene in a Stage. I think you can't put Swing components inside JavaFX components, so you must check this link: Integrating JavaFX into Swing Applications
Code to make a JavaFX dialog:
Stage stage = new Stage();
stage.setTitle("Title");
stage.setResizable(false);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(primaryStage);
stage.initStyle(StageStyle.UTILITY);
(...)
Scene scene = new Scene(...);
stage.setScene(scene);
In the code above the Stage will behave exactly like a dialog.
Also check this:
How to create a JavaFX dialog?
Hello World, JavaFX Style
Back to the first (base) question: It's not possible to make a Stage the owner of a JDialog. But, after setting the content of the SwingNode
swingNode.setContent(xxx);
you can get the parent of xxx (may be the parent of the parent of xxx too...) until you find a (AWT-) Frame or (AWT-) Dialog. In the case of SwingNode it's a JLightweightFrame. This object you can use as the owner to create your JDialog.
But, I think there is a bug in the JavaFX SwingNode mechanism. The JDialog appears functionally modal, if you set it modal (setModal(true)), but it may appear behind the main (JavaFX) window. It's still possible to move the main window, to bring it in front and so on, what should not be possible if there is a modal dialog. But this is another topic.
To view a Jasper Report in javafx ( jdk 1.8> ) stage
JasperPrint jp = JasperFillManager.fillReport(reportsource, params,getCon());
SwingNode swingNode = new SwingNode();
swingNode.setContent(new JRViewer(jp));
AnchorPane anchorPane = new AnchorPane();
AnchorPane.setTopAnchor(swingNode,0.0);
AnchorPane.setBottomAnchor(swingNode,0.0);
AnchorPane.setLeftAnchor(swingNode,0.0);
AnchorPane.setRightAnchor(swingNode,0.0);
anchorPane.getChildren().add(swingNode);
Scene scene = new Scene(anchorPane);
Stage stage = new Stage();
stage.setHeight(550);
stage.setWidth(600);
stage.setAlwaysOnTop(true);
stage.setScene(scene);
stage.showAndWait();
Alternatively how to display a Jasper report inside a javafx stage, scene?
Output HTML from your Jasper report and load the report in a JavaFX WebView.
Make a javafx stage the owner of a JDialog?
I don't think this is possible. What should work instead is to make your application a Swing application and host your JavaFX content inside a JFXPanel inside a Swing JFrame. Then you have no Stage and the owner of the JDialog can be the JFrame hosting the JFXPanel.
now it's possible to do that
what you need now is install jdk 8
because swing content in javafx is introduced in jdk8
#FXML
private SwingNode swingNode;
...
JasperPrint jasperPrint = JasperFillManager.fillReport(FileName, hash, connect);
swingNode.setContent(new JRViewer(jasperPrint));
oracle just add tutorial for this too
for the next you can follow this link
oracle tutorial embed swing in javaFX
I have written a JasperReports print preview stage class in JavaFX 8. It is a pure JavaFX code and there is no need for use of SwingNode class.
In comparison to the JRViewer it does not support links and parts, but print preview window does not need it anyway. On the other side, code is much, much shorter and therefore it is easy to understand and to adopt further to everyone's needs.
Source code for this class is freely available at GitHub repository.

Resources