macOS: Ensure all windows are created on a valid screen

We rely on AppKit repositioning the window if the original position
is not on any of the available screens. We do this by keeping the
original position, but using the primary screen as reference.

This doesn't work unless the window has a title bar, so in the corner
case where the window has an invalid position, we apply the title bar
style mask for a brief moment, so that AppKit will place the window
correctly.

Task-number: QTBUG-69221
Change-Id: If66cac36bf36f051570ba5854951ce4504fe771f
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Tor Arne Vestbø 2018-07-02 13:21:00 +02:00
parent 55fc9a5dd9
commit 6caaa050ab

View File

@ -1355,6 +1355,15 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
{
QMacAutoReleasePool pool;
Qt::WindowType type = window()->type();
Qt::WindowFlags flags = window()->flags();
// Note: The macOS window manager has a bug, where if a screen is rotated, it will not allow
// a window to be created within the area of the screen that has a Y coordinate (I quadrant)
// higher than the height of the screen in its non-rotated state, unless the window is
// created with the NSWindowStyleMaskBorderless style mask.
NSWindowStyleMask styleMask = windowStyleMask(flags);
QRect rect = geometry();
QScreen *targetScreen = nullptr;
@ -1366,26 +1375,22 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
}
if (!targetScreen) {
qCWarning(lcQpaCocoaWindow) << "Window position outside any known screen, using primary screen";
qCWarning(lcQpaCocoaWindow) << "Window position" << rect << "outside any known screen, using primary screen";
targetScreen = QGuiApplication::primaryScreen();
// AppKit will only reposition a window that's outside the target screen area if
// the window has a title bar. If left out, the window ends up with no screen.
// The style mask will be corrected to the original style mask in setWindowFlags.
styleMask |= NSWindowStyleMaskTitled;
}
rect.translate(-targetScreen->geometry().topLeft());
QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen *>(targetScreen->handle());
NSRect frame = QCocoaScreen::mapToNative(rect, cocoaScreen);
// Note: The macOS window manager has a bug, where if a screen is rotated, it will not allow
// a window to be created within the area of the screen that has a Y coordinate (I quadrant)
// higher than the height of the screen in its non-rotated state, unless the window is
// created with the NSWindowStyleMaskBorderless style mask.
Qt::WindowType type = window()->type();
Qt::WindowFlags flags = window()->flags();
// Create NSWindow
Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:frame
styleMask:windowStyleMask(flags)
styleMask:styleMask
// Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
backing:NSBackingStoreBuffered defer:NO
@ -1394,6 +1399,9 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
Q_ASSERT_X(nsWindow.screen == cocoaScreen->nativeScreen(), "QCocoaWindow",
"Resulting NSScreen should match the requested NSScreen");
if (targetScreen != window()->screen())
QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen);
nsWindow.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
// Prevent Cocoa from releasing the window on close. Qt
@ -1413,9 +1421,6 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
});
}
if (targetScreen != window()->screen())
QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen);
nsWindow.restorable = NO;
nsWindow.level = windowLevel(flags);