From 1c4f0ba1dd0ba7af28a6366bec5b2a0325048af6 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 17 Feb 2022 11:47:34 -0800 Subject: [PATCH 1/2] Use initWithDynamicProvider + Add HC Support --- .../PlatformColorValueTypes.macos.js | 17 +- .../PlatformColorValueTypesMacOS.js | 2 + .../PlatformColorValueTypesMacOS.macos.js | 9 +- React/Base/RCTConvert.m | 27 ++- React/Base/macOS/RCTDynamicColor.h | 28 --- React/Base/macOS/RCTDynamicColor.m | 217 ------------------ .../PlatformColor/PlatformColorExample.js | 21 ++ 7 files changed, 69 insertions(+), 252 deletions(-) delete mode 100644 React/Base/macOS/RCTDynamicColor.h delete mode 100644 React/Base/macOS/RCTDynamicColor.m diff --git a/Libraries/StyleSheet/PlatformColorValueTypes.macos.js b/Libraries/StyleSheet/PlatformColorValueTypes.macos.js index 876e668f0fcbc9..58e36cde7bbd47 100644 --- a/Libraries/StyleSheet/PlatformColorValueTypes.macos.js +++ b/Libraries/StyleSheet/PlatformColorValueTypes.macos.js @@ -18,6 +18,8 @@ export opaque type NativeColorValue = { dynamic?: { light: ?(ColorValue | ProcessedColorValue), dark: ?(ColorValue | ProcessedColorValue), + highContrastLight?: ?(ColorValue | ProcessedColorValue), + highContrastDark?: ?(ColorValue | ProcessedColorValue), }, colorWithSystemEffect?: { baseColor: ?(ColorValue | ProcessedColorValue), @@ -51,12 +53,21 @@ export const ColorWithSystemEffectMacOSPrivate = ( export type DynamicColorMacOSTuplePrivate = { light: ColorValue, dark: ColorValue, + highContrastLight?: ColorValue, + highContrastDark?: ColorValue, }; export const DynamicColorMacOSPrivate = ( tuple: DynamicColorMacOSTuplePrivate, ): ColorValue => { - return {dynamic: {light: tuple.light, dark: tuple.dark}}; + return { + dynamic: { + light: tuple.light, + dark: tuple.dark, + highContrastLight: tuple.highContrastLight, + highContrastDark: tuple.highContrastDark, + }, + }; }; export const normalizeColorObject = ( @@ -74,6 +85,8 @@ export const normalizeColorObject = ( dynamic: { light: normalizeColor(dynamic.light), dark: normalizeColor(dynamic.dark), + highContrastLight: normalizeColor(dynamic.highContrastLight), + highContrastDark: normalizeColor(dynamic.highContrastDark), }, }; return dynamicColor; @@ -104,6 +117,8 @@ export const processColorObject = ( dynamic: { light: processColor(dynamic.light), dark: processColor(dynamic.dark), + highContrastLight: processColor(dynamic.highContrastLight), + highContrastDark: processColor(dynamic.highContrastDark), }, }; return dynamicColor; diff --git a/Libraries/StyleSheet/PlatformColorValueTypesMacOS.js b/Libraries/StyleSheet/PlatformColorValueTypesMacOS.js index 8a2c2e8eab684f..53955c5200c91d 100644 --- a/Libraries/StyleSheet/PlatformColorValueTypesMacOS.js +++ b/Libraries/StyleSheet/PlatformColorValueTypesMacOS.js @@ -15,6 +15,8 @@ import type {ColorValue} from './StyleSheet'; export type DynamicColorMacOSTuple = { light: ColorValue, dark: ColorValue, + highContrastLight?: ColorValue, + highContrastDark?: ColorValue, }; export const DynamicColorMacOS = ( diff --git a/Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js b/Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js index 76d4cfd3782896..a3d5d99d59c45c 100644 --- a/Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js +++ b/Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js @@ -19,12 +19,19 @@ import { export type DynamicColorMacOSTuple = { light: ColorValue, dark: ColorValue, + highContrastLight?: ColorValue, + highContrastDark?: ColorValue, }; export const DynamicColorMacOS = ( tuple: DynamicColorMacOSTuple, ): ColorValue => { - return DynamicColorMacOSPrivate({light: tuple.light, dark: tuple.dark}); + return DynamicColorMacOSPrivate({ + light: tuple.light, + dark: tuple.dark, + highContrastLight: tuple.highContrastLight, + highContrastDark: tuple.highContrastDark, + }); }; export type SystemEffectMacOS = diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index 55af6b684ccbb3..a4c6ceca748a80 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -16,10 +16,6 @@ #import "RCTParserUtils.h" #import "RCTUtils.h" -#if TARGET_OS_OSX // [TODO(macOS GH#774) -#import "RCTDynamicColor.h" -#endif // ]TODO(macOS GH#774) - @implementation RCTConvert RCT_CONVERTER(id, id, self) @@ -1043,7 +1039,28 @@ + (RCTUIColor *)UIColor:(id)json // TODO(macOS GH#750) RCTUIColor *highContrastDarkColor = [RCTConvert UIColor:highContrastDark]; // TODO(macOS GH#750) if (lightColor != nil && darkColor != nil) { #if TARGET_OS_OSX - RCTDynamicColor *color = [[RCTDynamicColor alloc] initWithAquaColor:lightColor darkAquaColor:darkColor]; + NSColor *color = [NSColor colorWithName:nil dynamicProvider:^NSColor * _Nonnull(NSAppearance * _Nonnull appearance) { + NSMutableArray *appearances = [NSMutableArray arrayWithArray:@[NSAppearanceNameAqua,NSAppearanceNameDarkAqua]]; + if (highContrastLightColor != nil) { + [appearances addObject:NSAppearanceNameAccessibilityHighContrastAqua]; + } + if (highContrastDarkColor != nil) { + [appearances addObject:NSAppearanceNameAccessibilityHighContrastDarkAqua]; + } + NSAppearanceName bestMatchingAppearance = [appearance bestMatchFromAppearancesWithNames:appearances]; + if (bestMatchingAppearance == NSAppearanceNameAqua) { + return lightColor; + } else if (bestMatchingAppearance == NSAppearanceNameDarkAqua) { + return darkColor; + } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastAqua) { + return highContrastLightColor; + } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastDarkAqua) { + return highContrastDarkColor; + } else { + RCTLogConvertError(json, @"a UIColor. Expected a dynamic appearance aware color."); + return lightColor; + } + }]; return color; #else #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 diff --git a/React/Base/macOS/RCTDynamicColor.h b/React/Base/macOS/RCTDynamicColor.h deleted file mode 100644 index 7919ca4f8bd1c0..00000000000000 --- a/React/Base/macOS/RCTDynamicColor.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// TODO(macOS GH#774) - -#include - -/** A dynamic, theme aware subclass of NSColor. - * It is a tuple that contains two NSColors for light and dark - * theme appearances. Like a semantic NSColor or an - * asset catalog named NSColor, the effective color values - * returned by the various methods and properties vary - * depending on the current [NSAppearance currentAppearance]. - */ -@interface RCTDynamicColor : NSColor - -/** Inits a RCTDynamicColor with a pair of NSColors - * @param aquaColor the color to use when the current appearance is not dark - * @param darkAquaColor the color to use when the current appearance is dark - */ -- (instancetype)initWithAquaColor:(NSColor *)aquaColor - darkAquaColor:(nullable NSColor *)darkAquaColor; - -@end diff --git a/React/Base/macOS/RCTDynamicColor.m b/React/Base/macOS/RCTDynamicColor.m deleted file mode 100644 index fbc93d1e07af2c..00000000000000 --- a/React/Base/macOS/RCTDynamicColor.m +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// TODO(macOS GH#774) - -#import "RCTDynamicColor.h" - -#define RCT_FORWARD_PROPERTY( PROP, TYPE ) \ -- (TYPE)PROP { return [[self effectiveColor] PROP]; } - -static NSString *const RCTAquaColor = @"aquaColor"; -static NSString *const RCTDarkAquaColor = @"darkAquaColor"; - -@implementation RCTDynamicColor -{ - NSColor *_aquaColor; - NSColor *_darkAquaColor; -} - -- (instancetype)initWithAquaColor:(NSColor *)aquaColor - darkAquaColor:(NSColor *)darkAquaColor -{ - self = [super init]; - if (self) { - _aquaColor = [aquaColor copy]; - _darkAquaColor = [darkAquaColor copy]; - } - return self; -} - -+ (BOOL)supportsSecureCoding -{ - return YES; -} - -- (instancetype)initWithCoder:(NSCoder *)coder -{ - self = [super initWithCoder:coder]; - if (self) { - _aquaColor = [coder decodeObjectOfClass:[NSColor class] forKey:RCTAquaColor]; - _darkAquaColor = [coder decodeObjectOfClass:[NSColor class] forKey:RCTDarkAquaColor]; - } - return self; -} - -- (void)encodeWithCoder:(NSCoder *)aCoder -{ - [super encodeWithCoder:aCoder]; - [aCoder encodeObject:_aquaColor forKey:RCTAquaColor]; - if (_darkAquaColor) { - [aCoder encodeObject:_darkAquaColor forKey:RCTDarkAquaColor]; - } -} - -- (NSColor *)effectiveColor -{ - NSColor *effectiveColor = _aquaColor; - NSAppearance *appearance = [NSAppearance currentAppearance] ?: [NSApp effectiveAppearance]; - - NSAppearanceName appearanceName = [appearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]]; - - if (_darkAquaColor != nil && [appearanceName isEqualToString:NSAppearanceNameDarkAqua]) { - effectiveColor = _darkAquaColor; - } - return effectiveColor; -} - -RCT_FORWARD_PROPERTY(colorSpace, NSColorSpace *) -- (NSColor *)colorUsingColorSpace:(NSColorSpace *)space -{ - return [[self effectiveColor] colorUsingColorSpace:space]; -} - -RCT_FORWARD_PROPERTY(colorSpaceName, NSColorSpaceName) -- (NSColor *)colorUsingColorSpaceName:(NSColorSpaceName)name -{ - return [[self effectiveColor] colorUsingColorSpaceName:name]; -} - -RCT_FORWARD_PROPERTY(numberOfComponents, NSInteger) -- (void)getComponents:(CGFloat *)components -{ - return [[self effectiveColor] getComponents:components]; -} - -#pragma mark - RGB colorspace - -RCT_FORWARD_PROPERTY(redComponent, CGFloat) -RCT_FORWARD_PROPERTY(greenComponent, CGFloat) -RCT_FORWARD_PROPERTY(blueComponent, CGFloat) - -- (void)getRed:(nullable CGFloat *)red green:(nullable CGFloat *)green blue:(nullable CGFloat *)blue alpha:(nullable CGFloat *)alpha -{ - return [[self effectiveColor] getRed:red green:green blue:blue alpha:alpha]; -} - -#pragma mark - HSB colorspace - -RCT_FORWARD_PROPERTY(hueComponent, CGFloat) -RCT_FORWARD_PROPERTY(saturationComponent, CGFloat) -RCT_FORWARD_PROPERTY(brightnessComponent, CGFloat) - -- (void)getHue:(nullable CGFloat *)hue saturation:(nullable CGFloat *)saturation brightness:(nullable CGFloat *)brightness alpha:(nullable CGFloat *)alpha -{ - return [[self effectiveColor] getHue:hue saturation:saturation brightness:brightness alpha:alpha]; -} - -#pragma mark - Gray colorspace - -RCT_FORWARD_PROPERTY(whiteComponent, CGFloat) - -- (void)getWhite:(CGFloat *)white alpha:(CGFloat *)alpha -{ - return [[self effectiveColor] getWhite:white alpha:alpha]; -} - -#pragma mark - CMYK colorspace - -RCT_FORWARD_PROPERTY(cyanComponent, CGFloat) -RCT_FORWARD_PROPERTY(magentaComponent, CGFloat) -RCT_FORWARD_PROPERTY(yellowComponent, CGFloat) -RCT_FORWARD_PROPERTY(blackComponent, CGFloat) - -- (void)getCyan:(nullable CGFloat *)cyan magenta:(nullable CGFloat *)magenta yellow:(nullable CGFloat *)yellow black:(nullable CGFloat *)black alpha:(nullable CGFloat *)alpha -{ - return [[self effectiveColor] getCyan:cyan magenta:magenta yellow:yellow black:black alpha:alpha]; -} - -#pragma mark - Others - -RCT_FORWARD_PROPERTY(alphaComponent, CGFloat) -RCT_FORWARD_PROPERTY(CGColor, CGColorRef) -RCT_FORWARD_PROPERTY(catalogNameComponent, NSColorListName) -RCT_FORWARD_PROPERTY(colorNameComponent, NSColorName) -RCT_FORWARD_PROPERTY(localizedCatalogNameComponent, NSColorListName) -RCT_FORWARD_PROPERTY(localizedColorNameComponent, NSString *) - -- (void)setStroke -{ - [[self effectiveColor] setStroke]; -} - -- (void)setFill -{ - [[self effectiveColor] setFill]; -} - -- (void)set -{ - [[self effectiveColor] set]; -} - -- (nullable NSColor *)highlightWithLevel:(CGFloat)val -{ - return [[self effectiveColor] highlightWithLevel:val]; -} - -- (NSColor *)shadowWithLevel:(CGFloat)val -{ - return [[self effectiveColor] shadowWithLevel:val]; -} - -- (NSColor *)colorWithAlphaComponent:(CGFloat)alpha -{ - return [[self effectiveColor] colorWithAlphaComponent:alpha]; -} - -- (nullable NSColor *)blendedColorWithFraction:(CGFloat)fraction ofColor:(NSColor *)color -{ - return [[self effectiveColor] blendedColorWithFraction:fraction ofColor:color]; -} - -- (NSColor *)colorWithSystemEffect:(NSColorSystemEffect)systemEffect NS_AVAILABLE_MAC(10_14) -{ - NSColor *aquaColorWithSystemEffect = [_aquaColor colorWithSystemEffect:systemEffect]; - NSColor *darkAquaColorWithSystemEffect = [_darkAquaColor colorWithSystemEffect:systemEffect]; - return [[RCTDynamicColor alloc] initWithAquaColor:aquaColorWithSystemEffect darkAquaColor:darkAquaColorWithSystemEffect]; -} - -- (NSUInteger)hash -{ - const NSUInteger prime = 31; - NSUInteger result = 1; - result = prime * result + [_aquaColor hash]; - result = prime * result + [_darkAquaColor hash]; - return result; -} - -- (BOOL)isEqual:(id)other { - if (other == self) { - return YES; - } - - return other != nil && [other isKindOfClass:[self class]] && [self isEqualToDynamicColor:other]; -} - -- (BOOL)isEqualToDynamicColor:(RCTDynamicColor *)other { - if (self == other) { - return YES; - } - - if ([_aquaColor isNotEqualTo:other->_aquaColor]) { - return NO; - } - - if ([_darkAquaColor isNotEqualTo:other->_darkAquaColor]) { - return NO; - } - - return YES; -} - -@end diff --git a/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js b/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js index ea55de56c44c96..c13166c917ed4f 100644 --- a/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js +++ b/packages/rn-tester/js/examples/PlatformColor/PlatformColorExample.js @@ -266,6 +266,27 @@ function DynamicColorsExample() { }} /> + + + DynamicColorMacOS({'{\n'} + {' '}light: 'red',{'\n'} + {' '}dark: 'blue',{'\n'} + {' '}highContrastLight: 'green',{'\n'} + {' '}highContrastDark: 'orange',{'\n'} + {'}'}) + + + ) : // ]TODO(macOS GH#774) Platform.OS === 'ios' ? ( From 60a9b1461620544939b175fac4c382af318ee9dd Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Fri, 18 Feb 2022 09:39:23 -0800 Subject: [PATCH 2/2] Update log error --- React/Base/RCTConvert.m | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index a4c6ceca748a80..6a317543e6b118 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -1039,28 +1039,28 @@ + (RCTUIColor *)UIColor:(id)json // TODO(macOS GH#750) RCTUIColor *highContrastDarkColor = [RCTConvert UIColor:highContrastDark]; // TODO(macOS GH#750) if (lightColor != nil && darkColor != nil) { #if TARGET_OS_OSX - NSColor *color = [NSColor colorWithName:nil dynamicProvider:^NSColor * _Nonnull(NSAppearance * _Nonnull appearance) { - NSMutableArray *appearances = [NSMutableArray arrayWithArray:@[NSAppearanceNameAqua,NSAppearanceNameDarkAqua]]; - if (highContrastLightColor != nil) { - [appearances addObject:NSAppearanceNameAccessibilityHighContrastAqua]; - } - if (highContrastDarkColor != nil) { - [appearances addObject:NSAppearanceNameAccessibilityHighContrastDarkAqua]; - } - NSAppearanceName bestMatchingAppearance = [appearance bestMatchFromAppearancesWithNames:appearances]; - if (bestMatchingAppearance == NSAppearanceNameAqua) { - return lightColor; - } else if (bestMatchingAppearance == NSAppearanceNameDarkAqua) { - return darkColor; - } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastAqua) { - return highContrastLightColor; - } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastDarkAqua) { - return highContrastDarkColor; - } else { - RCTLogConvertError(json, @"a UIColor. Expected a dynamic appearance aware color."); - return lightColor; - } - }]; + NSColor *color = [NSColor colorWithName:nil dynamicProvider:^NSColor * _Nonnull(NSAppearance * _Nonnull appearance) { + NSMutableArray *appearances = [NSMutableArray arrayWithArray:@[NSAppearanceNameAqua,NSAppearanceNameDarkAqua]]; + if (highContrastLightColor != nil) { + [appearances addObject:NSAppearanceNameAccessibilityHighContrastAqua]; + } + if (highContrastDarkColor != nil) { + [appearances addObject:NSAppearanceNameAccessibilityHighContrastDarkAqua]; + } + NSAppearanceName bestMatchingAppearance = [appearance bestMatchFromAppearancesWithNames:appearances]; + if (bestMatchingAppearance == NSAppearanceNameAqua) { + return lightColor; + } else if (bestMatchingAppearance == NSAppearanceNameDarkAqua) { + return darkColor; + } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastAqua) { + return highContrastLightColor; + } else if (bestMatchingAppearance == NSAppearanceNameAccessibilityHighContrastDarkAqua) { + return highContrastDarkColor; + } else { + RCTLogConvertError(json, @"an NSColorColor. Could not resolve current appearance. Defaulting to light."); + return lightColor; + } + }]; return color; #else #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000