From 2f549d61ec1d2c95ddcddc69060f16f9d7938f75 Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Sun, 6 Dec 2015 23:26:52 -0800 Subject: Add some functions to convert an NSColor to a QBrush. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NSColors can be quite complex and store patterns, gradients, dynamic system colors that change based on global state (!), etc. QBrush is a better equivalent to it than QColor. Change-Id: I4b66a1ab85342bbb9efcf78011965ec74e5fc738 Reviewed-by: Richard Moe Gustavsen Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/qcocoahelpers.h | 3 + src/plugins/platforms/cocoa/qcocoahelpers.mm | 119 +++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index affe6e74ff..b83977c7d8 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -46,6 +46,7 @@ // #include "qt_mac_p.h" #include +#include #include QT_BEGIN_NAMESPACE @@ -75,6 +76,8 @@ QRect qt_mac_toQRect(const NSRect &rect); QColor qt_mac_toQColor(const NSColor *color); QColor qt_mac_toQColor(CGColorRef color); +QBrush qt_mac_toQBrush(CGColorRef color); +QBrush qt_mac_toQBrush(const NSColor *color, QPalette::ColorGroup colorGroup = QPalette::Normal); // Creates a mutable shape, it's the caller's responsibility to release. HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 32ac235528..657295c29c 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -259,6 +259,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; +} + // Use this method to keep all the information in the TextSegment. As long as it is ordered // we are in OK shape, and we can influence that ourselves. struct KeyPair -- cgit v1.2.3