javafxnetbeansfxml

JavaFX 20 open new windows on same screen


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.

UPDATE

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.


Solution

  • Well, this turned out to be a more interesting challenge than I expected.

    tl;dr

    If you want correct sensible behavior, ๐Ÿ‘‰๐Ÿผ set the X & Y properties of each newly opened window.

    Which monitor varies

    The behavior I am seeing varies, depending on ๐Ÿ‘‰๐Ÿผ whether your code relocates the newly opened window.

    Example app

    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:

    1. Click the Open another window button, move the now window to another display/monitor, and click the button again. Notice the weird behavior that the new window appears back on the original display.
    2. On the second display, check the Relocate each new window checkbox. Then click the Open another window button. Notice how the new window now appears on this second display rather than the original display. Correct behavior.
    3. Turn off the Relocate each new window checkbox, and see the weird behavior return.

    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.

    screenshot of example app running, with a window offering a button to open another window

    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.