diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r-- | src/plugins/platforms/cocoa/cocoa.pro | 41 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm | 48 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 85 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoanativeinterface.mm | 10 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemsettings.mm | 6 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemtrayicon.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 9 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 28 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_dragging.mm | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_drawing.mm | 208 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_mouse.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnswindow.mm | 14 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qpaintengine_mac.mm | 4 |
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 ®ion, 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 ®ion, 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> |