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()
}
Related
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).
In my main window there is a list of items which the user can add to. I want to have a dialog window open each time they add an item where they can add information about that item such as the name of it etc., and then it will be added into the list on the main window with all provided data. I would also like them to be able to click on the item later and open the same dialog in which they can edit the data.
I am having trouble figuring out how to implement this, mainly in how to carry the information from the dialog back to the main window, and in the case of editing the item carrying the initial information already in the list to the dialog for it to be edited. What would be a good way to do this?
I am sure there is a common method for it since this is a very common feature in many software applications, I've just been unable to find guidance on how to do it.
In a previous application I sent the index of the item in the list to the dialog as well as a struct or enum of some kind containing the information between the windows so it could insert the item into the list at the correct index with the correct data, but this became very messy and clunky.
#FXML
private ListView<String> taskListView;
public void addTask() {
taskListView.getItems().add("New");
}
public void editTask() throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("EditWindowView.fxml"));
Stage stage = new Stage();
Scene scene = new Scene(root);
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle("Edit Task");
stage.setScene(scene);
stage.show();
}
I open the dialog with the editTask method so this is where I would put in information that needs to be carried back and forth, I'm just really unsure of how to do that.
I know this is quite a broad question so please bear with me. Even if anyone could provide a link to a tutorial which teaches something like this that would be greatly appreciated. If you need any more information from me please let me know. Thank you.
I'm learning basic JavaFX right now, and I don't understand this statement from the book I'm reading: "No, a node such as a text field can be added to only one pane and once. Adding a node to a pane multiple times or to different panes will cause a runtime error." I can see from the UML diagram the book provides that it is a composition, but I don't understand why (library class code implementation) that is.
For instance, why does this result in a compile error? Isn't a new text field instantiated within the pane since it's a composition?
FlowPane pane = new FlowPane();
StackPane pane2 = new StackPane();
TextField tf = new TextField();
pane.getChildren().add(tf);
pane.getChildren().add(tf);
Also, why does the following run but not show the text field placed in pane?
FlowPane pane = new FlowPane();
StackPane pane2 = new StackPane();
TextField tf = new TextField();
pane.getChildren().add(tf);
pane2.getChildren().add(tf);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
This is basically a (deliberate) consequence of the way the API is designed. Each Node has a collection of properties, including a parent property (the - one and only one - parent of the node in the scene graph), along with properties such as layoutX and layoutY which are the coordinates of the node in relation to its parent. Consequently, a node can only belong to one parent, and can only be added to a parent once (as it can only have one location in the parent). Organizing things this way enables a very efficient layout process.
Another way to think of this: suppose your first code block did what you wanted; so the text field tf appeared twice in the flow pane. What result would you expect to get from tf.getBoundsInParent()? Since tf appears twice in the parent, the API would not be able to give a sensible value for this call.
There are a couple of inaccuracies in statements you make in your question:
For instance, why does this result in a compile error? Isn't a new
text field instantiated within the pane since it's a composition?
First, technically, this is aggregation, not composition; though I'm not sure understanding the difference will aid your understanding of what is happening at this point.
Second, there is no compile error here; you get an error at runtime (the pane detects that the same node has been added twice; the complier has no way to check this).
Third, parents do not instantiate copies of the nodes you add to them. If so, you wouldn't be able to change the properties of nodes that were displayed. For example, if the FlowPane in your example instantiated a new TextField when you called pane.getChildren().add(tf);, and then displayed that new text field, then if you subsequently called tf.setText("new text"), it would have no effect, as it would not be changing the text of the text field that pane was displaying.
When you call pane.getChildren().add(...) you pass a reference to the node you want to be added; it is that node that is then displayed as a child of the pane. Any other implementation would produce pretty counter-intuitive behavior.
In your second code block:
pane.getChildren().add(tf);
pane2.getChildren().add(tf);
the second call implicitly sets the parent property of tf to pane2; consequently tf is no longer a child of pane. So this code has the effect of removing tf from the first parent, pane. As far as I am aware, this side-effect is not documented, so you probably should avoid writing code like this.
Try this:
TextField tf = new TextField();
TextField tf2 = new TextField();
pane.getChildren().add(tf);
pane.getChildren().add(tf2);
The reason you cannot add the same node twice is that only one node with the same specifications and dimensions can be viewable in the gui. It would be like copying an identical blue circle onto an original blue circle. To the user it looks the same, but it takes up more memory.
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);
Apologies for asking such a simple question but english is not my first language, so I cannot find the right word to describe my problem and cannot google up the the right question.
I have a javafx application and there is a button whereby if the user clicks it, it generates a new window (like a display box just to display more info to a user). The problem is when the new window is displayed and I click anywhere of the Javafx application, I cannot get back to it. I have to close the new window first in order to interact with the original javafx application.
How can I have the ability to open the new window on a button click, while retaining the ability to interact the original javafx application without having to close the new window ?
Button moreInfo = new Button("More Info");
moreInfo.setOnAction(e -> {
MoreInfoDisplayBox.display();
});
public class MoreInfoDisplayBox {
public static void display(){
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle("More Info");
window.setMinWidth(400);
window.setMinHeight(400);
window.show();
}
}
The behavior you describe is caused by calling
window.initModality(Modality.APPLICATION_MODAL);
(See docs.)
Instead, just use the default, which you can do explicitly with
window.initModality(Modality.NONE);
or of course just omit that line entirely.