diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 256f5ee6fb..748280af6a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -52,32 +52,42 @@ QT_FORWARD_DECLARE_CLASS(QCocoaWindow) -@class QNSWindowDelegate; - -@interface QNSWindow : NSPanel { -@public - QCocoaWindow *m_cocoaPlatformWindow; +@interface QNSWindow : NSWindow +{ + @public QCocoaWindow *m_cocoaPlatformWindow; } +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSUInteger)windowStyle + qPlatformWindow:(QCocoaWindow *)qpw; - (void)clearPlatformWindow; @end -QT_BEGIN_NAMESPACE +@interface QNSPanel : NSPanel +{ + @public QCocoaWindow *m_cocoaPlatformWindow; +} +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSUInteger)windowStyle + qPlatformWindow:(QCocoaWindow *)qpw; + +- (void)clearPlatformWindow; +@end + +@class QNSWindowDelegate; + +QT_BEGIN_NAMESPACE // QCocoaWindow // -// A QCocoaWindow is backed by a NSView and optionally a NSWindow. +// QCocoaWindow is an NSView (not an NSWindow!) in the sense +// that it relies on a NSView for all event handling and +// graphics output and does not require a NSWindow, except for +// for the window-related functions like setWindowTitle. // -// The NSView is used for most event handling and graphics output. -// -// Top-level QWindows are always backed by a NSWindow in addition to -// the NSView. Child QWindows can also be backed by NSWindows, which -// enables proper stacking of GL Widgets and threaded GL rendering -// to multiple contexts. -// -// It is possible to embed the QCocoaWindow in an NSView hierarchy -// by getting a pointer to the backing NSView and not calling -// QCocoaWindow::show(): +// As a consequence of this it is possible to embed the QCocoaWindow +// in an NSView hierarchy by getting a pointer to the "backing" +// NSView and not calling QCocoaWindow::show(): // // QWindow *qtWindow = new MyWindow(); // qtWindow->create(); @@ -139,7 +149,6 @@ public: void windowDidResize(); bool windowShouldClose(); bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const; - bool windowShouldBehaveAsPanel() const; void setSynchedWindowStateFromWindow(); @@ -174,9 +183,11 @@ public: QWindow *childWindowAt(QPoint windowPoint); protected: void recreateWindow(const QPlatformWindow *parentWindow); - QNSWindow *createNSWindow(); - void setNSWindow(QNSWindow *window); - void clearNSWindow(QNSWindow *window); + NSWindow *createNSWindow(); + void setNSWindow(NSWindow *window); + void clearNSWindow(NSWindow *window); + + bool shouldUseNSPanel(); QRect windowGeometry() const; QCocoaWindow *parentCocoaWindow() const; @@ -191,7 +202,7 @@ public: // for QNSView NSView *m_contentView; QNSView *m_qtView; - QNSWindow *m_nsWindow; + NSWindow *m_nsWindow; QCocoaWindow *m_forwardWindow; // TODO merge to one variable if possible diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index d71fe1ee18..d972782f31 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -81,10 +81,15 @@ static bool isMouseEvent(NSEvent *ev) } @interface NSWindow (CocoaWindowCategory) +- (void) clearPlatformWindow; - (NSRect) legacyConvertRectFromScreen:(NSRect) rect; @end @implementation NSWindow (CocoaWindowCategory) +- (void) clearPlatformWindow +{ +} + - (NSRect) legacyConvertRectFromScreen:(NSRect) rect { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 @@ -123,17 +128,9 @@ static bool isMouseEvent(NSEvent *ev) if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->m_isNSWindowChild) return NO; - // Only tool or dialog windows should become key: - if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel()) { - Qt::WindowType type = m_cocoaPlatformWindow->window()->type(); - if (type == Qt::Tool || type == Qt::Dialog) - return YES; - return NO; - } - - // All other windows can become the key window. This includes - // popup windows such as the combobox popup, which is a title-bar - // less window that by default can't become key. + // The default implementation returns NO for title-bar less windows, + // override and return yes here to make sure popup windows such as + // the combobox popup can become the key window. return YES; } @@ -147,9 +144,6 @@ static bool isMouseEvent(NSEvent *ev) || m_cocoaPlatformWindow->window()->transientParent()) canBecomeMain = NO; - if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel()) - canBecomeMain = NO; - return canBecomeMain; } @@ -198,6 +192,64 @@ static bool isMouseEvent(NSEvent *ev) @end +@implementation QNSPanel + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSUInteger)windowStyle + qPlatformWindow:(QCocoaWindow *)qpw +{ + self = [super initWithContentRect:contentRect + styleMask:windowStyle + backing:NSBackingStoreBuffered + defer:NO]; // Deferring window creation breaks OpenGL (the GL context is + // set up before the window is shown and needs a proper window) + + if (self) { + m_cocoaPlatformWindow = qpw; + } + return self; +} + +- (BOOL)canBecomeKeyWindow +{ + if (!m_cocoaPlatformWindow) + return NO; + + // Only tool or dialog windows should become key: + if (m_cocoaPlatformWindow + && (m_cocoaPlatformWindow->window()->type() == Qt::Tool || + m_cocoaPlatformWindow->window()->type() == Qt::Dialog)) + return YES; + return NO; +} + +- (void) sendEvent: (NSEvent*) theEvent +{ + [super sendEvent: theEvent]; + + if (!m_cocoaPlatformWindow) + return; + + if (m_cocoaPlatformWindow->frameStrutEventsEnabled() && isMouseEvent(theEvent)) { + NSPoint loc = [theEvent locationInWindow]; + NSRect windowFrame = [self legacyConvertRectFromScreen:[self frame]]; + NSRect contentFrame = [[self contentView] frame]; + if (NSMouseInRect(loc, windowFrame, NO) && + !NSMouseInRect(loc, contentFrame, NO)) + { + QNSView *contentView = (QNSView *) m_cocoaPlatformWindow->contentView(); + [contentView handleFrameStrutMouseEvent: theEvent]; + } + } +} + +- (void)clearPlatformWindow +{ + m_cocoaPlatformWindow = 0; +} + +@end + const int QCocoaWindow::NoAlertRequest = -1; QCocoaWindow::QCocoaWindow(QWindow *tlw) @@ -675,21 +727,15 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; - if (windowShouldBehaveAsPanel()) { - behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; - behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; - } else { + Qt::WindowType type = window()->type(); + if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { + NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; if (flags & Qt::WindowFullscreenButtonHint) behavior |= NSWindowCollectionBehaviorFullScreenPrimary; else behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; + [m_nsWindow setCollectionBehavior:behavior]; } - [m_nsWindow setCollectionBehavior:behavior]; - - [m_nsWindow setAnimationBehavior:(flags & Qt::Popup) == Qt::Popup - ? NSWindowAnimationBehaviorUtilityWindow - : NSWindowAnimationBehaviorDefault]; } #endif } @@ -1013,14 +1059,6 @@ bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const return ((type & Qt::Popup) == Qt::Popup); } -bool QCocoaWindow::windowShouldBehaveAsPanel() const -{ - // Before merging QNSPanel and QNSWindow, we used NSPanel for popup-type - // windows (Popup, Tool, ToolTip, SplashScreen) and dialogs - Qt::WindowType type = window()->type(); - return (type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog; -} - void QCocoaWindow::setCurrentContext(QCocoaGLContext *context) { m_glContext = context; @@ -1034,17 +1072,21 @@ QCocoaGLContext *QCocoaWindow::currentContext() const void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) { bool wasNSWindowChild = m_isNSWindowChild; + // TODO Set value for m_isNSWindowChild here bool needsNSWindow = m_isNSWindowChild || !parentWindow; QCocoaWindow *oldParentCocoaWindow = m_parentCocoaWindow; m_parentCocoaWindow = const_cast(static_cast(parentWindow)); + bool usesNSPanel = [m_nsWindow isKindOfClass:[QNSPanel class]]; + // No child QNSWindow should notify its QNSView if (m_nsWindow && m_qtView && m_parentCocoaWindow && !oldParentCocoaWindow) [[NSNotificationCenter defaultCenter] removeObserver:m_qtView name:nil object:m_nsWindow]; + // Remove current window (if any) - if (m_nsWindow && !needsNSWindow) { + if ((m_nsWindow && !needsNSWindow) || (usesNSPanel != shouldUseNSPanel())) { clearNSWindow(m_nsWindow); [m_nsWindow close]; [m_nsWindow release]; @@ -1075,7 +1117,6 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) setNSWindow(m_nsWindow); } - if (m_contentViewIsToBeEmbedded) { // An embedded window doesn't have its own NSWindow. } else if (!parentWindow) { @@ -1147,13 +1188,22 @@ void QCocoaWindow::requestActivateWindow() [ window makeKeyWindow ]; } -QNSWindow * QCocoaWindow::createNSWindow() +bool QCocoaWindow::shouldUseNSPanel() +{ + Qt::WindowType type = window()->type(); + + return !m_isNSWindowChild && + ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog); +} + +NSWindow * QCocoaWindow::createNSWindow() { QCocoaAutoReleasePool pool; QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight); NSRect frame = qt_mac_flipRect(rect, window()); + Qt::WindowType type = window()->type(); Qt::WindowFlags flags = window()->flags(); NSUInteger styleMask; @@ -1162,17 +1212,44 @@ QNSWindow * QCocoaWindow::createNSWindow() } else { styleMask = windowStyleMask(flags); } + NSWindow *createdWindow = 0; - QNSWindow *createdWindow = [[QNSWindow alloc] initWithContentRect:frame styleMask:styleMask qPlatformWindow:this]; - - Qt::WindowFlags type = window()->type(); - createdWindow.hidesOnDeactivate = type == Qt::Tool || type == Qt::ToolTip; + // Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen) + // and dialogs + if (shouldUseNSPanel()) { + QNSPanel *window; + window = [[QNSPanel alloc] initWithContentRect:frame + styleMask: styleMask + qPlatformWindow:this]; + if ((type & Qt::Popup) == Qt::Popup) + [window setHasShadow:YES]; + [window setHidesOnDeactivate: NO]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - [createdWindow setRestorable: NO]; - } + if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + // Make popup winows show on the same desktop as the parent full-screen window. + [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + + if ((type & Qt::Popup) == Qt::Popup) + [window setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow]; + } #endif + createdWindow = window; + } else { + QNSWindow *window; + window = [[QNSWindow alloc] initWithContentRect:frame + styleMask: styleMask + qPlatformWindow:this]; + createdWindow = window; + } + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if ([createdWindow respondsToSelector:@selector(setRestorable:)]) + [createdWindow setRestorable: NO]; +#endif + + NSInteger level = windowLevel(flags); + [createdWindow setLevel:level]; if (window()->format().alphaBufferSize() > 0) { [createdWindow setBackgroundColor:[NSColor clearColor]]; @@ -1186,12 +1263,10 @@ QNSWindow * QCocoaWindow::createNSWindow() return createdWindow; } -void QCocoaWindow::setNSWindow(QNSWindow *window) +void QCocoaWindow::setNSWindow(NSWindow *window) { - if (!m_nsWindowDelegate) { - m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; - [window setDelegate:m_nsWindowDelegate]; - } + m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; + [window setDelegate:m_nsWindowDelegate]; // Prevent Cocoa from releasing the window on close. Qt // handles the close event asynchronously and we want to @@ -1206,11 +1281,12 @@ void QCocoaWindow::setNSWindow(QNSWindow *window) } } -void QCocoaWindow::clearNSWindow(QNSWindow *window) +void QCocoaWindow::clearNSWindow(NSWindow *window) { [window setContentView:nil]; [window setDelegate:nil]; [window clearPlatformWindow]; + if (m_isNSWindowChild) { m_parentCocoaWindow->removeChildWindow(this); } diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 9920d428e9..58c732de98 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -1383,12 +1383,6 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) [self tryToPerform:aSelector with:self]; } -- (void)cancelOperation:(id)sender -{ - if (!m_platformWindow || m_platformWindow->windowShouldBehaveAsPanel()) - [super cancelOperation:sender]; -} - - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange { Q_UNUSED(replacementRange)