1

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.

skomisa
  • 16,436
  • 7
  • 61
  • 102
AD5XJ
  • 31
  • 3
  • 3
    Create and post a [mre] demonstrating the problem. This should not be too difficult: you can use the [`Screen`](https://openjfx.io/javadoc/20/javafx.graphics/javafx/stage/Screen.html) API to get the logical bounds of each physical screen, get the `x` and `y` coordinates of the current `Stage` and use those to figure out which physical screen(s) contain the current stage. – James_D Aug 24 '23 at 15:43
  • 2
    Have you tried `editWindow.initOwner(primaryWindow)`? – Slaw Aug 24 '23 at 16:36
  • I would suggest using a `Custom Dialog` over a new window in this case. Also, do as @Slaw suggested. I would guess that should fix the issue. https://stackoverflow.com/a/76853142/2423906 – SedJ601 Aug 25 '23 at 15:10
  • Your update didn't make it clear to me: Did using `initOwner` work for you? Because it works for me on Windows 10 using JavaFX 20. You can mess around with the modality if needed (`initModality`), though I would expect an editor window to be modal (if you use a `Dialog` then [it's application modal by default](https://openjfx.io/javadoc/20/javafx.controls/javafx/scene/control/Dialog.html#initModality(javafx.stage.Modality))). The only possible disadvantage I can think of is that the child window will always be on top of the owner window. – Slaw Aug 25 '23 at 16:23
  • Sorry I was not more clear. No, the initOwner() did not work for me on the second window. I have not been able to make it position on the secondary screen along with the main window. BTW this application runs on Linux Mint LMDE 5 from NetBeans 18. – AD5XJ Aug 25 '23 at 16:34
  • initOwner() should work. You may want to double check the parameter you are passing to it. It should be your main stage. – user9035826 Aug 26 '23 at 00:40
  • Yes I pass the right parameter. The main window is opened by NetBeans on the primary screen but when the stored settings are read and displayed it is relocated to the secondary screen. From there the edit window is launched. But the main stage is passed to the edit window and the .initOwner() is executed before the stage.show() – AD5XJ Aug 26 '23 at 11:07
  • OK, I found the problem. A logical mistake in the settings display that did not move the window to the position stored in the settings. I apologize for the error and any confusion I may have caused. [SOLVED] – AD5XJ Aug 28 '23 at 13:08
  • @AD5XJ Since you _"found the problem"_, consider posting and accepting an answer. That is much more helpful to the community than burying your resolution in a brief comment. Also, I have removed _[SOLVED]_ from your title. That is never done here on SO, and doubly so when there is no accepted answer. – skomisa Sep 01 '23 at 05:34

1 Answers1

1

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.

  • By default, new window opens on original monitor.
  • If X & Y bumped, new window opens on current monitor.

Which monitor varies

The behavior I am seeing varies, depending on whether your code relocates the newly opened window.

  • If you simple do 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.
  • If you the programmer programmatically move the stage in between 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.

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • I tried your code on windows 10/Java17, and it seemed to behave as I would expect. My understanding is that any new window created will be put on the primary display by default, unless you explicitly relocate it, either through setX()/setY or initOwner() etc. I also tried to add stage.initOwner(mainStage) call to where you are creating child windows. It causes the child windows opened on the same display as the parent window, wherever the parent window is. This seems to be expected to me too. – user9035826 Aug 25 '23 at 23:48