Opening Modal Windows on Second Screen

I’ve been a little slow to come around to this topic, until I recently hooked up a second screen (I knew there was some potential issues).

But yes, when the Main (parent) Window of my app is open on a second screen - some Modal Windows that I open will always appear on the “Main” computer screen, and not the screen my app is on. I would prefer of course, that these child Windows of the calling Window appear on the same screen.

https://documentation.xojo.com/api/deprecated/window.html#window-locations says that either ParentWindow or ParentWindowScreen should do the trick. And they usually do - providing the new child Window has ImplicitInstance set to True.

But when ImplicitInstance=False, the DefaultLocation setting doesn’t appear to apply. And maybe there is a good reasons for that?

Anyway, because a lot of my Modal Windows have ImplicitInstance=False (because of custom Constructors), I thought I would be clever and use the Bounds property and Rect to come up with a more dynamic positioning solution. The idea came from @Norman Palardy excellent article here.

After getting a Rect object of both the opener (parent) Window and the new (child) Window, and experimenting with child.Top = opener.Top and child.Left = opener.Left to make sure I wasn’t going to blow up something (which worked, but not with the desired outcome), I thought my “centering” positioning solution could be something like the following:

Var newWin As New MyModalWindow Var bChild As Rect = newWin.Bounds Var bParent As Rect = Self.Bounds bChild.Center = bParent.Center // me, thinking I was being clever (nope) newWin.Bounds = bChild newWin.ShowModalWithIn(Self)
The above resulted in my new Modal Window still not on the second screen, but also not where it was before. The Modal moved, but not where I expected it.

I then tried like the following:

... bChild.VerticalCenter = bParent.VerticalCenter bChild.HorizontalCenter = bParent.HorizontalCenter ...
Which also didn’t give me what I expected, but it did give me a clue in the IDE Debugger because I saw that the newly assigned bChild.VerticalCenter was always half of the value of bParent.VerticalCenter (same with HorizontalCenter). So I tried:

... bChild.VerticalCenter = bParent.VerticalCenter * 2 bChild.HorizontalCenter = bParent.HorizontalCenter * 2 ...
And it worked!

But what I don’t understand is, why?

Is there some math/geometry here I’m just not getting? Or is this a bug?

What do you folks think? Any feedback on my solution would be appreciated, like - dude, this was covered in the first chapter of …

Here is a link to an example project to demonstrate: https://basic.fyi/screenshots/ModalOpeningOnSecondScreen.xojo_binary_project See App.CenteredWindowGet()

Using Xojo 2019r3.1 (API 2.0)
Tested on: macOS 10.15.3 Catalina & Windows 10 Pro 1909

Note: My solution appears to even work when my screens are arranged like the following (tried on both macOS & Windows):

I’m seeing the same thing. Mac 10.14, Xojo 2019r3.1.

I observed the same behavior you did with setting the centers equal. Then I tried

bChild.HorizontalCenter = bParent.Left + (bParent.Width / 2)
bChild.VerticalCenter = bParent.Top + (bParent.Height / 2)

Same thing. Only by using the “old school” way (below) or using your multiply by two did I end up with both windows where I expected.

bChild.Left = bParent.Left + (bParent.Width / 2) - (bChild.Width / 2)
bChild.Top = bParent.Top + (bParent.Height / 2) - (bChild.Height / 2)

It seems a bug to me, because the behavior is not what I would expect based on the documentation.

when you look at the scale fator of each screen is one 1x and one 2x perhaps ?

I don’t think it’s scale factor, even though I tested on a Retina MBP and didn’t try a display with a 1x scale factor.

Simple logic to me says that a.VerticalCenter = b.VerticalCenter would then result in both having the same value. But that’s not what happens, even if both windows are already visible and on the same screen.

there seems to be “something” screwy and i has to do with the scale factor of a screen

my main screen is 2880x1800 @ 2x
and I have a 1x screen set to the left and another to the right
to get a window to show up at position 0 on the screen to the right of the built in retina one I have to set its left to … 1440 not 2880

and this also holds true for vertical orientation
if I move a 1x screen to logically be below the retina screen then to get that same window to show up aa position 0 on the 1x screen that is now below the retina one I have to set the top to 900 not 1800

it all definitely feels wrong though

Thank you for chiming in Ed, I’m glad to know it’s not just me. For my part, I was totally trying to get around having to keep doing this the “old school” way.

Good question Norman. I just re-ran my example in the debugger to make sure I was remembering my results right, but yes - the ScaleFactor of both Windows (parent & child) is 1.0 at runtime. In my case, I’m currently running two standard resolution monitors. Neither are Retina.

You can see in the debugger that the “source” parent Rect has the expected correct Vertical & HorizontalCenter values prior to assigning to the “target” child Rect, but immediately after stepping past that assignment, you can see the child Rect Vertical & HorizontalCenter values are not what was assigned to them (the new values are exactly half of what was assigned, thus the multiplier).

Like I said, maybe I just don’t understand the math or the geometry enough?

[quote=485821:@Norman Palardy]there seems to be “something” screwy and i has to do with the scale factor of a screen

my main screen is 2880x1800 @ 2x
and I have a 1x screen set to the left and another to the right
to get a window to show up at position 0 on the screen to the right of the built in retina one I have to set its left to … 1440 not 2880

and this also holds true for vertical orientation
if I move a 1x screen to logically be below the retina screen then to get that same window to show up aa position 0 on the 1x screen that is now below the retina one I have to set the top to 900 not 1800

it all definitely feels wrong though[/quote]

Back when I still had my MBP, I had the same Retina resolution, plus a 4K monitor. I noticed back then how Xojo uses a “point” system that compensates for scaling, rather than the actual pixel count. I like this because it takes the worry away about whether your end-user has standard or HiDPI/Retina.

I wish I had played with this Bounds/Rect issue back then, before I got my current Mac Mini. I still have my 4K monitor, but only one of them (not currently hooked up). When I have time, I’ll play around with having my 4K as the second screen and see what happens. If I get a different result then, then I’ll file a report.

Thank you and @Ed Palmer for the feedback.

that sure seems as if the rect has a 2x scale factor hidden in it somewhere

which goes back to my “something” sure feels wrong in there somewhere

[quote=485826:@Norman Palardy]that sure seems as if the rect has a 2x scale factor hidden in it somewhere

which goes back to my “something” sure feels wrong in there somewhere[/quote]
It raised some eyebrows for me and sure made me wonder.

I guess I’ll go with it for now and report if there is any further impact.

Thanks again!

Apparently this has been around forever: feedback://showreport?report_id=20671 . Opened March 18, 2012. I guess after eight years we shouldn’t expect it to get fixed.

I have a MacBook Pro (3360 x 2100 @ 144dpi) and an external monitor (1920 x 1080 / 21"). Xojo 2015r1.

If I do not set Main Screen, the opened window goes where it want (most of the time in the built in Monitor because I nearly always set the windws there the default)…

Nota: my deep feeling is the OS set the newly opened window to the monitor where the current window is.

So, I trashed the idea to command where the window will go…

Example: For the Gimp application, it recalls (more or less, the current version) where I opened htat version for the last time, excepted for the “Do you want to Quit…” window who always appears on the external (low Res, not Retina) screen.

The internet connect window always appears above the displayed window regarless from where it is displayed.

When I Tab-Shift to the Desktop, I never know where I will be (but usually it is the external monitor, grrrr…). I gave up the idea to understand how it works…

I hope this help.

Well, isn’t that interesting.

Thank you Ed for finding that it is a verified bug.

Note: I’ve added my example to the Feedback case.

[quote=485890:@Emile Schwarz]If I do not set Main Screen, the opened window goes where it want (most of the time in the built in Monitor because I nearly always set the windws there the default)…

Nota: my deep feeling is the OS set the newly opened window to the monitor where the current window is.

So, I trashed the idea to command where the window will go…[/quote]
I’m curious Emile, if you incorporated the same solution @Ed Palmer and I used, whether that would help you control where your opened Windows go?

My example project ModalOpeningOnSecondScreen.xojo_binary_project is written for 2019r3.1, but it should be relatively easy to port the logic backwards to 2015r1. I don’t think much has changed about using the Rect type except the namespace and the Constructor. See Realbasic.Rect. As well, the use of Screen.ScreenAt(index As Integer) should just need changing to Screen(index As Integer).

If they ever fix the bug, then you just have to remove the *2 multiplier. I hope that helps too!

Thanks Scott, I will try.

Realbasic.Rect: I already use that, but in the built-in screen (set the window to the screen top-right for example).

I don’t know if I am not too late to this old topic but I just recently battled the problem:

Main window is located on different monitor than main monitor (main screen). When calling .showmodalwithin(…) the new MovableModal window always opens on main monitor instead of main window monitor which is annoying.

Just put these on top of Open event of that MovableModal:

Var w As Window = Window(0)
if w<>nil then
me.Left=w.Left+(w.Width/2)-(me.Width/2)
me.top=w.top+(w.Height/2)-(me.Height/2)
end if

…and that’s it. I found that Window(0) still refers to Main Window of my app, even in Open event of new window to be displayed…

In my case that Modal Window displays some detailed information about something and can be invoked from more than one window - so I needed some universal solution to find out the “master” window…

Hi Pawel,

If I understand your question correctly, I think what you’re looking for is the location of the screen that contains the macOS menubar, right?

If so, I think Screen.ScreenAt(0) is what you want instead of Window(0). So try the following:

Var s As Screen = Screen.ScreenAt(0)
Me.Left = s.Left + (s.Width / 2) - (Me.Width / 2)
Me.Top = s.Top + (s.Height / 2) - (Me.Height / 2)

Note: the above code is not tested.

Edited to add that Window(0) refers to the GUI Window of your application that is currently in front (has focus). See Application.Window for more information.

I hope that helps.

If you take the solution from @Pawel_Soltysinski and find the screen that window( 0 ) is on, you then combine it with @anon93744516 's solution and it will make the Modal dialog appear on the same screen as the current document window.

Also check windowCount to make sure that there is a window open at the time.

Excellent point @Sam_Rowlands, gotta remember that. Thank you!

1 Like

Isn’t there an option to have a menubar on every screen?

1 Like

You’re quite right Beatrix. I was thinking of the screen(s) that you see in the “Arrangement” tab of the “Displays” preferences. My apologies for not being more clear.