macOS: Deliver NSWindow notifications to all windows, not just top level
Child QWindows (or in the case of QWindows embedded in native applications: top level QWindows where the corresponding NSView is a child of another view, so not being the contentView of its window), still need some of the NSWindow notifications to e.g. update their exposed state when the window becomes visible. We make sure to send the notification to all QCococaWindow children of the relevant NSWindow, and let each callback decide if it should only apply to content views. This fixes an issue where a QWindow would never be exposed if the window was a child NSView and added to a NSWindow that was yet to be shown. Change-Id: I7f7df8bc5f4ca3ac553a2c146f8c3229b197c059 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
44c304cefb
commit
49154acde3
@ -134,11 +134,12 @@ public:
|
|||||||
|
|
||||||
void setEmbeddedInForeignView(bool subwindow);
|
void setEmbeddedInForeignView(bool subwindow);
|
||||||
|
|
||||||
|
Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
|
||||||
|
Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
|
||||||
|
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowWillMoveNotification) void windowWillMove();
|
Q_NOTIFICATION_HANDLER(NSWindowWillMoveNotification) void windowWillMove();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
|
Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
|
Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
|
||||||
Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
|
|
||||||
Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
|
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
|
Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
|
Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
|
Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
|
||||||
@ -148,8 +149,8 @@ public:
|
|||||||
Q_NOTIFICATION_HANDLER(NSWindowDidEnterFullScreenNotification) void windowDidEnterFullScreen();
|
Q_NOTIFICATION_HANDLER(NSWindowDidEnterFullScreenNotification) void windowDidEnterFullScreen();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowWillExitFullScreenNotification) void windowWillExitFullScreen();
|
Q_NOTIFICATION_HANDLER(NSWindowWillExitFullScreenNotification) void windowWillExitFullScreen();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidExitFullScreenNotification) void windowDidExitFullScreen();
|
Q_NOTIFICATION_HANDLER(NSWindowDidExitFullScreenNotification) void windowDidExitFullScreen();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
|
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOnScreenAndFinishAnimatingNotification) void windowDidOrderOnScreen();
|
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOnScreenAndFinishAnimatingNotification) void windowDidOrderOnScreen();
|
||||||
|
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
|
Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
|
Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
|
||||||
Q_NOTIFICATION_HANDLER(NSWindowWillCloseNotification) void windowWillClose();
|
Q_NOTIFICATION_HANDLER(NSWindowWillCloseNotification) void windowWillClose();
|
||||||
|
@ -97,34 +97,30 @@ static void qRegisterNotificationCallbacks()
|
|||||||
[center addObserverForName:notificationName.toNSString() object:nil queue:nil
|
[center addObserverForName:notificationName.toNSString() object:nil queue:nil
|
||||||
usingBlock:^(NSNotification *notification) {
|
usingBlock:^(NSNotification *notification) {
|
||||||
|
|
||||||
NSView *view = nullptr;
|
QVarLengthArray<QCocoaWindow *, 32> cocoaWindows;
|
||||||
if ([notification.object isKindOfClass:[NSWindow class]]) {
|
if ([notification.object isKindOfClass:[NSWindow class]]) {
|
||||||
NSWindow *window = notification.object;
|
NSWindow *nsWindow = notification.object;
|
||||||
if (!window.contentView)
|
for (const QWindow *window : QGuiApplication::allWindows()) {
|
||||||
return;
|
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()))
|
||||||
|
if (cocoaWindow->nativeWindow() == nsWindow)
|
||||||
view = window.contentView;
|
cocoaWindows += cocoaWindow;
|
||||||
|
}
|
||||||
} else if ([notification.object isKindOfClass:[NSView class]]) {
|
} else if ([notification.object isKindOfClass:[NSView class]]) {
|
||||||
view = notification.object;
|
if (QNSView *qnsView = qnsview_cast(notification.object))
|
||||||
|
cocoaWindows += qnsView.platformWindow;
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcQpaCocoaWindow) << "Unhandled notifcation"
|
qCWarning(lcQpaCocoaWindow) << "Unhandled notifcation"
|
||||||
<< notification.name << "for" << notification.object;
|
<< notification.name << "for" << notification.object;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Q_ASSERT(view);
|
|
||||||
|
|
||||||
QCocoaWindow *cocoaWindow = nullptr;
|
|
||||||
if (QNSView *qnsView = qnsview_cast(view))
|
|
||||||
cocoaWindow = qnsView.platformWindow;
|
|
||||||
|
|
||||||
// FIXME: Could be a foreign window, look up by iterating top level QWindows
|
// FIXME: Could be a foreign window, look up by iterating top level QWindows
|
||||||
|
|
||||||
if (!cocoaWindow)
|
for (QCocoaWindow *cocoaWindow : cocoaWindows) {
|
||||||
return;
|
if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
|
||||||
|
qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for"
|
||||||
if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
|
<< notification.name << "on" << cocoaWindow;
|
||||||
qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for"
|
}
|
||||||
<< notification.name << "on" << cocoaWindow;
|
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -844,32 +840,7 @@ void QCocoaWindow::setEmbeddedInForeignView(bool embedded)
|
|||||||
m_nsWindow = 0;
|
m_nsWindow = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------- NSWindow notifications -----------------------
|
// ----------------------- NSView notifications -----------------------
|
||||||
|
|
||||||
void QCocoaWindow::windowWillMove()
|
|
||||||
{
|
|
||||||
// Close any open popups on window move
|
|
||||||
qt_closePopups();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaWindow::windowDidMove()
|
|
||||||
{
|
|
||||||
handleGeometryChange();
|
|
||||||
|
|
||||||
// Moving a window might bring it out of maximized state
|
|
||||||
handleWindowStateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaWindow::windowDidResize()
|
|
||||||
{
|
|
||||||
if (!isContentView())
|
|
||||||
return;
|
|
||||||
|
|
||||||
handleGeometryChange();
|
|
||||||
|
|
||||||
if (!m_view.inLiveResize)
|
|
||||||
handleWindowStateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaWindow::viewDidChangeFrame()
|
void QCocoaWindow::viewDidChangeFrame()
|
||||||
{
|
{
|
||||||
@ -888,13 +859,54 @@ void QCocoaWindow::viewDidChangeGlobalFrame()
|
|||||||
[m_view setNeedsDisplay:YES];
|
[m_view setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------- NSWindow notifications -----------------------
|
||||||
|
|
||||||
|
// Note: The following notifications are delivered to every QCocoaWindow
|
||||||
|
// that is a child of the NSWindow that triggered the notification. Each
|
||||||
|
// callback should make sure to filter out notifications if they do not
|
||||||
|
// apply to that QCocoaWindow, e.g. if the window is not a content view.
|
||||||
|
|
||||||
|
void QCocoaWindow::windowWillMove()
|
||||||
|
{
|
||||||
|
// Close any open popups on window move
|
||||||
|
qt_closePopups();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QCocoaWindow::windowDidMove()
|
||||||
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
|
handleGeometryChange();
|
||||||
|
|
||||||
|
// Moving a window might bring it out of maximized state
|
||||||
|
handleWindowStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QCocoaWindow::windowDidResize()
|
||||||
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
|
handleGeometryChange();
|
||||||
|
|
||||||
|
if (!m_view.inLiveResize)
|
||||||
|
handleWindowStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowDidEndLiveResize()
|
void QCocoaWindow::windowDidEndLiveResize()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
handleWindowStateChanged();
|
handleWindowStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowDidBecomeKey()
|
void QCocoaWindow::windowDidBecomeKey()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
if (isForeignWindow())
|
if (isForeignWindow())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -911,6 +923,9 @@ void QCocoaWindow::windowDidBecomeKey()
|
|||||||
|
|
||||||
void QCocoaWindow::windowDidResignKey()
|
void QCocoaWindow::windowDidResignKey()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
if (isForeignWindow())
|
if (isForeignWindow())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -927,16 +942,25 @@ void QCocoaWindow::windowDidResignKey()
|
|||||||
|
|
||||||
void QCocoaWindow::windowDidMiniaturize()
|
void QCocoaWindow::windowDidMiniaturize()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
handleWindowStateChanged();
|
handleWindowStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowDidDeminiaturize()
|
void QCocoaWindow::windowDidDeminiaturize()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
handleWindowStateChanged();
|
handleWindowStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowWillEnterFullScreen()
|
void QCocoaWindow::windowWillEnterFullScreen()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
// The NSWindow needs to be resizable, otherwise we'll end up with
|
// The NSWindow needs to be resizable, otherwise we'll end up with
|
||||||
// the normal window geometry, centered in the middle of the screen
|
// the normal window geometry, centered in the middle of the screen
|
||||||
// on a black background. The styleMask will be reset below.
|
// on a black background. The styleMask will be reset below.
|
||||||
@ -945,6 +969,9 @@ void QCocoaWindow::windowWillEnterFullScreen()
|
|||||||
|
|
||||||
void QCocoaWindow::windowDidEnterFullScreen()
|
void QCocoaWindow::windowDidEnterFullScreen()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
Q_ASSERT_X(m_view.window.qt_fullScreen, "QCocoaWindow",
|
Q_ASSERT_X(m_view.window.qt_fullScreen, "QCocoaWindow",
|
||||||
"FullScreen category processes window notifications first");
|
"FullScreen category processes window notifications first");
|
||||||
|
|
||||||
@ -956,6 +983,9 @@ void QCocoaWindow::windowDidEnterFullScreen()
|
|||||||
|
|
||||||
void QCocoaWindow::windowWillExitFullScreen()
|
void QCocoaWindow::windowWillExitFullScreen()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
// The NSWindow needs to be resizable, otherwise we'll end up with
|
// The NSWindow needs to be resizable, otherwise we'll end up with
|
||||||
// a weird zoom animation. The styleMask will be reset below.
|
// a weird zoom animation. The styleMask will be reset below.
|
||||||
m_view.window.styleMask |= NSResizableWindowMask;
|
m_view.window.styleMask |= NSResizableWindowMask;
|
||||||
@ -963,6 +993,9 @@ void QCocoaWindow::windowWillExitFullScreen()
|
|||||||
|
|
||||||
void QCocoaWindow::windowDidExitFullScreen()
|
void QCocoaWindow::windowDidExitFullScreen()
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
Q_ASSERT_X(!m_view.window.qt_fullScreen, "QCocoaWindow",
|
Q_ASSERT_X(!m_view.window.qt_fullScreen, "QCocoaWindow",
|
||||||
"FullScreen category processes window notifications first");
|
"FullScreen category processes window notifications first");
|
||||||
|
|
||||||
@ -981,16 +1014,16 @@ void QCocoaWindow::windowDidExitFullScreen()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowDidOrderOffScreen()
|
|
||||||
{
|
|
||||||
handleExposeEvent(QRegion());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QCocoaWindow::windowDidOrderOnScreen()
|
void QCocoaWindow::windowDidOrderOnScreen()
|
||||||
{
|
{
|
||||||
[m_view setNeedsDisplay:YES];
|
[m_view setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QCocoaWindow::windowDidOrderOffScreen()
|
||||||
|
{
|
||||||
|
handleExposeEvent(QRegion());
|
||||||
|
}
|
||||||
|
|
||||||
void QCocoaWindow::windowDidChangeOcclusionState()
|
void QCocoaWindow::windowDidChangeOcclusionState()
|
||||||
{
|
{
|
||||||
if (m_view.window.occlusionState & NSWindowOcclusionStateVisible)
|
if (m_view.window.occlusionState & NSWindowOcclusionStateVisible)
|
||||||
@ -1422,15 +1455,15 @@ QRect QCocoaWindow::nativeWindowGeometry() const
|
|||||||
*/
|
*/
|
||||||
void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
|
void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
|
||||||
{
|
{
|
||||||
|
if (!isContentView())
|
||||||
|
return;
|
||||||
|
|
||||||
const Qt::WindowState currentState = windowState();
|
const Qt::WindowState currentState = windowState();
|
||||||
const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
|
const Qt::WindowState newState = QWindowPrivate::effectiveState(requestedState);
|
||||||
|
|
||||||
if (newState == currentState)
|
if (newState == currentState)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!isContentView())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const NSSize contentSize = m_view.frame.size;
|
const NSSize contentSize = m_view.frame.size;
|
||||||
if (contentSize.width <= 0 || contentSize.height <= 0) {
|
if (contentSize.width <= 0 || contentSize.height <= 0) {
|
||||||
// If content view width or height is 0 then the window animations will crash so
|
// If content view width or height is 0 then the window animations will crash so
|
||||||
|
@ -87,8 +87,6 @@
|
|||||||
options:NSTrackingActiveInActiveApp | NSTrackingInVisibleRect | NSTrackingCursorUpdate
|
options:NSTrackingActiveInActiveApp | NSTrackingInVisibleRect | NSTrackingCursorUpdate
|
||||||
owner:contentView userInfo:nil]];
|
owner:contentView userInfo:nil]];
|
||||||
|
|
||||||
window.contentView = contentView;
|
|
||||||
|
|
||||||
// Create the QWindow, add its NSView to the content view
|
// Create the QWindow, add its NSView to the content view
|
||||||
m_window = new RasterWindow;
|
m_window = new RasterWindow;
|
||||||
m_window->setObjectName("RasterWindow");
|
m_window->setObjectName("RasterWindow");
|
||||||
@ -104,10 +102,15 @@
|
|||||||
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 80, 25)];
|
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 80, 25)];
|
||||||
[(NSView*)childWindow->winId() addSubview:textField];
|
[(NSView*)childWindow->winId() addSubview:textField];
|
||||||
|
|
||||||
[window.contentView addSubview:reinterpret_cast<NSView *>(m_window->winId())];
|
[contentView addSubview:reinterpret_cast<NSView *>(m_window->winId())];
|
||||||
|
|
||||||
// Show the NSWindow
|
window.contentView = contentView;
|
||||||
[window makeKeyAndOrderFront:NSApp];
|
|
||||||
|
// Show the NSWindow delayed, so that we can verify that Qt picks up the right
|
||||||
|
// notifications to expose the window when it does become visible.
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[window makeKeyAndOrderFront:NSApp];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)notification
|
- (void)applicationWillTerminate:(NSNotification *)notification
|
||||||
|
@ -148,6 +148,11 @@ bool RasterWindow::event(QEvent *e)
|
|||||||
|
|
||||||
void RasterWindow::render()
|
void RasterWindow::render()
|
||||||
{
|
{
|
||||||
|
if (!isExposed()) {
|
||||||
|
qDebug() << "Skipping render, not exposed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QRect rect(QPoint(), geometry().size());
|
QRect rect(QPoint(), geometry().size());
|
||||||
|
|
||||||
m_backingStore->resize(rect.size());
|
m_backingStore->resize(rect.size());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user