diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
18 files changed, 652 insertions, 238 deletions
diff --git a/src/plugins/platforms/cocoa/messages.cpp b/src/plugins/platforms/cocoa/messages.cpp index 3db1618a50..1fe80b28b1 100644 --- a/src/plugins/platforms/cocoa/messages.cpp +++ b/src/plugins/platforms/cocoa/messages.cpp @@ -93,4 +93,9 @@ QPlatformMenuItem::MenuRole detectMenuRole(const QString &caption) return QPlatformMenuItem::NoRole; } +QString msgDialogButtonDiscard() +{ + return QCoreApplication::translate("QCocoaTheme", "Don't Save"); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/messages.h b/src/plugins/platforms/cocoa/messages.h index 09705c1e21..97f3ea7009 100644 --- a/src/plugins/platforms/cocoa/messages.h +++ b/src/plugins/platforms/cocoa/messages.h @@ -53,6 +53,8 @@ QString qt_mac_applicationmenu_string(int type); QPlatformMenuItem::MenuRole detectMenuRole(const QString &caption); +QString msgDialogButtonDiscard(); + QT_END_NAMESPACE #endif // MESSAGES_H diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.h b/src/plugins/platforms/cocoa/qcocoaapplication.h index ffb12ea846..bb218bcabe 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.h +++ b/src/plugins/platforms/cocoa/qcocoaapplication.h @@ -86,7 +86,7 @@ // /* - Cocoa Application Categories + Cocoa Application Categories */ #include "qglobal.h" #include "private/qcore_mac_p.h" diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index f9767ce716..327ca00ad6 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -114,7 +114,7 @@ static void cleanupCocoaApplicationDelegate() - (void)updateScreens:(NSNotification *)notification { Q_UNUSED(notification); - if (QCocoaIntegration *ci = dynamic_cast<QCocoaIntegration *>(QGuiApplicationPrivate::platformIntegration())) + if (QCocoaIntegration *ci = QCocoaIntegration::instance()) ci->updateScreens(); } diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 8f74a71b1e..4b637a707d 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -143,7 +143,7 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo [pixelFormat release]; - const GLint interval = 1; + const GLint interval = format.swapInterval() >= 0 ? format.swapInterval() : 1; [m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; if (format.alphaBufferSize() > 0) { diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 3e402673f3..64e1640a69 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -116,6 +116,7 @@ inline NSPoint qt_mac_flipPoint(const QPoint &p) inline NSPoint qt_mac_flipPoint(const QPointF &p) { return NSMakePoint(p.x(), qt_mac_flipYCoordinate(p.y())); } +NSRect qt_mac_flipRect(const QRect &rect); NSRect qt_mac_flipRect(const QRect &rect, QWindow *window); Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 4a5696b35e..d27c134fa3 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -585,6 +585,12 @@ QString qt_mac_applicationName() return appName; } +NSRect qt_mac_flipRect(const QRect &rect) +{ + int flippedY = qt_mac_flipYCoordinate(rect.y() + rect.height()); + return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height()); +} + /* Mac window coordinates are in the first quadrant: 0, 0 is at the lower-left corner of the primary screen. This function converts the given rect to an diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 111329aaee..8babfcf8ae 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -46,6 +46,10 @@ #include "qcocoaautoreleasepool.h" #include "qcocoacursor.h" +#include "qcocoawindow.h" +#include "qcocoanativeinterface.h" +#include "qcocoainputcontext.h" +#include "qcocoaaccessibility.h" #include "qcocoaclipboard.h" #include "qcocoadrag.h" #include "qcocoaservices.h" @@ -53,6 +57,7 @@ #include <QtCore/QScopedPointer> #include <qpa/qplatformintegration.h> +#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h> QT_BEGIN_NAMESPACE @@ -103,23 +108,25 @@ public: QCocoaIntegration(); ~QCocoaIntegration(); + static QCocoaIntegration *instance(); + bool hasCapability(QPlatformIntegration::Capability cap) const; QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const; QAbstractEventDispatcher *createEventDispatcher() const; - QPlatformFontDatabase *fontDatabase() const; - QPlatformNativeInterface *nativeInterface() const; - QPlatformInputContext *inputContext() const; - QPlatformAccessibility *accessibility() const; - QPlatformClipboard *clipboard() const; - QPlatformDrag *drag() const; + QCoreTextFontDatabase *fontDatabase() const; + QCocoaNativeInterface *nativeInterface() const; + QCocoaInputContext *inputContext() const; + QCocoaAccessibility *accessibility() const; + QCocoaClipboard *clipboard() const; + QCocoaDrag *drag() const; QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; - QPlatformServices *services() const; + QCocoaServices *services() const; QVariant styleHint(StyleHint hint) const; QList<int> possibleKeys(const QKeyEvent *event) const; @@ -128,18 +135,19 @@ public: QCocoaScreen *screenAtIndex(int index); private: + static QCocoaIntegration *mInstance; - QScopedPointer<QPlatformFontDatabase> mFontDb; + QScopedPointer<QCoreTextFontDatabase> mFontDb; - QScopedPointer<QPlatformInputContext> mInputContext; + QScopedPointer<QCocoaInputContext> mInputContext; #ifndef QT_NO_ACCESSIBILITY - QScopedPointer<QPlatformAccessibility> mAccessibility; + QScopedPointer<QCocoaAccessibility> mAccessibility; #endif QScopedPointer<QPlatformTheme> mPlatformTheme; QList<QCocoaScreen *> mScreens; QCocoaClipboard *mCocoaClipboard; QScopedPointer<QCocoaDrag> mCocoaDrag; - QScopedPointer<QPlatformNativeInterface> mNativeInterface; + QScopedPointer<QCocoaNativeInterface> mNativeInterface; QScopedPointer<QCocoaServices> mServices; QScopedPointer<QCocoaKeyMapper> mKeyboardMapper; }; diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 5f01274d98..e8cf5ca69b 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -58,7 +58,6 @@ #include <qpa/qplatformaccessibility.h> #include <QtCore/qcoreapplication.h> -#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h> #include <IOKit/graphics/IOGraphicsLib.h> static void initResources() @@ -214,6 +213,8 @@ QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height return windowPixmap; } +QCocoaIntegration *QCocoaIntegration::mInstance = 0; + QCocoaIntegration::QCocoaIntegration() : mFontDb(new QCoreTextFontDatabase()) , mInputContext(new QCocoaInputContext) @@ -226,6 +227,10 @@ QCocoaIntegration::QCocoaIntegration() , mServices(new QCocoaServices) , mKeyboardMapper(new QCocoaKeyMapper) { + if (mInstance != 0) + qWarning("Creating multiple Cocoa platform integrations is not supported"); + mInstance = this; + initResources(); QCocoaAutoReleasePool pool; @@ -273,6 +278,8 @@ QCocoaIntegration::QCocoaIntegration() QCocoaIntegration::~QCocoaIntegration() { + mInstance = 0; + qt_resetNSApplicationSendEvent(); QCocoaAutoReleasePool pool; @@ -296,6 +303,11 @@ QCocoaIntegration::~QCocoaIntegration() } } +QCocoaIntegration *QCocoaIntegration::instance() +{ + return mInstance; +} + /*! \brief Synchronizes the screen list, adds new screens, removes deleted ones */ @@ -388,22 +400,22 @@ QAbstractEventDispatcher *QCocoaIntegration::createEventDispatcher() const return new QCocoaEventDispatcher; } -QPlatformFontDatabase *QCocoaIntegration::fontDatabase() const +QCoreTextFontDatabase *QCocoaIntegration::fontDatabase() const { return mFontDb.data(); } -QPlatformNativeInterface *QCocoaIntegration::nativeInterface() const +QCocoaNativeInterface *QCocoaIntegration::nativeInterface() const { return mNativeInterface.data(); } -QPlatformInputContext *QCocoaIntegration::inputContext() const +QCocoaInputContext *QCocoaIntegration::inputContext() const { return mInputContext.data(); } -QPlatformAccessibility *QCocoaIntegration::accessibility() const +QCocoaAccessibility *QCocoaIntegration::accessibility() const { #ifndef QT_NO_ACCESSIBILITY return mAccessibility.data(); @@ -412,12 +424,12 @@ QPlatformAccessibility *QCocoaIntegration::accessibility() const #endif } -QPlatformClipboard *QCocoaIntegration::clipboard() const +QCocoaClipboard *QCocoaIntegration::clipboard() const { return mCocoaClipboard; } -QPlatformDrag *QCocoaIntegration::drag() const +QCocoaDrag *QCocoaIntegration::drag() const { return mCocoaDrag.data(); } @@ -434,7 +446,7 @@ QPlatformTheme *QCocoaIntegration::createPlatformTheme(const QString &name) cons return QPlatformIntegration::createPlatformTheme(name); } -QPlatformServices *QCocoaIntegration::services() const +QCocoaServices *QCocoaIntegration::services() const { return mServices.data(); } diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index 5c59c73847..4bcb348acb 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -137,6 +137,7 @@ private: static void setContentBorderThickness(QWindow *window, int topThickness, int bottomThickness); }; +QT_END_NAMESPACE + #endif // QCOCOANATIVEINTERFACE_H -QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index e4237c9b3e..d60cdf10d1 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -73,6 +73,7 @@ public: QPlatformTheme::IconOptions options = 0) const; QVariant themeHint(ThemeHint hint) const; + QString standardButtonText(int button) const Q_DECL_OVERRIDE; static const char *name; diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index d863861288..109649f24e 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -42,6 +42,7 @@ #import <Cocoa/Cocoa.h> #include "qcocoatheme.h" +#include "messages.h" #include <QtCore/QVariant> @@ -300,6 +301,11 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const return QPlatformTheme::themeHint(hint); } +QString QCocoaTheme::standardButtonText(int button) const +{ + return button == QMessageDialogOptions::Discard ? msgDialogButtonDiscard() : QPlatformTheme::standardButtonText(button); +} + QPlatformMenuItem *QCocoaTheme::createPlatformMenuItem() const { return new QCocoaMenuItem(); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 452be90108..7b9768fcd9 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -52,34 +52,32 @@ QT_FORWARD_DECLARE_CLASS(QCocoaWindow) -@interface QNSWindow : NSWindow { - @public QCocoaWindow *m_cocoaPlatformWindow; -} - -- (void)clearPlatformWindow; -- (BOOL)canBecomeKeyWindow; -@end +@class QNSWindowDelegate; -@interface QNSPanel : NSPanel { - @public QCocoaWindow *m_cocoaPlatformWindow; +@interface QNSWindow : NSPanel { +@public + QCocoaWindow *m_cocoaPlatformWindow; } + - (void)clearPlatformWindow; -- (BOOL)canBecomeKeyWindow; @end -@class QNSWindowDelegate; - QT_BEGIN_NAMESPACE + // QCocoaWindow // -// 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. +// A QCocoaWindow is backed by a NSView and optionally a NSWindow. +// +// The NSView is used for most event handling and graphics output. // -// 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(): +// 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(): // // QWindow *qtWindow = new MyWindow(); // qtWindow->create(); @@ -100,6 +98,10 @@ public: void setGeometry(const QRect &rect); void setCocoaGeometry(const QRect &rect); + void clipChildWindows(); + void clipWindow(const NSRect &clipRect); + void show(bool becauseOfAncestor = false); + void hide(bool becauseOfAncestor = false); void setVisible(bool visible); void setWindowFlags(Qt::WindowFlags flags); void setWindowState(Qt::WindowState state); @@ -135,6 +137,7 @@ public: void windowDidResize(); bool windowShouldClose(); bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const; + bool windowShouldBehaveAsPanel() const; void setSynchedWindowStateFromWindow(); @@ -167,16 +170,16 @@ public: void updateExposedGeometry(); QWindow *childWindowAt(QPoint windowPoint); protected: - // NSWindow handling. The QCocoaWindow/QNSView can either be displayed - // in an existing NSWindow or in one created by Qt. void recreateWindow(const QPlatformWindow *parentWindow); - NSWindow *createNSWindow(); - void setNSWindow(NSWindow *window); - void clearNSWindow(NSWindow *window); + QNSWindow *createNSWindow(); + void setNSWindow(QNSWindow *window); + void clearNSWindow(QNSWindow *window); QRect windowGeometry() const; QCocoaWindow *parentCocoaWindow() const; void syncWindowState(Qt::WindowState newState); + void reinsertChildWindow(QCocoaWindow *child); + void removeChildWindow(QCocoaWindow *child); // private: public: // for QNSView @@ -185,12 +188,17 @@ public: // for QNSView NSView *m_contentView; QNSView *m_qtView; - NSWindow *m_nsWindow; + QNSWindow *m_nsWindow; + QCocoaWindow *m_forwardWindow; // TODO merge to one variable if possible bool m_contentViewIsEmbedded; // true if the m_contentView is actually embedded in a "foreign" NSView hiearchy bool m_contentViewIsToBeEmbedded; // true if the m_contentView is intended to be embedded in a "foreign" NSView hiearchy + QCocoaWindow *m_parentCocoaWindow; + bool m_isNSWindowChild; // this window is a non-top level QWindow with a NSWindow. + QList<QCocoaWindow *> m_childWindows; + QNSWindowDelegate *m_nsWindowDelegate; Qt::WindowFlags m_windowFlags; Qt::WindowState m_synchedWindowState; @@ -211,6 +219,8 @@ public: // for QNSView QRect m_exposedGeometry; int m_registerTouchCount; bool m_resizableTransientParent; + bool m_hiddenByClipping; + bool m_hiddenByAncestor; static const int NoAlertRequest; NSInteger m_alertRequest; @@ -219,6 +229,11 @@ public: // for QNSView bool m_drawContentBorderGradient; int m_topContentBorderThickness; int m_bottomContentBorderThickness; + + // used by showFullScreen in fake mode + QRect m_normalGeometry; + Qt::WindowFlags m_oldWindowFlags; + NSApplicationPresentationOptions m_presentationOptions; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 211ecd60ab..70a08bbea5 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -80,15 +80,10 @@ 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 @@ -104,11 +99,40 @@ 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 { - // 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. + // 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; + + // 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. return YES; } @@ -118,7 +142,11 @@ 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; + + if (m_cocoaPlatformWindow && m_cocoaPlatformWindow->windowShouldBehaveAsPanel()) canBecomeMain = NO; return canBecomeMain; @@ -126,47 +154,24 @@ static bool isMouseEvent(NSEvent *ev) - (void) sendEvent: (NSEvent*) theEvent { - [super sendEvent: 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]; + } - if (!m_cocoaPlatformWindow) - return; + 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]; + if (theEvent.type == NSLeftMouseDown) { + m_cocoaPlatformWindow->m_forwardWindow = 0; } } -} - -- (void)clearPlatformWindow -{ - m_cocoaPlatformWindow = 0; -} - -@end - -@implementation QNSPanel - -- (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) @@ -199,8 +204,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 +223,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; @@ -259,8 +270,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 +296,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 +323,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 +342,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 +499,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 +526,27 @@ void QCocoaWindow::setVisible(bool visible) // qDebug() << "close" << this; if (m_glContext) m_glContext->windowWasHidden(); + QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher()); + Q_ASSERT(cocoaEventDispatcher != 0); + QCocoaEventDispatcherPrivate *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()); 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->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 +653,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]; @@ -532,15 +665,21 @@ 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) { - Qt::WindowType type = window()->type(); - if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { - NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; + NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; + if (windowShouldBehaveAsPanel()) { + behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; + behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; + } else { 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 } @@ -618,16 +757,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 @@ -773,6 +951,9 @@ void QCocoaWindow::windowWillMove() void QCocoaWindow::windowDidMove() { + if (m_isNSWindowChild) + return; + [m_qtView updateGeometry]; } @@ -781,6 +962,10 @@ void QCocoaWindow::windowDidResize() if (!m_nsWindow) return; + if (m_isNSWindowChild) + return; + + clipChildWindows(); [m_qtView updateGeometry]; } @@ -808,6 +993,14 @@ 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; @@ -820,8 +1013,18 @@ QCocoaGLContext *QCocoaWindow::currentContext() const void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) { + bool wasNSWindowChild = m_isNSWindowChild; + bool needsNSWindow = m_isNSWindowChild || !parentWindow; + + QCocoaWindow *oldParentCocoaWindow = m_parentCocoaWindow; + m_parentCocoaWindow = const_cast<QCocoaWindow *>(static_cast<const QCocoaWindow *>(parentWindow)); + + // 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) { clearNSWindow(m_nsWindow); [m_nsWindow close]; [m_nsWindow release]; @@ -830,22 +1033,64 @@ 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]; @@ -857,6 +1102,19 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) setOpacity(opacity); } +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() { NSWindow *window = [m_contentView window]; @@ -864,63 +1122,33 @@ void QCocoaWindow::requestActivateWindow() [ window makeKeyWindow ]; } -NSWindow * QCocoaWindow::createNSWindow() +QNSWindow * 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 = 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) { - 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.). - 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) { - // 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 - window->m_cocoaPlatformWindow = this; - createdWindow = window; + NSUInteger styleMask; + if (m_isNSWindowChild) { + styleMask = NSBorderlessWindowMask; } 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; - - createdWindow = window; + styleMask = windowStyleMask(flags); } + QNSWindow *createdWindow = [[QNSWindow alloc] initWithContentRect:frame styleMask:styleMask qPlatformWindow:this]; + + Qt::WindowFlags type = window()->type(); + createdWindow.hidesOnDeactivate = type == Qt::Tool || type == Qt::ToolTip; + #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([createdWindow respondsToSelector:@selector(setRestorable:)]) + if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { [createdWindow setRestorable: NO]; + } #endif - NSInteger level = windowLevel(flags); - [createdWindow setLevel:level]; - if (window()->format().alphaBufferSize() > 0) { [createdWindow setBackgroundColor:[NSColor clearColor]]; [createdWindow setOpaque:NO]; @@ -933,10 +1161,12 @@ NSWindow * QCocoaWindow::createNSWindow() return createdWindow; } -void QCocoaWindow::setNSWindow(NSWindow *window) +void QCocoaWindow::setNSWindow(QNSWindow *window) { - m_nsWindowDelegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this]; - [window setDelegate:m_nsWindowDelegate]; + if (!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 @@ -944,31 +1174,33 @@ 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) +void QCocoaWindow::clearNSWindow(QNSWindow *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 +1247,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 +1308,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) diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index ed9aad1654..58c732de98 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -229,7 +229,21 @@ static QTouchDevice *touchDevice = 0; - (void)updateGeometry { QRect geometry; - if (m_platformWindow->m_nsWindow) { + + if (m_platformWindow->m_isNSWindowChild) { + return; +#if 0 + //geometry = qt_mac_toQRect([self frame]); + qDebug() << "nsview updateGeometry" << m_platformWindow->window(); + QRect screenRect = qt_mac_toQRect([m_platformWindow->m_nsWindow convertRectToScreen:[self frame]]); + qDebug() << "screenRect" << screenRect; + + screenRect.moveTop(qt_mac_flipYCoordinate(screenRect.y() + screenRect.height())); + geometry = QRect(m_platformWindow->window()->parent()->mapFromGlobal(screenRect.topLeft()), screenRect.size()); + qDebug() << "geometry" << geometry; +#endif + //geometry = QRect(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y + screenRect.size.height), screenRect.size.width, screenRect.size.height); + } else if (m_platformWindow->m_nsWindow) { // top level window, get window rect and flip y. NSRect rect = [self frame]; NSRect windowRect = [[self window] frame]; @@ -310,10 +324,9 @@ static QTouchDevice *touchDevice = 0; m_platformWindow->exposeWindow(); } else if (notificationName == NSWindowDidChangeScreenNotification) { if (m_window) { - QCocoaIntegration *ci = static_cast<QCocoaIntegration *>(QGuiApplicationPrivate::platformIntegration()); NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen]; if (screenIndex != NSNotFound) { - QCocoaScreen *cocoaScreen = ci->screenAtIndex(screenIndex); + QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenAtIndex(screenIndex); QWindowSystemInterface::handleWindowScreenChanged(m_window, cocoaScreen->screen()); } } @@ -551,14 +564,20 @@ static QTouchDevice *touchDevice = 0; QPointF qtWindowPoint; QPointF qtScreenPoint; - [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + QNSView *targetView = self; + if (m_platformWindow && m_platformWindow->m_forwardWindow + && (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp)) { + targetView = m_platformWindow->m_forwardWindow->m_qtView; + } + + [targetView convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; ulong timestamp = [theEvent timestamp] * 1000; - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); nativeDrag->setLastMouseEvent(theEvent, self); Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; - QWindowSystemInterface::handleMouseEvent(m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers); + QWindowSystemInterface::handleMouseEvent(targetView->m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers); } - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent @@ -689,8 +708,18 @@ static QTouchDevice *touchDevice = 0; -(void)cursorUpdate:(NSEvent *)theEvent { Q_UNUSED(theEvent) - if (m_platformWindow->m_windowCursor) + // Set the cursor manually if there is no NSWindow. + if (!m_platformWindow->m_nsWindow && m_platformWindow->m_windowCursor) [m_platformWindow->m_windowCursor set]; + else + [super cursorUpdate:theEvent]; +} + +-(void)resetCursorRects +{ + // Use the cursor rect API if there is a NSWindow + if (m_platformWindow->m_nsWindow && m_platformWindow->m_windowCursor) + [self addCursorRect:[self visibleRect] cursor:m_platformWindow->m_windowCursor]; } - (void)mouseMoved:(NSEvent *)theEvent @@ -1619,7 +1648,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (NSDragOperation) draggingSourceOperationMaskForLocal:(BOOL)isLocal { Q_UNUSED(isLocal); - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); return qt_mac_mapDropActions(nativeDrag->currentDrag()->supportedActions()); } @@ -1650,7 +1679,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) QPlatformDragQtResponse response(false, Qt::IgnoreAction, QRect()); if ([sender draggingSource] != nil) { - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); response = QWindowSystemInterface::handleDrag(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed); } else { QCocoaDropData mimeData([sender draggingPasteboard]); @@ -1678,14 +1707,14 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) QPlatformDropQtResponse response(false, Qt::IgnoreAction); if ([sender draggingSource] != nil) { - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); response = QWindowSystemInterface::handleDrop(m_window, nativeDrag->platformDropData(), qt_windowPoint, qtAllowed); } else { QCocoaDropData mimeData([sender draggingPasteboard]); response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed); } if (response.isAccepted()) { - QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); + QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag(); nativeDrag->setAcceptedAction(response.acceptedAction()); } return response.isAccepted(); diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index e8f26aa8c4..a438950a55 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -45,7 +45,7 @@ #include "qcocoahelpers.h" #include "qcocoaaccessibility.h" #include "qcocoaaccessibilityelement.h" -#include <qpa/qplatformintegration.h> +#include "qcocoaintegration.h" #include <QtGui/qaccessible.h> #include <QtCore/QDebug> @@ -63,7 +63,7 @@ - (id)accessibilityAttributeValue:(NSString *)attribute { // activate accessibility updates - QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true); + QCocoaIntegration::instance()->accessibility()->setActive(true); if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { if (m_window->accessibleRoot()) diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index f363b1772f..3e92a45a62 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -591,20 +591,43 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va return; switch (key) { - case PPK_CollateCopies: + + // The following keys are properties or derived values and so cannot be set + case PPK_PageRect: + break; + case PPK_PaperRect: + break; + case PPK_PaperSources: + break; + case PPK_SupportsMultipleCopies: + break; + case PPK_SupportedResolutions: break; + + // The following keys are settings that are unsupported by the Mac PrintEngine case PPK_ColorMode: break; - case PPK_Creator: + case PPK_CustomBase: break; - case PPK_DocumentName: + case PPK_Duplex: + // TODO Add support using PMSetDuplex / PMGetDuplex + break; + case PPK_FontEmbedding: break; case PPK_PageOrder: + // TODO Check if can be supported via Cups Options break; case PPK_PaperSource: + // TODO Check if can be supported via Cups Options + break; + case PPK_PrinterProgram: break; case PPK_SelectionOption: break; + case PPK_WindowsPageSize: + break; + + // The following keys are properties and settings that are supported by the Mac PrintEngine case PPK_Resolution: { PMPrinter printer; UInt32 count; @@ -633,7 +656,15 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); break; } - + case PPK_CollateCopies: + PMSetCollate(d->settings(), value.toBool()); + break; + case PPK_Creator: + d->m_creator = value.toString(); + break; + case PPK_DocumentName: + PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString())); + break; case PPK_FullPage: d->fullPage = value.toBool(); break; @@ -642,18 +673,15 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va PMSetCopies(d->settings(), value.toInt(), false); break; case PPK_Orientation: { - if (d->state == QPrinter::Active) { - qWarning("QMacPrintEngine::setOrientation: Orientation cannot be changed during a print job, ignoring change"); - } else { - QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt()); - if (d->hasCustomPaperSize && (d->orient != newOrientation)) - d->customSize = QSizeF(d->customSize.height(), d->customSize.width()); - d->orient = newOrientation; - PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape; - PMSetOrientation(d->format(), o, false); - PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); - } - break; } + QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt()); + if (d->hasCustomPaperSize && (d->orient != newOrientation)) + d->customSize = QSizeF(d->customSize.height(), d->customSize.width()); + d->orient = newOrientation; + PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape; + PMSetOrientation(d->format(), o, false); + PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); + break; + } case PPK_OutputFileName: d->outputFilename = value.toString(); break; @@ -709,9 +737,7 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va d->hasCustomPageMargins = true; break; } - - default: - break; + // No default so that compiler will complain if new keys added and not handled in this engine } } @@ -724,16 +750,63 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const return *d->valueCache.find(key); switch (key) { - case PPK_CollateCopies: - ret = false; - break; + + // The following keys are settings that are unsupported by the Mac PrintEngine + // Return sensible default values to ensure consistent behavior across platforms case PPK_ColorMode: ret = QPrinter::Color; break; + case PPK_CustomBase: + // Special case, leave null + break; + case PPK_Duplex: + // TODO Add support using PMSetDuplex / PMGetDuplex + ret = QPrinter::DuplexNone; + break; + case PPK_FontEmbedding: + ret = false; + break; + case PPK_PageOrder: + // TODO Check if can be supported via Cups Options + ret = QPrinter::FirstPageFirst; + break; + case PPK_PaperSource: + // TODO Check if can be supported via Cups Options + ret = QPrinter::Auto; + break; + case PPK_PaperSources: { + // TODO Check if can be supported via Cups Options + QList<QVariant> out; + out << int(QPrinter::Auto); + ret = out; + break; + } + case PPK_PrinterProgram: + ret = QString(); + break; + case PPK_SelectionOption: + ret = QString(); + break; + case PPK_WindowsPageSize: + // Special case, leave null + break; + + // The following keys are properties and settings that are supported by the Mac PrintEngine + case PPK_CollateCopies: { + Boolean status; + PMGetCollate(d->settings(), &status); + ret = bool(status); + break; + } case PPK_Creator: + ret = d->m_creator; break; - case PPK_DocumentName: + case PPK_DocumentName: { + CFStringRef name; + PMPrintSettingsGetJobName(d->settings(), &name); + ret = QCFString::toQString(name); break; + } case PPK_FullPage: ret = d->fullPage; break; @@ -757,10 +830,6 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const case PPK_OutputFileName: ret = d->outputFilename; break; - case PPK_PageOrder: - break; - case PPK_PaperSource: - break; case PPK_PageRect: { // PageRect is returned in device pixels QRect r; @@ -855,8 +924,7 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const ret = margins; break; } - default: - break; + // No default so that compiler will complain if new keys added and not handled in this engine } return ret; } diff --git a/src/plugins/platforms/cocoa/qprintengine_mac_p.h b/src/plugins/platforms/cocoa/qprintengine_mac_p.h index 644a07184f..e3a8520811 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac_p.h +++ b/src/plugins/platforms/cocoa/qprintengine_mac_p.h @@ -125,6 +125,7 @@ public: NSPrintInfo *printInfo; PMResolution resolution; QString outputFilename; + QString m_creator; bool fullPage; QPaintEngine *paintEngine; bool hasCustomPaperSize; |