Issue
The following elementary code works perfectly. It determines the list of available screens, and then identifies the largest one, and then sets the size of the stage equal to the dimensions of the largest monitor.
public class MaximizedStage extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
ObservableList<Screen> monitors = Screen.getScreens();
//determine the larger monitor
Rectangle2D bounds = monitors.get(0).getVisualBounds(); //get the bounds of the default monitor
int preferred_mon = 0;
var x = bounds.getMinX();
var y = bounds.getMinY();
var w = bounds.getWidth();
var h = bounds.getHeight();
System.out.println("Count of monitors : "+monitors.size()+" default monitor size : w: "+w+" h: "+h);
for (int mon = 1; mon < monitors.size(); mon++) { // for each of the other monitors
Rectangle2D next_monitor_bounds = monitors.get(mon).getVisualBounds(); //get the size
if ((next_monitor_bounds.getHeight() > h) && (next_monitor_bounds.getWidth() > w)) {
//larger monitor, update parameters
preferred_mon = mon;
x = next_monitor_bounds.getMinX();
y = next_monitor_bounds.getMinY();
w = next_monitor_bounds.getWidth();
h = next_monitor_bounds.getHeight();
}
}
System.out.println("Largest monitor : index: "+preferred_mon+" w: "+w+" h: "+h);
stage.setScene(new Scene(new Group()));
stage.setTitle("Full screen");
// Set the position and size of the stage equal to the position and
// size of the screen
stage.setX(x);
stage.setY(y);
stage.setWidth(w);
stage.setHeight(h);
// Show the stage
stage.show();
}
}
My question is: where in this code does the stage (or the Scene) know which is the final selection among the screens to display on? Am struggling to understand the linkage between stage/scene and the choice of the monitor. The final objective is to prepare multiple different stages, run each one on a different thread, and direct their output to a different monitor.
Thanks for your attention.
Solution
The bounds of each screen are relative to some virtual coordinate system. The bounds describe the width and height of each screen, but also the position of the screen in that coordinate system, thereby defining the locations of the different display devices relative to each other. Typically these are defined by user preferences set at the OS level.
On the set up I'm currently using, I have a MacBook Pro connected to an external display (which is configured as the primary display). In System Preferences, this looks like:
which is a decent approximation to the physical layout I have. This means I can drag windows from the main screen (the one shown on the right) to the left edge of that screen, and then onto the laptop screen (shown on the left).
The larger screen has dimensions 3008x1692, and the laptop screen has dimensions 1680x1050. The bounds are reported as
Rectangle2D [minX = 0.0, minY=0.0, maxX=3008.0, maxY=1692.0, width=3008.0, height=1692.0]
Rectangle2D [minX = -1680.0, minY=213.0, maxX=0.0, maxY=1263.0, width=1680.0, height=1050.0]
So the primary screen is listed first, with location (0,0), and the laptop screen is listed second, offset 1680 pixels to the left and 213 pixels lower than the primary screen.
In your code, the x
and y
coordinates of the stage are set to the x
and y
coordinates of the chosen (largest) screen in this coordinate system, so the top left of the stage will be at the top left of that screen. By setting the width and height of the stage to the width and height of the screen, the stage will exactly fill that screen (though you may lose some of the stage behind the system task bar or dock).
Here's a similar test example, which makes the window half the width and height of the screen, and centers it:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.io.IOException;
public class ScreenDemo extends Application {
@Override
public void start(Stage stage) throws IOException {
VBox buttons = new VBox(5);
buttons.setFillWidth(true);
buttons.setAlignment(Pos.CENTER);
int index = 1 ;
for (Screen screen : Screen.getScreens()) {
System.out.println(screen.getBounds());
Button button = new Button("Screen "+index);
button.setOnAction(e -> centerInScreen(stage, screen));
button.setMaxWidth(Double.MAX_VALUE);
buttons.getChildren().add(button);
index++;
}
HBox root = new HBox(buttons);
root.setFillHeight(true);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root);
stage.setScene(scene);
centerInScreen(stage, Screen.getPrimary());
stage.show();
}
private void centerInScreen(Stage stage, Screen screen) {
// make window half screen size and center in screen:
Rectangle2D screenBounds = screen.getBounds();
double x = screenBounds.getMinX();
double y = screenBounds.getMinY();
double w = screenBounds.getWidth();
double h = screenBounds.getHeight();
stage.setX(x + w/4);
stage.setY(y + h/4);
stage.setWidth(w/2);
stage.setHeight(h/2);
}
public static void main(String[] args) {
launch();
}
}
Here's a similar example which centers a stage in every screen:
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.io.IOException;
public class ScreenDemo extends Application {
@Override
public void start(Stage stage) throws IOException {
int index = 1 ;
for (Screen screen : Screen.getScreens()) {
if (screen != Screen.getPrimary()) {
Label label = new Label("Window in screen "+index);
BorderPane root = new BorderPane(label);
Scene scene = new Scene(root);
Stage window = new Stage();
window.setScene(scene);
centerInScreen(window, screen);
window.show();
}
index++;
}
Label label = new Label("Window in primary screen");
BorderPane root = new BorderPane(label);
Scene scene = new Scene(root);
stage.setScene(scene);
centerInScreen(stage, Screen.getPrimary());
stage.show();
}
private void centerInScreen(Stage stage, Screen screen) {
// make window half screen size and center in screen:
Rectangle2D screenBounds = screen.getBounds();
double x = screenBounds.getMinX();
double y = screenBounds.getMinY();
double w = screenBounds.getWidth();
double h = screenBounds.getHeight();
stage.setX(x + w/4);
stage.setY(y + h/4);
stage.setWidth(w/2);
stage.setHeight(h/2);
}
public static void main(String[] args) {
launch();
}
}
Answered By - James_D
Answer Checked By - Katrina (JavaFixing Volunteer)