Restoring window location - Mac, Windows, Linux

Are-you using Rect to set your window bounds ?

DesktopWindows.Bounds

Please, if not, share your few lines of code to set the Window location (and size) values…

Sorry, I cannot test the code on m1 MacBook Pro (connecting my Philips monitor to it does not work)

If all else fails, add a Fullscreen Toggle menubar command. Even if the window is on a non-existent screen, toggling fullscreen on will make it visible on a valid screen. Don’t toggle it again (as it will place it back on the missing screen), but drag it out of fullscreen mode and it will display on the valid screen.

1 Like

If this user is using Windows… then it wouldn’t surprise me.

Positioning dialog windows with “MultiMonitors” that also have different ScaleFactors never worked as expected with Xojo. I doubt that this has been fixed in Xojo’s 2022 releases.

That’s why I’m using this: GitHub: jo-tools/monitors
This helps to:

  • detect correctly which Screen/Monitor a dialog window is currently on, also read the relative position on that Screen/Monitor
  • position a dialog window at a relative position on Screen/Monitor x
  • a convenience method: aWindowInstance.FitOnMonitor() - to make sure it is fully visible

I hadn’t thought about the possibility of different scale factors, in this case it’s entirely possible. I’ll give it a try, thank you!

Yes, I’m using Rect to set the window bounds, exactly as shown in the Xojo documentation.

For decades now, as a user, I’ve used a QuicKey or Keyboard Maestro macro to “pull into main screen” (frontmost window). It has saved me in those situations where a screen configuration has changed and the application hasn’t handled it.

This isn’t a solution to the problem, but a possible workaround for the user. You could have such an option available to the user for cases where a window is out of bounds.

:+1: A jack of all knifes

1 Like

So back here again… frustrated…

I have tried about 10 different approaches to saving/restoring window location and nothing seems to work. I’ve done this for years and years in Borland products and Visual Studio using C# and I’m just not understanding what is so hard about this.

My simple test:

When my main window is moved or resized I gather: top, left, height, width and store them in a file. I also store these after the window opens.

So on my first run the file contains: top: 100, left: 100, height: 600, width: 800

I move the window on the same screen and the coords update and are saved. Running the program again on the same screen will restore to the saved position. All that is great.

Now I drag the window to my big monitor, setup the the right. If I position the window up far enough on this big monitor the coords become something like: top: -301, left: 2535, height: 600, width: 800. These get saved to my file:

I_MAINWINDOWTOP|-301
I_MAINWINDOWLEFT|2535
I_MAINWINDOWHEIGHT|600
I_MAINWINDOWWIDTH|800

Kill the program and reload. I debug into the code that sets the top, left, height, width of the window on opening and I can see that the values retrieved from the file are correct. But the window appears back on my main screen, no longer on the big monitor.

Here is a screen shot in the debugger showing the correct values. The rect has the right values saved in the file and they are assigned to win top, left, width, height.

I would have hoped this would place the window back on the big monitor where it was. But it does not. It places it back on the main monitor (laptop display). I added code to show the top, left, width, height of the window and after the setting in the debug screen shot above the window now resides at:

Top: 354, Left: 197, Height: 600, Width: 800.

So I’m not sure if something strange is happening in MacOS to cause this or if it is something unique about the initial open of the window or what.

From this thread I looked into the various answers and thought the idea of keeping track of the screen the app was on would be the way to go. So I’ve added that code. Now when I store the window position I also store the screen it is on. In the example above when I moved the window to the big monitor my screen ID became:

I_MAINWINDOWSCREENID|1

I also implemented code in my window restore position to try and figure out what screen this rect fits on and that works. It determines that screen 1 is the right screen. But the moment I set top/left/height/width it pops back to the main laptop screen.

Anyone solved this?

Yes, macOS is strange.

This is the last code is used before switching to some macOS function from Sam:

'load the position of the ParentWindow

if ParentWindow = nil then return
dim isNewPrefs as Boolean = not Globals.thePrefs.IsPrefAvailable(WindowTitle + "_Top")

'get pref values
dim PrefValueTop as Integer = Globals.thePrefs.GetPrefNumber(WindowTitle + "_Top")
dim PrefValueLeft as Integer = Globals.thePrefs.GetPrefNumber(WindowTitle + "_Left")
dim PrefValueWidth as Integer = Globals.thePrefs.GetPrefNumber(WindowTitle + "_Width")
dim PrefValueHeight as Integer = Globals.thePrefs.GetPrefNumber(WindowTitle + "_Height")

if PrefValueWidth = 0 then PrefValueWidth = ParentWindow.Bounds.Width
if PrefValueHeight = 0 then PrefValueHeight = ParentWindow.Bounds.Height
if ParentWindow.MinimumWidth > PrefValueWidth then PrefValueWidth = ParentWindow.MinimumWidth
if ParentWindow.MinimumHeight > PrefValueHeight then PrefValueHeight = ParentWindow.MinimumHeight

'set the rect
dim theRect as REALbasic.Rect
if isNewPrefs then
  theRect = new REALbasic.Rect(100, 100, ParentWindow.Width, ParentWindow.Height) 'dummy values
else
  theRect = new REALbasic.Rect(PrefValueLeft, PrefValueTop, PrefValueWidth, PrefValueHeight)
end if

'Find screen.
Dim screenID as integer
For l as integer = 0 to screenCount - 1
  if theRect.top >= screen(l).top and theRect.center.x > screen(l).left and theRect.center.x < (screen(l).left + screen(l).width) then screenID = l
next
lastScreen = screenID

''extra logging
'Globals.theErrorLog.logitem(currentMethodName + " Screen: " + Str(screenID))
Dim ShowOnScreen as screen = screen(screenID)

'for left and top we need to recalculate the values if the prefs are new, only for top if the prefs are not new
if isNewPrefs then
  if ParentWindow.Width < 900 then PrefValueTop = (Screen(ShowOnScreen).Height - ParentWindow.Bounds.Height)/2
  PrefValueLeft = (Screen(ShowOnScreen).Width - ParentWindow.Width)/2
  theRect = new REALbasic.Rect(PrefValueLeft, PrefValueTop, PrefValueWidth, PrefValueHeight)
end if

dim ScreenHeight as Integer = ShowOnScreen.AvailableHeight
dim theTop as Integer
if ShowOnScreen.AvailableTop < 0 then
  theTop = -min(-theRect.top, -ShowOnScreen.availabletop)
else
  theTop = max(theRect.top, ShowOnScreen.availabletop)
end if

if ShowOnScreen.AvailableTop > 0 then
  theRect.Top = Min(theTop - ShowOnScreen.AvailableTop, ScreenHeight) + ShowOnScreen.AvailableTop
else
  theRect.Top = min(theTop, ScreenHeight)
end if
theRect.Left = min(max(theRect.left, ShowOnScreen.availableLeft), ShowOnScreen.availableLeft + (ShowOnScreen.availableWidth))

'only needed for < Monterey
dim theVersion as System.VersionData = System.Version
if theVersion.MajorVersion < 12 then
  if ParentWindow.ToolbarVisibleMBS and theRect.Height <= ParentWindow.MinimumHeight + 78 then
    theRect.Height = ParentWindow.MinimumHeight + 78
  end if
end if
theRect.Height = min(theRect.height, ShowOnScreen.AvailableHeight)
theRect.Width = min(theRect.Width, ShowOnScreen.AvailableWidth - 20)

ParentWindow.Bounds = theRect

HTH

I don’t think that the code covers every possibility but it should do most.

1 Like

Beatrix,

Thank you for that. This creates yet another puzzler.

I put that exact code in my app, I hard coded the pref values to a position on my big monitor and my main window (the target window) still opens on the laptop monitor.

So I created a simple test app. I pasted in the code you provided. Few minor changes like defaulting new prefs to false and setting a hard coded top, left, height, width. And YES the window opens on the big monitor!

I tried a few other positions both on the laptop screen and big monitor and it works. So very cool.

Back to my app. I disabled EVERYTHING the main window is doing except the opening event handler. I also confirmed the settings of the window like height, width, type etc match the window on the test app. No matter what I do it opens on the laptop monitor.

So I created a band new window in my app, called it TestPositionWindow. Copied over all the code I had in the test app into the event handler in this window. I then pointed my app to open that window by default. Again it opens on the laptop monitor only.

So clearly something in my app is forcing windows to the laptop monitor. I have yet to figure out what.

So I’ll keep searching but very strange behavior. Not sure what is going on. I’ve look at the App object and there really are not any settings I can see that would seem to change this.

For debugging purposes, you can always put code like this:

Function MouseDown(x As Integer, y As Integer)
self.left=SavedLeft
self.top=SavedTop
self.width=SavedWidth
self.height=SavedHeight
End Function
(an event you add to the window)

Then you manually click on the window and see whether the code alone works, in the offending project.
Then you know whether the issue is happening only at open time, or because of a more large issue.

Do you have any Declares regarding Screens or Windows in your App?
Like Soft Declare Sub center Lib "Cocoa" selector "center" (windowRef As Integer) for Centering a Window?

Thanks all for the suggestions. Slowly working through them.

I created a single window desktop app and I have been slowly adding in to that all the things my main app is doing, including using RubberControls. So far nothing is triggering the behavior.

Arnaud, Thanks for the suggestion of trying this behavior after open. Indeed it does seem to be something during open.

In my test window, in my main app, I have a method “SetWindowPos()” that contains the code from a above and an arbitrary position which is on the big monitor. I call this function in “Opening” event handler and the window still appears on the laptop monitor.

After it is visible I have a test button on it that simply calls the same function again. This does move the window to the expected position.

So this has narrowed the search to whatever is happening during open.

So I still have no idea why my primary app always tries to place windows on main laptop screen.

But taking Arnaud’s advice I tried, after opening, repositioning the window worked. So until I figure this out I have an initial timer on windows that will reposition and resize them after opening.

It appears RubberControls kind of needs this too anyway. A tiny bit annoying but functional.

What do you mean ? Perhaps this can be fixed if you care to elaborate.

It is the issue you sent you a test program for a month or two back where the resizing didn’t seem to open if called on “opening” event. You suggested I put in a short timer to trigger it and now I’m using that for window position as well.

Indeed, Mark, the timer is the solution.

By the way, I do save the position of windows in my apps, based on the size and position when the window is closed.