diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoahelpers.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.mm | 295 |
1 files changed, 212 insertions, 83 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 058209da7e..606b2e7032 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -1,36 +1,44 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include <qpa/qplatformtheme.h> + #include "qcocoahelpers.h" @@ -46,6 +54,27 @@ #include <algorithm> +#include <Carbon/Carbon.h> + +@interface NSGraphicsContext (QtAdditions) + ++ (NSGraphicsContext *)qt_graphicsContextWithCGContext:(CGContextRef)graphicsPort flipped:(BOOL)initialFlippedState; + +@end + +@implementation NSGraphicsContext (QtAdditions) + ++ (NSGraphicsContext *)qt_graphicsContextWithCGContext:(CGContextRef)graphicsPort flipped:(BOOL)initialFlippedState +{ +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_10, __IPHONE_NA) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_10) + return [self graphicsContextWithCGContext:graphicsPort flipped:initialFlippedState]; +#endif + return [self graphicsContextWithGraphicsPort:graphicsPort flipped:initialFlippedState]; +} + +@end + QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaCocoaWindow, "qt.qpa.cocoa.window"); @@ -238,6 +267,125 @@ QColor qt_mac_toQColor(CGColorRef color) return qtColor; } +QBrush qt_mac_toQBrush(CGColorRef color) +{ + QBrush qtBrush; + CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(color)); + if (model == kCGColorSpaceModelPattern) { + // Colorspace we can't deal with; the color is drawn directly using a callback. + qWarning("Qt: qt_mac_toQBrush: cannot convert from colorspace model: %d", model); + Q_ASSERT(false); + } else { + qtBrush.setStyle(Qt::SolidPattern); + qtBrush.setColor(qt_mac_toQColor(color)); + } + return qtBrush; +} + +static bool qt_mac_isSystemColorOrInstance(const NSColor *color, NSString *colorNameComponent, NSString *className) +{ + // We specifically do not want isKindOfClass: here + if ([color.className isEqualToString:className]) // NSPatternColorSpace + return true; + if ([color.catalogNameComponent isEqualToString:@"System"] && + [color.colorNameComponent isEqualToString:colorNameComponent] && + [color.colorSpaceName isEqualToString:NSNamedColorSpace]) + return true; + return false; +} + +QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup) +{ + QBrush qtBrush; + + // QTBUG-49773: This calls NSDrawMenuItemBackground to render a 1 by n gradient; could use HITheme + if ([color.className isEqualToString:@"NSMenuItemHighlightColor"]) { + qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMenuItemHighlightColor"); + return qtBrush; + } + + // Not a catalog color or a manifestation of System.windowBackgroundColor; + // only retrieved from NSWindow.backgroundColor directly + if ([color.className isEqualToString:@"NSMetalPatternColor"]) { + // NSTexturedBackgroundWindowMask, could theoretically handle this without private API by + // creating a window with the appropriate properties and then calling NSWindow.backgroundColor.patternImage, + // which returns a texture sized 1 by (window height, including frame), backed by a CGPattern + // which follows the window key state... probably need to allow QBrush to store a function pointer + // like CGPattern does + qWarning("Qt: qt_mac_toQBrush: cannot convert from NSMetalPatternColor"); + return qtBrush; + } + + // No public API to get these colors/stops; + // both accurately obtained through runtime object inspection on OS X 10.11 + // (the NSColor object has NSGradient i-vars for both color groups) + if (qt_mac_isSystemColorOrInstance(color, @"_sourceListBackgroundColor", @"NSSourceListBackgroundColor")) { + QLinearGradient gradient; + if (colorGroup == QPalette::Active) { + gradient.setColorAt(0, QColor(233, 237, 242)); + gradient.setColorAt(0.5, QColor(225, 229, 235)); + gradient.setColorAt(1, QColor(209, 216, 224)); + } else { + gradient.setColorAt(0, QColor(248, 248, 248)); + gradient.setColorAt(0.5, QColor(240, 240, 240)); + gradient.setColorAt(1, QColor(235, 235, 235)); + } + return QBrush(gradient); + } + + // A couple colors are special... they are actually instances of NSGradientPatternColor, which + // override set/setFill/setStroke to instead initialize an internal color + // ([NSColor colorWithCalibratedWhite:0.909804 alpha:1.000000]) while still returning the + // ruled lines pattern image (from OS X 10.4) to the user from -[NSColor patternImage] + // (and providing no public API to get the underlying color without this insanity) + if (qt_mac_isSystemColorOrInstance(color, @"controlColor", @"NSGradientPatternColor") || + qt_mac_isSystemColorOrInstance(color, @"windowBackgroundColor", @"NSGradientPatternColor")) { + static QColor newColor; + if (!newColor.isValid()) { +#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_NA) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + newColor = qt_mac_toQColor(color.CGColor); + } else +#endif + { + NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil + pixelsWide:1 + pixelsHigh:1 + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:4 + bitsPerPixel:32]; + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]]; + NSEraseRect(NSMakeRect(0, 0, 1, 1)); + [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)]; + [NSGraphicsContext restoreGraphicsState]; + NSUInteger pixel[4]; + [offscreenRep getPixel:pixel atX:0 y:0]; + [offscreenRep release]; + newColor = QColor(pixel[0], pixel[1], pixel[2], pixel[3]); + } + } + + qtBrush.setStyle(Qt::SolidPattern); + qtBrush.setColor(newColor); + return qtBrush; + } + + if (NSColor *patternColor = [color colorUsingColorSpaceName:NSPatternColorSpace]) { + NSImage *patternImage = patternColor.patternImage; + const QSizeF sz(patternImage.size.width, patternImage.size.height); + qtBrush.setTexture(qt_mac_toQPixmap(patternImage, sz)); // QTBUG-49774 + } else { + qtBrush.setStyle(Qt::SolidPattern); + qtBrush.setColor(qt_mac_toQColor(color)); + } + return qtBrush; +} + struct dndenum_mapper { NSDragOperation mac_code; @@ -304,88 +452,49 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) // Misc // -// Changes the process type for this process to kProcessTransformToForegroundApplication, +// Sets the activation policy for this process to NSApplicationActivationPolicyRegular, // unless either LSUIElement or LSBackgroundOnly is set in the Info.plist. void qt_mac_transformProccessToForegroundApplication() { - ProcessSerialNumber psn; - if (GetCurrentProcess(&psn) == noErr) { - bool forceTransform = true; - CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), - CFSTR("LSUIElement")); + bool forceTransform = true; + CFTypeRef value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), + CFSTR("LSUIElement")); + if (value) { + CFTypeID valueType = CFGetTypeID(value); + // Officially it's supposed to be a string, a boolean makes sense, so we'll check. + // A number less so, but OK. + if (valueType == CFStringGetTypeID()) + forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt()); + else if (valueType == CFBooleanGetTypeID()) + forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value)); + else if (valueType == CFNumberGetTypeID()) { + int valueAsInt; + CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt); + forceTransform = !valueAsInt; + } + } + + if (forceTransform) { + value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), + CFSTR("LSBackgroundOnly")); if (value) { CFTypeID valueType = CFGetTypeID(value); - // Officially it's supposed to be a string, a boolean makes sense, so we'll check. - // A number less so, but OK. - if (valueType == CFStringGetTypeID()) - forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt()); - else if (valueType == CFBooleanGetTypeID()) + if (valueType == CFBooleanGetTypeID()) forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value)); + else if (valueType == CFStringGetTypeID()) + forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt()); else if (valueType == CFNumberGetTypeID()) { int valueAsInt; CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt); forceTransform = !valueAsInt; } } - - if (forceTransform) { - value = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), - CFSTR("LSBackgroundOnly")); - if (value) { - CFTypeID valueType = CFGetTypeID(value); - if (valueType == CFBooleanGetTypeID()) - forceTransform = !CFBooleanGetValue(static_cast<CFBooleanRef>(value)); - else if (valueType == CFStringGetTypeID()) - forceTransform = !(QCFString::toQString(static_cast<CFStringRef>(value)).toInt()); - else if (valueType == CFNumberGetTypeID()) { - int valueAsInt; - CFNumberGetValue(static_cast<CFNumberRef>(value), kCFNumberIntType, &valueAsInt); - forceTransform = !valueAsInt; - } - } - } - - if (forceTransform) { - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - } } -} -QString qt_mac_removeMnemonics(const QString &original) -{ - QString returnText(original.size(), 0); - int finalDest = 0; - int currPos = 0; - int l = original.length(); - while (l) { - if (original.at(currPos) == QLatin1Char('&') - && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) { - ++currPos; - --l; - if (l == 0) - break; - } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 && - original.at(currPos + 1) == QLatin1Char('&') && - original.at(currPos + 2) != QLatin1Char('&') && - original.at(currPos + 3) == QLatin1Char(')')) { - /* remove mnemonics its format is "\s*(&X)" */ - int n = 0; - while (finalDest > n && returnText.at(finalDest - n - 1).isSpace()) - ++n; - finalDest -= n; - currPos += 4; - l -= 4; - continue; - } - returnText[finalDest] = original.at(currPos); - ++currPos; - ++finalDest; - --l; + if (forceTransform) { + [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular]; } - returnText.truncate(finalDest); - return returnText; } - static CGColorSpaceRef m_genericColorSpace = 0; static QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash; static bool m_postRoutineRegistered = false; @@ -640,7 +749,7 @@ bool qt_mac_execute_apple_script(const QString &script, AEDesc *ret) QString qt_mac_removeAmpersandEscapes(QString s) { - return qt_mac_removeMnemonics(s).trimmed(); + return QPlatformTheme::removeMnemonics(s).trimmed(); } /*! \internal @@ -665,11 +774,11 @@ CGContextRef qt_mac_cg_context(QPaintDevice *pdev) if (data && data->classId() == QPlatformPixmap::RasterClass) { image = data->buffer(); } else { - qDebug() << "qt_mac_cg_context: Unsupported pixmap class"; + qDebug("qt_mac_cg_context: Unsupported pixmap class"); } } else if (pdev->devType() == QInternal::Widget) { // TODO test: image = static_cast<QImage *>(static_cast<const QWidget *>(pdev)->backingStore()->paintDevice()); - qDebug() << "qt_mac_cg_context: not implemented: Widget class"; + qDebug("qt_mac_cg_context: not implemented: Widget class"); } if (!image) @@ -686,6 +795,26 @@ CGContextRef qt_mac_cg_context(QPaintDevice *pdev) return ret; } +QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size) +{ + const NSSize pixmapSize = NSMakeSize(size.width(), size.height()); + QPixmap pixmap(pixmapSize.width, pixmapSize.height); + pixmap.fill(Qt::transparent); + [image setSize:pixmapSize]; + const NSRect iconRect = NSMakeRect(0, 0, pixmapSize.width, pixmapSize.height); + QMacCGContext ctx(&pixmap); + if (!ctx) + return QPixmap(); + NSGraphicsContext *gc = [NSGraphicsContext qt_graphicsContextWithCGContext:ctx flipped:YES]; + if (!gc) + return QPixmap(); + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext:gc]; + [image drawInRect:iconRect fromRect:iconRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + [NSGraphicsContext restoreGraphicsState]; + return pixmap; +} + QImage qt_mac_toQImage(CGImageRef image) { const size_t w = CGImageGetWidth(image), |