Reputation: 328845
In my application, I have several independent (non modal) Stages.
I would like the following behaviour:
The first two requirements are easy (unless I missed something), something like:
mainStage.iconifiedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null && newValue != oldValue) {
for (Stage s : otherStages) { s.setIconified(newValue); }
}
});
However I'm stuck on the third one. I have tried using the focusedProperty
but it does not work (if I click on a menu in one of the stages, for example, because it first brings the other stages to the front, it loses focus and the menu does not open)...
//do this for each stage
stage.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (Boolean.TRUE.equals(newValue) && newValue != oldValue) {
for (Stage s : otherStages) {
s.setIconified(false);
s.toFront();
}
//request the focus back, but that creates issues
stage.requestFocus();
}
});
Any ideas on how to implement the third requirement?
Upvotes: 5
Views: 2112
Reputation: 159566
Solution
Use stage.initOwner(parentStage).
Sample App
Here is quick test application which initializes the owners of all of the created stages to the primary application stage.
The test app seems to fulfill all of your requirements (tested on Windows 7, JavaFX 8b122).
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class LotsaStages extends Application {
private static final Color[] STAGE_COLORS = {
Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW
};
private static final double STAGE_OFFSET = 50;
@Override
public void start(Stage primaryStage) throws Exception {
addContent(primaryStage, Color.LIGHTBLUE);
primaryStage.show();
double offset = STAGE_OFFSET;
for (Color color: STAGE_COLORS) {
Stage child = new Stage();
child.initOwner(primaryStage);
child.initStyle(StageStyle.UTILITY);
child.setX(primaryStage.getX() + offset);
child.setY(primaryStage.getY() + offset);
addContent(child, color);
child.show();
offset += STAGE_OFFSET;
}
}
private void addContent(Stage child, Color color) {
child.setScene(
new Scene(
new Group(
new Rectangle(150, 70, color)
)
)
);
}
public static void main(String[] args) { launch(args); }
}
The test app is pretty simple and I didn't try to replicate your menu based issues, so I'm not sure if it will pass correctly for the menu based processing or other requirements you may have.
Additional Questions
So basically initOwner "links" the children to the main stage?
Yes, MSDN explains how window ownage works on Windows. Behaviour may differ slighty on other platforms (which is why the JavaFX Javadoc on the matter is deliberately vague), but I think most of the principles are the same and it should work on OS X and Linux is a similar manner.
From MSDN:
To allow you to create a relationship between a child window and a parent window, Window supports the notion of ownership. Ownership is established when the Owner property of a window (the owned window) is set with a reference to another window (the owner window).
Once this relationship is established, the following behaviors are exhibited:
- If an owner window is minimized, all its owned windows are minimized as well.
- If an owned window is minimized, its owner is not minimized.
- If an owner window is maximized, both the owner window and its owned windows are restored.
- An owner window can never cover an owned window.
- Owned windows that were not opened using ShowDialog are not modal. The user can still interact with the owner window.
- If you close an owner window, its owned windows are also closed.
- If an owned window was opened by its owner window using Show, and the owner window is closed, the owned window's Closing event is not raised.
When you open a child window by calling ShowDialog, you should also set the Owner property of the child window. If you don't, then your users won't be able to restore both child window and parent window by pressing the task bar button. Instead, pressing the task bar button will yield a list of windows, including both child and parent window, for them to select; only the selected window is restored.
Can that work with children such as
child = new Stage(UNDECORATED); child.initModality(Modality.NONE);
?
Yes, it does.
Upvotes: 7