Skip to content

Commit 868a823

Browse files
CopilotSaadnajmi
andcommitted
Add macOS-specific semantic colors support (RCTPlatformColorUtils)
Co-authored-by: Saadnajmi <6722175+Saadnajmi@users.noreply.github.com>
1 parent ea497ad commit 868a823

File tree

1 file changed

+240
-0
lines changed
  • packages/react-native/ReactCommon/react/renderer/graphics/platform/macos/react/renderer/graphics

1 file changed

+240
-0
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
// [macOS]
9+
10+
#import "RCTPlatformColorUtils.h"
11+
12+
#import <Foundation/Foundation.h>
13+
#import <React/RCTUIKit.h>
14+
#import <react/renderer/graphics/HostPlatformColor.h>
15+
#import <react/utils/ManagedObjectWrapper.h>
16+
17+
#include <string>
18+
19+
NS_ASSUME_NONNULL_BEGIN
20+
21+
static NSString *const kColorSuffix = @"Color";
22+
static NSString *const kFallbackARGBKey = @"fallback-argb";
23+
static NSString *const kFallbackKey = @"fallback";
24+
static NSString *const kSelectorKey = @"selector";
25+
static NSString *const kIndexKey = @"index";
26+
27+
static NSDictionary<NSString *, NSDictionary *> *_PlatformColorSelectorsDict()
28+
{
29+
static NSDictionary<NSString *, NSDictionary *> *dict;
30+
static dispatch_once_t once_token;
31+
dispatch_once(&once_token, ^(void) {
32+
NSMutableDictionary<NSString *, NSDictionary *> *map = [@{
33+
// https://developer.apple.com/documentation/appkit/nscolor/ui_element_colors
34+
// Label Colors
35+
@"labelColor": @{}, // 10_10
36+
@"secondaryLabelColor": @{}, // 10_10
37+
@"tertiaryLabelColor": @{}, // 10_10
38+
@"quaternaryLabelColor": @{}, // 10_10
39+
// Text Colors
40+
@"textColor": @{},
41+
@"placeholderTextColor": @{}, // 10_10
42+
@"selectedTextColor": @{},
43+
@"textBackgroundColor": @{},
44+
@"selectedTextBackgroundColor": @{},
45+
@"keyboardFocusIndicatorColor": @{},
46+
@"unemphasizedSelectedTextColor": @{ // 10_14
47+
kFallbackKey: @"selectedTextColor"
48+
},
49+
@"unemphasizedSelectedTextBackgroundColor": @{ // 10_14
50+
kFallbackKey: @"textBackgroundColor"
51+
},
52+
// Content Colors
53+
@"linkColor": @{}, // 10_10
54+
@"separatorColor": @{ // 10_14
55+
kFallbackKey: @"gridColor"
56+
},
57+
@"selectedContentBackgroundColor": @{ // 10_14
58+
kFallbackKey: @"alternateSelectedControlColor"
59+
},
60+
@"unemphasizedSelectedContentBackgroundColor": @{ // 10_14
61+
kFallbackKey: @"secondarySelectedControlColor"
62+
},
63+
// Menu Colors
64+
@"selectedMenuItemTextColor": @{},
65+
// Table Colors
66+
@"gridColor": @{},
67+
@"headerTextColor": @{},
68+
@"alternatingEvenContentBackgroundColor": @{ // 10_14
69+
kSelectorKey: @"alternatingContentBackgroundColors",
70+
kIndexKey: @0,
71+
kFallbackKey: @"controlAlternatingRowBackgroundColors"
72+
},
73+
@"alternatingOddContentBackgroundColor": @{ // 10_14
74+
kSelectorKey: @"alternatingContentBackgroundColors",
75+
kIndexKey: @1,
76+
kFallbackKey: @"controlAlternatingRowBackgroundColors"
77+
},
78+
// Control Colors
79+
@"controlAccentColor": @{ // 10_14
80+
kFallbackKey: @"controlColor"
81+
},
82+
@"controlColor": @{},
83+
@"controlBackgroundColor": @{},
84+
@"controlTextColor": @{},
85+
@"disabledControlTextColor": @{},
86+
@"selectedControlColor": @{},
87+
@"selectedControlTextColor": @{},
88+
@"alternateSelectedControlTextColor": @{},
89+
@"scrubberTexturedBackgroundColor": @{}, // 10_12_2
90+
// Window Colors
91+
@"windowBackgroundColor": @{},
92+
@"windowFrameTextColor": @{},
93+
@"underPageBackgroundColor": @{}, // 10_8
94+
// Highlights and Shadows
95+
@"findHighlightColor": @{ // 10_13
96+
kFallbackKey: @"highlightColor"
97+
},
98+
@"highlightColor": @{},
99+
@"shadowColor": @{},
100+
// https://developer.apple.com/documentation/appkit/nscolor/standard_colors
101+
// Standard Colors
102+
@"systemBlueColor": @{}, // 10_10
103+
@"systemBrownColor": @{}, // 10_10
104+
@"systemGrayColor": @{}, // 10_10
105+
@"systemGreenColor": @{}, // 10_10
106+
@"systemOrangeColor": @{}, // 10_10
107+
@"systemPinkColor": @{}, // 10_10
108+
@"systemPurpleColor": @{}, // 10_10
109+
@"systemRedColor": @{}, // 10_10
110+
@"systemYellowColor": @{}, // 10_10
111+
// Transparent Color
112+
@"clearColor" : @{},
113+
} mutableCopy];
114+
115+
// Create aliases for Swift-style names (without "Color" suffix)
116+
NSMutableDictionary<NSString *, NSDictionary *> *aliases = [NSMutableDictionary new];
117+
for (NSString *objcSelector in map) {
118+
NSMutableDictionary *entry = [map[objcSelector] mutableCopy];
119+
if ([entry objectForKey:kSelectorKey] == nil) {
120+
entry[kSelectorKey] = objcSelector;
121+
}
122+
if ([objcSelector hasSuffix:kColorSuffix]) {
123+
NSString *swiftSelector = [objcSelector substringToIndex:[objcSelector length] - [kColorSuffix length]];
124+
aliases[swiftSelector] = entry;
125+
}
126+
}
127+
[map addEntriesFromDictionary:aliases];
128+
129+
dict = [map copy];
130+
});
131+
return dict;
132+
}
133+
134+
static RCTUIColor *_UIColorFromHexValue(NSNumber *hexValue)
135+
{
136+
NSUInteger hexIntValue = [hexValue unsignedIntegerValue];
137+
138+
CGFloat red = ((CGFloat)((hexIntValue & 0xFF000000) >> 24)) / 255.0;
139+
CGFloat green = ((CGFloat)((hexIntValue & 0xFF0000) >> 16)) / 255.0;
140+
CGFloat blue = ((CGFloat)((hexIntValue & 0xFF00) >> 8)) / 255.0;
141+
CGFloat alpha = ((CGFloat)(hexIntValue & 0xFF)) / 255.0;
142+
143+
return [RCTUIColor colorWithRed:red green:green blue:blue alpha:alpha];
144+
}
145+
146+
static RCTUIColor *_Nullable _UIColorFromSemanticString(NSString *semanticString)
147+
{
148+
NSString *platformColorString = [semanticString hasSuffix:kColorSuffix]
149+
? [semanticString substringToIndex:[semanticString length] - [kColorSuffix length]]
150+
: semanticString;
151+
NSDictionary<NSString *, NSDictionary *> *platformColorSelectorsDict = _PlatformColorSelectorsDict();
152+
NSDictionary<NSString *, id> *colorInfo = platformColorSelectorsDict[platformColorString];
153+
if (colorInfo) {
154+
// Get the selector name, defaulting to the platform color string
155+
NSString *selectorName = colorInfo[kSelectorKey];
156+
if (selectorName == nil) {
157+
selectorName = [platformColorString stringByAppendingString:kColorSuffix];
158+
}
159+
160+
SEL objcColorSelector = NSSelectorFromString(selectorName);
161+
if (![RCTUIColor respondsToSelector:objcColorSelector]) {
162+
// Try fallback ARGB value
163+
NSNumber *fallbackRGB = colorInfo[kFallbackARGBKey];
164+
if (fallbackRGB) {
165+
return _UIColorFromHexValue(fallbackRGB);
166+
}
167+
// Try fallback color name
168+
NSString *fallbackColorName = colorInfo[kFallbackKey];
169+
if (fallbackColorName) {
170+
return _UIColorFromSemanticString(fallbackColorName);
171+
}
172+
} else {
173+
Class colorClass = [RCTUIColor class];
174+
IMP imp = [colorClass methodForSelector:objcColorSelector];
175+
id (*getColor)(id, SEL) = ((id(*)(id, SEL))imp);
176+
id colorObject = getColor(colorClass, objcColorSelector);
177+
178+
// Handle array results (like alternatingContentBackgroundColors)
179+
if ([colorObject isKindOfClass:[NSArray class]]) {
180+
NSNumber *index = colorInfo[kIndexKey];
181+
if (index != nil) {
182+
NSArray *colors = colorObject;
183+
NSUInteger idx = [index unsignedIntegerValue];
184+
if (idx < colors.count) {
185+
colorObject = colors[idx];
186+
}
187+
}
188+
}
189+
190+
if ([colorObject isKindOfClass:[RCTUIColor class]]) {
191+
return colorObject;
192+
}
193+
}
194+
}
195+
return nil;
196+
}
197+
198+
static inline NSString *_NSStringFromCString(
199+
const std::string &string,
200+
const NSStringEncoding &encoding = NSUTF8StringEncoding)
201+
{
202+
return [NSString stringWithCString:string.c_str() encoding:encoding];
203+
}
204+
205+
static inline facebook::react::ColorComponents _ColorComponentsFromUIColor(RCTUIColor *color)
206+
{
207+
CGFloat rgba[4];
208+
color = [color colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
209+
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
210+
return {(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]};
211+
}
212+
213+
facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(std::vector<std::string> &semanticItems)
214+
{
215+
return _ColorComponentsFromUIColor(RCTPlatformColorFromSemanticItems(semanticItems));
216+
}
217+
218+
RCTUIColor *RCTPlatformColorFromSemanticItems(std::vector<std::string> &semanticItems)
219+
{
220+
for (const auto &semanticCString : semanticItems) {
221+
NSString *semanticNSString = _NSStringFromCString(semanticCString);
222+
RCTUIColor *uiColor = [RCTUIColor colorNamed:semanticNSString];
223+
if (uiColor != nil) {
224+
return uiColor;
225+
}
226+
uiColor = _UIColorFromSemanticString(semanticNSString);
227+
if (uiColor != nil) {
228+
return uiColor;
229+
}
230+
}
231+
232+
return RCTUIColor.clearColor;
233+
}
234+
235+
RCTUIColor *RCTPlatformColorFromColor(const facebook::react::Color &color)
236+
{
237+
return (RCTUIColor *)facebook::react::unwrapManagedObject(color.getUIColor());
238+
}
239+
240+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)