Reputation: 160
I have
The latter does not work, even if I use setAlwaysOnTop(true)
for the secondary stages they will disappear behind the primary stage once the user clicks on the primary stage.
This only happens when the primary stage is in full screen mode, everything works fine if the primary stage is not in fullscreen mode.
How can I enable this concept of tools windows in front of a fullscreen stage? Example code:
public class Test extends Application {
@Override
public void start(Stage stage) {
VBox vbox = new VBox();
Scene scene = new Scene(vbox);
stage.setScene(scene);
Button button1 = new Button("New Tool Window");
button1.setOnAction((e) -> {
Stage toolStage = new Stage();
Scene toolScene = new Scene(new Label("Am I on top?"), 300, 250);
toolStage.setScene(toolScene);
toolStage.initOwner(stage);
toolStage.setAlwaysOnTop(true);
toolStage.show();
});
Button button2 = new Button("Close");
button2.setOnAction((e) -> System.exit(0));
vbox.getChildren().addAll(button1, button2);
stage.show();
stage.setFullScreen(true);
}
public static void main(String[] args) {
launch(args);
}
}
Update 8/20/2016: Confirmed as a bug: JDK-8164210
Upvotes: 3
Views: 2649
Reputation: 102
The trick is using setAlwaysOnTop when detecting primary stage on top. It could be used another separate thread to detect. I used jnativehook to detect.
<!-- https://mvnrepository.com/artifact/com.1stleg/jnativehook -->
<dependency>
<groupId>com.1stleg</groupId>
<artifactId>jnativehook</artifactId>
<version>2.1.0</version>
</dependency>
Your working example is here:
public class Test extends Application {
@Override
public void start(Stage stage) {
VBox vbox = new VBox();
Scene scene = new Scene(vbox);
stage.setScene(scene);
Button button1 = new Button("New Tool Window");
button1.setOnAction((e) -> {
Stage toolStage = new Stage();
Scene toolScene = new Scene(new Label("Am I on top?"), 300, 250);
toolStage.setScene(toolScene);
toolStage.initOwner(stage);
toolStage.setAlwaysOnTop(true);
setAlwaysOnTop(toolStage);
toolStage.show();
});
Button button2 = new Button("Close");
button2.setOnAction((e) -> System.exit(0));
vbox.getChildren().addAll(button1, button2);
stage.show();
stage.setFullScreen(true);
}
public static void setAlwaysOnTop(Stage stage){
initialize();
stageOnTop = stage;
}
private static boolean initialized = false;
private static Stage stageOnTop;
private static void initialize(){
if(initialized){
return;
}
/* Note: JNativeHook does *NOT* operate on the event dispatching thread.
* Because Swing components must be accessed on the event dispatching
* thread, you *MUST* wrap access to Swing components using the
* SwingUtilities.invokeLater() or EventQueue.invokeLater() methods.
*/
try {
GlobalScreen.registerNativeHook();
} catch (NativeHookException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
GlobalScreen.setEventDispatcher(new SwingDispatchService());
GlobalScreen.addNativeMouseListener(new NativeMouseListener() {
@Override
public void nativeMouseClicked(NativeMouseEvent nativeMouseEvent) {}
@Override
public void nativeMousePressed(NativeMouseEvent nativeMouseEvent) {
if(stageOnTop != null && stageOnTop.isShowing()){
Platform.runLater(() -> {
stageOnTop.setAlwaysOnTop(false);
stageOnTop.setAlwaysOnTop(true);
});
}
}
@Override
public void nativeMouseReleased(NativeMouseEvent nativeMouseEvent) {}
});
initialized = true;
}
public static void main(String[] args) {
launch(args);
}
}
Upvotes: 0
Reputation: 1
I recently created a picture checker to show the application on top of taskbar. But it may cause issues with security with javafx.scene.robot.Robot
, because snapshot may be not allowed.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.image.WritableImage;
import javafx.scene.robot.Robot;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.Timer;
import java.util.TimerTask;
public class Main extends Application {
private Timer timer;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setWidth(200);
root.setHeight(45);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setScene(new Scene(root));
primaryStage.setAlwaysOnTop(true);
primaryStage.setX(Screen.getPrimary().getVisualBounds().getMaxX() - 500);
primaryStage.setY(Screen.getPrimary().getVisualBounds().getMaxY());
primaryStage.show();
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
final Robot robot = new Robot();
@Override
public void run() {
Platform.runLater(this::runFX);
}
private void runFX() {
final int width = (int) Math.round(primaryStage.getWidth());
final int height = (int) Math.round(primaryStage.getHeight());
WritableImage image = robot.getScreenCapture(new WritableImage(width, height),
new Rectangle2D(
primaryStage.getX(), primaryStage.getY(),
width, height
));
WritableImage inner = primaryStage.getScene().snapshot(new WritableImage(
width,
height
));
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (image.getPixelReader().getArgb(x, y) != inner.getPixelReader().getArgb(x, y)){
primaryStage.setAlwaysOnTop(false);
primaryStage.setAlwaysOnTop(true);
return;
}
}
}
}
}, 0, 500);
}
}
Upvotes: 0
Reputation: 582
you need to set initmodality after set initowner
toolStage.initOwner(stage);
toolStage.initModality(Modality.APPLICATION_MODAL);
Upvotes: 0
Reputation: 396
A way to bypass this limitation is to: Deactivate fullscreen mode Create a keyCombination for psuedo fullscreen Set the stage style undecorated and not resizable Se the screen to the size of the user screen and position it at 0,0.
It is easy to create your own border for minimizing and closing the program as shown here:
JavaFX Stage.setMaximized only works once on Mac OSX (10.9.5)
And here:
JavaFX 8 Taskbar Icon Listener
Upvotes: 0