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.pro41
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm48
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm85
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoanativeinterface.mm10
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemsettings.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm28
-rw-r--r--src/plugins/platforms/cocoa/qnsview_dragging.mm5
-rw-r--r--src/plugins/platforms/cocoa/qnsview_drawing.mm208
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm4
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm14
-rw-r--r--src/plugins/platforms/cocoa/qpaintengine_mac.mm4
16 files changed, 289 insertions, 188 deletions
diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro
index 02e00039ae..4cf9e64447 100644
--- a/src/plugins/platforms/cocoa/cocoa.pro
+++ b/src/plugins/platforms/cocoa/cocoa.pro
@@ -21,8 +21,6 @@ SOURCES += main.mm \
qcocoamenuloader.mm \
qcocoahelpers.mm \
qmultitouch_mac.mm \
- qcocoaaccessibilityelement.mm \
- qcocoaaccessibility.mm \
qcocoacursor.mm \
qcocoaclipboard.mm \
qcocoadrag.mm \
@@ -57,8 +55,6 @@ HEADERS += qcocoaintegration.h \
qcocoamenuloader.h \
qcocoahelpers.h \
qmultitouch_mac_p.h \
- qcocoaaccessibilityelement.h \
- qcocoaaccessibility.h \
qcocoacursor.h \
qcocoaclipboard.h \
qcocoadrag.h \
@@ -83,13 +79,21 @@ qtConfig(vulkan) {
HEADERS += qcocoavulkaninstance.h
}
+qtConfig(accessibility) {
+ QT += accessibility_support-private
+ SOURCES += qcocoaaccessibilityelement.mm \
+ qcocoaaccessibility.mm
+ HEADERS += qcocoaaccessibilityelement.h \
+ qcocoaaccessibility.h
+}
+
RESOURCES += qcocoaresources.qrc
LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -framework IOSurface -lcups
QT += \
core-private gui-private \
- accessibility_support-private clipboard_support-private theme_support-private \
+ clipboard_support-private theme_support-private \
fontdatabase_support-private graphics_support-private
qtConfig(vulkan): QT += vulkan_support-private
@@ -99,17 +103,20 @@ CONFIG += no_app_extension_api_only
qtHaveModule(widgets) {
QT_FOR_CONFIG += widgets
- SOURCES += \
- qpaintengine_mac.mm \
- qprintengine_mac.mm \
- qcocoaprintersupport.mm \
- qcocoaprintdevice.mm \
-
- HEADERS += \
- qpaintengine_mac_p.h \
- qprintengine_mac_p.h \
- qcocoaprintersupport.h \
- qcocoaprintdevice.h \
+ SOURCES += qpaintengine_mac.mm
+ HEADERS += qpaintengine_mac_p.h
+
+ qtHaveModule(printsupport) {
+ QT += printsupport-private
+ SOURCES += \
+ qprintengine_mac.mm \
+ qcocoaprintersupport.mm \
+ qcocoaprintdevice.mm
+ HEADERS += \
+ qcocoaprintersupport.h \
+ qcocoaprintdevice.h \
+ qprintengine_mac_p.h
+ }
qtConfig(colordialog) {
SOURCES += qcocoacolordialoghelper.mm
@@ -126,7 +133,7 @@ qtHaveModule(widgets) {
HEADERS += qcocoafontdialoghelper.h
}
- QT += widgets-private printsupport-private
+ QT += widgets-private
}
OTHER_FILES += cocoa.json
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 2cf6672da9..2df85c791b 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -171,12 +171,8 @@ QT_USE_NAMESPACE
// This function will only be called when NSApp is actually running.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
- // The reflection delegate gets precedence
- if (reflectionDelegate) {
- if ([reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)])
- return [reflectionDelegate applicationShouldTerminate:sender];
- return NSTerminateNow;
- }
+ if ([reflectionDelegate respondsToSelector:_cmd])
+ return [reflectionDelegate applicationShouldTerminate:sender];
if ([self canQuit]) {
if (!startedQuit) {
@@ -228,10 +224,6 @@ QT_USE_NAMESPACE
*/
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
[eventManager setEventHandler:self
- andSelector:@selector(appleEventQuit:withReplyEvent:)
- forEventClass:kCoreEventClass
- andEventID:kAEQuitApplication];
- [eventManager setEventHandler:self
andSelector:@selector(getUrl:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
@@ -241,7 +233,6 @@ QT_USE_NAMESPACE
- (void)removeAppleEventHandlers
{
NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager];
- [eventManager removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEQuitApplication];
[eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
}
@@ -282,26 +273,22 @@ QT_USE_NAMESPACE
QWindowSystemInterface::handleFileOpenEvent(qtFileName);
}
- if (reflectionDelegate &&
- [reflectionDelegate respondsToSelector:@selector(application:openFiles:)])
+ if ([reflectionDelegate respondsToSelector:_cmd])
[reflectionDelegate application:sender openFiles:filenames];
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
- // If we have a reflection delegate, that will get to call the shots.
- if (reflectionDelegate
- && [reflectionDelegate respondsToSelector:
- @selector(applicationShouldTerminateAfterLastWindowClosed:)])
+ if ([reflectionDelegate respondsToSelector:_cmd])
return [reflectionDelegate applicationShouldTerminateAfterLastWindowClosed:sender];
+
return NO; // Someday qApp->quitOnLastWindowClosed(); when QApp and NSApp work closer together.
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
- if (reflectionDelegate
- && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)])
+ if ([reflectionDelegate respondsToSelector:_cmd])
[reflectionDelegate applicationDidBecomeActive:notification];
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
@@ -309,8 +296,7 @@ QT_USE_NAMESPACE
- (void)applicationDidResignActive:(NSNotification *)notification
{
- if (reflectionDelegate
- && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)])
+ if ([reflectionDelegate respondsToSelector:_cmd])
[reflectionDelegate applicationDidResignActive:notification];
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
@@ -318,10 +304,7 @@ QT_USE_NAMESPACE
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
{
- Q_UNUSED(theApplication);
- Q_UNUSED(flag);
- if (reflectionDelegate
- && [reflectionDelegate respondsToSelector:@selector(applicationShouldHandleReopen:hasVisibleWindows:)])
+ if ([reflectionDelegate respondsToSelector:_cmd])
return [reflectionDelegate applicationShouldHandleReopen:theApplication hasVisibleWindows:flag];
/*
@@ -354,16 +337,13 @@ QT_USE_NAMESPACE
- (BOOL)respondsToSelector:(SEL)aSelector
{
- BOOL result = [super respondsToSelector:aSelector];
- if (!result && reflectionDelegate)
- result = [reflectionDelegate respondsToSelector:aSelector];
- return result;
+ return [super respondsToSelector:aSelector] || [reflectionDelegate respondsToSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
SEL invocationSelector = [invocation selector];
- if (reflectionDelegate && [reflectionDelegate respondsToSelector:invocationSelector])
+ if ([reflectionDelegate respondsToSelector:invocationSelector])
[invocation invokeWithTarget:reflectionDelegate];
else
[self doesNotRecognizeSelector:invocationSelector];
@@ -375,14 +355,6 @@ QT_USE_NAMESPACE
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
QWindowSystemInterface::handleFileOpenEvent(QUrl(QString::fromNSString(urlString)));
}
-
-- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
-{
- Q_UNUSED(event);
- Q_UNUSED(replyEvent);
- [NSApp terminate:self];
-}
-
@end
@implementation QCocoaApplicationDelegate (Menus)
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 9be6814ae7..a874936ce6 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -54,6 +54,8 @@ class QCocoaBackingStore : public QRasterBackingStore
protected:
QCocoaBackingStore(QWindow *window);
QCFType<CGColorSpaceRef> colorSpace() const;
+ QMacNotificationObserver m_backingPropertiesObserver;
+ virtual void backingPropertiesChanged() = 0;
};
class QNSWindowBackingStore : public QCocoaBackingStore
@@ -69,6 +71,7 @@ private:
bool windowHasUnifiedToolbar() const;
QImage::Format format() const override;
void redrawRoundedBottomCorners(CGRect) const;
+ void backingPropertiesChanged() override;
};
class QCALayerBackingStore : public QCocoaBackingStore
@@ -115,6 +118,8 @@ private:
bool recreateBackBufferIfNeeded();
bool prepareForFlush();
+ void backingPropertiesChanged() override;
+
std::list<std::unique_ptr<GraphicsBuffer>> m_buffers;
};
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 15e0236107..b17302a640 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -51,6 +51,17 @@ QT_BEGIN_NAMESPACE
QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
: QRasterBackingStore(window)
{
+ // Ideally this would be plumbed from the platform layer to QtGui, and
+ // the QBackingStore would be recreated, but we don't have that code yet,
+ // so at least make sure we invalidate our backingstore when the backing
+ // properties (color space e.g.) are changed.
+ NSView *view = static_cast<QCocoaWindow *>(window->handle())->view();
+ m_backingPropertiesObserver = QMacNotificationObserver(view.window,
+ NSWindowDidChangeBackingPropertiesNotification, [this]() {
+ qCDebug(lcQpaBackingStore) << "Backing properties for"
+ << this->window() << "did change";
+ backingPropertiesChanged();
+ });
}
QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
@@ -64,6 +75,37 @@ QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window)
: QCocoaBackingStore(window)
{
+ // Choose an appropriate window depth based on the requested surface format.
+ // On deep color displays the default bit depth is 16-bit, so unless we need
+ // that level of precision we opt out of it (and the expensive RGB32 -> RGB64
+ // conversions that come with it if our backingstore depth does not match).
+
+ NSWindow *nsWindow = static_cast<QCocoaWindow *>(window->handle())->view().window;
+ auto colorSpaceName = NSColorSpaceFromDepth(nsWindow.depthLimit);
+
+ static const int kDefaultBitDepth = 8;
+ auto surfaceFormat = window->requestedFormat();
+ auto bitsPerSample = qMax(kDefaultBitDepth, qMax(surfaceFormat.redBufferSize(),
+ qMax(surfaceFormat.greenBufferSize(), surfaceFormat.blueBufferSize())));
+
+ // NSBestDepth does not seem to guarantee a window depth deep enough for the
+ // given bits per sample, even if documented as such. For example, requesting
+ // 10 bits per sample will not give us a 16-bit format, even if that's what's
+ // available. Work around this by manually bumping the bit depth.
+ bitsPerSample = !(bitsPerSample & (bitsPerSample - 1))
+ ? bitsPerSample : qNextPowerOfTwo(bitsPerSample);
+
+ auto bestDepth = NSBestDepth(colorSpaceName, bitsPerSample, 0, NO, nullptr);
+
+ // Disable dynamic depth limit, otherwise our depth limit will be overwritten
+ // by AppKit if the window moves to a screen with a different depth. We call
+ // this before setting the depth limit, as the call will reset the depth to 0.
+ [nsWindow setDynamicDepthLimit:NO];
+
+ qCDebug(lcQpaBackingStore) << "Using" << NSBitsPerSampleFromDepth(bestDepth)
+ << "bit window depth for" << nsWindow;
+
+ nsWindow.depthLimit = bestDepth;
}
QNSWindowBackingStore::~QNSWindowBackingStore()
@@ -212,9 +254,6 @@ void QNSWindowBackingStore::flush(QWindow *window, const QRegion &region, const
CGRect viewRect = viewLocalRect.toCGRect();
- if (windowHasUnifiedToolbar())
- NSDrawWindowBackground(viewRect);
-
[backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect()
operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil];
@@ -302,6 +341,11 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
#endif
}
+void QNSWindowBackingStore::backingPropertiesChanged()
+{
+ m_image = QImage();
+}
+
// ----------------------------------------------------------------------------
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
@@ -516,17 +560,26 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
flushedView.layer.contents = nil;
}
- qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface
- << "to" << flushedView.layer << "of" << flushedView;
-
- flushedView.layer.contents = backBufferSurface;
+ if (flushedView == backingStoreView) {
+ qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface
+ << "to" << flushedView.layer << "of" << flushedView;
+ flushedView.layer.contents = backBufferSurface;
+ } else {
+ auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView];
+ auto scale = flushedView.layer.contentsScale;
+ subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale));
+
+ // We make a copy of the image data up front, which means we don't
+ // need to mark the IOSurface as being in use. FIXME: Investigate
+ // if there's a cheaper way to get sub-image data to a layer.
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess);
+ QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect());
+ m_buffers.back()->unlock();
- if (flushedView != backingStoreView) {
- const CGSize backingStoreSize = backingStoreView.bounds.size;
- flushedView.layer.contentsRect = CGRectApplyAffineTransform(
- [flushedView convertRect:flushedView.bounds toView:backingStoreView],
- // The contentsRect is in unit coordinate system
- CGAffineTransformMakeScale(1.0 / backingStoreSize.width, 1.0 / backingStoreSize.height));
+ qCInfo(lcQpaBackingStore) << "Flushing" << subImage
+ << "to" << flushedView.layer << "of subview" << flushedView;
+ QCFType<CGImageRef> cgImage = subImage.toCGImage();
+ flushedView.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage);
}
// Since we may receive multiple flushes before a new frame is started, we do not
@@ -565,6 +618,12 @@ QImage QCALayerBackingStore::toImage() const
return imageCopy;
}
+void QCALayerBackingStore::backingPropertiesChanged()
+{
+ m_buffers.clear();
+ m_buffers.resize(1);
+}
+
QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const
{
return m_buffers.back().get();
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index d3bb0711f0..e87fc39c42 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -881,7 +881,7 @@ void QCocoaEventDispatcherPrivate::processPostedEvents()
return;
}
- int serial = serialNumber.load();
+ int serial = serialNumber.loadRelaxed();
if (!threadData->canWait || (serial != lastSerial)) {
lastSerial = serial;
QCoreApplication::sendPostedEvents();
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index 5f32400af0..03677ef0bc 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -214,7 +214,7 @@ static QString strippedText(QString s)
NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
- || [self panel:nil shouldEnableURL:url];
+ || [self panel:mOpenPanel shouldEnableURL:url];
[self updateProperties];
[mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""];
@@ -233,7 +233,7 @@ static QString strippedText(QString s)
NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
- || [self panel:nil shouldEnableURL:url];
+ || [self panel:mSavePanel shouldEnableURL:url];
[mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]];
[mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""];
@@ -263,7 +263,7 @@ static QString strippedText(QString s)
NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
- || [self panel:nil shouldEnableURL:url];
+ || [self panel:mSavePanel shouldEnableURL:url];
[self updateProperties];
[mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]];
diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
index 9bd19dd07c..d0e69bdca5 100644
--- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
+++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm
@@ -59,7 +59,7 @@
#include "qguiapplication.h"
#include <qdebug.h>
-#ifndef QT_NO_WIDGETS
+#if !defined(QT_NO_WIDGETS) && defined(QT_PRINTSUPPORT_LIB)
#include "qcocoaprintersupport.h"
#include "qprintengine_mac_p.h"
#include <qpa/qplatformprintersupport.h>
@@ -153,24 +153,24 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter
QPlatformPrinterSupport *QCocoaNativeInterface::createPlatformPrinterSupport()
{
-#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER)
+#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB)
return new QCocoaPrinterSupport();
#else
- qFatal("Printing is not supported when Qt is configured with -no-widgets");
+ qFatal("Printing is not supported when Qt is configured with -no-widgets or -no-feature-printer");
return nullptr;
#endif
}
void *QCocoaNativeInterface::NSPrintInfoForPrintEngine(QPrintEngine *printEngine)
{
-#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER)
+#if !defined(QT_NO_WIDGETS) && !defined(QT_NO_PRINTER) && defined(QT_PRINTSUPPORT_LIB)
QMacPrintEnginePrivate *macPrintEnginePriv = static_cast<QMacPrintEngine *>(printEngine)->d_func();
if (macPrintEnginePriv->state == QPrinter::Idle && !macPrintEnginePriv->isPrintSessionInitialized())
macPrintEnginePriv->initialize();
return macPrintEnginePriv->printInfo;
#else
Q_UNUSED(printEngine);
- qFatal("Printing is not supported when Qt is configured with -no-widgets");
+ qFatal("Printing is not supported when Qt is configured with -no-widgets or -no-feature-printer");
return nullptr;
#endif
}
diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
index 9b6dc94d33..cb25bd7d81 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -52,6 +52,8 @@
@property (class, strong, readonly) NSColor *unemphasizedSelectedTextColor NS_AVAILABLE_MAC(10_14);
@property (class, strong, readonly) NSColor *unemphasizedSelectedContentBackgroundColor NS_AVAILABLE_MAC(10_14);
@property (class, strong, readonly) NSArray<NSColor *> *alternatingContentBackgroundColors NS_AVAILABLE_MAC(10_14);
+// Missing from non-Mojave SDKs, even if introduced in 10.10
+@property (class, strong, readonly) NSColor *linkColor NS_AVAILABLE_MAC(10_10);
@end
#endif
@@ -111,6 +113,8 @@ QPalette * qt_mac_createSystemPalette()
palette->setBrush(QPalette::ToolTipBase, qt_mac_toQBrush([NSColor controlColor]));
+ palette->setColor(QPalette::Normal, QPalette::Link, qt_mac_toQColor([NSColor linkColor]));
+
return palette;
}
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
index 6779bda491..738c40aba6 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h
@@ -65,7 +65,7 @@ public:
void updateMenu(QPlatformMenu *menu) override;
QRect geometry() const override;
void showMessage(const QString &title, const QString &msg,
- const QIcon& icon, MessageIcon iconType, int secs) override;
+ const QIcon& icon, MessageIcon iconType, int msecs) override;
bool isSystemTrayAvailable() const override;
bool supportsMessages() const override;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index db64702b8d..1390ace632 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -264,7 +264,7 @@ bool QCocoaSystemTrayIcon::supportsMessages() const
}
void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &message,
- const QIcon& icon, MessageIcon, int)
+ const QIcon& icon, MessageIcon, int msecs)
{
if (!m_sys)
return;
@@ -282,6 +282,10 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess
NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
center.delegate = m_sys->item;
[center deliverNotification:notification];
+ if (msecs) {
+ NSTimeInterval timeout = msecs / 1000.0;
+ [center performSelector:@selector(removeDeliveredNotification:) withObject:notification afterDelay:timeout];
+ }
[notification release];
}
QT_END_NAMESPACE
@@ -434,8 +438,7 @@ QT_END_NAMESPACE
}
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
- Q_UNUSED(center);
- Q_UNUSED(notification);
+ [center removeDeliveredNotification:notification];
emit systray->messageClicked();
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 7621508bdd..f212b3e3f0 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1272,14 +1272,11 @@ void QCocoaWindow::windowWillClose()
bool QCocoaWindow::windowShouldClose()
{
qCDebug(lcQpaWindow) << "QCocoaWindow::windowShouldClose" << window();
- // This callback should technically only determine if the window
- // should (be allowed to) close, but since our QPA API to determine
- // that also involves actually closing the window we do both at the
- // same time, instead of doing the latter in windowWillClose.
- bool accepted = false;
- QWindowSystemInterface::handleCloseEvent(window(), &accepted);
- QWindowSystemInterface::flushWindowSystemEvents();
- return accepted;
+ // This callback should technically only determine if the window
+ // should (be allowed to) close, but since our QPA API to determine
+ // that also involves actually closing the window we do both at the
+ // same time, instead of doing the latter in windowWillClose.
+ return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window());
}
// ----------------------------- QPA forwarding -----------------------------
@@ -1646,21 +1643,6 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
applyContentBorderThickness(nsWindow);
- // Prevent CoreGraphics RGB32 -> RGB64 backing store conversions on deep color
- // displays by forcing 8-bit components, unless a deep color format has been
- // requested. This conversion uses significant CPU time.
- QSurface::SurfaceType surfaceType = QPlatformWindow::window()->surfaceType();
- bool usesCoreGraphics = surfaceType == QSurface::RasterSurface || surfaceType == QSurface::RasterGLSurface;
- QSurfaceFormat surfaceFormat = QPlatformWindow::window()->format();
- bool usesDeepColor = surfaceFormat.redBufferSize() > 8 ||
- surfaceFormat.greenBufferSize() > 8 ||
- surfaceFormat.blueBufferSize() > 8;
- bool usesLayer = view().layer;
- if (usesCoreGraphics && !usesDeepColor && !usesLayer) {
- [nsWindow setDynamicDepthLimit:NO];
- [nsWindow setDepthLimit:NSWindowDepthTwentyfourBitRGB];
- }
-
if (format().colorSpace() == QSurfaceFormat::sRGBColorSpace)
nsWindow.colorSpace = NSColorSpace.sRGBColorSpace;
diff --git a/src/plugins/platforms/cocoa/qnsview_dragging.mm b/src/plugins/platforms/cocoa/qnsview_dragging.mm
index 37e972dba9..41b96b2df6 100644
--- a/src/plugins/platforms/cocoa/qnsview_dragging.mm
+++ b/src/plugins/platforms/cocoa/qnsview_dragging.mm
@@ -270,6 +270,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
// The drag was started from within the application
response = QWindowSystemInterface::handleDrop(target, nativeDrag->dragMimeData(),
point, qtAllowed, buttons, modifiers);
+ nativeDrag->setAcceptedAction(response.acceptedAction());
} else {
QCocoaDropData mimeData(sender.draggingPasteboard);
response = QWindowSystemInterface::handleDrop(target, &mimeData,
@@ -282,6 +283,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
{
Q_UNUSED(session);
Q_UNUSED(screenPoint);
+ Q_UNUSED(operation);
if (!m_platformWindow)
return;
@@ -290,8 +292,7 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
if (!target)
return;
- QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
- nativeDrag->setAcceptedAction(qt_mac_mapNSDragOperation(operation));
+ QCocoaIntegration::instance()->drag();
// Qt starts drag-and-drop on a mouse button press event. Cococa in
// this case won't send the matching release event, so we have to
diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm
index d2e6f848a0..ce5488ead0 100644
--- a/src/plugins/platforms/cocoa/qnsview_drawing.mm
+++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm
@@ -43,9 +43,7 @@
- (void)initDrawing
{
- self.wantsLayer = [self layerExplicitlyRequested]
- || [self shouldUseMetalLayer]
- || [self layerEnabledByMacOS];
+ [self updateLayerBacking];
// Enable high-DPI OpenGL for retina displays. Enabling has the side
// effect that Cocoa will start calling glViewport(0, 0, width, height),
@@ -71,22 +69,13 @@
return YES;
}
-- (void)drawRect:(NSRect)dirtyRect
-{
- Q_UNUSED(dirtyRect);
+// ----------------------- Layer setup -----------------------
- if (!m_platformWindow)
- return;
-
- QRegion exposedRegion;
- const NSRect *dirtyRects;
- NSInteger numDirtyRects;
- [self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects];
- for (int i = 0; i < numDirtyRects; ++i)
- exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect();
-
- qCDebug(lcQpaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion;
- m_platformWindow->handleExposeEvent(exposedRegion);
+- (void)updateLayerBacking
+{
+ self.wantsLayer = [self layerEnabledByMacOS]
+ || [self layerExplicitlyRequested]
+ || [self shouldUseMetalLayer];
}
- (BOOL)layerEnabledByMacOS
@@ -123,40 +112,81 @@
return surfaceType == QWindow::MetalSurface || surfaceType == QWindow::VulkanSurface;
}
+/*
+ This method is called by AppKit when layer-backing is requested by
+ setting wantsLayer too YES (via -[NSView _updateLayerBackedness]),
+ or in cases where AppKit itself decides that a view should be
+ layer-backed.
+
+ Note however that some code paths in AppKit will not go via this
+ method for creating the backing layer, and will instead create the
+ layer manually, and just call setLayer. An example of this is when
+ an NSOpenGLContext is attached to a view, in which case AppKit will
+ create a new layer in NSOpenGLContextSetLayerOnViewIfNecessary.
+
+ For this reason we leave the implementation of this override as
+ minimal as possible, only focusing on creating the appropriate
+ layer type, and then leave it up to setLayer to do the work of
+ making sure the layer is set up correctly.
+*/
- (CALayer *)makeBackingLayer
{
if ([self shouldUseMetalLayer]) {
// Check if Metal is supported. If it isn't then it's most likely
// too late at this point and the QWindow will be non-functional,
// but we can at least print a warning.
- if (![MTLCreateSystemDefaultDevice() autorelease]) {
- qWarning() << "QWindow initialization error: Metal is not supported";
- return [super makeBackingLayer];
+ if ([MTLCreateSystemDefaultDevice() autorelease]) {
+ return [CAMetalLayer layer];
+ } else {
+ qCWarning(lcQpaDrawing) << "Failed to create QWindow::MetalSurface."
+ << "Metal is not supported by any of the GPUs in this system.";
}
-
- CAMetalLayer *layer = [CAMetalLayer layer];
-
- // Set the contentsScale for the layer. This is normally done in
- // viewDidChangeBackingProperties, however on startup that function
- // is called before the layer is created here. The layer's drawableSize
- // is updated from layoutSublayersOfLayer as usual.
- layer.contentsScale = self.window.backingScaleFactor;
-
- return layer;
}
return [super makeBackingLayer];
}
+/*
+ This method is called by AppKit whenever the view is asked to change
+ its layer, which can happen both as a result of enabling layer-backing,
+ or when a layer is set explicitly. The latter can happen both when a
+ view is layer-hosting, or when AppKit internals are switching out the
+ layer-backed view, as described above for makeBackingLayer.
+*/
- (void)setLayer:(CALayer *)layer
{
- qCDebug(lcQpaDrawing) << "Making" << self << "layer-backed with" << layer
- << "due to being" << ([self layerExplicitlyRequested] ? "explicitly requested"
+ qCDebug(lcQpaDrawing) << "Making" << self
+ << (self.wantsLayer ? "layer-backed" : "layer-hosted")
+ << "with" << layer << "due to being" << ([self layerExplicitlyRequested] ? "explicitly requested"
: [self shouldUseMetalLayer] ? "needed by surface type" : "enabled by macOS");
+
+ if (layer.delegate && layer.delegate != self) {
+ qCWarning(lcQpaDrawing) << "Layer already has delegate" << layer.delegate
+ << "This delegate is responsible for all view updates for" << self;
+ } else {
+ layer.delegate = self;
+ }
+
[super setLayer:layer];
- layer.delegate = self;
+
+ // When adding a view to a view hierarchy the backing properties will change
+ // which results in updating the contents scale, but in case of switching the
+ // layer on a view that's already in a view hierarchy we need to manually ensure
+ // the scale is up to date.
+ if (self.superview)
+ [self updateLayerContentsScale];
+
+ if (self.opaque && lcQpaDrawing().isDebugEnabled()) {
+ // If the view claims to be opaque we expect it to fill the entire
+ // layer with content, in which case we want to detect any areas
+ // where it doesn't.
+ layer.backgroundColor = NSColor.magentaColor.CGColor;
+ }
+
}
+// ----------------------- Layer updates -----------------------
+
- (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy
{
// We need to set this explicitly since the super implementation
@@ -164,64 +194,106 @@
return NSViewLayerContentsRedrawDuringViewResize;
}
-#if 0 // Disabled until we enable lazy backingstore resizing
- (NSViewLayerContentsPlacement)layerContentsPlacement
{
- // Always place the layer at top left without any automatic scaling,
- // so that we can re-use larger layers when resizing a window down.
+ // Always place the layer at top left without any automatic scaling.
+ // This will highlight situations where we're missing content for the
+ // layer by not responding to the displayLayer: request synchronously.
+ // It also allows us to re-use larger layers when resizing a window down.
return NSViewLayerContentsPlacementTopLeft;
}
-#endif
-- (void)updateMetalLayerDrawableSize:(CAMetalLayer *)layer
+- (void)viewDidChangeBackingProperties
{
- CGSize drawableSize = layer.bounds.size;
- drawableSize.width *= layer.contentsScale;
- drawableSize.height *= layer.contentsScale;
- layer.drawableSize = drawableSize;
+ qCDebug(lcQpaDrawing) << "Backing properties changed for" << self;
+
+ if (self.layer)
+ [self updateLayerContentsScale];
+
+ // Ideally we would plumb this situation through QPA in a way that lets
+ // clients invalidate their own caches, recreate QBackingStore, etc.
+ // For now we trigger an expose, and let QCocoaBackingStore deal with
+ // buffer invalidation internally.
+ [self setNeedsDisplay:YES];
}
-- (void)layoutSublayersOfLayer:(CALayer *)layer
+- (void)updateLayerContentsScale
{
- if ([layer isKindOfClass:CAMetalLayer.class])
- [self updateMetalLayerDrawableSize:static_cast<CAMetalLayer* >(layer)];
+ // We expect clients to fill the layer with retina aware content,
+ // based on the devicePixelRatio of the QWindow, so we set the
+ // layer's content scale to match that. By going via devicePixelRatio
+ // instead of applying the NSWindow's backingScaleFactor, we also take
+ // into account OpenGL views with wantsBestResolutionOpenGLSurface set
+ // to NO. In this case the window will have a backingScaleFactor of 2,
+ // but the QWindow will have a devicePixelRatio of 1.
+ auto devicePixelRatio = m_platformWindow->devicePixelRatio();
+ qCDebug(lcQpaDrawing) << "Updating" << self.layer << "content scale to" << devicePixelRatio;
+ self.layer.contentsScale = devicePixelRatio;
}
-- (void)displayLayer:(CALayer *)layer
+/*
+ This method is called by AppKit to determine whether it should update
+ the contentScale of the layer to match the window backing scale.
+
+ We always return NO since we're updating the contents scale manually.
+*/
+- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)scale fromWindow:(NSWindow *)window
{
- if (!NSThread.isMainThread) {
- // Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads,
- // which we shouldn't do. This may result in AppKit (wrongly) triggering a display on
- // the thread where we made the call, so block it here and defer to the main thread.
- qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread";
- dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; });
- return;
- }
+ Q_UNUSED(layer); Q_UNUSED(scale); Q_UNUSED(window);
+ return NO;
+}
- Q_ASSERT(layer == self.layer);
+// ----------------------- Draw callbacks -----------------------
+
+/*
+ This method is called by AppKit for the non-layer case, where we are
+ drawing into the NSWindow's surface.
+*/
+- (void)drawRect:(NSRect)dirtyBoundingRect
+{
+ Q_ASSERT_X(!self.layer, "QNSView",
+ "The drawRect code path should not be hit when we are layer backed");
if (!m_platformWindow)
return;
- qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window();
+ QRegion exposedRegion;
+ const NSRect *dirtyRects;
+ NSInteger numDirtyRects;
+ [self getRectsBeingDrawn:&dirtyRects count:&numDirtyRects];
+ for (int i = 0; i < numDirtyRects; ++i)
+ exposedRegion += QRectF::fromCGRect(dirtyRects[i]).toRect();
- // FIXME: Find out if there's a way to resolve the dirty rect like in drawRect:
- m_platformWindow->handleExposeEvent(QRectF::fromCGRect(self.bounds).toRect());
+ if (exposedRegion.isEmpty())
+ exposedRegion = QRectF::fromCGRect(dirtyBoundingRect).toRect();
+
+ qCDebug(lcQpaDrawing) << "[QNSView drawRect:]" << m_platformWindow->window() << exposedRegion;
+ m_platformWindow->handleExposeEvent(exposedRegion);
}
-- (void)viewDidChangeBackingProperties
+/*
+ This method is called by AppKit when we are layer-backed, where
+ we are drawing into the layer.
+*/
+- (void)displayLayer:(CALayer *)layer
{
- CALayer *layer = self.layer;
- if (!layer)
- return;
+ Q_ASSERT_X(self.layer && layer == self.layer, "QNSView",
+ "The displayLayer code path should only be hit for our own layer");
- layer.contentsScale = self.window.backingScaleFactor;
+ if (!m_platformWindow)
+ return;
- // Metal layers must be manually updated on e.g. screen change
- if ([layer isKindOfClass:CAMetalLayer.class]) {
- [self updateMetalLayerDrawableSize:static_cast<CAMetalLayer* >(layer)];
- [self setNeedsDisplay:YES];
+ if (!NSThread.isMainThread) {
+ // Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads,
+ // which we shouldn't do. This may result in AppKit (wrongly) triggering a display on
+ // the thread where we made the call, so block it here and defer to the main thread.
+ qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread";
+ dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; });
+ return;
}
+
+ qCDebug(lcQpaDrawing) << "[QNSView displayLayer]" << m_platformWindow->window();
+ m_platformWindow->handleExposeEvent(QRectF::fromCGRect(self.bounds).toRect());
}
@end
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index 7c566442f0..30613eca32 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -394,14 +394,16 @@
}
// Close the popups if the click was outside.
if (!inside) {
+ bool selfClosed = false;
Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type();
while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) {
+ selfClosed = self == popup->view();
QWindowSystemInterface::handleCloseEvent(popup->window());
QWindowSystemInterface::flushWindowSystemEvents();
}
// Consume the mouse event when closing the popup, except for tool tips
// were it's expected that the event is processed normally.
- if (type != Qt::ToolTip)
+ if (type != Qt::ToolTip || selfClosed)
return;
}
}
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index 52f765eb31..6b4e110af2 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -253,20 +253,10 @@ static bool isMouseEvent(NSEvent *ev)
return m_platformWindow ? m_platformWindow->isOpaque() : [super isOpaque];
}
-/*!
- Borderless windows need a transparent background
-
- Technically windows with NSWindowStyleMaskTexturedBackground
- (such as windows with unified toolbars) need to draw the textured
- background of the NSWindow, and can't have a transparent
- background, but as NSWindowStyleMaskBorderless is 0, you can't
- have a window with NSWindowStyleMaskTexturedBackground that is
- also borderless.
-*/
- (NSColor *)backgroundColor
{
- return self.styleMask == NSWindowStyleMaskBorderless
- ? [NSColor clearColor] : [super backgroundColor];
+ return self.styleMask == NSWindowStyleMaskBorderless ?
+ [NSColor clearColor] : [super backgroundColor];
}
- (void)sendEvent:(NSEvent*)theEvent
diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm
index 3677877538..00b2267f0d 100644
--- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm
+++ b/src/plugins/platforms/cocoa/qpaintengine_mac.mm
@@ -38,14 +38,18 @@
****************************************************************************/
#include "qpaintengine_mac_p.h"
+#if defined(QT_PRINTSUPPORT_LIB)
#include "qprintengine_mac_p.h"
+#endif
#include <qbitmap.h>
#include <qpaintdevice.h>
#include <qpainterpath.h>
#include <qpixmapcache.h>
#include <private/qpaintengine_raster_p.h>
+#if defined(QT_PRINTSUPPORT_LIB)
#include <qprinter.h>
+#endif
#include <qstack.h>
#include <qwidget.h>
#include <qvarlengtharray.h>