Who's moving my window?

Running a Mini under macOS Sonoma 14.6.1, with two screens. I move my app’s main window to the second screen, and make it fit in there nicely, and quit it. It saves its position (x, y, height, width), which should be restored when I open it again. But it sits jammed up against the left edge of the main screen.

So I added a Moved event, which fires twice during startup, once to set Left to the correct (saved) value (which would put it on the second screen), and a second time to set it to 4. Next I raise a runtime exception which fires on the second Moved event, showing this in the Stack:

22 Sep 2024 22:00:24 Stack: --------------------
22 Sep 2024 22:00:24 Sub MainWindow.MainWindow.Event_Moved (MainWindow.MainWindow)
22 Sep 2024 22:00:24 Sub DesktopApplication._CallFunctionWithExceptionHandling ()
22 Sep 2024 22:00:24 Stack done ----------------

(the stack output has been tidied up with @Beatrix_Willius 's excellent stack cleaner method).

Am I right in thinking that this just indicates that neither the app nor the user (me) moved the window - in which case it must be the OS? Or am I missing something?

Behaves the same under 2023 r4 and 2024 versions.

Wild guess, are you restoring the position in the Constructor?

You should be able to see what is causing your moved event. I put an f.delete into the moved event for a nil folderitem and got the following result:

Sub MaxModel.Notify(string)
Sub NotificationCenter.Post(string, string)
Sub NotificationManager.Post(string)
Sub ErrorException.Constructor(RuntimeException, string)
Sub MainWindow.MainWindow.Event_Moved(MainWindow.MainWindow)
Function NSWindowMBS.setFrameUsingName(string, boolean) as boolean
Sub LoadWindowPosition.Constructor(Window, string)
Sub MainWindow.MainWindow.Event_Open(MainWindow.MainWindow)
Sub Window.Constructor()
Sub DBWindow.Constructor(MainWindowController)
Sub App.Event_Open()
Sub Application._CallFunctionWithExceptionHandling()

I use window.open to load the load the window position. There is also a boolean OpenDone so that setting the window position doesn’t save it again. The moved event of the main window:

if not OpenDone then Return
dim theSaver as new SaveWindowPosition(self, self.TrueWindow.getWindowName)
#Pragma Unused theSaver

Implicit instance.

All the initialisation for the app (well, most of it) is done in the app’s Opening event. Probably more than should be being done there, but at the time I was converting this app to Xojo from a mixture of PHP and javascript, I was much less well versed in the concept of having controls do their own initialisation.

Having scanned through various sets of Xojo search results I can’t find any other place where I am setting the window position in code.

I only added the Moved event as a debugging asset - normally it does not exist in the app. The position save for the window is done when the app quits.

Edit: I should also add that, I’m only triggering an exception in the Moved event so that the app will save the Stack for rme as it crashes. And it’s only doing that when window.left gets set to less than 10.

It seems that after all initialisation is complete (including setting the window bounds to saved values), something gets triggered to move the window 2560 px leftwards, if .Left is then greater than 2560. This just happens to be the exact width of the DisplayAt(0).

I may have a workaround, however. I tried using Timer.CallLater to set the bounds after some delay, and provided the delay is 600msec or so, or more, then moving the window to its saved position sticks. This is, of course, a hack.

Edit: but the window has to have .Visible = True for even this hack to work.

I would do something like this:

  • In the IDE, set the window visible = false
  • In your app, create the window (if it’s an implicit instance, you can do this by just referencing any property)
  • at the end of the Window.Open event, set the top, left (and width height) while the window is still hidden
  • finally, show the window (You may want to do this step on a short timer)

Where can we find @Beatrix_Willius stack cleaner?

If I try this, it refuses to relocate the window to the second screen no matter how long the delay. It only relocates after the timer expires provided the window is visible while the timer is running.

I’m having to do everything (that is, almost all initialisation) in the app’s Opening event, because that is the only way I can guarantee that app variables are properly initialised and databases exist before I try to use them.

(Heavily whispered aside: too bad Xojo doesn’t have a main() function that runs first when the app starts, before anything else at all, where I could put all this startup code).

The code to set the bounds after a delay is:

timer.CallLater (1000, AddressOf moveMain, new Rect (framex, framey, winWidth, winHeight))

.
and moveMain consists of:

Sub moveMain (b as Variant)
MainWindow.Bounds = Rect(b)
End Sub

This is under macOS with each screen having its own set of Spaces.

Here’s a tiny stripped down project which does this:

  • Window1 is not visible in the IDE
  • In App.Open, timer.CallLater(1000, window1.Relocate)
  • Window1.relocate() sets the bounds to the 2nd monitor
  • then shows the Window

It works as expected for me (Xojo 2024r2.1, macOS 14.7, 2 monitors) showing this in the Messages pane:

3:47:16 PM : windowLocation Launched
: Window1.Opening
3:47:17 PM : Window1.ScaleFactorChanged
: Window1.Moved 2560,28
: Window1.Resized 2560x1412
: Window1.Paint

Curious if you can try this and see what’s different?

windowLocation.zip (6.3 KB)

So are you setting the window’s properties in app.opening? That should be done in window.opening.

1 Like

You will have plenty troubles if you update your macOS to Sequoia (it decide where to place the windows and its size…).

It depends… no issues so far on my side… :crossed_fingers:

Move your window beside thelft or right screen borders… I had enough of that and decnnect the feature earlier (thanks ars technica → Window Title)

That would be one of the first things to turn off, should I be moving to that OS. I also need to find out how to disable it under Win-11. That would make the one day a week where I have to use Windows, rather less irritating.

Not enough infrastructure of the app is guaranteed to exist, at that point. Amongst other things, the log file.

I think Tim is talking exclusively about Window’s properties. Everything else, like log, can stay in app.opening.

1 Like

@TimStreater have you had a chance to test the sample project yet? See Who's moving my window? - #10 by Mike_D

Sure. But to know where the user left the window, I have to read its saved coordinates from an SQLite database. My SQLite wrappers handle errors and report into the Logfile - which must therefore exist at the time. Circular problem.

1 Like

Don’t try and position windows in the App open event. Do it in the Window’s open event. Otherwise the window initialisation can position the window after you do.

2 Likes