Skip to content

Commit 54e68ee

Browse files
brianwphamSFcursoragentMrJul
committed
osx: keep window creation on-screen during display reconfiguration (AvaloniaUI#21543)
* osx: anchor new windows to the primary screen origin WindowBaseImpl::CreateNSWindow anchors the new NSWindow's content rect to the primary screen's frame.origin. The primary screen is at Cocoa (0,0) on a healthy Mac, but can be offset (often negative) while the display server is mid-reconfiguration -- e.g. CGMainDisplayID() briefly returns 0 after wake-from-sleep -- or on multi-monitor layouts where the primary is not at the origin. Anchoring keeps the window on a real screen so it is positioned on-screen and its flipped Position lands inside a Screen.Bounds. See AvaloniaUI#18895. Co-authored-by: Cursor <cursoragent@cursor.com> * osx: keep window construction resilient when no screen contains it When locating the screen that sizes a new window, fall back to a default size if AppKit reports no screens -- or none whose bounds contain the last-known position -- during a display reconfiguration. The window is repositioned on the next ScreenChanged event once the display server settles. See AvaloniaUI#18895. Co-authored-by: Cursor <cursoragent@cursor.com> * Less verbose comments --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
1 parent af3f942 commit 54e68ee

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

native/Avalonia.Native/src/OSX/WindowBaseImpl.mm

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,21 @@ static void ActivateApplication() {
450450
}
451451

452452
void WindowBaseImpl::CreateNSWindow(bool usePanel) {
453+
// Anchor the content rect to the primary screen's origin. It's usually at (0,0), but can be offset while the
454+
// display server is mid-reconfiguration. This ensures the window is always on a proper screen.
455+
// See https://github.com/AvaloniaUI/Avalonia/issues/18895
456+
NSPoint origin = NSZeroPoint;
457+
NSArray<NSScreen*>* screens = [NSScreen screens];
458+
if (screens.count > 0)
459+
origin = screens.firstObject.frame.origin;
460+
461+
NSRect contentRect = NSRect { origin.x, origin.y, lastSize };
462+
453463
if (usePanel) {
454-
Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:NSWindowStyleMaskBorderless];
464+
Window = [[AvnPanel alloc] initWithParent:this contentRect:contentRect styleMask:NSWindowStyleMaskBorderless];
455465
[Window setHidesOnDeactivate:false];
456466
} else {
457-
Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:NSWindowStyleMaskBorderless];
467+
Window = [[AvnWindow alloc] initWithParent:this contentRect:contentRect styleMask:NSWindowStyleMaskBorderless];
458468
}
459469
}
460470

src/Avalonia.Native/WindowImplBase.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,23 @@ internal override void Init(MacOSTopLevelHandle handle)
5151

5252
base.Init(handle);
5353

54+
int defaultWidth = 0, defaultHeight = 0;
55+
5456
var monitor = this.TryGetFeature<IScreenImpl>()!.AllScreens
5557
.OrderBy(x => x.Scaling)
56-
.First(m => m.Bounds.Contains(Position));
58+
.FirstOrDefault(m => m.Bounds.Contains(Position));
59+
60+
if (monitor != null)
61+
{
62+
// Emulate Windows 7+ default window size behavior.
63+
defaultWidth = (int)(monitor.WorkingArea.Width * 0.75d);
64+
defaultHeight = (int)(monitor.WorkingArea.Height * 0.7d);
65+
}
66+
67+
defaultWidth = Math.Max(defaultWidth, 300);
68+
defaultHeight = Math.Max(defaultHeight, 200);
5769

58-
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d), WindowResizeReason.Layout);
70+
Resize(new Size(defaultWidth, defaultHeight), WindowResizeReason.Layout);
5971
}
6072

6173
public void Activate()

0 commit comments

Comments
 (0)