I have a small javafx 20 app that displays a main window (FXML with CSS) which contains a list. A double click of any row will open a new window to edit that row.
The problem is that, if the main window is on a secondary screen, the edit window opens on the primary screen and not the secondary screen where the main window is open.
I have tried to use the xProperty() and yProperty() double values, saved as settings, to position the second window on open, but that only worked on the main window. The edit window seems confined to the values of the primary screen.
Is there a way to get the screen that opened it and use the x and y of that screen rather than the primary screen.
BTW NetBeans runs in the primary screen. That may be a clue...maybe not.
OK, so I have tried a number of things including those suggested below. Here are some snippets from the current state of the app:
mainwindow.java is started on the primary screen but x and y place it on the secondary screen.
public Window getOwner()
{
stage.getOwner();
}
...
private void actionEntryEdit()
{
int sel_id;
FXMLLoader editloader;
DlgPopupEditController maincont = null;
Parent editroot = null;
Stage editstage = null;
Scene editscene;
if ( tblNotesView.getSelectionModel().getSelectedItem() != null )
{
Note selnote = selModel.getSelectedItem();
sel_id = selnote.getID();
}
else
{
sel_id = -1;
}
// we have selected a row so get it to the edit window
if ( sel_id > 0 )
{
editstage.toFront();
maincont.setNote(sel_id);
editstage.show();
}
}
dlgpopupedit.java where stored settings should place it on the extended second screen:
@Override
public void initialize(URL url, ResourceBundle rb)
{
initLocalVars();
readSettings();
}
...
public void setController(MainWindowController mwc)
{
MWC = mwc;
stage.initOwner(MWC.getOwner());
}
...
public void setScene(Scene sc)
{
displaySettings();
}
Note: settings are saved in Windows style .ini file (using Ini4J) as double from the xProperty() and yProperty() double values.
Well, this turned out to be a more interesting challenge than I expected.
If you want correct sensible behavior, ๐๐ผ set the X & Y properties of each newly opened window.
The behavior I am seeing varies, depending on ๐๐ผ whether your code relocates the newly opened window.
new Stage
& stage.show()
, JavaFX insists on opening the new window on the display/monitor first used when the app launched. The user can move the window to another display/monitor, but JavaFX insists on opening the new window back on the first display. Weird. JavaFX seems ignorant as to the existence of the other display, even if that display contains the current window.new Stage
& stage.show()
, the behavior changes. Now JavaFX exhibits the behavior we expect. The new window appears on the display containing the current window. If the user drags that current window to another display, the new window appears there, not back on the original display. This behavior makes senseโฆ but how odd that programmatically relocating the stage triggers this sensible behavior but by default we get the weird behavior.The behavior is easy to demonstrate. See my little example app below. I hope someone can verify my code to see if I am doing anything wrong.
To use the app:
I am guessing that the weird behavior is a bug, but I cannot really discern.
I used JavaFX 20.0.2 on Java 20.0.2 on a MacBook Pro 16" (Apple Silicon, M1 Pro) with a BenQ 32-inch 4K monitor attached via DisplayPort, macOS Ventura, launched from IntelliJ IDE.
package work.basil.example.exfxgatherinput;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.time.Instant;
import java.util.concurrent.atomic.AtomicInteger;
public class OpenWindowApp extends Application
{
private final AtomicInteger countWindows = new AtomicInteger ( 0 );
@Override
public void start ( Stage stage )
{
// Scene
Scene scene = this.makeScene ( false );
// Stage
this.decorate ( stage );
stage.setScene ( scene );
stage.show ( );
}
private void decorate ( Stage stage )
{
stage.setTitle ( "Window # " + this.countWindows.incrementAndGet ( ) );
stage.setWidth ( 380 );
stage.setHeight ( 170 );
}
private Scene makeScene ( final boolean relocateNewWindow )
{
// Widgets
CheckBox relocateEachNewWindow = new CheckBox ( "Relocate each new window" );
relocateEachNewWindow.setSelected ( relocateNewWindow );
Label nowLabel = new Label ( "Window created at: " + Instant.now ( ).toString ( ) );
Button newWindowButton = new Button ( );
newWindowButton.setText ( "Open another window" );
// Behavior
newWindowButton.setOnAction ( ( ActionEvent actionEvent ) ->
{
Stage stage = new Stage ( );
this.decorate ( stage );
stage.setScene ( this.makeScene ( relocateEachNewWindow.isSelected ( ) ) );
if ( relocateEachNewWindow.isSelected ( ) )
{
this.relocateWindow ( actionEvent , stage );
}
stage.show ( );
} );
// Arrange
VBox vbox = new VBox ( relocateEachNewWindow , nowLabel , newWindowButton );
vbox.setPadding ( new Insets ( 20 , 20 , 20 , 20 ) );
vbox.setSpacing ( 12 );
vbox.setAlignment ( Pos.CENTER );
return new Scene ( vbox );
}
private void relocateWindow ( ActionEvent actionEvent , Stage stage )
{
Window currentWindow = ( ( Node ) actionEvent.getSource ( ) ).getScene ( ).getWindow ( );
stage.setX ( currentWindow.getX ( ) + 50 );
stage.setY ( currentWindow.getY ( ) + 50 );
}
public static void main ( String[] args )
{
launch ( );
}
}
Caveat: I am a JavaFX newbie, so my understanding may be faulty.