summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/cocoa.pro2
-rw-r--r--src/plugins/platforms/cocoa/images/copyarrowcursor.pngbin1976 -> 0 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/forbiddencursor.pngbin1745 -> 0 bytes
-rw-r--r--src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.pngbin356 -> 0 bytes
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplication.mm24
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm3
-rw-r--r--src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm14
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoadrag.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm3
-rw-r--r--src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm14
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm3
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h14
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm110
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm7
-rw-r--r--src/plugins/platforms/cocoa/qcocoaresources.qrc17
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h84
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm959
-rw-r--r--src/plugins/platforms/cocoa/qnsview.h9
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm146
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.h8
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm73
27 files changed, 902 insertions, 631 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index 0664841c2d..62935210be 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -86,6 +86,8 @@ QT += \
accessibility_support-private clipboard_support-private theme_support-private \
fontdatabase_support-private graphics_support-private cgl_support-private
+CONFIG += no_app_extension_api_only
+
qtHaveModule(widgets) {
OBJECTIVE_SOURCES += \
qpaintengine_mac.mm \
diff --git a/src/plugins/platforms/cocoa/images/copyarrowcursor.png b/src/plugins/platforms/cocoa/images/copyarrowcursor.png
deleted file mode 100644
index 13dfca95bc..0000000000
--- a/src/plugins/platforms/cocoa/images/copyarrowcursor.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/forbiddencursor.png b/src/plugins/platforms/cocoa/images/forbiddencursor.png
deleted file mode 100644
index a9f21b4a5e..0000000000
--- a/src/plugins/platforms/cocoa/images/forbiddencursor.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png b/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png
deleted file mode 100644
index 6716597046..0000000000
--- a/src/plugins/platforms/cocoa/images/leopard-unified-toolbar-on.png
+++ /dev/null
Binary files differ
diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm
index c5ae4bc2bf..3b950efa55 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplication.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm
@@ -76,6 +76,7 @@
#include "qcocoaintrospection.h"
#include "qcocoaapplicationdelegate.h"
#include "qcocoahelpers.h"
+#include "qcocoawindow.h"
#include <qguiapplication.h>
#include <qdebug.h>
@@ -148,6 +149,21 @@ static const QByteArray q_macLocalEventType = QByteArrayLiteral("mac_generic_NSE
@end
+static void qt_maybeSendKeyEquivalentUpEvent(NSEvent *event)
+{
+ // Cocoa is known for not sending key up events for key
+ // equivalents, regardless of whether it's an actual
+ // recognized key equivalent. We decide to force fate
+ // and forward the key event to the key (focus) window.
+ // However, non-Qt windows will not (and should not) get
+ // any special treatment, only QWindow-owned NSWindows.
+ if (event.type == NSKeyUp && (event.modifierFlags & NSCommandKeyMask)) {
+ NSWindow *targetWindow = event.window;
+ if ([targetWindow.class conformsToProtocol:@protocol(QNSWindowProtocol)])
+ [targetWindow sendEvent:event];
+ }
+}
+
@implementation QT_MANGLE_NAMESPACE(QNSApplication)
- (void)QT_MANGLE_NAMESPACE(qt_sendEvent_original):(NSEvent *)event
@@ -164,16 +180,20 @@ static const QByteArray q_macLocalEventType = QByteArrayLiteral("mac_generic_NSE
// be called instead of sendEvent if redirection occurs.
// 'self' will then be an instance of NSApplication
// (and not QNSApplication)
- if (![NSApp QT_MANGLE_NAMESPACE(qt_filterEvent):event])
+ if (![NSApp QT_MANGLE_NAMESPACE(qt_filterEvent):event]) {
[self QT_MANGLE_NAMESPACE(qt_sendEvent_original):event];
+ qt_maybeSendKeyEquivalentUpEvent(event);
+ }
}
- (void)sendEvent:(NSEvent *)event
{
// This method will be called if
// no redirection occurs
- if (![NSApp QT_MANGLE_NAMESPACE(qt_filterEvent):event])
+ if (![NSApp QT_MANGLE_NAMESPACE(qt_filterEvent):event]) {
[super sendEvent:event];
+ qt_maybeSendKeyEquivalentUpEvent(event);
+ }
}
@end
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index a74995319b..1d7ad772dc 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -57,7 +57,8 @@ QCocoaBackingStore::~QCocoaBackingStore()
QImage::Format QCocoaBackingStore::format() const
{
- if (static_cast<QCocoaWindow *>(window()->handle())->m_drawContentBorderGradient)
+ QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle());
+ if (cocoaWindow && cocoaWindow->m_drawContentBorderGradient)
return QImage::Format_ARGB32_Premultiplied;
return QRasterBackingStore::format();
diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
index e53c085e41..a8974c4de5 100644
--- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
@@ -80,7 +80,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
mHelper = 0;
mStolenContentView = 0;
mPanelButtons = nil;
- mResultCode = NSCancelButton;
+ mResultCode = NSModalResponseCancel;
mDialogIsExecuting = false;
mResultSet = false;
mClosingDueToKnownButton = false;
@@ -168,7 +168,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
mClosingDueToKnownButton = true;
[mColorPanel close];
[self updateQtColor];
- [self finishOffWithCode:NSOKButton];
+ [self finishOffWithCode:NSModalResponseOK];
}
- (void)onCancelClicked
@@ -177,7 +177,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
mClosingDueToKnownButton = true;
[mColorPanel close];
mQtColor = QColor();
- [self finishOffWithCode:NSCancelButton];
+ [self finishOffWithCode:NSModalResponseCancel];
}
}
@@ -238,12 +238,12 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[NSApp runModalForWindow:mColorPanel];
mDialogIsExecuting = false;
- return (mResultCode == NSOKButton);
+ return (mResultCode == NSModalResponseOK);
}
- (QPlatformDialogHelper::DialogCode)dialogResultCode
{
- return (mResultCode == NSOKButton) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
+ return (mResultCode == NSModalResponseOK) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
}
- (BOOL)windowShouldClose:(id)window
@@ -252,7 +252,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
if (!mPanelButtons)
[self updateQtColor];
if (mDialogIsExecuting) {
- [self finishOffWithCode:NSCancelButton];
+ [self finishOffWithCode:NSModalResponseCancel];
} else {
mResultSet = true;
if (mHelper)
@@ -278,7 +278,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
// This check will prevent any such recursion.
if (!mResultSet) {
mResultSet = true;
- if (mResultCode == NSCancelButton) {
+ if (mResultCode == NSModalResponseCancel) {
emit mHelper->reject();
} else {
emit mHelper->accept();
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index 3df2a7c962..99a136d384 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -97,6 +97,9 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
case Qt::ArrowCursor:
cocoaCursor= [NSCursor arrowCursor];
break;
+ case Qt::ForbiddenCursor:
+ cocoaCursor = [NSCursor operationNotAllowedCursor];
+ break;
case Qt::CrossCursor:
cocoaCursor = [NSCursor crosshairCursor];
break;
@@ -123,7 +126,7 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
cocoaCursor = [NSCursor crosshairCursor];
break;
case Qt::DragCopyCursor:
- cocoaCursor = [NSCursor crosshairCursor];
+ cocoaCursor = [NSCursor dragCopyCursor];
break;
case Qt::DragLinkCursor:
cocoaCursor = [NSCursor dragLinkCursor];
@@ -235,10 +238,6 @@ NSCursor *QCocoaCursor::createCursorData(QCursor *cursor)
QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/waitcursor.png"));
return createCursorFromPixmap(pixmap, hotspot);
break; }
- case Qt::ForbiddenCursor: {
- QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/forbiddencursor.png"));
- return createCursorFromPixmap(pixmap, hotspot);
- break; }
#define QT_USE_APPROXIMATE_CURSORS
#ifdef QT_USE_APPROXIMATE_CURSORS
case Qt::SizeVerCursor:
diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm
index a0967750e7..c71e80d191 100644
--- a/src/plugins/platforms/cocoa/qcocoadrag.mm
+++ b/src/plugins/platforms/cocoa/qcocoadrag.mm
@@ -132,7 +132,7 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
QPixmap pm = dragPixmap(m_drag, hotSpot);
QSize pmDeviceIndependentSize = pm.size() / pm.devicePixelRatio();
NSImage *nsimage = qt_mac_create_nsimage(pm);
- [nsimage setSize:pmDeviceIndependentSize.toCGSize()];
+ [nsimage setSize:NSSizeFromCGSize(pmDeviceIndependentSize.toCGSize())];
QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacInternalPasteboardMime::MIME_DND);
m_drag->mimeData()->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 72c7856c2d..d2f985ec87 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -401,7 +401,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
// [NSApp run], which is the normal code path for cocoa applications.
if (NSModalSession session = d->currentModalSession()) {
QBoolBlocker execGuard(d->currentExecIsNSAppRun, false);
- while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt)
+ while ([NSApp runModalSession:session] == NSModalResponseContinue && !d->interrupt)
qt_mac_waitForMoreEvents(NSModalPanelRunLoopMode);
if (!d->interrupt && session == d->currentModalSessionCached) {
@@ -435,7 +435,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if (flags & QEventLoop::WaitForMoreEvents)
qt_mac_waitForMoreEvents(NSModalPanelRunLoopMode);
NSInteger status = [NSApp runModalSession:session];
- if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) {
+ if (status != NSModalResponseContinue && session == d->currentModalSessionCached) {
// INVARIANT: Someone called [NSApp stopModal:] from outside the event
// dispatcher (e.g to stop a native dialog). But that call wrongly stopped
// 'session' as well. As a result, we need to restart all internal sessions:
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index 234da57f59..41a809cdd2 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -63,6 +63,7 @@
#include <stdlib.h>
#include <qabstracteventdispatcher.h>
#include <qsysinfo.h>
+#include <qoperatingsystemversion.h>
#include <qglobal.h>
#include <QDir>
@@ -164,7 +165,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate);
[mSavePanel setDelegate:self];
#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_11)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_11)
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::OSXElCapitan)
mOpenPanel.accessoryViewDisclosed = YES;
#endif
diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
index 33dd4260a5..e4b796dcde 100644
--- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
@@ -106,7 +106,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
mHelper = 0;
mStolenContentView = 0;
mPanelButtons = 0;
- mResultCode = NSCancelButton;
+ mResultCode = NSModalResponseCancel;
mDialogIsExecuting = false;
mResultSet = false;
@@ -171,7 +171,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
- (void)onOkClicked
{
[mFontPanel close];
- [self finishOffWithCode:NSOKButton];
+ [self finishOffWithCode:NSModalResponseOK];
}
- (void)onCancelClicked
@@ -179,7 +179,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
if (mPanelButtons) {
[mFontPanel close];
mQtFont = QFont();
- [self finishOffWithCode:NSCancelButton];
+ [self finishOffWithCode:NSModalResponseCancel];
}
}
@@ -224,12 +224,12 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[NSApp runModalForWindow:mFontPanel];
mDialogIsExecuting = false;
- return (mResultCode == NSOKButton);
+ return (mResultCode == NSModalResponseOK);
}
- (QPlatformDialogHelper::DialogCode)dialogResultCode
{
- return (mResultCode == NSOKButton) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
+ return (mResultCode == NSModalResponseOK) ? QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
}
- (BOOL)windowShouldClose:(id)window
@@ -238,7 +238,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
if (!mPanelButtons)
[self updateQtFont];
if (mDialogIsExecuting) {
- [self finishOffWithCode:NSCancelButton];
+ [self finishOffWithCode:NSModalResponseCancel];
} else {
mResultSet = true;
if (mHelper)
@@ -264,7 +264,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
// This check will prevent any such recursion.
if (!mResultSet) {
mResultSet = true;
- if (mResultCode == NSCancelButton) {
+ if (mResultCode == NSModalResponseCancel) {
emit mHelper->reject();
} else {
emit mHelper->accept();
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index a7cc19b3bf..9e688f4d1b 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -255,7 +255,8 @@ void QCocoaGLContext::setActiveWindow(QWindow *window)
QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
cocoaWindow->setCurrentContext(this);
- [(QNSView *) cocoaWindow->view() setQCocoaGLContext:this];
+ Q_ASSERT(!cocoaWindow->isForeignWindow());
+ [qnsview_cast(cocoaWindow->view()) setQCocoaGLContext:this];
}
void QCocoaGLContext::updateSurfaceFormat()
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 3ab6b641fa..232e40769b 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -151,7 +151,8 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
a no-op.
For extra verbosity and clearer code, please consider checking
- that window()->type() != Qt::ForeignWindow before using this cast.
+ that the platform window is not a foreign window before using
+ this cast, via QPlatformWindow::isForeignWindow().
Do not use this method soley to check for foreign windows, as
that will make the code harder to read for people not working
@@ -160,10 +161,8 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
*/
QNSView *qnsview_cast(NSView *view)
{
- if (![view isKindOfClass:[QNSView class]]) {
- qCWarning(lcQpaCocoaWindow) << "NSView is not QNSView, consider checking for Qt::ForeignWindow";
+ if (![view isKindOfClass:[QNSView class]])
return nil;
- }
return static_cast<QNSView *>(view);
}
@@ -235,13 +234,13 @@ QString qt_mac_applicationName()
return appName;
}
-int qt_mac_mainScreenHeight()
+int qt_mac_primaryScreenHeight()
{
QMacAutoReleasePool pool;
NSArray *screens = [NSScreen screens];
if ([screens count] > 0) {
- // The first screen in the screens array is documented
- // to have the (0,0) origin.
+ // The first screen in the screens array is documented to
+ // have the (0,0) origin and is designated the primary screen.
NSRect screenFrame = [[screens objectAtIndex: 0] frame];
return screenFrame.size.height;
}
@@ -250,12 +249,12 @@ int qt_mac_mainScreenHeight()
int qt_mac_flipYCoordinate(int y)
{
- return qt_mac_mainScreenHeight() - y;
+ return qt_mac_primaryScreenHeight() - y;
}
qreal qt_mac_flipYCoordinate(qreal y)
{
- return qt_mac_mainScreenHeight() - y;
+ return qt_mac_primaryScreenHeight() - y;
}
QPointF qt_mac_flipPoint(const NSPoint &p)
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index 32f6fe0af1..ecdd20c4dc 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -84,9 +84,18 @@ public:
// ----------------------------------------------------
// Additional methods
void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
- NSScreen *osScreen() const;
+ NSScreen *nativeScreen() const;
void updateGeometry();
+ QPointF mapToNative(const QPointF &pos) const { return flipCoordinate(pos); }
+ QRectF mapToNative(const QRectF &rect) const { return flipCoordinate(rect); }
+ QPointF mapFromNative(const QPointF &pos) const { return flipCoordinate(pos); }
+ QRectF mapFromNative(const QRectF &rect) const { return flipCoordinate(rect); }
+
+private:
+ QPointF flipCoordinate(const QPointF &pos) const;
+ QRectF flipCoordinate(const QRectF &rect) const;
+
public:
int m_screenIndex;
QRect m_geometry;
@@ -117,6 +126,7 @@ public:
bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
+ QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const Q_DECL_OVERRIDE;
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE;
#endif
@@ -144,7 +154,7 @@ public:
QList<int> possibleKeys(const QKeyEvent *event) const Q_DECL_OVERRIDE;
void updateScreens();
- QCocoaScreen *screenAtIndex(int index);
+ QCocoaScreen *screenForNSScreen(NSScreen *nsScreen);
void setToolbar(QWindow *window, NSToolbar *toolbar);
NSToolbar *toolbar(QWindow *window) const;
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 18340f4ee1..91f408e5c2 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -69,8 +69,8 @@ static void initResources()
QT_BEGIN_NAMESPACE
-QCocoaScreen::QCocoaScreen(int screenIndex) :
- QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
+QCocoaScreen::QCocoaScreen(int screenIndex)
+ : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
{
updateGeometry();
m_cursor = new QCocoaCursor;
@@ -81,41 +81,65 @@ QCocoaScreen::~QCocoaScreen()
delete m_cursor;
}
-NSScreen *QCocoaScreen::osScreen() const
+NSScreen *QCocoaScreen::nativeScreen() const
{
NSArray *screens = [NSScreen screens];
- return ((NSUInteger)m_screenIndex < [screens count]) ? [screens objectAtIndex:m_screenIndex] : nil;
+
+ // Stale reference, screen configuration has changed
+ if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
+ return nil;
+
+ return [screens objectAtIndex:m_screenIndex];
+}
+
+/*!
+ Flips the Y coordinate of the point between quadrant I and IV.
+
+ The native coordinate system on macOS uses quadrant I, with origin
+ in bottom left, and Qt uses quadrant IV, with origin in top left.
+
+ By flippig the Y coordinate, we can map the position between the
+ two coordinate systems.
+*/
+QPointF QCocoaScreen::flipCoordinate(const QPointF &pos) const
+{
+ return QPointF(pos.x(), m_geometry.height() - pos.y());
+}
+
+/*!
+ Flips the Y coordinate of the rectangle between quadrant I and IV.
+
+ The native coordinate system on macOS uses quadrant I, with origin
+ in bottom left, and Qt uses quadrant IV, with origin in top left.
+
+ By flippig the Y coordinate, we can map the rectangle between the
+ two coordinate systems.
+*/
+QRectF QCocoaScreen::flipCoordinate(const QRectF &rect) const
+{
+ return QRectF(flipCoordinate(rect.topLeft() + QPoint(0, rect.height())), rect.size());
}
void QCocoaScreen::updateGeometry()
{
- NSScreen *nsScreen = osScreen();
+ NSScreen *nsScreen = nativeScreen();
if (!nsScreen)
return;
- NSRect frameRect = [nsScreen frame];
+ // At this point the geometry is in native coordinates, but the size
+ // is correct, which we take advantage of next when we map the native
+ // coordinates to the Qt coordinate system.
+ m_geometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.frame)).toRect();
+ m_availableGeometry = QRectF::fromCGRect(NSRectToCGRect(nsScreen.visibleFrame)).toRect();
- if (m_screenIndex == 0) {
- m_geometry = QRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
- // This is the primary screen, the one that contains the menubar. Its origin should be
- // (0, 0), and it's the only one whose available geometry differs from its full geometry.
- NSRect visibleRect = [nsScreen visibleFrame];
- m_availableGeometry = QRect(visibleRect.origin.x,
- frameRect.size.height - (visibleRect.origin.y + visibleRect.size.height), // invert y
- visibleRect.size.width, visibleRect.size.height);
- } else {
- // NSScreen origin is at the bottom-left corner, QScreen is at the top-left corner.
- // When we get the NSScreen frame rect, we need to re-align its origin y coordinate
- // w.r.t. the primary screen, whose origin is (0, 0).
- NSRect r = [[[NSScreen screens] objectAtIndex:0] frame];
- QRect referenceScreenGeometry = QRect(r.origin.x, r.origin.y, r.size.width, r.size.height);
- m_geometry = QRect(frameRect.origin.x,
- referenceScreenGeometry.height() - (frameRect.origin.y + frameRect.size.height),
- frameRect.size.width, frameRect.size.height);
-
- // Not primary screen. See above.
- m_availableGeometry = m_geometry;
- }
+ // The reference screen for the geometry is always the primary screen, but since
+ // we may be in the process of creating and registering the primary screen, we
+ // must special-case that and assign it direcly.
+ QCocoaScreen *primaryScreen = (nsScreen == [[NSScreen screens] firstObject]) ?
+ this : static_cast<QCocoaScreen*>(QGuiApplication::primaryScreen()->handle());
+
+ m_geometry = primaryScreen->mapFromNative(m_geometry).toRect();
+ m_availableGeometry = primaryScreen->mapFromNative(m_availableGeometry).toRect();
m_format = QImage::Format_RGB32;
m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
@@ -147,8 +171,8 @@ void QCocoaScreen::updateGeometry()
qreal QCocoaScreen::devicePixelRatio() const
{
QMacAutoReleasePool pool;
- NSScreen * screen = osScreen();
- return qreal(screen ? [screen backingScaleFactor] : 1.0);
+ NSScreen *nsScreen = nativeScreen();
+ return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
}
QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
@@ -427,7 +451,7 @@ void QCocoaIntegration::updateScreens()
// NSScreen documentation says do not cache the array returned from [NSScreen screens].
// However in practice, we can identify a screen by its pointer: if resolution changes,
// the NSScreen object will be the same instance, just with different values.
- if (existingScr->osScreen() == scr) {
+ if (existingScr->nativeScreen() == scr) {
screen = existingScr;
break;
}
@@ -451,20 +475,27 @@ void QCocoaIntegration::updateScreens()
// Now the leftovers in remainingScreens are no longer current, so we can delete them.
foreach (QCocoaScreen* screen, remainingScreens) {
mScreens.removeOne(screen);
+ // Prevent stale references to NSScreen during destroy
+ screen->m_screenIndex = -1;
destroyScreen(screen);
}
}
-QCocoaScreen *QCocoaIntegration::screenAtIndex(int index)
+QCocoaScreen *QCocoaIntegration::screenForNSScreen(NSScreen *nsScreen)
{
- if (index >= mScreens.count())
+ NSUInteger index = [[NSScreen screens] indexOfObject:nsScreen];
+ if (index == NSNotFound)
+ return 0;
+
+ if (index >= unsigned(mScreens.count()))
updateScreens();
- // It is possible that the screen got removed while updateScreens was called
- // so we do a sanity check to be certain
- if (index >= mScreens.count())
- return 0;
- return mScreens.at(index);
+ for (QCocoaScreen *screen : mScreens) {
+ if (screen->nativeScreen() == nsScreen)
+ return screen;
+ }
+
+ return 0;
}
bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -493,6 +524,11 @@ QPlatformWindow *QCocoaIntegration::createPlatformWindow(QWindow *window) const
return new QCocoaWindow(window);
}
+QPlatformWindow *QCocoaIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const
+{
+ return new QCocoaWindow(window, nativeHandle);
+}
+
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index e177a24e73..8e47974d12 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -581,8 +581,8 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect,
// The calls above block, and also swallow any mouse release event,
// so we need to clear any mouse button that triggered the menu popup.
- if ([view isKindOfClass:[QNSView class]])
- [(QNSView *)view resetMouseButtons];
+ if (!cocoaWindow->isForeignWindow())
+ [qnsview_cast(view) resetMouseButtons];
}
void QCocoaMenu::dismiss()
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index 972230349b..26ab07ffaf 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -172,11 +172,12 @@ void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine
QPixmap QCocoaNativeInterface::defaultBackgroundPixmapForQWizard()
{
- QCFType<CFURLRef> url;
const int ExpectedImageWidth = 242;
const int ExpectedImageHeight = 414;
- if (LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("com.apple.KeyboardSetupAssistant"),
- 0, 0, &url) == noErr) {
+ QCFType<CFArrayRef> urls = LSCopyApplicationURLsForBundleIdentifier(
+ CFSTR("com.apple.KeyboardSetupAssistant"), nullptr);
+ if (urls && CFArrayGetCount(urls) > 0) {
+ CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(urls, 0);
QCFType<CFBundleRef> bundle = CFBundleCreate(kCFAllocatorDefault, url);
if (bundle) {
url = CFBundleCopyResourceURL(bundle, CFSTR("Background"), CFSTR("png"), 0);
diff --git a/src/plugins/platforms/cocoa/qcocoaresources.qrc b/src/plugins/platforms/cocoa/qcocoaresources.qrc
index 4255bfba9d..1c4b941b9b 100644
--- a/src/plugins/platforms/cocoa/qcocoaresources.qrc
+++ b/src/plugins/platforms/cocoa/qcocoaresources.qrc
@@ -1,12 +1,7 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource prefix="/qt-project.org/mac/cursors">
-<file>images/copyarrowcursor.png</file>
-<file>images/forbiddencursor.png</file>
-<file>images/spincursor.png</file>
-<file>images/waitcursor.png</file>
-<file>images/sizeallcursor.png</file>
-</qresource>
-<qresource prefix="/qt-project.org/mac/style">
-<file>images/leopard-unified-toolbar-on.png</file>
-</qresource>
+<RCC>
+ <qresource prefix="/qt-project.org/mac/cursors">
+ <file>images/spincursor.png</file>
+ <file>images/waitcursor.png</file>
+ <file>images/sizeallcursor.png</file>
+ </qresource>
</RCC>
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index d47e620fbb..27c071a8cd 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -74,6 +74,7 @@ public:
QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE;
QString standardButtonText(int button) const Q_DECL_OVERRIDE;
+ QKeySequence standardButtonShortcut(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 4d74c11581..d2345f9abc 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -344,6 +344,12 @@ QString QCocoaTheme::standardButtonText(int button) const
return button == QPlatformDialogHelper::Discard ? msgDialogButtonDiscard() : QPlatformTheme::standardButtonText(button);
}
+QKeySequence QCocoaTheme::standardButtonShortcut(int button) const
+{
+ return button == QPlatformDialogHelper::Discard ? QKeySequence(Qt::CTRL | Qt::Key_Delete)
+ : QPlatformTheme::standardButtonShortcut(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 16639fd8b1..567eb7438b 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -96,6 +96,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindowHelper);
@property (nonatomic, readonly) QNSWindowHelper *helper;
- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw;
@@ -111,6 +112,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow);
@property (nonatomic, readonly) QNSWindowHelper *helper;
- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
styleMask:(NSUInteger)windowStyle
qPlatformWindow:(QCocoaWindow *)qpw;
@@ -141,13 +143,20 @@ QT_BEGIN_NAMESPACE
// See the qt_on_cocoa manual tests for a working example, located
// in tests/manual/cocoa at the time of writing.
+#ifdef Q_MOC_RUN
+#define Q_NOTIFICATION_HANDLER(notification) Q_INVOKABLE Q_COCOA_NOTIFICATION_##notification
+#else
+#define Q_NOTIFICATION_HANDLER(notification)
+#define Q_NOTIFICATION_PREFIX QT_STRINGIFY2(Q_COCOA_NOTIFICATION_)
+#endif
+
class QCocoaMenuBar;
class QCocoaWindow : public QObject, public QPlatformWindow
{
Q_OBJECT
public:
- QCocoaWindow(QWindow *tlw);
+ QCocoaWindow(QWindow *tlw, WId nativeHandle = 0);
~QCocoaWindow();
void setGeometry(const QRect &rect) Q_DECL_OVERRIDE;
@@ -177,6 +186,8 @@ public:
QMargins frameMargins() const Q_DECL_OVERRIDE;
QSurfaceFormat format() const Q_DECL_OVERRIDE;
+ bool isForeignWindow() const Q_DECL_OVERRIDE;
+
void requestActivateWindow() Q_DECL_OVERRIDE;
WId winId() const Q_DECL_OVERRIDE;
@@ -187,15 +198,30 @@ public:
void setEmbeddedInForeignView(bool subwindow);
- void windowWillMove();
- void windowDidMove();
- void windowDidResize();
- void windowDidEndLiveResize();
+ Q_NOTIFICATION_HANDLER(NSWindowWillMoveNotification) void windowWillMove();
+ Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
+ Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
+ Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
+ Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
+ Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
+ Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
+ Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
+ Q_NOTIFICATION_HANDLER(NSWindowDidMiniaturizeNotification) void windowDidMiniaturize();
+ Q_NOTIFICATION_HANDLER(NSWindowDidDeminiaturizeNotification) void windowDidDeminiaturize();
+ Q_NOTIFICATION_HANDLER(NSWindowWillEnterFullScreenNotification) void windowWillEnterFullScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidEnterFullScreenNotification) void windowDidEnterFullScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowWillExitFullScreenNotification) void windowWillExitFullScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidExitFullScreenNotification) void windowDidExitFullScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidOrderOnScreenAndFinishAnimatingNotification) void windowDidOrderOnScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
+ Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
+ Q_NOTIFICATION_HANDLER(NSWindowWillCloseNotification) void windowWillClose();
+
bool windowShouldClose();
- void windowWillClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
- void setSynchedWindowStateFromWindow();
+ void reportCurrentWindowState(bool unconditionally = false);
NSInteger windowLevel(Qt::WindowFlags flags);
NSUInteger windowStyleMask(Qt::WindowFlags flags);
@@ -239,19 +265,37 @@ public:
static QPoint bottomLeftClippedByNSWindowOffsetStatic(QWindow *window);
QPoint bottomLeftClippedByNSWindowOffset() const;
+
+ enum RecreationReason {
+ RecreationNotNeeded = 0,
+ ParentChanged = 0x1,
+ MissingWindow = 0x2,
+ WindowModalityChanged = 0x4,
+ ChildNSWindowChanged = 0x8,
+ ContentViewChanged = 0x10,
+ PanelChanged = 0x20,
+ };
+ Q_DECLARE_FLAGS(RecreationReasons, RecreationReason)
+ Q_FLAG(RecreationReasons)
+
protected:
- void recreateWindow(const QPlatformWindow *parentWindow);
- QCocoaNSWindow *createNSWindow();
- void setNSWindow(QCocoaNSWindow *window);
+ bool isChildNSWindow() const;
+ bool isContentView() const;
+
+ void foreachChildNSWindow(void (^block)(QCocoaWindow *));
- bool shouldUseNSPanel();
+ void recreateWindowIfNeeded();
+ QCocoaNSWindow *createNSWindow(bool shouldBeChildNSWindow, bool shouldBePanel);
QRect nativeWindowGeometry() const;
- QCocoaWindow *parentCocoaWindow() const;
- void syncWindowState(Qt::WindowState newState);
void reinsertChildWindow(QCocoaWindow *child);
void removeChildWindow(QCocoaWindow *child);
- bool isNativeWindowTypeInconsistent();
+
+ Qt::WindowState windowState() const;
+ void applyWindowState(Qt::WindowState newState);
+ void toggleMaximized();
+ void toggleFullScreen();
+ bool isTransitioningToFullScreen() const;
// private:
public: // for QNSView
@@ -268,13 +312,8 @@ public: // for QNSView
bool m_viewIsEmbedded; // true if the m_view is actually embedded in a "foreign" NSView hiearchy
bool m_viewIsToBeEmbedded; // true if the m_view 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;
-
Qt::WindowFlags m_windowFlags;
- bool m_effectivelyMaximized;
- Qt::WindowState m_synchedWindowState;
+ Qt::WindowState m_lastReportedWindowState;
Qt::WindowModality m_windowModality;
QPointer<QWindow> m_enterLeaveTargetWindow;
bool m_windowUnderMouse;
@@ -308,11 +347,6 @@ public: // for QNSView
int m_topContentBorderThickness;
int m_bottomContentBorderThickness;
- // used by showFullScreen in fake mode
- QRect m_normalGeometry;
- Qt::WindowFlags m_oldWindowFlags;
- NSApplicationPresentationOptions m_presentationOptions;
-
struct BorderRange {
BorderRange(quintptr i, int u, int l) : identifier(i), upper(u), lower(l) { }
quintptr identifier;
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 40666772c5..75c13d6bbc 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -87,6 +87,36 @@ static void qt_closePopups()
}
}
+@interface NSWindow (FullScreenProperty)
+@property(readonly) BOOL qt_fullScreen;
+@end
+
+@implementation NSWindow (FullScreenProperty)
+
++ (void)load
+{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserverForName:NSWindowDidEnterFullScreenNotification object:nil queue:nil
+ usingBlock:^(NSNotification *notification) {
+ objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
+ [NSNumber numberWithBool:YES], OBJC_ASSOCIATION_RETAIN);
+ }
+ ];
+ [center addObserverForName:NSWindowDidExitFullScreenNotification object:nil queue:nil
+ usingBlock:^(NSNotification *notification) {
+ objc_setAssociatedObject(notification.object, @selector(qt_fullScreen),
+ nil, OBJC_ASSOCIATION_RETAIN);
+ }
+ ];
+}
+
+- (BOOL)qt_fullScreen
+{
+ NSNumber *number = objc_getAssociatedObject(self, @selector(qt_fullScreen));
+ return [number boolValue];
+}
+@end
+
@implementation QNSWindowHelper
@synthesize window = _window;
@@ -130,8 +160,7 @@ static void qt_closePopups()
[forwardView mouseDragged:theEvent];
}
}
-
- if (!pw->m_isNSWindowChild && theEvent.type == NSLeftMouseDown) {
+ if (pw->window()->isTopLevel() && theEvent.type == NSLeftMouseDown) {
pw->m_forwardWindow.clear();
}
}
@@ -202,13 +231,14 @@ static void qt_closePopups()
@synthesize helper = _helper;
- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
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
+ defer:NO screen:screen]; // Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
if (self) {
@@ -222,7 +252,7 @@ static void qt_closePopups()
// Prevent child NSWindows from becoming the key window in
// order keep the active apperance of the top-level window.
QCocoaWindow *pw = self.helper.platformWindow;
- if (!pw || pw->m_isNSWindowChild)
+ if (!pw || !pw->window()->isTopLevel())
return NO;
if (pw->shouldRefuseKeyWindowAndFirstResponder())
@@ -241,7 +271,7 @@ static void qt_closePopups()
// Windows with a transient parent (such as combobox popup windows)
// cannot become the main window:
QCocoaWindow *pw = self.helper.platformWindow;
- if (!pw || pw->m_isNSWindowChild || pw->window()->transientParent())
+ if (!pw || !pw->window()->isTopLevel() || pw->window()->transientParent())
canBecomeMain = NO;
return canBecomeMain;
@@ -284,13 +314,14 @@ static void qt_closePopups()
@synthesize helper = _helper;
- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
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
+ defer:NO screen:screen]; // Deferring window creation breaks OpenGL (the GL context is
// set up before the window is shown and needs a proper window)
if (self) {
@@ -343,18 +374,72 @@ static void qt_closePopups()
@end
+static void qRegisterNotificationCallbacks()
+{
+ static const QLatin1String notificationHandlerPrefix(Q_NOTIFICATION_PREFIX);
+
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+ const QMetaObject *metaObject = QMetaType::metaObjectForType(qRegisterMetaType<QCocoaWindow*>());
+ Q_ASSERT(metaObject);
+
+ for (int i = 0; i < metaObject->methodCount(); ++i) {
+ QMetaMethod method = metaObject->method(i);
+ const QString methodTag = QString::fromLatin1(method.tag());
+ if (!methodTag.startsWith(notificationHandlerPrefix))
+ continue;
+
+ const QString notificationName = methodTag.mid(notificationHandlerPrefix.size());
+ [center addObserverForName:notificationName.toNSString() object:nil queue:nil
+ usingBlock:^(NSNotification *notification) {
+
+ NSView *view = nullptr;
+ if ([notification.object isKindOfClass:[NSWindow class]]) {
+ NSWindow *window = notification.object;
+ // Only top level NSWindows should notify their QNSViews
+ if (window.parentWindow)
+ return;
+
+ if (!window.contentView)
+ return;
+
+ view = window.contentView;
+ } else if ([notification.object isKindOfClass:[NSView class]]) {
+ view = notification.object;
+ } else {
+ qCWarning(lcQpaCocoaWindow) << "Unhandled notifcation"
+ << notification.name << "for" << notification.object;
+ return;
+ }
+ Q_ASSERT(view);
+
+ QCocoaWindow *cocoaWindow = nullptr;
+ if (QNSView *qnsView = qnsview_cast(view))
+ cocoaWindow = qnsView.platformWindow;
+
+ // FIXME: Could be a foreign window, look up by iterating top level QWindows
+
+ if (!cocoaWindow)
+ return;
+
+ if (!method.invoke(cocoaWindow, Qt::DirectConnection)) {
+ qCWarning(lcQpaCocoaWindow) << "Failed to invoke NSNotification callback for"
+ << notification.name << "on" << cocoaWindow;
+ }
+ }];
+ }
+}
+Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
+
const int QCocoaWindow::NoAlertRequest = -1;
-QCocoaWindow::QCocoaWindow(QWindow *tlw)
+QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle)
: QPlatformWindow(tlw)
, m_view(nil)
, m_nsWindow(0)
, m_viewIsEmbedded(false)
, m_viewIsToBeEmbedded(false)
- , m_parentCocoaWindow(0)
- , m_isNSWindowChild(false)
- , m_effectivelyMaximized(false)
- , m_synchedWindowState(Qt::WindowActive)
+ , m_lastReportedWindowState(Qt::WindowNoState)
, m_windowModality(Qt::NonModal)
, m_windowUnderMouse(false)
, m_inConstructor(true)
@@ -379,15 +464,15 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
, m_drawContentBorderGradient(false)
, m_topContentBorderThickness(0)
, m_bottomContentBorderThickness(0)
- , m_normalGeometry(QRect(0,0,-1,-1))
, m_hasWindowFilePath(false)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window();
QMacAutoReleasePool pool;
- if (tlw->type() == Qt::ForeignWindow) {
- m_view = (NSView *)WId(tlw->property("_q_foreignWinId").value<WId>());
+ if (nativeHandle) {
+ m_view = reinterpret_cast<NSView *>(nativeHandle);
+ [m_view retain];
} else {
m_view = [[QNSView alloc] initWithCocoaWindow:this];
// Enable high-dpi OpenGL for retina displays. Enabling has the side
@@ -405,7 +490,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw)
[m_view setWantsLayer:enable];
}
setGeometry(tlw->geometry());
- recreateWindow(QPlatformWindow::parent());
+ recreateWindowIfNeeded();
tlw->setGeometry(geometry());
if (tlw->isTopLevel())
setWindowIcon(tlw->icon());
@@ -420,18 +505,16 @@ QCocoaWindow::~QCocoaWindow()
[m_nsWindow makeFirstResponder:nil];
[m_nsWindow setContentView:nil];
[m_nsWindow.helper detachFromPlatformWindow];
- if (m_isNSWindowChild) {
- if (m_parentCocoaWindow)
- m_parentCocoaWindow->removeChildWindow(this);
- } else if ([m_view superview]) {
+ if (m_view.window.parentWindow)
+ [m_view.window.parentWindow removeChildWindow:m_view.window];
+ else if ([m_view superview])
[m_view removeFromSuperview];
- }
removeMonitor();
// Make sure to disconnect observer in all case if view is valid
// to avoid notifications received when deleting when using Qt::AA_NativeWindows attribute
- if (window()->type() != Qt::ForeignWindow)
+ if (!isForeignWindow())
[[NSNotificationCenter defaultCenter] removeObserver:m_view];
// While it is unlikely that this window will be in the popup stack
@@ -440,10 +523,9 @@ QCocoaWindow::~QCocoaWindow()
QCocoaIntegration::instance()->popupWindowStack()->removeAll(this);
}
- foreach (QCocoaWindow *child, m_childWindows) {
- [m_nsWindow removeChildWindow:child->m_nsWindow];
- child->m_parentCocoaWindow = 0;
- }
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
+ [m_nsWindow removeChildWindow:childWindow->m_nsWindow];
+ });
[m_view release];
[m_nsWindow release];
@@ -481,6 +563,11 @@ void QCocoaWindow::setGeometry(const QRect &rectIn)
setCocoaGeometry(rect);
}
+bool QCocoaWindow::isForeignWindow() const
+{
+ return ![m_view isKindOfClass:[QNSView class]];
+}
+
QRect QCocoaWindow::geometry() const
{
// QWindows that are embedded in a NSView hiearchy may be considered
@@ -491,7 +578,7 @@ QRect QCocoaWindow::geometry() const
NSRect screenRect = [[m_view window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
NSPoint screenPoint = screenRect.origin;
QPoint position = qt_mac_flipPoint(screenPoint).toPoint();
- QSize size = QRectF::fromCGRect([m_view bounds]).toRect().size();
+ QSize size = QRectF::fromCGRect(NSRectToCGRect([m_view bounds])).toRect().size();
return QRect(position, size);
}
@@ -504,7 +591,7 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
QMacAutoReleasePool pool;
if (m_viewIsEmbedded) {
- if (window()->type() != Qt::ForeignWindow) {
+ if (!isForeignWindow()) {
[m_view setFrame:NSMakeRect(0, 0, rect.width(), rect.height())];
} else {
QPlatformWindow::setGeometry(rect);
@@ -512,9 +599,9 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
return;
}
- if (m_isNSWindowChild) {
+ if (isChildNSWindow()) {
QPlatformWindow::setGeometry(rect);
- NSWindow *parentNSWindow = m_parentCocoaWindow->m_nsWindow;
+ NSWindow *parentNSWindow = m_view.window.parentWindow;
NSRect parentWindowFrame = [parentNSWindow contentRectForFrameRect:parentNSWindow.frame];
clipWindow(parentWindowFrame);
@@ -528,7 +615,7 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
[m_view setFrame:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height())];
}
- if (window()->type() == Qt::ForeignWindow)
+ if (isForeignWindow())
QPlatformWindow::setGeometry(rect);
// will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm)
@@ -536,14 +623,14 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
void QCocoaWindow::clipChildWindows()
{
- foreach (QCocoaWindow *childWindow, m_childWindows) {
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
childWindow->clipWindow(m_nsWindow.frame);
- }
+ });
}
void QCocoaWindow::clipWindow(const NSRect &clipRect)
{
- if (!m_isNSWindowChild)
+ if (!isChildNSWindow())
return;
NSRect clippedWindowRect = NSZeroRect;
@@ -568,15 +655,15 @@ void QCocoaWindow::clipWindow(const NSRect &clipRect)
m_hiddenByClipping = false;
if (!m_hiddenByAncestor) {
[m_nsWindow orderFront:nil];
- m_parentCocoaWindow->reinsertChildWindow(this);
+ static_cast<QCocoaWindow *>(QPlatformWindow::parent())->reinsertChildWindow(this);
}
}
}
// recurse
- foreach (QCocoaWindow *childWindow, m_childWindows) {
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
childWindow->clipWindow(clippedWindowRect);
- }
+ });
}
void QCocoaWindow::hide(bool becauseOfAncestor)
@@ -593,8 +680,9 @@ void QCocoaWindow::hide(bool becauseOfAncestor)
if (!visible) // Could have been clipped before
return;
- foreach (QCocoaWindow *childWindow, m_childWindows)
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
childWindow->hide(true);
+ });
[m_nsWindow orderOut:nil];
}
@@ -604,20 +692,21 @@ void QCocoaWindow::show(bool becauseOfAncestor)
if ([m_nsWindow isVisible])
return;
- if (m_parentCocoaWindow && ![m_parentCocoaWindow->m_nsWindow isVisible]) {
+ if (m_view.window.parentWindow && !m_view.window.parentWindow.visible) {
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) {
+ if (isChildNSWindow()) {
m_hiddenByAncestor = false;
setCocoaGeometry(windowGeometry());
}
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)
+ if (isChildNSWindow())
+ static_cast<QCocoaWindow *>(QPlatformWindow::parent())->reinsertChildWindow(this);
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
childWindow->show(true);
+ });
}
}
}
@@ -626,7 +715,7 @@ void QCocoaWindow::setVisible(bool visible)
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::setVisible" << window() << visible;
- if (m_isNSWindowChild && m_hiddenByClipping)
+ if (isChildNSWindow() && m_hiddenByClipping)
return;
m_inSetVisible = true;
@@ -638,8 +727,7 @@ void QCocoaWindow::setVisible(bool visible)
if (visible) {
// We need to recreate if the modality has changed as the style mask will need updating
- if (m_windowModality != window()->modality() || isNativeWindowTypeInconsistent())
- recreateWindow(QPlatformWindow::parent());
+ recreateWindowIfNeeded();
// Register popup windows. The Cocoa platform plugin will forward mouse events
// to them and close them when needed.
@@ -674,14 +762,14 @@ void QCocoaWindow::setVisible(bool visible)
// setWindowState might have been called while the window was hidden and
// will not change the NSWindow state in that case. Sync up here:
- syncWindowState(window()->windowState());
+ applyWindowState(window()->windowState());
if (window()->windowState() != Qt::WindowMinimized) {
if ((window()->modality() == Qt::WindowModal
|| window()->type() == Qt::Sheet)
&& parentCocoaWindow) {
// show the window as a sheet
- [NSApp beginSheet:m_nsWindow modalForWindow:parentCocoaWindow->m_nsWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
+ [parentCocoaWindow->m_nsWindow beginSheet:m_nsWindow completionHandler:nil];
} else if (window()->modality() != Qt::NonModal) {
// show the window as application modal
QCocoaEventDispatcher *cocoaEventDispatcher = qobject_cast<QCocoaEventDispatcher *>(QGuiApplication::instance()->eventDispatcher());
@@ -700,8 +788,9 @@ void QCocoaWindow::setVisible(bool visible)
else
[m_nsWindow orderFront:nil];
- foreach (QCocoaWindow *childWindow, m_childWindows)
+ foreachChildNSWindow(^(QCocoaWindow *childWindow) {
childWindow->show(true);
+ });
} else {
show();
}
@@ -742,8 +831,10 @@ void QCocoaWindow::setVisible(bool visible)
cocoaEventDispatcherPrivate->endModalSession(window());
m_hasModalSession = false;
} else {
- if ([m_nsWindow isSheet])
- [NSApp endSheet:m_nsWindow];
+ if ([m_nsWindow isSheet]) {
+ Q_ASSERT_X(parentCocoaWindow, "QCocoaWindow", "Window modal dialog has no transient parent.");
+ [parentCocoaWindow->m_nsWindow endSheet:m_nsWindow];
+ }
}
hide();
@@ -856,6 +947,10 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
if (m_drawContentBorderGradient)
styleMask |= NSTexturedBackgroundWindowMask;
+ // Don't wipe fullscreen state
+ if (m_nsWindow.styleMask & NSFullScreenWindowMask)
+ styleMask |= NSFullScreenWindowMask;
+
return styleMask;
}
@@ -873,13 +968,14 @@ void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags)
// in line with the platform style guidelines.
bool fixedSizeNoZoom = (windowMinimumSize().isValid() && windowMaximumSize().isValid()
&& windowMinimumSize() == windowMaximumSize());
- bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint) && !(flags & Qt::WindowMaximizeButtonHint));
+ bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint)
+ && !(flags & (Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint)));
[[m_nsWindow standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)];
}
void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
{
- if (m_nsWindow && !m_isNSWindowChild) {
+ if (m_nsWindow && !isChildNSWindow()) {
NSUInteger styleMask = windowStyleMask(flags);
NSInteger level = this->windowLevel(flags);
// While setting style mask we can have -updateGeometry calls on a content
@@ -908,13 +1004,16 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
setWindowZoomButton(flags);
}
+ if (m_nsWindow)
+ m_nsWindow.ignoresMouseEvents = flags & Qt::WindowTransparentForInput;
+
m_windowFlags = flags;
}
void QCocoaWindow::setWindowState(Qt::WindowState state)
{
if (window()->isVisible())
- syncWindowState(state); // Window state set for hidden windows take effect when show() is called.
+ applyWindowState(state); // Window state set for hidden windows take effect when show() is called
}
void QCocoaWindow::setWindowTitle(const QString &title)
@@ -983,19 +1082,16 @@ void QCocoaWindow::raise()
// ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
if (!m_nsWindow)
return;
- if (m_isNSWindowChild) {
- QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows;
- siblings.removeOne(this);
- siblings.append(this);
+ if (isChildNSWindow()) {
if (m_hiddenByClipping)
return;
}
if ([m_nsWindow isVisible]) {
- if (m_isNSWindowChild) {
+ if (isChildNSWindow()) {
// -[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;
+ NSWindow *parentNSWindow = m_view.window.parentWindow;
[parentNSWindow removeChildWindow:m_nsWindow];
[parentNSWindow addChildWindow:m_nsWindow ordered:NSWindowAbove];
} else {
@@ -1021,20 +1117,17 @@ void QCocoaWindow::lower()
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::lower" << window();
if (!m_nsWindow)
return;
- if (m_isNSWindowChild) {
- QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows;
- siblings.removeOne(this);
- siblings.prepend(this);
+ if (isChildNSWindow()) {
if (m_hiddenByClipping)
return;
}
if ([m_nsWindow isVisible]) {
- if (m_isNSWindowChild) {
+ if (isChildNSWindow()) {
// -[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;
+ NSWindow *parentNSWindow = m_view.window.parentWindow;
NSArray *children = [parentNSWindow.childWindows copy];
for (NSWindow *child in children)
if (m_nsWindow != child) {
@@ -1095,7 +1188,7 @@ void QCocoaWindow::propagateSizeHints()
QSize sizeIncrement = windowSizeIncrement();
if (sizeIncrement.isEmpty())
sizeIncrement = QSize(1, 1);
- [m_nsWindow setResizeIncrements:sizeIncrement.toCGSize()];
+ [m_nsWindow setResizeIncrements:NSSizeFromCGSize(sizeIncrement.toCGSize())];
QRect rect = geometry();
QSize baseSize = windowBaseSize();
@@ -1158,7 +1251,7 @@ void QCocoaWindow::setParent(const QPlatformWindow *parentWindow)
// recreate the window for compatibility
bool unhideAfterRecreate = parentWindow && !m_viewIsToBeEmbedded && ![m_view isHidden];
- recreateWindow(parentWindow);
+ recreateWindowIfNeeded();
if (unhideAfterRecreate)
[m_view setHidden:NO];
setCocoaGeometry(geometry());
@@ -1182,6 +1275,8 @@ void QCocoaWindow::setEmbeddedInForeignView(bool embedded)
m_nsWindow = 0;
}
+// ----------------------- NSWindow notifications -----------------------
+
void QCocoaWindow::windowWillMove()
{
// Close any open popups on window move
@@ -1190,10 +1285,13 @@ void QCocoaWindow::windowWillMove()
void QCocoaWindow::windowDidMove()
{
- if (m_isNSWindowChild)
+ if (isChildNSWindow())
return;
[qnsview_cast(m_view) updateGeometry];
+
+ // Moving a window might bring it out of maximized state
+ reportCurrentWindowState();
}
void QCocoaWindow::windowDidResize()
@@ -1201,21 +1299,172 @@ void QCocoaWindow::windowDidResize()
if (!m_nsWindow)
return;
- if (m_isNSWindowChild)
+ if (isChildNSWindow())
return;
clipChildWindows();
[qnsview_cast(m_view) updateGeometry];
+
+ if (!m_view.inLiveResize)
+ reportCurrentWindowState();
+}
+
+void QCocoaWindow::viewDidChangeFrame()
+{
+ [qnsview_cast(m_view) updateGeometry];
+}
+
+/*!
+ Callback for NSViewGlobalFrameDidChangeNotification.
+
+ Posted whenever an NSView object that has attached surfaces (that is,
+ NSOpenGLContext objects) moves to a different screen, or other cases
+ where the NSOpenGLContext object needs to be updated.
+*/
+void QCocoaWindow::viewDidChangeGlobalFrame()
+{
+ updateExposedGeometry();
}
void QCocoaWindow::windowDidEndLiveResize()
{
- if (m_synchedWindowState == Qt::WindowMaximized && ![m_nsWindow isZoomed]) {
- m_effectivelyMaximized = false;
- [qnsview_cast(m_view) notifyWindowStateChanged:Qt::WindowNoState];
+ reportCurrentWindowState();
+}
+
+void QCocoaWindow::windowDidBecomeKey()
+{
+ if (isForeignWindow())
+ return;
+
+ if (m_windowUnderMouse) {
+ QPointF windowPoint;
+ QPointF screenPoint;
+ [qnsview_cast(m_view) convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
+ QWindowSystemInterface::handleEnterEvent(m_enterLeaveTargetWindow, windowPoint, screenPoint);
}
+
+ if (!windowIsPopupType() && !qnsview_cast(m_view).isMenuView)
+ QWindowSystemInterface::handleWindowActivated(window());
}
+void QCocoaWindow::windowDidResignKey()
+{
+ if (isForeignWindow())
+ return;
+
+ // Key window will be non-nil if another window became key, so do not
+ // set the active window to zero here -- the new key window's
+ // NSWindowDidBecomeKeyNotification hander will change the active window.
+ NSWindow *keyWindow = [NSApp keyWindow];
+ if (!keyWindow || keyWindow == m_view.window) {
+ // No new key window, go ahead and set the active window to zero
+ if (!windowIsPopupType() && !qnsview_cast(m_view).isMenuView)
+ QWindowSystemInterface::handleWindowActivated(0);
+ }
+}
+
+void QCocoaWindow::windowDidMiniaturize()
+{
+ reportCurrentWindowState();
+}
+
+void QCocoaWindow::windowDidDeminiaturize()
+{
+ reportCurrentWindowState();
+}
+
+void QCocoaWindow::windowWillEnterFullScreen()
+{
+ // The NSWindow needs to be resizable, otherwise we'll end up with
+ // the normal window geometry, centered in the middle of the screen
+ // on a black background. The styleMask will be reset below.
+ m_nsWindow.styleMask |= NSResizableWindowMask;
+}
+
+void QCocoaWindow::windowDidEnterFullScreen()
+{
+ Q_ASSERT_X(m_nsWindow.qt_fullScreen, "QCocoaWindow",
+ "FullScreen category processes window notifications first");
+
+ // Reset to original styleMask
+ setWindowFlags(m_windowFlags);
+
+ reportCurrentWindowState();
+}
+
+void QCocoaWindow::windowWillExitFullScreen()
+{
+ // The NSWindow needs to be resizable, otherwise we'll end up with
+ // a weird zoom animation. The styleMask will be reset below.
+ m_nsWindow.styleMask |= NSResizableWindowMask;
+}
+
+void QCocoaWindow::windowDidExitFullScreen()
+{
+ Q_ASSERT_X(!m_nsWindow.qt_fullScreen, "QCocoaWindow",
+ "FullScreen category processes window notifications first");
+
+ // Reset to original styleMask
+ setWindowFlags(m_windowFlags);
+
+ Qt::WindowState requestedState = window()->windowState();
+
+ // Deliver update of QWindow state
+ reportCurrentWindowState();
+
+ if (requestedState != windowState() && requestedState != Qt::WindowFullScreen) {
+ // We were only going out of full screen as an intermediate step before
+ // progressing into the final step, so re-sync the desired state.
+ applyWindowState(requestedState);
+ }
+}
+
+void QCocoaWindow::windowDidOrderOffScreen()
+{
+ obscureWindow();
+}
+
+void QCocoaWindow::windowDidOrderOnScreen()
+{
+ exposeWindow();
+}
+
+void QCocoaWindow::windowDidChangeOcclusionState()
+{
+ // Several unit tests expect paint and/or expose events for windows that are
+ // sometimes (unpredictably) occluded and some unit tests depend on QWindow::isExposed.
+ // Don't send Expose/Obscure events when running under QTestLib.
+ static const bool onTestLib = qt_mac_resolveOption(false, "QT_QTESTLIB_RUNNING");
+ if (!onTestLib) {
+ if ((NSUInteger)[m_view.window occlusionState] & NSWindowOcclusionStateVisible) {
+ exposeWindow();
+ } else {
+ // Send Obscure events on window occlusion to stop animations.
+ obscureWindow();
+ }
+ }
+}
+
+void QCocoaWindow::windowDidChangeScreen()
+{
+ if (!window())
+ return;
+
+ if (QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen))
+ QWindowSystemInterface::handleWindowScreenChanged(window(), cocoaScreen->screen());
+
+ updateExposedGeometry();
+}
+
+void QCocoaWindow::windowWillClose()
+{
+ // Close any open popups on window closing.
+ if (window() && !windowIsPopupType(window()->type()))
+ qt_closePopups();
+}
+
+// ----------------------- NSWindowDelegate callbacks -----------------------
+
bool QCocoaWindow::windowShouldClose()
{
qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::windowShouldClose" << window();
@@ -1229,18 +1478,7 @@ bool QCocoaWindow::windowShouldClose()
return accepted;
}
-void QCocoaWindow::windowWillClose()
-{
- // Close any open popups on window closing.
- if (window() && !windowIsPopupType(window()->type()))
- qt_closePopups();
-}
-
-void QCocoaWindow::setSynchedWindowStateFromWindow()
-{
- if (QWindow *w = window())
- m_synchedWindowState = w->windowState();
-}
+// --------------------------------------------------------------------------
bool QCocoaWindow::windowIsPopupType(Qt::WindowType type) const
{
@@ -1264,62 +1502,141 @@ QCocoaGLContext *QCocoaWindow::currentContext() const
}
#endif
-void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
+/*!
+ Checks if the window is a non-top level QWindow with a NSWindow.
+
+ \sa _q_platform_MacUseNSWindow, QT_MAC_USE_NSWINDOW
+*/
+bool QCocoaWindow::isChildNSWindow() const
+{
+ return m_view.window.parentWindow != nil;
+}
+
+/*!
+ Checks if the window is the content view of its immediate NSWindow.
+
+ Being the content view of a NSWindow means the QWindow is
+ the highest accessible NSView object in the window's view
+ hierarchy.
+
+ This can only happen in two cases, either if the QWindow is
+ itself a top level window, or if it's a child NSWindow.
+
+ \sa isChildNSWindow
+*/
+bool QCocoaWindow::isContentView() const
+{
+ return m_view.window.contentView == m_view;
+}
+
+/*!
+ Iterates child NSWindows that have a corresponding QCocoaWindow.
+*/
+void QCocoaWindow::foreachChildNSWindow(void (^block)(QCocoaWindow *))
{
- qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindow" << window()
+ NSArray *windows = m_view.window.childWindows;
+ [windows enumerateObjectsUsingBlock:^(NSWindow *window, NSUInteger index, BOOL *stop) {
+ Q_UNUSED(index);
+ Q_UNUSED(stop);
+ if (QNSView *view = qnsview_cast(window.contentView))
+ block(view.platformWindow);
+ }];
+}
+
+/*!
+ Recreates (or removes) the NSWindow for this QWindow, if needed.
+
+ A QWindow may need a corresponding NSWindow, depending on whether
+ or not it's a top level or not (or explicitly set to be a child
+ NSWindow), whether it is a NSPanel or not, etc.
+*/
+void QCocoaWindow::recreateWindowIfNeeded()
+{
+ QPlatformWindow *parentWindow = QPlatformWindow::parent();
+ qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::recreateWindowIfNeeded" << window()
<< "parent" << (parentWindow ? parentWindow->window() : 0);
- bool wasNSWindowChild = m_isNSWindowChild;
- BOOL requestNSWindowChild = qt_mac_resolveOption(NO, window(), "_q_platform_MacUseNSWindow",
- "QT_MAC_USE_NSWINDOW");
- m_isNSWindowChild = parentWindow && requestNSWindowChild;
- bool needsNSWindow = m_isNSWindowChild || !parentWindow;
+ RecreationReasons recreateReason = RecreationNotNeeded;
+
+ QCocoaWindow *oldParentCocoaWindow = nullptr;
+ if (QNSView *qnsView = qnsview_cast(m_view.superview))
+ oldParentCocoaWindow = qnsView.platformWindow;
+
+ if (parentWindow != oldParentCocoaWindow)
+ recreateReason |= ParentChanged;
+
+ if (!m_view.window)
+ recreateReason |= MissingWindow;
+
+ // If the modality has changed the style mask will need updating
+ if (m_windowModality != window()->modality())
+ recreateReason |= WindowModalityChanged;
+
+ const bool shouldBeChildNSWindow = parentWindow && qt_mac_resolveOption(NO,
+ window(), "_q_platform_MacUseNSWindow", "QT_MAC_USE_NSWINDOW");
+
+ if (isChildNSWindow() != shouldBeChildNSWindow)
+ recreateReason |= ChildNSWindowChanged;
+
+ const bool shouldBeContentView = !parentWindow || shouldBeChildNSWindow;
+ if (isContentView() != shouldBeContentView)
+ recreateReason |= ContentViewChanged;
+
+ Qt::WindowType type = window()->type();
+ const bool isPanel = isContentView() && [m_view.window isKindOfClass:[QNSPanel class]];
+ const bool shouldBePanel = shouldBeContentView && !shouldBeChildNSWindow &&
+ ((type & Qt::Popup) == Qt::Popup || (type & Qt::Dialog) == Qt::Dialog);
+
+ if (isPanel != shouldBePanel)
+ recreateReason |= PanelChanged;
+
+ if (recreateReason == RecreationNotNeeded) {
+ qCDebug(lcQpaCocoaWindow) << "No need to recreate NSWindow";
+ return;
+ }
+
+ qCDebug(lcQpaCocoaWindow) << "Recreating NSWindow due to" << recreateReason;
+
+ QCocoaWindow *parentCocoaWindow = static_cast<QCocoaWindow *>(parentWindow);
- QCocoaWindow *oldParentCocoaWindow = m_parentCocoaWindow;
- m_parentCocoaWindow = const_cast<QCocoaWindow *>(static_cast<const QCocoaWindow *>(parentWindow));
- if (m_parentCocoaWindow && m_isNSWindowChild) {
- QWindow *parentQWindow = m_parentCocoaWindow->window();
+ if (shouldBeChildNSWindow) {
+ QWindow *parentQWindow = parentWindow->window();
+ // Ensure that all parents in the hierarchy are also child NSWindows
if (!parentQWindow->property("_q_platform_MacUseNSWindow").toBool()) {
parentQWindow->setProperty("_q_platform_MacUseNSWindow", QVariant(true));
- m_parentCocoaWindow->recreateWindow(m_parentCocoaWindow->m_parentCocoaWindow);
+ parentCocoaWindow->recreateWindowIfNeeded();
}
}
- bool usesNSPanel = [m_nsWindow isKindOfClass:[QNSPanel class]];
-
- // No child QNSWindow should notify its QNSView
- if (m_nsWindow && (window()->type() != Qt::ForeignWindow) && m_parentCocoaWindow && !oldParentCocoaWindow)
- [[NSNotificationCenter defaultCenter] removeObserver:m_view
- name:nil object:m_nsWindow];
-
// Remove current window (if any)
- if ((m_nsWindow && !needsNSWindow) || (usesNSPanel != shouldUseNSPanel())) {
+ if ((isContentView() && !shouldBeContentView) || (recreateReason & PanelChanged)) {
[m_nsWindow closeAndRelease];
- if (wasNSWindowChild && oldParentCocoaWindow)
- oldParentCocoaWindow->removeChildWindow(this);
+ if (isChildNSWindow())
+ [m_view.window.parentWindow removeChildWindow:m_view.window];
m_nsWindow = 0;
}
- if (needsNSWindow) {
+ if (shouldBeContentView) {
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 ((window()->type() != Qt::ForeignWindow) && (noPreviousWindow || (wasNSWindowChild && !m_isNSWindowChild)))
- [[NSNotificationCenter defaultCenter] addObserver:m_view
- selector:@selector(windowNotification:)
- name:nil // Get all notifications
- object:m_nsWindow];
-
- if (oldParentCocoaWindow) {
- if (!m_isNSWindowChild || oldParentCocoaWindow != m_parentCocoaWindow)
- oldParentCocoaWindow->removeChildWindow(this);
+ m_nsWindow = createNSWindow(shouldBeChildNSWindow, shouldBePanel);
+
+ if (m_view.window.parentWindow) {
+ if (!shouldBeChildNSWindow || (recreateReason & ParentChanged))
+ [m_view.window.parentWindow removeChildWindow:m_view.window];
m_forwardWindow = oldParentCocoaWindow;
}
- setNSWindow(m_nsWindow);
+ // Move view to new NSWindow if needed
+ if (m_nsWindow.contentView != m_view) {
+ [m_view setPostsFrameChangedNotifications:NO];
+ [m_view retain];
+ if (m_view.superview) // m_view comes from another NSWindow
+ [m_view removeFromSuperview];
+ [m_nsWindow setContentView:m_view];
+ [m_view release];
+ [m_view setPostsFrameChangedNotifications:YES];
+ }
}
if (m_viewIsToBeEmbedded) {
@@ -1330,32 +1647,21 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
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
- | NSWindowCollectionBehaviorFullScreenAuxiliary;
- m_nsWindow.animationBehavior = NSWindowAnimationBehaviorNone;
- m_nsWindow.collectionBehavior = collectionBehavior;
- setCocoaGeometry(windowGeometry());
-
- 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 if (shouldBeChildNSWindow) {
+ if (!m_hiddenByClipping) {
+ [parentCocoaWindow->m_nsWindow addChildWindow:m_nsWindow ordered:NSWindowAbove];
+ parentCocoaWindow->reinsertChildWindow(this);
}
+
+ // Set properties after the window has been made a child NSWindow
+ setCocoaGeometry(windowGeometry());
+ setWindowFlags(window()->flags());
} else {
// Child windows have no NSWindow, link the NSViews instead.
if ([m_view superview])
[m_view removeFromSuperview];
- [m_parentCocoaWindow->m_view addSubview:m_view];
+ [parentCocoaWindow->m_view addSubview:m_view];
QRect rect = windowGeometry();
// Prevent setting a (0,0) window size; causes opengl context
// "Invalid Drawable" warnings.
@@ -1366,9 +1672,6 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
[m_view setHidden:!window()->isVisible()];
}
- m_nsWindow.ignoresMouseEvents =
- (window()->flags() & Qt::WindowTransparentForInput) == Qt::WindowTransparentForInput;
-
const qreal opacity = qt_window_private(window())->opacity;
if (!qFuzzyCompare(opacity, qreal(1.0)))
setOpacity(opacity);
@@ -1381,11 +1684,17 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow)
void QCocoaWindow::reinsertChildWindow(QCocoaWindow *child)
{
- int childIndex = m_childWindows.indexOf(child);
+ const QObjectList &childWindows = window()->children();
+ int childIndex = childWindows.indexOf(child->window());
Q_ASSERT(childIndex != -1);
- for (int i = childIndex; i < m_childWindows.size(); i++) {
- NSWindow *nsChild = m_childWindows[i]->m_nsWindow;
+ for (int i = childIndex; i < childWindows.size(); ++i) {
+ QWindow *window = static_cast<QWindow *>(childWindows.at(i));
+ QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle());
+ if (!cocoaWindow)
+ continue;
+
+ NSWindow *nsChild = cocoaWindow->m_nsWindow;
if (i != childIndex)
[m_nsWindow removeChildWindow:nsChild];
[m_nsWindow addChildWindow:nsChild ordered:NSWindowAbove];
@@ -1399,114 +1708,80 @@ 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);
-}
-
-QCocoaNSWindow * QCocoaWindow::createNSWindow()
+QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBeChildNSWindow, bool shouldBePanel)
{
QMacAutoReleasePool pool;
QRect rect = initialGeometry(window(), windowGeometry(), defaultWindowWidth, defaultWindowHeight);
- NSRect frame = qt_mac_flipRect(rect);
-
- Qt::WindowType type = window()->type();
- Qt::WindowFlags flags = window()->flags();
- NSUInteger styleMask;
- if (m_isNSWindowChild) {
- styleMask = NSBorderlessWindowMask;
- } else {
- styleMask = windowStyleMask(flags);
+ QScreen *targetScreen = nullptr;
+ for (QScreen *screen : QGuiApplication::screens()) {
+ if (screen->geometry().contains(rect.topLeft())) {
+ targetScreen = screen;
+ break;
+ }
}
- QCocoaNSWindow *createdWindow = 0;
-
- // 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];
-
- // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set.
- QVariant showWithoutActivating = QPlatformWindow::window()->property("_q_macAlwaysShowToolWindow");
- bool shouldHideOnDeactivate = ((type & Qt::Tool) == Qt::Tool) &&
- !(showWithoutActivating.isValid() && showWithoutActivating.toBool());
- [window setHidesOnDeactivate: shouldHideOnDeactivate];
-
- // Make popup windows show on the same desktop as the parent full-screen window.
- [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
- if ((type & Qt::Popup) == Qt::Popup)
- [window setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow];
-
- createdWindow = window;
- } else {
- QNSWindow *window;
- window = [[QNSWindow alloc] initWithContentRect:frame
- styleMask: styleMask
- qPlatformWindow:this];
- createdWindow = window;
+
+ if (!targetScreen) {
+ qCWarning(lcQpaCocoaWindow) << "Window position outside any known screen, using primary screen";
+ targetScreen = QGuiApplication::primaryScreen();
}
- if ([createdWindow respondsToSelector:@selector(setRestorable:)])
- [createdWindow setRestorable: NO];
+ rect.translate(-targetScreen->geometry().topLeft());
+ QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen *>(targetScreen->handle());
+ NSRect frame = NSRectFromCGRect(cocoaScreen->mapToNative(rect).toCGRect());
- NSInteger level = windowLevel(flags);
- [createdWindow setLevel:level];
+ // Note: The macOS window manager has a bug, where if a screen is rotated, it will not allow
+ // a window to be created within the area of the screen that has a Y coordinate (I quadrant)
+ // higher than the height of the screen in its non-rotated state, unless the window is
+ // created with the NSWindowStyleMaskBorderless style mask.
- // OpenGL surfaces can be ordered either above(default) or below the NSWindow.
- // When ordering below the window must be tranclucent and have a clear background color.
- static GLint openglSourfaceOrder = qt_mac_resolveOption(1, "QT_MAC_OPENGL_SURFACE_ORDER");
+ Qt::WindowType type = window()->type();
+ Qt::WindowFlags flags = window()->flags();
+
+ // Create NSWindow
+ Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
+ NSUInteger styleMask = shouldBeChildNSWindow ? NSBorderlessWindowMask : windowStyleMask(flags);
+ QCocoaNSWindow *window = [[windowClass alloc] initWithContentRect:frame
+ screen:cocoaScreen->nativeScreen() styleMask:styleMask qPlatformWindow:this];
+
+ window.restorable = NO;
+ window.level = shouldBeChildNSWindow ? NSNormalWindowLevel : windowLevel(flags);
- bool isTranslucent = window()->format().alphaBufferSize() > 0
- || (surface()->supportsOpenGL() && openglSourfaceOrder == -1);
- if (isTranslucent) {
- [createdWindow setBackgroundColor:[NSColor clearColor]];
- [createdWindow setOpaque:NO];
+ if (!isOpaque()) {
+ window.backgroundColor = [NSColor clearColor];
+ window.opaque = NO;
}
- m_windowModality = window()->modality();
+ Q_ASSERT(!(shouldBePanel && shouldBeChildNSWindow));
- applyContentBorderThickness(createdWindow);
+ if (shouldBePanel) {
+ // Qt::Tool windows hide on app deactivation, unless Qt::WA_MacAlwaysShowToolWindow is set
+ window.hidesOnDeactivate = ((type & Qt::Tool) == Qt::Tool) &&
+ !qt_mac_resolveOption(false, QPlatformWindow::window(), "_q_macAlwaysShowToolWindow", "");
- return createdWindow;
-}
+ // Make popup windows show on the same desktop as the parent full-screen window
+ window.collectionBehavior = NSWindowCollectionBehaviorFullScreenAuxiliary;
-void QCocoaWindow::setNSWindow(QCocoaNSWindow *window)
-{
- if (window.contentView != m_view) {
- [m_view setPostsFrameChangedNotifications:NO];
- [m_view retain];
- if (m_view.superview) // m_view comes from another NSWindow
- [m_view removeFromSuperview];
- [window setContentView:m_view];
- [m_view release];
- [m_view setPostsFrameChangedNotifications:YES];
+ if ((type & Qt::Popup) == Qt::Popup) {
+ window.hasShadow = YES;
+ window.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
+ }
+ } else if (shouldBeChildNSWindow) {
+ window.collectionBehavior =
+ NSWindowCollectionBehaviorManaged
+ | NSWindowCollectionBehaviorIgnoresCycle
+ | NSWindowCollectionBehaviorFullScreenAuxiliary;
+ window.hasShadow = NO;
+ window.animationBehavior = NSWindowAnimationBehaviorNone;
}
-}
-
-void QCocoaWindow::removeChildWindow(QCocoaWindow *child)
-{
- m_childWindows.removeOne(child);
- [m_nsWindow removeChildWindow:child->m_nsWindow];
-}
-bool QCocoaWindow::isNativeWindowTypeInconsistent()
-{
- if (!m_nsWindow)
- return false;
+ // Persist modality so we can detect changes later on
+ m_windowModality = QPlatformWindow::window()->modality();
- const bool isPanel = [m_nsWindow isKindOfClass:[QNSPanel class]];
- const bool usePanel = shouldUseNSPanel();
+ applyContentBorderThickness(window);
- return isPanel != usePanel;
+ return window;
}
void QCocoaWindow::removeMonitor()
@@ -1520,7 +1795,7 @@ void QCocoaWindow::removeMonitor()
// Returns the current global screen geometry for the nswindow associated with this window.
QRect QCocoaWindow::nativeWindowGeometry() const
{
- if (!m_nsWindow || m_isNSWindowChild)
+ if (!m_nsWindow || isChildNSWindow())
return geometry();
NSRect rect = [m_nsWindow frame];
@@ -1530,94 +1805,144 @@ QRect QCocoaWindow::nativeWindowGeometry() const
return qRect;
}
-// Returns a pointer to the parent QCocoaWindow for this window, or 0 if there is none.
-QCocoaWindow *QCocoaWindow::parentCocoaWindow() const
-{
- if (window() && window()->transientParent()) {
- return static_cast<QCocoaWindow*>(window()->transientParent()->handle());
- }
- return 0;
-}
+/*!
+ Applies the given state to the NSWindow, going in/out of minimize/zoomed/fullscreen
-// Syncs the NSWindow minimize/maximize/fullscreen state with the current QWindow state
-void QCocoaWindow::syncWindowState(Qt::WindowState newState)
+ When this is called from QWindow::setWindowState(), the QWindow state has not been
+ updated yet, so window()->windowState() will reflect the previous state that was
+ reported to QtGui.
+*/
+void QCocoaWindow::applyWindowState(Qt::WindowState newState)
{
+ const Qt::WindowState currentState = windowState();
+ if (newState == currentState)
+ return;
+
if (!m_nsWindow)
return;
- // if content view width or height is 0 then the window animations will crash so
- // do nothing except set the new state
- NSRect contentRect = m_view.frame;
- if (contentRect.size.width <= 0 || contentRect.size.height <= 0) {
+
+ const NSSize contentSize = m_view.frame.size;
+ if (contentSize.width <= 0 || contentSize.height <= 0) {
+ // If content view width or height is 0 then the window animations will crash so
+ // do nothing. We report the current state back to reflect the failed operation.
qWarning("invalid window content view size, check your window geometry");
- m_synchedWindowState = newState;
+ reportCurrentWindowState(true);
return;
}
- Qt::WindowState predictedState = newState;
- if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
- const int styleMask = [m_nsWindow styleMask];
- const bool usePerform = styleMask & NSResizableWindowMask;
- [m_nsWindow setStyleMask:styleMask | NSResizableWindowMask];
- if (usePerform)
- [m_nsWindow performZoom : m_nsWindow]; // toggles
- else
- [m_nsWindow zoom : m_nsWindow]; // toggles
- [m_nsWindow setStyleMask:styleMask];
+ if (m_nsWindow.styleMask & NSUtilityWindowMask) {
+ // Utility panels cannot be fullscreen
+ qWarning() << window()->type() << "windows can not be made full screen";
+ reportCurrentWindowState(true);
+ return;
}
- if ((m_synchedWindowState & Qt::WindowMinimized) != (newState & Qt::WindowMinimized)) {
- if (newState & Qt::WindowMinimized) {
- if ([m_nsWindow styleMask] & NSMiniaturizableWindowMask)
- [m_nsWindow performMiniaturize : m_nsWindow];
- else
- [m_nsWindow miniaturize : m_nsWindow];
- } else {
- [m_nsWindow deminiaturize : m_nsWindow];
- }
+ const id sender = m_nsWindow;
+
+ // First we need to exit states that can't transition directly to other states
+ switch (currentState) {
+ case Qt::WindowMinimized:
+ [m_nsWindow deminiaturize:sender];
+ Q_ASSERT_X(windowState() != Qt::WindowMinimized, "QCocoaWindow",
+ "[NSWindow deminiaturize:] is synchronous");
+ break;
+ case Qt::WindowFullScreen: {
+ toggleFullScreen();
+ // Exiting fullscreen is not synchronous, so we need to wait for the
+ // NSWindowDidExitFullScreenNotification before continuing to apply
+ // the new state.
+ return;
}
-
- const bool effMax = m_effectivelyMaximized;
- if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized) || (m_effectivelyMaximized && newState == Qt::WindowNoState)) {
- if ((m_synchedWindowState & Qt::WindowFullScreen) == (newState & Qt::WindowFullScreen)) {
- [m_nsWindow zoom : m_nsWindow]; // toggles
- m_effectivelyMaximized = !effMax;
- } else if (!(newState & Qt::WindowMaximized)) {
- // it would be nice to change the target geometry that toggleFullScreen will animate toward
- // but there is no known way, so the maximized state is not possible at this time
- predictedState = static_cast<Qt::WindowState>(static_cast<int>(newState) | Qt::WindowMaximized);
- m_effectivelyMaximized = true;
- }
+ default:
+ Q_FALLTHROUGH();
}
- if ((m_synchedWindowState & Qt::WindowFullScreen) != (newState & Qt::WindowFullScreen)) {
- if (window()->flags() & Qt::WindowFullscreenButtonHint) {
- if (m_effectivelyMaximized && m_synchedWindowState == Qt::WindowFullScreen)
- predictedState = Qt::WindowMaximized;
- [m_nsWindow toggleFullScreen : m_nsWindow];
- } else {
- 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 = nativeWindowGeometry();
- 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];
- }
+ // Then we apply the new state if needed
+ if (newState == windowState())
+ return;
+
+ switch (newState) {
+ case Qt::WindowFullScreen:
+ toggleFullScreen();
+ break;
+ case Qt::WindowMaximized:
+ toggleMaximized();
+ break;
+ case Qt::WindowMinimized:
+ [m_nsWindow miniaturize:sender];
+ break;
+ case Qt::WindowNoState:
+ switch (windowState()) {
+ case Qt::WindowMaximized:
+ toggleMaximized();
+ default:
+ Q_FALLTHROUGH();
}
+ break;
+ default:
+ Q_UNREACHABLE();
}
+}
+
+void QCocoaWindow::toggleMaximized()
+{
+ // The NSWindow needs to be resizable, otherwise the window will
+ // not be possible to zoom back to non-zoomed state.
+ const bool wasResizable = m_nsWindow.styleMask & NSResizableWindowMask;
+ m_nsWindow.styleMask |= NSResizableWindowMask;
+
+ const id sender = m_nsWindow;
+ [m_nsWindow zoom:sender];
+
+ if (!wasResizable)
+ m_nsWindow.styleMask &= ~NSResizableWindowMask;
+}
+
+void QCocoaWindow::toggleFullScreen()
+{
+ // The window needs to have the correct collection behavior for the
+ // toggleFullScreen call to have an effect. The collection behavior
+ // will be reset in windowDidEnterFullScreen/windowDidLeaveFullScreen.
+ m_nsWindow.collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
+
+ const id sender = m_nsWindow;
+ [m_nsWindow toggleFullScreen:sender];
+}
+
+bool QCocoaWindow::isTransitioningToFullScreen() const
+{
+ NSWindow *window = m_view.window;
+ return window.styleMask & NSFullScreenWindowMask && !window.qt_fullScreen;
+}
+
+Qt::WindowState QCocoaWindow::windowState() const
+{
+ // FIXME: Support compound states (Qt::WindowStates)
+
+ NSWindow *window = m_view.window;
+ if (window.miniaturized)
+ return Qt::WindowMinimized;
+ if (window.qt_fullScreen)
+ return Qt::WindowFullScreen;
+ if ((window.zoomed && !isTransitioningToFullScreen())
+ || (m_lastReportedWindowState == Qt::WindowMaximized && isTransitioningToFullScreen()))
+ return Qt::WindowMaximized;
+
+ // Note: We do not report Qt::WindowActive, even if isActive()
+ // is true, as QtGui does not expect this window state to be set.
+
+ return Qt::WindowNoState;
+}
+
+void QCocoaWindow::reportCurrentWindowState(bool unconditionally)
+{
+ Qt::WindowState currentState = windowState();
+ if (!unconditionally && currentState == m_lastReportedWindowState)
+ return;
- // New state is now the current synched state
- m_synchedWindowState = predictedState;
+ QWindowSystemInterface::handleWindowStateChanged<QWindowSystemInterface::SynchronousDelivery>(
+ window(), currentState, m_lastReportedWindowState);
+ m_lastReportedWindowState = currentState;
}
bool QCocoaWindow::setWindowModified(bool modified)
@@ -1671,7 +1996,7 @@ void QCocoaWindow::setWindowCursor(NSCursor *cursor)
return;
// Setting a cursor in a foregin view is not supported.
- if (window()->type() == Qt::ForeignWindow)
+ if (isForeignWindow())
return;
[m_windowCursor release];
@@ -1814,15 +2139,13 @@ void QCocoaWindow::exposeWindow()
if (!isWindowExposable())
return;
- // Update the QWindow's screen property. This property is set
- // to QGuiApplication::primaryScreen() at QWindow construciton
- // time, and we won't get a NSWindowDidChangeScreenNotification
- // on show. The case where the window is initially displayed
- // on a non-primary screen needs special handling here.
- NSUInteger screenIndex = [[NSScreen screens] indexOfObject:m_nsWindow.screen];
- if (screenIndex != NSNotFound) {
- QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenAtIndex(screenIndex);
- if (cocoaScreen)
+ if (window()->isTopLevel()) {
+ // Update the QWindow's screen property. This property is set
+ // to QGuiApplication::primaryScreen() at QWindow construciton
+ // time, and we won't get a NSWindowDidChangeScreenNotification
+ // on show. The case where the window is initially displayed
+ // on a non-primary screen needs special handling here.
+ if (QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenForNSScreen(m_nsWindow.screen))
window()->setScreen(cocoaScreen->screen());
}
diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h
index f226547b90..75a508370f 100644
--- a/src/plugins/platforms/cocoa/qnsview.h
+++ b/src/plugins/platforms/cocoa/qnsview.h
@@ -74,7 +74,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
QStringList *currentCustomDragTypes;
bool m_sendUpAsRightButton;
Qt::KeyboardModifiers currentWheelModifiers;
- bool m_subscribesForGlobalFrameNotifications;
#ifndef QT_NO_OPENGL
QCocoaGLContext *m_glContext;
bool m_shouldSetGLContextinDrawRect;
@@ -101,9 +100,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
- (void)drawRect:(NSRect)dirtyRect;
- (void)drawBackingStoreUsingCoreGraphics:(NSRect)dirtyRect;
- (void)updateGeometry;
-- (void)notifyWindowStateChanged:(Qt::WindowState)newState;
-- (void)windowNotification : (NSNotification *) windowNotification;
-- (void)notifyWindowWillZoom:(BOOL)willZoom;
- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification;
- (void)viewDidHide;
- (void)viewDidUnhide;
@@ -152,6 +148,11 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper));
@end
+@interface QT_MANGLE_NAMESPACE(QNSView) (QtExtras)
+@property (nonatomic, readonly) QCocoaWindow *platformWindow;
+@property (nonatomic, readonly) BOOL isMenuView;
+@end
+
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSView);
#endif //QNSVIEW_H
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 970ce9e785..fbf4f99f2e 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -149,7 +149,6 @@ static bool _q_dontOverrideCtrlLMB = false;
m_acceptedMouseDowns = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
m_sendKeyEvent = false;
- m_subscribesForGlobalFrameNotifications = false;
#ifndef QT_NO_OPENGL
m_glContext = 0;
m_shouldSetGLContextinDrawRect = false;
@@ -181,7 +180,6 @@ static bool _q_dontOverrideCtrlLMB = false;
CGImageRelease(m_maskImage);
[m_trackingArea release];
m_maskImage = 0;
- m_subscribesForGlobalFrameNotifications = false;
[m_inputSource release];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[m_mouseMoveHelper release];
@@ -217,11 +215,6 @@ static bool _q_dontOverrideCtrlLMB = false;
#endif
[self registerDragTypes];
- [self setPostsFrameChangedNotifications : YES];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(updateGeometry)
- name:NSViewFrameDidChangeNotification
- object:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:)
@@ -240,27 +233,9 @@ static bool _q_dontOverrideCtrlLMB = false;
//was unable to set view
m_shouldSetGLContextinDrawRect = true;
}
-
- if (!m_subscribesForGlobalFrameNotifications) {
- // NSOpenGLContext expects us to repaint (or update) the view when
- // it changes position on screen. Since this happens unnoticed for
- // the view when the parent view moves, we need to register a special
- // notification that lets us handle this case:
- m_subscribesForGlobalFrameNotifications = true;
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(globalFrameChanged:)
- name:NSViewGlobalFrameDidChangeNotification
- object:self];
- }
}
#endif
-- (void) globalFrameChanged:(NSNotification*)notification
-{
- Q_UNUSED(notification);
- m_platformWindow->updateExposedGeometry();
-}
-
- (void)viewDidMoveToSuperview
{
if (!(m_platformWindow->m_viewIsToBeEmbedded))
@@ -281,22 +256,6 @@ static bool _q_dontOverrideCtrlLMB = false;
m_backingStore = Q_NULLPTR;
}
-- (void)viewWillMoveToWindow:(NSWindow *)newWindow
-{
- // ### Merge "normal" window code path with this one for 5.1.
- if (!(m_platformWindow->window()->type() & Qt::SubWindow))
- return;
-
- if (newWindow) {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(windowNotification:)
- name:nil // Get all notifications
- object:newWindow];
- }
- if ([self window])
- [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:[self window]];
-}
-
- (QWindow *)topLevelWindow
{
QWindow *focusWindow = m_platformWindow->window();
@@ -316,7 +275,7 @@ static bool _q_dontOverrideCtrlLMB = false;
{
QRect geometry;
- if (m_platformWindow->m_isNSWindowChild) {
+ if (self.window.parentWindow) {
return;
#if 0
//geometry = QRectF::fromCGRect([self frame]).toRect();
@@ -336,10 +295,10 @@ static bool _q_dontOverrideCtrlLMB = false;
geometry = QRect(windowRect.origin.x, qt_mac_flipYCoordinate(windowRect.origin.y + rect.size.height), rect.size.width, rect.size.height);
} else if (m_platformWindow->m_viewIsToBeEmbedded) {
// embedded child window, use the frame rect ### merge with case below
- geometry = QRectF::fromCGRect([self bounds]).toRect();
+ geometry = QRectF::fromCGRect(NSRectToCGRect([self bounds])).toRect();
} else {
// child window, use the frame rect
- geometry = QRectF::fromCGRect([self frame]).toRect();
+ geometry = QRectF::fromCGRect(NSRectToCGRect([self frame])).toRect();
}
if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry())
@@ -380,79 +339,6 @@ static bool _q_dontOverrideCtrlLMB = false;
}
}
-- (void)notifyWindowStateChanged:(Qt::WindowState)newState
-{
- // If the window was maximized, then fullscreen, then tried to go directly to "normal" state,
- // this notification will say that it is "normal", but it will still look maximized, and
- // if you called performZoom it would actually take it back to "normal".
- // So we should say that it is maximized because it actually is.
- if (newState == Qt::WindowNoState && m_platformWindow->m_effectivelyMaximized)
- newState = Qt::WindowMaximized;
- QWindowSystemInterface::handleWindowStateChanged(m_platformWindow->window(), newState);
- // We want to read the window state back from the window,
- // but the event we just sent may be asynchronous.
- QWindowSystemInterface::flushWindowSystemEvents();
- m_platformWindow->setSynchedWindowStateFromWindow();
-}
-
-- (void)windowNotification : (NSNotification *) windowNotification
-{
- //qDebug() << "windowNotification" << QString::fromNSString([windowNotification name]);
-
- NSString *notificationName = [windowNotification name];
- if (notificationName == NSWindowDidBecomeKeyNotification) {
- if (!m_platformWindow->windowIsPopupType() && !m_isMenuView)
- QWindowSystemInterface::handleWindowActivated(m_platformWindow->window());
- } else if (notificationName == NSWindowDidResignKeyNotification) {
- // key window will be non-nil if another window became key... do not
- // set the active window to zero here, the new key window's
- // NSWindowDidBecomeKeyNotification hander will change the active window
- NSWindow *keyWindow = [NSApp keyWindow];
- if (!keyWindow || keyWindow == windowNotification.object) {
- // no new key window, go ahead and set the active window to zero
- if (!m_platformWindow->windowIsPopupType() && !m_isMenuView)
- QWindowSystemInterface::handleWindowActivated(0);
- }
- } else if (notificationName == NSWindowDidMiniaturizeNotification
- || notificationName == NSWindowDidDeminiaturizeNotification) {
- Qt::WindowState newState = notificationName == NSWindowDidMiniaturizeNotification ?
- Qt::WindowMinimized : Qt::WindowNoState;
- [self notifyWindowStateChanged:newState];
- } else if ([notificationName isEqualToString: @"NSWindowDidOrderOffScreenNotification"]) {
- m_platformWindow->obscureWindow();
- } else if ([notificationName isEqualToString: @"NSWindowDidOrderOnScreenAndFinishAnimatingNotification"]) {
- m_platformWindow->exposeWindow();
- } else if ([notificationName isEqualToString:NSWindowDidChangeOcclusionStateNotification]) {
- // Several unit tests expect paint and/or expose events for windows that are
- // sometimes (unpredictably) occluded and some unit tests depend on QWindow::isExposed -
- // don't send Expose/Obscure events when running under QTestLib.
- static const bool onTestLib = qt_mac_resolveOption(false, "QT_QTESTLIB_RUNNING");
- if (!onTestLib) {
- if ((NSUInteger)[self.window occlusionState] & NSWindowOcclusionStateVisible) {
- m_platformWindow->exposeWindow();
- } else {
- // Send Obscure events on window occlusion to stop animations.
- m_platformWindow->obscureWindow();
- }
- }
- } else if (notificationName == NSWindowDidChangeScreenNotification) {
- if (m_platformWindow->window()) {
- NSUInteger screenIndex = [[NSScreen screens] indexOfObject:self.window.screen];
- if (screenIndex != NSNotFound) {
- QCocoaScreen *cocoaScreen = QCocoaIntegration::instance()->screenAtIndex(screenIndex);
- if (cocoaScreen)
- QWindowSystemInterface::handleWindowScreenChanged(m_platformWindow->window(), cocoaScreen->screen());
- m_platformWindow->updateExposedGeometry();
- }
- }
- } else if (notificationName == NSWindowDidEnterFullScreenNotification
- || notificationName == NSWindowDidExitFullScreenNotification) {
- Qt::WindowState newState = notificationName == NSWindowDidEnterFullScreenNotification ?
- Qt::WindowFullScreen : Qt::WindowNoState;
- [self notifyWindowStateChanged:newState];
- }
-}
-
- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification
{
Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification)
@@ -462,14 +348,6 @@ static bool _q_dontOverrideCtrlLMB = false;
}
}
-- (void)notifyWindowWillZoom:(BOOL)willZoom
-{
- Qt::WindowState newState = willZoom ? Qt::WindowMaximized : Qt::WindowNoState;
- if (!willZoom)
- m_platformWindow->m_effectivelyMaximized = false;
- [self notifyWindowStateChanged:newState];
-}
-
- (void)viewDidHide
{
m_platformWindow->obscureWindow();
@@ -553,7 +431,7 @@ static bool _q_dontOverrideCtrlLMB = false;
- (void) drawRect:(NSRect)dirtyRect
{
- qCDebug(lcQpaCocoaWindow) << "[QNSView drawRect:]" << m_platformWindow->window() << QRectF::fromCGRect(dirtyRect);
+ qCDebug(lcQpaCocoaWindow) << "[QNSView drawRect:]" << m_platformWindow->window() << QRectF::fromCGRect(NSRectToCGRect(dirtyRect));
#ifndef QT_NO_OPENGL
if (m_glContext && m_shouldSetGLContextinDrawRect) {
@@ -1333,7 +1211,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
- (bool)handleGestureAsBeginEnd:(NSEvent *)event
{
- if (QSysInfo::QSysInfo::MacintoshVersion < QSysInfo::MV_10_11)
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::OSXElCapitan)
return false;
if ([event phase] == NSEventPhaseBegan) {
@@ -2199,3 +2077,17 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
}
@end
+
+@implementation QT_MANGLE_NAMESPACE(QNSView) (QtExtras)
+
+- (QCocoaWindow*)platformWindow
+{
+ return m_platformWindow.data();;
+}
+
+- (BOOL)isMenuView
+{
+ return m_isMenuView;
+}
+
+@end
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h
index f29aa97b68..d2078b5786 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.h
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h
@@ -49,15 +49,9 @@
QCocoaWindow *m_cocoaWindow;
}
-- (id)initWithQCocoaWindow: (QCocoaWindow *) cocoaWindow;
+- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow;
-- (void)windowDidBecomeKey:(NSNotification *)notification;
-- (void)windowDidResize:(NSNotification *)notification;
-- (void)windowDidMove:(NSNotification *)notification;
-- (void)windowWillMove:(NSNotification *)notification;
- (BOOL)windowShouldClose:(NSNotification *)notification;
-- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame;
-- (void)windowWillClose:(NSNotification *)notification;
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu;
- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard;
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
index 7f988ac963..ce74aa9973 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
@@ -41,61 +41,17 @@
#include "qcocoahelpers.h"
#include <QDebug>
+#include <qpa/qplatformscreen.h>
#include <qpa/qwindowsysteminterface.h>
@implementation QNSWindowDelegate
-- (id) initWithQCocoaWindow: (QCocoaWindow *) cocoaWindow
+- (id)initWithQCocoaWindow:(QCocoaWindow *)cocoaWindow
{
- self = [super init];
-
- if (self) {
+ if (self = [super init])
m_cocoaWindow = cocoaWindow;
- }
- return self;
-}
-
-- (void)windowDidBecomeKey:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow->m_windowUnderMouse) {
- QPointF windowPoint;
- QPointF screenPoint;
- [qnsview_cast(m_cocoaWindow->view()) convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleEnterEvent(m_cocoaWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint);
- }
-}
-- (void)windowDidResize:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow) {
- m_cocoaWindow->windowDidResize();
- }
-}
-
-- (void)windowDidEndLiveResize:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow) {
- m_cocoaWindow->windowDidEndLiveResize();
- }
-}
-
-- (void)windowWillMove:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow) {
- m_cocoaWindow->windowWillMove();
- }
-}
-
-- (void)windowDidMove:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow) {
- m_cocoaWindow->windowDidMove();
- }
+ return self;
}
- (BOOL)windowShouldClose:(NSNotification *)notification
@@ -107,20 +63,19 @@
return YES;
}
-
-- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+/*!
+ Overridden to ensure that the zoomed state always results in a maximized
+ window, which would otherwise not be the case for borderless windows.
+*/
+- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame
{
Q_UNUSED(newFrame);
- if (m_cocoaWindow && m_cocoaWindow->window()->type() != Qt::ForeignWindow)
- [qnsview_cast(m_cocoaWindow->view()) notifyWindowWillZoom:![window isZoomed]];
- return YES;
-}
-- (void)windowWillClose:(NSNotification *)notification
-{
- Q_UNUSED(notification);
- if (m_cocoaWindow)
- m_cocoaWindow->windowWillClose();
+ // We explicitly go through the QScreen API here instead of just using
+ // window.screen.visibleFrame directly, as that ensures we have the same
+ // behavior for both use-cases/APIs.
+ Q_ASSERT(window == m_cocoaWindow->nativeWindow());
+ return m_cocoaWindow->screen()->availableGeometry().toCGRect();
}
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu