diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoawindow.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 515 |
1 files changed, 444 insertions, 71 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 211ecd60ab..d972782f31 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -45,6 +45,7 @@ #include "qcocoaeventdispatcher.h" #include "qcocoaglcontext.h" #include "qcocoahelpers.h" +#include "qcocoanativeinterface.h" #include "qnsview.h" #include <QtCore/qfileinfo.h> #include <QtCore/private/qcore_mac_p.h> @@ -104,8 +105,29 @@ static bool isMouseEvent(NSEvent *ev) @implementation QNSWindow +- (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 { + // Prevent child NSWindows from becoming the key window in + // order keep the active apperance of the top-level window. + if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->m_isNSWindowChild) + return NO; + // 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. @@ -118,7 +140,8 @@ static bool isMouseEvent(NSEvent *ev) // Windows with a transient parent (such as combobox popup windows) // cannot become the main window: - if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->window()->transientParent()) + if (!m_cocoaPlatformWindow || m_cocoaPlatformWindow->m_isNSWindowChild + || m_cocoaPlatformWindow->window()->transientParent()) canBecomeMain = NO; return canBecomeMain; @@ -126,6 +149,24 @@ static bool isMouseEvent(NSEvent *ev) - (void) sendEvent: (NSEvent*) theEvent { + if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->m_forwardWindow) { + if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) { + QNSView *forwardView = m_cocoaPlatformWindow->m_qtView; + if (theEvent.type == NSLeftMouseUp) { + [forwardView mouseUp:theEvent]; + m_cocoaPlatformWindow->m_forwardWindow = 0; + } else { + [forwardView mouseDragged:theEvent]; + } + + return; + } + + if (theEvent.type == NSLeftMouseDown) { + m_cocoaPlatformWindow->m_forwardWindow = 0; + } + } + [super sendEvent: theEvent]; if (!m_cocoaPlatformWindow) @@ -153,6 +194,22 @@ static bool isMouseEvent(NSEvent *ev) @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) @@ -160,7 +217,8 @@ static bool isMouseEvent(NSEvent *ev) // Only tool or dialog windows should become key: if (m_cocoaPlatformWindow - && (m_cocoaPlatformWindow->window()->type() == Qt::Tool || m_cocoaPlatformWindow->window()->type() == Qt::Dialog)) + && (m_cocoaPlatformWindow->window()->type() == Qt::Tool || + m_cocoaPlatformWindow->window()->type() == Qt::Dialog)) return YES; return NO; } @@ -199,8 +257,11 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_contentView(nil) , m_qtView(nil) , m_nsWindow(0) + , m_forwardWindow(0) , m_contentViewIsEmbedded(false) , m_contentViewIsToBeEmbedded(false) + , m_parentCocoaWindow(0) + , m_isNSWindowChild(false) , m_nsWindowDelegate(0) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) @@ -215,11 +276,14 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_isExposed(false) , m_registerTouchCount(0) , m_resizableTransientParent(false) + , m_hiddenByClipping(false) + , m_hiddenByAncestor(false) , m_alertRequest(NoAlertRequest) , monitor(nil) , m_drawContentBorderGradient(false) , m_topContentBorderThickness(0) , m_bottomContentBorderThickness(0) + , m_normalGeometry(QRect(0,0,-1,-1)) { #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG qDebug() << "QCocoaWindow::QCocoaWindow" << this; @@ -239,9 +303,15 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) // problem, except if the appilcation wants to have a "custom" viewport. // (like the hellogl example) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 - && tlw->surfaceType() == QSurface::OpenGLSurface) - [m_contentView setWantsBestResolutionOpenGLSurface:YES]; + && tlw->surfaceType() == QSurface::OpenGLSurface) { + BOOL enable = qt_mac_resolveOption(YES, tlw, "_q_mac_wantsBestResolutionOpenGLSurface", + "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE"); + [m_contentView setWantsBestResolutionOpenGLSurface:enable]; + } #endif + BOOL enable = qt_mac_resolveOption(NO, tlw, "_q_mac_wantsLayer", + "QT_MAC_WANTS_LAYER"); + [m_contentView setWantsLayer:enable]; } setGeometry(tlw->geometry()); recreateWindow(parent()); @@ -259,8 +329,21 @@ QCocoaWindow::~QCocoaWindow() QCocoaAutoReleasePool pool; clearNSWindow(m_nsWindow); - if (parent()) + if (m_isNSWindowChild) { + if (m_parentCocoaWindow) + m_parentCocoaWindow->removeChildWindow(this); + } else if (parent()) { [m_contentView removeFromSuperview]; + } else if (m_qtView) { + [[NSNotificationCenter defaultCenter] removeObserver:m_qtView + name:nil object:m_nsWindow]; + } + + foreach (QCocoaWindow *child, m_childWindows) { + [m_nsWindow removeChildWindow:child->m_nsWindow]; + child->m_parentCocoaWindow = 0; + } + [m_contentView release]; [m_nsWindow release]; [m_nsWindowDelegate release]; @@ -272,8 +355,16 @@ QSurfaceFormat QCocoaWindow::format() const return window()->requestedFormat(); } -void QCocoaWindow::setGeometry(const QRect &rect) +void QCocoaWindow::setGeometry(const QRect &rectIn) { + QRect rect = rectIn; + // This means it is a call from QWindow::setFramePosition() and + // the coordinates include the frame (size is still the contents rectangle). + if (qt_window_private(const_cast<QWindow *>(window()))->positionPolicy + == QWindowPrivate::WindowFrameInclusive) { + const QMargins margins = frameMargins(); + rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top())); + } if (geometry() == rect) return; #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG @@ -291,7 +382,16 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect) return; } - if (m_nsWindow) { + if (m_isNSWindowChild) { + QPlatformWindow::setGeometry(rect); + NSWindow *parentNSWindow = m_parentCocoaWindow->m_nsWindow; + NSRect parentWindowFrame = [parentNSWindow contentRectForFrameRect:parentNSWindow.frame]; + clipWindow(parentWindowFrame); + + // call this here: updateGeometry in qnsview.mm is a no-op for this case + QWindowSystemInterface::handleGeometryChange(window(), rect); + QWindowSystemInterface::handleExposeEvent(window(), rect); + } else if (m_nsWindow) { NSRect bounds = qt_mac_flipRect(rect, window()); [m_nsWindow setFrame:[m_nsWindow frameRectForContentRect:bounds] display:YES animate:NO]; } else { @@ -301,8 +401,99 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect) // will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm) } +void QCocoaWindow::clipChildWindows() +{ + foreach (QCocoaWindow *childWindow, m_childWindows) { + childWindow->clipWindow(m_nsWindow.frame); + } +} + +void QCocoaWindow::clipWindow(const NSRect &clipRect) +{ + if (!m_isNSWindowChild) + return; + + NSRect clippedWindowRect = NSZeroRect; + if (!NSIsEmptyRect(clipRect)) { + NSRect windowFrame = qt_mac_flipRect(QRect(window()->mapToGlobal(QPoint(0, 0)), geometry().size()), window()); + clippedWindowRect = NSIntersectionRect(windowFrame, clipRect); + // Clipping top/left offsets the content. Move it back. + NSPoint contentViewOffset = NSMakePoint(qMax(CGFloat(0), NSMinX(clippedWindowRect) - NSMinX(windowFrame)), + qMax(CGFloat(0), NSMaxY(windowFrame) - NSMaxY(clippedWindowRect))); + [m_contentView setBoundsOrigin:contentViewOffset]; + } + + if (NSIsEmptyRect(clippedWindowRect)) { + if (!m_hiddenByClipping) { + // We dont call hide() here as we will recurse further down + [m_nsWindow orderOut:nil]; + m_hiddenByClipping = true; + } + } else { + [m_nsWindow setFrame:clippedWindowRect display:YES animate:NO]; + if (m_hiddenByClipping) { + m_hiddenByClipping = false; + if (!m_hiddenByAncestor) { + [m_nsWindow orderFront:nil]; + m_parentCocoaWindow->reinsertChildWindow(this); + } + } + } + + // recurse + foreach (QCocoaWindow *childWindow, m_childWindows) { + childWindow->clipWindow(clippedWindowRect); + } +} + +void QCocoaWindow::hide(bool becauseOfAncestor) +{ + bool visible = [m_nsWindow isVisible]; + + if (!m_hiddenByAncestor && !visible) // Already explicitly hidden + return; + if (m_hiddenByAncestor && becauseOfAncestor) // Trying to hide some child again + return; + + m_hiddenByAncestor = becauseOfAncestor; + + if (!visible) // Could have been clipped before + return; + + foreach (QCocoaWindow *childWindow, m_childWindows) + childWindow->hide(true); + + [m_nsWindow orderOut:nil]; +} + +void QCocoaWindow::show(bool becauseOfAncestor) +{ + if ([m_nsWindow isVisible]) + return; + + if (m_parentCocoaWindow && ![m_parentCocoaWindow->m_nsWindow isVisible]) { + m_hiddenByAncestor = true; // Parent still hidden, don't show now + } else if ((becauseOfAncestor == m_hiddenByAncestor) // Was NEITHER explicitly hidden + && !m_hiddenByClipping) { // ... NOR clipped + if (m_isNSWindowChild) { + m_hiddenByAncestor = false; + setCocoaGeometry(window()->geometry()); + } + if (!m_hiddenByClipping) { // setCocoaGeometry() can change the clipping status + [m_nsWindow orderFront:nil]; + if (m_isNSWindowChild) + m_parentCocoaWindow->reinsertChildWindow(this); + foreach (QCocoaWindow *childWindow, m_childWindows) + childWindow->show(true); + } + } +} + void QCocoaWindow::setVisible(bool visible) { + if (m_isNSWindowChild && m_hiddenByClipping) + return; + QCocoaAutoReleasePool pool; QCocoaWindow *parentCocoaWindow = 0; if (window()->transientParent()) @@ -367,8 +558,10 @@ void QCocoaWindow::setVisible(bool visible) m_hasModalSession = true; } else if ([m_nsWindow canBecomeKeyWindow]) { [m_nsWindow makeKeyAndOrderFront:nil]; + foreach (QCocoaWindow *childWindow, m_childWindows) + childWindow->show(true); } else { - [m_nsWindow orderFront: nil]; + show(); } // We want the events to properly reach the popup, dialog, and tool @@ -392,28 +585,30 @@ void QCocoaWindow::setVisible(bool visible) // qDebug() << "close" << this; if (m_glContext) m_glContext->windowWasHidden(); + QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher()); + QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = 0; + if (cocoaEventDispatcher) + cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher)); if (m_nsWindow) { if (m_hasModalSession) { - QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher()); - Q_ASSERT(cocoaEventDispatcher != 0); - QCocoaEventDispatcherPrivate *cocoaEventDispatcherPrivate = static_cast<QCocoaEventDispatcherPrivate *>(QObjectPrivate::get(cocoaEventDispatcher)); - cocoaEventDispatcherPrivate->endModalSession(window()); + if (cocoaEventDispatcherPrivate) + cocoaEventDispatcherPrivate->endModalSession(window()); m_hasModalSession = false; - - [m_nsWindow orderOut:m_nsWindow]; - if (m_nsWindow == [NSApp keyWindow] && !cocoaEventDispatcherPrivate->currentModalSession()) { - // Probably because we call runModalSession: outside [NSApp run] in QCocoaEventDispatcher - // (e.g., when show()-ing a modal QDialog instead of exec()-ing it), it can happen that - // the current NSWindow is still key after being ordered out. Then, after checking we - // don't have any other modal session left, it's safe to make the main window key again. - NSWindow *mainWindow = [NSApp mainWindow]; - if (mainWindow && [mainWindow canBecomeKeyWindow]) - [mainWindow makeKeyWindow]; - } } else { if ([m_nsWindow isSheet]) [NSApp endSheet:m_nsWindow]; - [m_nsWindow orderOut:m_nsWindow]; + } + + hide(); + if (m_nsWindow == [NSApp keyWindow] + && !(cocoaEventDispatcherPrivate && cocoaEventDispatcherPrivate->currentModalSession())) { + // Probably because we call runModalSession: outside [NSApp run] in QCocoaEventDispatcher + // (e.g., when show()-ing a modal QDialog instead of exec()-ing it), it can happen that + // the current NSWindow is still key after being ordered out. Then, after checking we + // don't have any other modal session left, it's safe to make the main window key again. + NSWindow *mainWindow = [NSApp mainWindow]; + if (mainWindow && [mainWindow canBecomeKeyWindow]) + [mainWindow makeKeyWindow]; } } else { [m_contentView setHidden:YES]; @@ -520,7 +715,7 @@ void QCocoaWindow::setWindowShadow(Qt::WindowFlags flags) void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) { - if (m_nsWindow) { + if (m_nsWindow && !m_isNSWindowChild) { NSUInteger styleMask = windowStyleMask(flags); NSInteger level = this->windowLevel(flags); [m_nsWindow setStyleMask:styleMask]; @@ -618,16 +813,55 @@ void QCocoaWindow::raise() // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm) if (!m_nsWindow) return; - if ([m_nsWindow isVisible]) - [m_nsWindow orderFront: m_nsWindow]; + if (m_isNSWindowChild) { + QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows; + siblings.removeOne(this); + siblings.append(this); + if (m_hiddenByClipping) + return; + } + if ([m_nsWindow isVisible]) { + if (m_isNSWindowChild) { + // -[NSWindow orderFront:] doesn't work with attached windows. + // The only solution is to remove and add the child window. + // This will place it on top of all the other NSWindows. + NSWindow *parentNSWindow = m_parentCocoaWindow->m_nsWindow; + [parentNSWindow removeChildWindow:m_nsWindow]; + [parentNSWindow addChildWindow:m_nsWindow ordered:NSWindowAbove]; + } else { + [m_nsWindow orderFront: m_nsWindow]; + } + } } void QCocoaWindow::lower() { if (!m_nsWindow) return; - if ([m_nsWindow isVisible]) - [m_nsWindow orderBack: m_nsWindow]; + if (m_isNSWindowChild) { + QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows; + siblings.removeOne(this); + siblings.prepend(this); + if (m_hiddenByClipping) + return; + } + if ([m_nsWindow isVisible]) { + if (m_isNSWindowChild) { + // -[NSWindow orderBack:] doesn't work with attached windows. + // The only solution is to remove and add all the child windows except this one. + // This will keep the current window at the bottom while adding the others on top of it, + // hopefully in the same order (this is not documented anywhere in the Cocoa documentation). + NSWindow *parentNSWindow = m_parentCocoaWindow->m_nsWindow; + NSArray *children = [parentNSWindow.childWindows copy]; + for (NSWindow *child in children) + if (m_nsWindow != child) { + [parentNSWindow removeChildWindow:child]; + [parentNSWindow addChildWindow:child ordered:NSWindowAbove]; + } + } else { + [m_nsWindow orderBack: m_nsWindow]; + } + } } bool QCocoaWindow::isExposed() const @@ -755,6 +989,16 @@ void QCocoaWindow::setContentView(NSView *contentView) recreateWindow(parent()); // Adds the content view to parent NSView } +QNSView *QCocoaWindow::qtView() const +{ + return m_qtView; +} + +NSWindow *QCocoaWindow::nativeWindow() const +{ + return m_nsWindow; +} + void QCocoaWindow::setEmbeddedInForeignView(bool embedded) { m_contentViewIsToBeEmbedded = embedded; @@ -773,6 +1017,9 @@ void QCocoaWindow::windowWillMove() void QCocoaWindow::windowDidMove() { + if (m_isNSWindowChild) + return; + [m_qtView updateGeometry]; } @@ -781,6 +1028,10 @@ void QCocoaWindow::windowDidResize() if (!m_nsWindow) return; + if (m_isNSWindowChild) + return; + + clipChildWindows(); [m_qtView updateGeometry]; } @@ -820,8 +1071,22 @@ 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<QCocoaWindow *>(static_cast<const QCocoaWindow *>(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) { + if ((m_nsWindow && !needsNSWindow) || (usesNSPanel != shouldUseNSPanel())) { clearNSWindow(m_nsWindow); [m_nsWindow close]; [m_nsWindow release]; @@ -830,22 +1095,63 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) m_nsWindowDelegate = 0; } + if (needsNSWindow) { + bool noPreviousWindow = m_nsWindow == 0; + if (noPreviousWindow) + m_nsWindow = createNSWindow(); + + // Only non-child QNSWindows should notify their QNSViews + // (but don't register more than once). + if (m_qtView && (noPreviousWindow || (wasNSWindowChild && !m_isNSWindowChild))) + [[NSNotificationCenter defaultCenter] addObserver:m_qtView + selector:@selector(windowNotification:) + name:nil // Get all notifications + object:m_nsWindow]; + + if (oldParentCocoaWindow) { + if (!m_isNSWindowChild || oldParentCocoaWindow != m_parentCocoaWindow) + oldParentCocoaWindow->removeChildWindow(this); + m_forwardWindow = oldParentCocoaWindow; + } + + setNSWindow(m_nsWindow); + } + if (m_contentViewIsToBeEmbedded) { // An embedded window doesn't have its own NSWindow. } else if (!parentWindow) { - // Create a new NSWindow if this is a top-level window. - m_nsWindow = createNSWindow(); - setNSWindow(m_nsWindow); - // QPlatformWindow subclasses must sync up with QWindow on creation: propagateSizeHints(); setWindowFlags(window()->flags()); setWindowTitle(window()->title()); setWindowState(window()->windowState()); + } else if (m_isNSWindowChild) { + m_nsWindow.styleMask = NSBorderlessWindowMask; + m_nsWindow.hasShadow = NO; + m_nsWindow.level = NSNormalWindowLevel; + NSWindowCollectionBehavior collectionBehavior = + NSWindowCollectionBehaviorManaged | NSWindowCollectionBehaviorIgnoresCycle; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + collectionBehavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; + m_nsWindow.animationBehavior = NSWindowAnimationBehaviorNone; + } +#endif + m_nsWindow.collectionBehavior = collectionBehavior; + setCocoaGeometry(window()->geometry()); + + QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows; + if (siblings.contains(this)) { + if (!m_hiddenByClipping) + m_parentCocoaWindow->reinsertChildWindow(this); + } else { + if (!m_hiddenByClipping) + [m_parentCocoaWindow->m_nsWindow addChildWindow:m_nsWindow ordered:NSWindowAbove]; + siblings.append(this); + } } else { // Child windows have no NSWindow, link the NSViews instead. - const QCocoaWindow *parentCococaWindow = static_cast<const QCocoaWindow *>(parentWindow); - [parentCococaWindow->m_contentView addSubview : m_contentView]; + [m_parentCocoaWindow->m_contentView addSubview : m_contentView]; QRect rect = window()->geometry(); NSRect frame = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); [m_contentView setFrame:frame]; @@ -855,6 +1161,24 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) const qreal opacity = qt_window_private(window())->opacity; if (!qFuzzyCompare(opacity, qreal(1.0))) setOpacity(opacity); + + // top-level QWindows may have an attached NSToolBar, call + // update function which will attach to the NSWindow. + if (!parentWindow) + updateNSToolbar(); +} + +void QCocoaWindow::reinsertChildWindow(QCocoaWindow *child) +{ + int childIndex = m_childWindows.indexOf(child); + Q_ASSERT(childIndex != -1); + + for (int i = childIndex; i < m_childWindows.size(); i++) { + NSWindow *nsChild = m_childWindows[i]->m_nsWindow; + if (i != childIndex) + [m_nsWindow removeChildWindow:nsChild]; + [m_nsWindow addChildWindow:nsChild ordered:NSWindowAbove]; + } } void QCocoaWindow::requestActivateWindow() @@ -864,6 +1188,14 @@ void QCocoaWindow::requestActivateWindow() [ window makeKeyWindow ]; } +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; @@ -874,18 +1206,21 @@ NSWindow * QCocoaWindow::createNSWindow() Qt::WindowType type = window()->type(); Qt::WindowFlags flags = window()->flags(); - NSUInteger styleMask = windowStyleMask(flags); + NSUInteger styleMask; + if (m_isNSWindowChild) { + styleMask = NSBorderlessWindowMask; + } else { + styleMask = windowStyleMask(flags); + } NSWindow *createdWindow = 0; // Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen) // and dialogs - if ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog) { + if (shouldUseNSPanel()) { QNSPanel *window; window = [[QNSPanel alloc] initWithContentRect:frame - styleMask: styleMask - 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.). + styleMask: styleMask + qPlatformWindow:this]; if ((type & Qt::Popup) == Qt::Popup) [window setHasShadow:YES]; [window setHidesOnDeactivate: NO]; @@ -899,17 +1234,12 @@ NSWindow * QCocoaWindow::createNSWindow() [window setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow]; } #endif - window->m_cocoaPlatformWindow = this; createdWindow = window; } else { QNSWindow *window; window = [[QNSWindow alloc] initWithContentRect:frame - styleMask: styleMask - 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.). - window->m_cocoaPlatformWindow = this; - + styleMask: styleMask + qPlatformWindow:this]; createdWindow = window; } @@ -944,16 +1274,11 @@ void QCocoaWindow::setNSWindow(NSWindow *window) // QCocoaWindow is deleted by Qt. [window setReleasedWhenClosed : NO]; - - if (m_qtView) - [[NSNotificationCenter defaultCenter] addObserver:m_qtView - selector:@selector(windowNotification:) - name:nil // Get all notifications - object:m_nsWindow]; - - [m_contentView setPostsFrameChangedNotifications: NO]; - [window setContentView:m_contentView]; - [m_contentView setPostsFrameChangedNotifications: YES]; + if (window.contentView != m_contentView) { + [m_contentView setPostsFrameChangedNotifications: NO]; + [window setContentView:m_contentView]; + [m_contentView setPostsFrameChangedNotifications: YES]; + } } void QCocoaWindow::clearNSWindow(NSWindow *window) @@ -961,14 +1286,22 @@ void QCocoaWindow::clearNSWindow(NSWindow *window) [window setContentView:nil]; [window setDelegate:nil]; [window clearPlatformWindow]; - [[NSNotificationCenter defaultCenter] removeObserver:m_contentView - name:nil object:window]; + + if (m_isNSWindowChild) { + m_parentCocoaWindow->removeChildWindow(this); + } +} + +void QCocoaWindow::removeChildWindow(QCocoaWindow *child) +{ + m_childWindows.removeOne(child); + [m_nsWindow removeChildWindow:child->m_nsWindow]; } // Returns the current global screen geometry for the nswindow associated with this window. QRect QCocoaWindow::windowGeometry() const { - if (!m_nsWindow) + if (!m_nsWindow || m_isNSWindowChild) return geometry(); NSRect rect = [m_nsWindow frame]; @@ -1015,13 +1348,35 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) } if ((m_synchedWindowState & Qt::WindowFullScreen) != (newState & Qt::WindowFullScreen)) { + bool fakeFullScreen = true; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - [m_nsWindow toggleFullScreen : m_nsWindow]; - } else { - // TODO: "normal" fullscreen + if (window()->flags() & Qt::WindowFullscreenButtonHint) { + fakeFullScreen = false; + [m_nsWindow toggleFullScreen : m_nsWindow]; + } } #endif + if (fakeFullScreen) { + if (newState & Qt::WindowFullScreen) { + QScreen *screen = window()->screen(); + if (screen) { + if (m_normalGeometry.width() < 0) { + m_oldWindowFlags = m_windowFlags; + window()->setFlags(window()->flags() | Qt::FramelessWindowHint); + m_normalGeometry = windowGeometry(); + setGeometry(screen->geometry()); + m_presentationOptions = [NSApp presentationOptions]; + [NSApp setPresentationOptions : m_presentationOptions | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; + } + } + } else { + window()->setFlags(m_oldWindowFlags); + setGeometry(m_normalGeometry); + m_normalGeometry.setRect(0, 0, -1, -1); + [NSApp setPresentationOptions : m_presentationOptions]; + } + } } // New state is now the current synched state @@ -1054,16 +1409,21 @@ void QCocoaWindow::setWindowCursor(NSCursor *cursor) // for a popup window.) Qt expects the set cursor to "stick": // it should be accociated with the window until a different // cursor is set. - - // Cocoa has different abstractions. We can set the cursor *now*: - if (m_windowUnderMouse) - [cursor set]; - // or we can set the cursor on mouse enter/leave using tracking - // areas. This is done in QNSView, save the cursor: if (m_windowCursor != cursor) { [m_windowCursor release]; m_windowCursor = [cursor retain]; } + + // Use the built in cursor rect API if the QCocoaWindow has a NSWindow. + // Othervise, set the cursor if this window is under the mouse. In + // this case QNSView::cursorUpdate will set the cursor as the pointer + // moves. + if (m_nsWindow && m_qtView) { + [m_nsWindow invalidateCursorRectsForView : m_qtView]; + } else { + if (m_windowUnderMouse) + [cursor set]; + } } void QCocoaWindow::registerTouch(bool enable) @@ -1106,6 +1466,19 @@ void QCocoaWindow::applyContentBorderThickness(NSWindow *window) } } +void QCocoaWindow::updateNSToolbar() +{ + if (!m_nsWindow) + return; + + NSToolbar *toolbar = QCocoaIntegration::instance()->toolbar(window()); + + if ([m_nsWindow toolbar] == toolbar) + return; + + [m_nsWindow setToolbar: toolbar]; + [m_nsWindow setShowsToolbarButton:YES]; +} qreal QCocoaWindow::devicePixelRatio() const { |