not-react-native-macos 0.87.1-rc.3 → 0.87.1-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/Libraries/Components/View/ViewPropTypes.js +58 -0
  2. package/Libraries/NativeComponent/BaseViewConfig.macos.js +66 -11
  3. package/Libraries/NativeComponent/ViewConfigIgnore.js +5 -1
  4. package/Libraries/StyleSheet/PlatformColorValueTypes.macos.js +147 -10
  5. package/Libraries/StyleSheet/PlatformColorValueTypesMacOS.js +34 -0
  6. package/Libraries/StyleSheet/PlatformColorValueTypesMacOS.macos.js +55 -0
  7. package/React/Base/RCTConvert.mm +49 -0
  8. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +150 -0
  9. package/ReactCommon/React-Fabric.podspec +9 -0
  10. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformTouch.h +16 -0
  11. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp +39 -0
  12. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.h +39 -0
  13. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEvents.h +59 -0
  14. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp +113 -0
  15. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.h +66 -0
  16. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewTraitsInitializer.h +37 -0
  17. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.h +6 -0
  18. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.mm +31 -0
  19. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/PlatformColorParser.mm +34 -0
  20. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm +23 -0
  21. package/index.js +10 -0
  22. package/macos/UIKitCompat/UIKit/UIColor.m +7 -1
  23. package/package.json +1 -1
@@ -128,6 +128,63 @@ type FocusEventProps = Readonly<{
128
128
  onFocusCapture?: ?(event: FocusEvent) => void,
129
129
  }>;
130
130
 
131
+ // [macOS] Props that only exist on macOS. They are declared here rather than in
132
+ // a .macos file because ViewPropTypes is shared, and a prop that is simply
133
+ // absent on other platforms costs them nothing. The native side is
134
+ // HostPlatformViewProps under components/view/platform/macos. macOS]
135
+ type MacOSViewProps = Readonly<{
136
+ /**
137
+ * The view's help tag, shown when the pointer rests over it.
138
+ *
139
+ * @platform macos
140
+ */
141
+ tooltip?: ?string,
142
+
143
+ /**
144
+ * Receive the click that activates a background window, rather than letting
145
+ * it be swallowed to bring the window forward.
146
+ *
147
+ * @platform macos
148
+ */
149
+ acceptsFirstMouse?: ?boolean,
150
+
151
+ /**
152
+ * Let the view blend with what is behind it when inside a vibrancy effect.
153
+ *
154
+ * @platform macos
155
+ */
156
+ allowsVibrancy?: ?boolean,
157
+
158
+ /**
159
+ * Draw the focus ring while the view is first responder. Defaults to true.
160
+ *
161
+ * @platform macos
162
+ */
163
+ enableFocusRing?: ?boolean,
164
+
165
+ /**
166
+ * Whether dragging the view moves the window. AppKit defaults this to true;
167
+ * set it false for a view that should handle its own drags.
168
+ *
169
+ * @platform macos
170
+ */
171
+ mouseDownCanMoveWindow?: ?boolean,
172
+
173
+ /**
174
+ * Called on a double click.
175
+ *
176
+ * @platform macos
177
+ */
178
+ onDoubleClick?: ?(event: MouseEvent) => void,
179
+
180
+ /**
181
+ * Called on a secondary (right) click.
182
+ *
183
+ * @platform macos
184
+ */
185
+ onAuxClick?: ?(event: MouseEvent) => void,
186
+ }>;
187
+
131
188
  type KeyEventProps = Readonly<{
132
189
  onKeyDown?: ?(event: KeyDownEvent) => void,
133
190
  onKeyDownCapture?: ?(event: KeyDownEvent) => void,
@@ -566,6 +623,7 @@ export type ViewProps = Readonly<{
566
623
  ...DirectEventProps,
567
624
  ...GestureResponderHandlers,
568
625
  ...MouseEventProps,
626
+ ...MacOSViewProps, // [macOS]
569
627
  ...PointerEventProps,
570
628
  ...FocusEventProps,
571
629
  ...KeyEventProps,
@@ -1,4 +1,4 @@
1
- /*
1
+ /**
2
2
  * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
@@ -8,16 +8,71 @@
8
8
  * @format
9
9
  */
10
10
 
11
- // [macOS] macOS resolves as `.macos` -> `.native` -> shared, so without this
12
- // file `BaseViewConfig` re-exports itself: the shared file is a
13
- // stub that expects a platform-suffixed sibling to answer. Point it at the iOS
14
- // implementation, which is what this fork uses on macOS wherever upstream has
15
- // no platform-neutral one. macOS]
11
+ // [macOS] The view config decides which props reach native at all: anything
12
+ // missing from `validAttributes` is dropped before it ever gets to C++. So the
13
+ // macOS-only props declared in HostPlatformViewProps have to be listed here as
14
+ // well, or setting them does nothing.
15
+ //
16
+ // Everything iOS declares still applies, so this extends that config rather
17
+ // than replacing it. Names match microsoft/react-native-macos. macOS]
16
18
 
17
- // NOTE: This file supports backwards compatibility of subpath (deep) imports
18
- // from 'react-native' with platform-specific extensions. It can be deleted
19
- // once we remove the "./*" mapping from package.json "exports".
19
+ import type {PartialViewConfigWithoutName} from './PlatformBaseViewConfig';
20
20
 
21
- import BaseViewConfig from './BaseViewConfig.ios';
21
+ // $FlowFixMe[cannot-resolve-module] macOS shares the iOS base config.
22
+ import PlatformBaseViewConfigIOS from './BaseViewConfig.ios';
23
+ import {ConditionallyIgnoredEventHandlers} from './ViewConfigIgnore';
22
24
 
23
- export default BaseViewConfig;
25
+ const bubblingEventTypes = {
26
+ ...PlatformBaseViewConfigIOS.bubblingEventTypes,
27
+ topKeyDown: {
28
+ phasedRegistrationNames: {
29
+ captured: 'onKeyDownCapture',
30
+ bubbled: 'onKeyDown',
31
+ },
32
+ },
33
+ topKeyUp: {
34
+ phasedRegistrationNames: {
35
+ captured: 'onKeyUpCapture',
36
+ bubbled: 'onKeyUp',
37
+ },
38
+ },
39
+ };
40
+
41
+ const directEventTypes = {
42
+ ...PlatformBaseViewConfigIOS.directEventTypes,
43
+ topDoubleClick: {registrationName: 'onDoubleClick'},
44
+ topAuxClick: {registrationName: 'onAuxClick'},
45
+ topMouseEnter: {registrationName: 'onMouseEnter'},
46
+ topMouseLeave: {registrationName: 'onMouseLeave'},
47
+ };
48
+
49
+ const validAttributesForNonEventProps = {
50
+ acceptsFirstMouse: true,
51
+ allowsVibrancy: true,
52
+ enableFocusRing: true,
53
+ focusable: true,
54
+ mouseDownCanMoveWindow: true,
55
+ tooltip: true,
56
+ };
57
+
58
+ const validAttributesForEventProps = ConditionallyIgnoredEventHandlers({
59
+ onAuxClick: true,
60
+ onDoubleClick: true,
61
+ onKeyDown: true,
62
+ onKeyUp: true,
63
+ onMouseEnter: true,
64
+ onMouseLeave: true,
65
+ });
66
+
67
+ const PlatformBaseViewConfigMacOS: PartialViewConfigWithoutName = {
68
+ bubblingEventTypes,
69
+ directEventTypes,
70
+ validAttributes: {
71
+ ...PlatformBaseViewConfigIOS.validAttributes,
72
+ ...validAttributesForNonEventProps,
73
+ // $FlowFixMe[exponential-spread]
74
+ ...validAttributesForEventProps,
75
+ },
76
+ };
77
+
78
+ export default PlatformBaseViewConfigMacOS;
@@ -39,7 +39,11 @@ export function DynamicallyInjectedByGestureHandler<T extends {...}>(
39
39
  export function ConditionallyIgnoredEventHandlers<
40
40
  const T extends {readonly [name: string]: true},
41
41
  >(value: T): T | void {
42
- if (Platform.OS === 'ios') {
42
+ // [macOS] macOS declares its view props the same way iOS does, so it keeps
43
+ // these too. Reporting Platform.OS as 'macos' without this drops every event
44
+ // handler in the base view config -- including iOS's own -- and nothing that
45
+ // depends on them ever fires. macOS]
46
+ if (Platform.OS === 'ios' || Platform.OS === 'macos') {
43
47
  return value;
44
48
  }
45
49
  return undefined;
@@ -1,4 +1,4 @@
1
- /*
1
+ /**
2
2
  * Copyright (c) Meta Platforms, Inc. and affiliates.
3
3
  *
4
4
  * This source code is licensed under the MIT license found in the
@@ -8,14 +8,151 @@
8
8
  * @format
9
9
  */
10
10
 
11
- // [macOS] macOS resolves as `.macos` -> `.native` -> shared, so without this
12
- // file `PlatformColorValueTypes` re-exports itself: the shared file is a
13
- // stub that expects a platform-suffixed sibling to answer. Point it at the iOS
14
- // implementation, which is what this fork uses on macOS wherever upstream has
15
- // no platform-neutral one. macOS]
11
+ // [macOS] The macOS colour types.
12
+ //
13
+ // `semantic` and `dynamic` carry over from iOS unchanged -- RCTConvert resolves
14
+ // both on this platform, and semantic names are looked up on NSColor, so
15
+ // AppKit's whole vocabulary (labelColor, windowBackgroundColor,
16
+ // controlAccentColor, ...) works. What iOS has no equivalent for is
17
+ // `colorWithSystemEffect`, AppKit's pressed / disabled / rollover variants of a
18
+ // colour, so that is added here. macOS]
16
19
 
17
- // NOTE: This file supports backwards compatibility of subpath (deep) imports
18
- // from 'react-native' with platform-specific extensions. It can be deleted
19
- // once we remove the "./*" mapping from package.json "exports".
20
20
 
21
- export * from './PlatformColorValueTypes.ios';
21
+ import type {ProcessedColorValue} from './processColor';
22
+ import type {ColorValue, NativeColorValue} from './StyleSheet';
23
+
24
+ /** The actual type of the opaque NativeColorValue on macOS platform */
25
+ type LocalNativeColorValue = {
26
+ semantic?: Array<string>,
27
+ colorWithSystemEffect?: {
28
+ baseColor: ?(ColorValue | ProcessedColorValue),
29
+ systemEffect: string,
30
+ },
31
+ dynamic?: {
32
+ light: ?(ColorValue | ProcessedColorValue),
33
+ dark: ?(ColorValue | ProcessedColorValue),
34
+ highContrastLight?: ?(ColorValue | ProcessedColorValue),
35
+ highContrastDark?: ?(ColorValue | ProcessedColorValue),
36
+ },
37
+ };
38
+
39
+ export const PlatformColor = (...names: Array<string>): NativeColorValue => {
40
+ // $FlowExpectedError[incompatible-type] LocalNativeColorValue is the iOS LocalNativeColorValue type
41
+ return {semantic: names} as LocalNativeColorValue;
42
+ };
43
+
44
+ export type DynamicColorMacOSTuplePrivate = {
45
+ light: ColorValue,
46
+ dark: ColorValue,
47
+ highContrastLight?: ColorValue,
48
+ highContrastDark?: ColorValue,
49
+ };
50
+
51
+ export const DynamicColorMacOSPrivate = (
52
+ tuple: DynamicColorMacOSTuplePrivate,
53
+ ): ColorValue => {
54
+ return {
55
+ dynamic: {
56
+ light: tuple.light,
57
+ dark: tuple.dark,
58
+ highContrastLight: tuple.highContrastLight,
59
+ highContrastDark: tuple.highContrastDark,
60
+ },
61
+ /* $FlowExpectedError[incompatible-type]
62
+ * LocalNativeColorValue is the actual type of the opaque NativeColorValue on macOS platform */
63
+ } as LocalNativeColorValue;
64
+ };
65
+
66
+ export type SystemEffectMacOSPrivate =
67
+ | 'none'
68
+ | 'pressed'
69
+ | 'deepPressed'
70
+ | 'disabled'
71
+ | 'rollover';
72
+
73
+ export const ColorWithSystemEffectMacOSPrivate = (
74
+ color: ColorValue,
75
+ effect: SystemEffectMacOSPrivate,
76
+ ): ColorValue => {
77
+ return {
78
+ colorWithSystemEffect: {
79
+ baseColor: color,
80
+ systemEffect: effect,
81
+ },
82
+ /* $FlowExpectedError[incompatible-type]
83
+ * LocalNativeColorValue is the actual type of the opaque NativeColorValue */
84
+ } as LocalNativeColorValue;
85
+ };
86
+
87
+ const _normalizeColorObject = (
88
+ color: LocalNativeColorValue,
89
+ ): ?LocalNativeColorValue => {
90
+ if ('colorWithSystemEffect' in color && color.colorWithSystemEffect !== undefined) {
91
+ const normalizeColor = require('./normalizeColor').default;
92
+ const spec = color.colorWithSystemEffect;
93
+ return {
94
+ colorWithSystemEffect: {
95
+ // $FlowFixMe[incompatible-call]
96
+ baseColor: normalizeColor(spec.baseColor),
97
+ systemEffect: spec.systemEffect,
98
+ },
99
+ };
100
+ } else if ('semantic' in color) {
101
+ // an AppKit or iOS semantic colour
102
+ return color;
103
+ } else if ('dynamic' in color && color.dynamic !== undefined) {
104
+ const normalizeColor = require('./normalizeColor').default;
105
+
106
+ // a dynamic, appearance aware color
107
+ const dynamic = color.dynamic;
108
+ const dynamicColor: LocalNativeColorValue = {
109
+ dynamic: {
110
+ // $FlowFixMe[incompatible-use]
111
+ light: normalizeColor(dynamic.light),
112
+ // $FlowFixMe[incompatible-use]
113
+ dark: normalizeColor(dynamic.dark),
114
+ // $FlowFixMe[incompatible-use]
115
+ highContrastLight: normalizeColor(dynamic.highContrastLight),
116
+ // $FlowFixMe[incompatible-use]
117
+ highContrastDark: normalizeColor(dynamic.highContrastDark),
118
+ },
119
+ };
120
+ return dynamicColor;
121
+ }
122
+ return null;
123
+ };
124
+
125
+ export const normalizeColorObject: (
126
+ color: NativeColorValue,
127
+ /* $FlowExpectedError[incompatible-type]
128
+ * LocalNativeColorValue is the actual type of the opaque NativeColorValue on macOS platform */
129
+ ) => ?ProcessedColorValue = _normalizeColorObject;
130
+
131
+ const _processColorObject = (
132
+ color: LocalNativeColorValue,
133
+ ): ?LocalNativeColorValue => {
134
+ if ('dynamic' in color && color.dynamic != null) {
135
+ const processColor = require('./processColor').default;
136
+ const dynamic = color.dynamic;
137
+ const dynamicColor: LocalNativeColorValue = {
138
+ dynamic: {
139
+ // $FlowFixMe[incompatible-use]
140
+ light: processColor(dynamic.light),
141
+ // $FlowFixMe[incompatible-use]
142
+ dark: processColor(dynamic.dark),
143
+ // $FlowFixMe[incompatible-use]
144
+ highContrastLight: processColor(dynamic.highContrastLight),
145
+ // $FlowFixMe[incompatible-use]
146
+ highContrastDark: processColor(dynamic.highContrastDark),
147
+ },
148
+ };
149
+ return dynamicColor;
150
+ }
151
+ return color;
152
+ };
153
+
154
+ export const processColorObject: (
155
+ color: NativeColorValue,
156
+ /* $FlowExpectedError[incompatible-type]
157
+ * LocalNativeColorValue is the actual type of the opaque NativeColorValue on macOS platform */
158
+ ) => ?NativeColorValue = _processColorObject;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ // [macOS] The off-macOS stand-in, matching how PlatformColorValueTypesIOS.js
12
+ // behaves elsewhere: importing is fine, calling is not.
13
+
14
+ import type {ColorValue} from './StyleSheet';
15
+
16
+ export type DynamicColorMacOSTuple = {
17
+ light: ColorValue,
18
+ dark: ColorValue,
19
+ highContrastLight?: ColorValue,
20
+ highContrastDark?: ColorValue,
21
+ };
22
+
23
+ export const DynamicColorMacOS = (_tuple: DynamicColorMacOSTuple): ColorValue => {
24
+ throw new Error('DynamicColorMacOS is not available on this platform.');
25
+ };
26
+
27
+ export type SystemEffectMacOS = 'none' | 'pressed' | 'deepPressed' | 'disabled' | 'rollover';
28
+
29
+ export const ColorWithSystemEffectMacOS = (
30
+ _color: ColorValue,
31
+ _effect: SystemEffectMacOS,
32
+ ): ColorValue => {
33
+ throw new Error('ColorWithSystemEffectMacOS is not available on this platform.');
34
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ // [macOS] The public macOS colour API, mirroring PlatformColorValueTypesIOS.
12
+
13
+ 'use strict';
14
+
15
+ import type {ColorValue} from './StyleSheet';
16
+
17
+ import {
18
+ ColorWithSystemEffectMacOSPrivate,
19
+ DynamicColorMacOSPrivate,
20
+ // $FlowFixMe[cannot-resolve-module] resolved by the macOS platform extension
21
+ } from './PlatformColorValueTypes.macos';
22
+
23
+ export type DynamicColorMacOSTuple = {
24
+ light: ColorValue,
25
+ dark: ColorValue,
26
+ highContrastLight?: ColorValue,
27
+ highContrastDark?: ColorValue,
28
+ };
29
+
30
+ /**
31
+ * A colour that follows the system appearance, the macOS counterpart of
32
+ * `DynamicColorIOS`.
33
+ */
34
+ export const DynamicColorMacOS = (tuple: DynamicColorMacOSTuple): ColorValue => {
35
+ return DynamicColorMacOSPrivate({
36
+ light: tuple.light,
37
+ dark: tuple.dark,
38
+ highContrastLight: tuple.highContrastLight,
39
+ highContrastDark: tuple.highContrastDark,
40
+ });
41
+ };
42
+
43
+ export type SystemEffectMacOS = 'none' | 'pressed' | 'deepPressed' | 'disabled' | 'rollover';
44
+
45
+ /**
46
+ * A colour with one of AppKit's system effects applied. There is no iOS
47
+ * equivalent: UIKit expects the caller to supply the pressed or disabled
48
+ * colour, where AppKit derives it.
49
+ */
50
+ export const ColorWithSystemEffectMacOS = (
51
+ color: ColorValue,
52
+ effect: SystemEffectMacOS,
53
+ ): ColorValue => {
54
+ return ColorWithSystemEffectMacOSPrivate(color, effect);
55
+ };
@@ -850,6 +850,26 @@ static UIColor *RCTColorFromSemanticColorName(NSString *semanticColorName)
850
850
  NSDictionary<NSString *, NSDictionary *> *colorMap = RCTSemanticColorsMap();
851
851
  UIColor *color = nil;
852
852
  NSDictionary<NSString *, id> *colorInfo = colorMap[semanticColorName];
853
+
854
+ #if TARGET_OS_OSX // [macOS
855
+ // The map above is iOS's vocabulary. AppKit has its own -- labelColor,
856
+ // windowBackgroundColor, controlAccentColor and the rest -- and they are all
857
+ // class properties on NSColor, so asking NSColor directly covers every one of
858
+ // them without a second table to keep in sync. Only reached for names iOS
859
+ // does not define, so iOS names keep their mapping and their fallbacks.
860
+ if (colorInfo == nil) {
861
+ SEL appKitSelector = NSSelectorFromString(semanticColorName);
862
+ if (appKitSelector != nil && [UIColor respondsToSelector:appKitSelector]) {
863
+ IMP imp = [[UIColor class] methodForSelector:appKitSelector];
864
+ id (*getColor)(id, SEL) = (id (*)(id, SEL))imp;
865
+ id candidate = getColor([UIColor class], appKitSelector);
866
+ if ([candidate isKindOfClass:[UIColor class]]) {
867
+ return candidate;
868
+ }
869
+ }
870
+ }
871
+ #endif // macOS]
872
+
853
873
  if (colorInfo) {
854
874
  NSString *semanticColorSelector = colorInfo[RCTSelector];
855
875
  if (semanticColorSelector == nil) {
@@ -1040,6 +1060,35 @@ void RCTSetDefaultColorSpace(RCTColorSpace colorSpace)
1040
1060
  RCTLogConvertError(json, @"a UIColor. Expected an iOS dynamic appearance aware color.");
1041
1061
  return nil;
1042
1062
  }
1063
+ #if TARGET_OS_OSX // [macOS
1064
+ } else if ((value = [dictionary objectForKey:@"colorWithSystemEffect"])) {
1065
+ // AppKit's pressed / disabled / rollover variants of a colour. There is
1066
+ // no UIKit equivalent, so this arm exists only on macOS.
1067
+ NSDictionary *spec = value;
1068
+ UIColor *baseColor = [RCTConvert UIColor:[spec objectForKey:@"baseColor"]];
1069
+ NSString *effect = [RCTConvert NSString:[spec objectForKey:@"systemEffect"]];
1070
+ if (baseColor == nil) {
1071
+ RCTLogConvertError(json, @"a UIColor. colorWithSystemEffect needs a baseColor.");
1072
+ return nil;
1073
+ }
1074
+ static NSDictionary<NSString *, NSNumber *> *effects;
1075
+ static dispatch_once_t onceToken;
1076
+ dispatch_once(&onceToken, ^{
1077
+ effects = @{
1078
+ @"none" : @(NSColorSystemEffectNone),
1079
+ @"pressed" : @(NSColorSystemEffectPressed),
1080
+ @"deepPressed" : @(NSColorSystemEffectDeepPressed),
1081
+ @"disabled" : @(NSColorSystemEffectDisabled),
1082
+ @"rollover" : @(NSColorSystemEffectRollover),
1083
+ };
1084
+ });
1085
+ NSNumber *resolved = effects[effect ?: @"none"];
1086
+ if (resolved == nil) {
1087
+ RCTLogConvertError(json, @"a UIColor. Unknown system effect.");
1088
+ return nil;
1089
+ }
1090
+ return [baseColor colorWithSystemEffect:(NSColorSystemEffect)resolved.integerValue];
1091
+ #endif // macOS]
1043
1092
  } else {
1044
1093
  RCTLogConvertError(json, @"a UIColor. Expected an iOS semantic color or dynamic appearance aware color.");
1045
1094
  return nil;
@@ -55,6 +55,13 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
55
55
  NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
56
56
  RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
57
57
  BOOL _focusable;
58
+ #if TARGET_OS_OSX // [macOS
59
+ // AppKit reads these rather than being told them, so they are answered from
60
+ // stored values by the overrides further down.
61
+ BOOL _allowsVibrancy;
62
+ BOOL _mouseDownCanMoveWindow;
63
+ NSTrackingArea *_mouseTrackingArea;
64
+ #endif // macOS]
58
65
  }
59
66
 
60
67
  #ifdef RCT_DYNAMIC_FRAMEWORKS
@@ -69,6 +76,10 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
69
76
  if (self = [super initWithFrame:frame]) {
70
77
  _props = ViewShadowNode::defaultSharedProps();
71
78
  _reactSubviews = [NSMutableArray new];
79
+ #if TARGET_OS_OSX // [macOS] mirror HostPlatformViewProps' defaults
80
+ _mouseDownCanMoveWindow = YES;
81
+ _allowsVibrancy = NO;
82
+ #endif // macOS]
72
83
  #if !TARGET_OS_TV
73
84
  self.multipleTouchEnabled = YES;
74
85
  #endif
@@ -608,9 +619,148 @@ static BOOL RCTLayerTransformCollapsesAxis(CALayer *layer)
608
619
 
609
620
  _needsInvalidateLayer = _needsInvalidateLayer || needsInvalidateLayer;
610
621
 
622
+ #if TARGET_OS_OSX // [macOS
623
+ [self _updateMacOSProps:oldViewProps newProps:newViewProps];
624
+ #endif // macOS]
625
+
611
626
  _props = std::static_pointer_cast<const ViewProps>(props);
612
627
  }
613
628
 
629
+ #if TARGET_OS_OSX // [macOS
630
+ /**
631
+ * The props AppKit has and UIKit does not. They are declared in
632
+ * HostPlatformViewProps, so `ViewProps` already carries them on this platform.
633
+ */
634
+ - (void)_updateMacOSProps:(const ViewProps &)oldViewProps newProps:(const ViewProps &)newViewProps
635
+ {
636
+ if (oldViewProps.tooltip != newViewProps.tooltip) {
637
+ self.toolTip = newViewProps.tooltip.has_value() ? RCTNSStringFromString(*newViewProps.tooltip) : nil;
638
+ }
639
+
640
+ if (oldViewProps.acceptsFirstMouse != newViewProps.acceptsFirstMouse) {
641
+ self.acceptsFirstMouse = newViewProps.acceptsFirstMouse;
642
+ }
643
+
644
+ if (oldViewProps.mouseDownCanMoveWindow != newViewProps.mouseDownCanMoveWindow) {
645
+ _mouseDownCanMoveWindow = newViewProps.mouseDownCanMoveWindow;
646
+ }
647
+
648
+ if (oldViewProps.enableFocusRing != newViewProps.enableFocusRing) {
649
+ self.enableFocusRing = newViewProps.enableFocusRing;
650
+ }
651
+
652
+ // -allowsVibrancy and -canBecomeKeyView are read by AppKit rather than set,
653
+ // so the values are stored and the overrides below answer from them.
654
+ if (oldViewProps.allowsVibrancy != newViewProps.allowsVibrancy) {
655
+ _allowsVibrancy = newViewProps.allowsVibrancy;
656
+ self.needsDisplay = YES;
657
+ }
658
+
659
+ if (oldViewProps.focusable != newViewProps.focusable) {
660
+ _focusable = newViewProps.focusable;
661
+ }
662
+
663
+ if (oldViewProps.hostPlatformEvents != newViewProps.hostPlatformEvents) {
664
+ [self _updateMouseTracking:newViewProps.hostPlatformEvents.wantsMouseTracking()];
665
+ }
666
+ }
667
+
668
+ /**
669
+ * A view only tracks the mouse while something is listening. Tracking every
670
+ * view would cost a dispatch per view per mouse move.
671
+ */
672
+ - (void)_updateMouseTracking:(BOOL)wanted
673
+ {
674
+ if (wanted == (_mouseTrackingArea != nil)) {
675
+ return;
676
+ }
677
+
678
+ if (!wanted) {
679
+ [self removeTrackingArea:_mouseTrackingArea];
680
+ _mouseTrackingArea = nil;
681
+ return;
682
+ }
683
+
684
+ _mouseTrackingArea = [[NSTrackingArea alloc]
685
+ initWithRect:NSZeroRect
686
+ options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
687
+ owner:self
688
+ userInfo:nil];
689
+ [self addTrackingArea:_mouseTrackingArea];
690
+ }
691
+
692
+ - (HostPlatformViewEventEmitter::MouseEvent)_mouseEventFromNSEvent:(NSEvent *)event
693
+ {
694
+ NSPoint inWindow = event.locationInWindow;
695
+ NSPoint inView = [self convertPoint:inWindow fromView:nil];
696
+ NSPoint onScreen = self.window != nil ? [self.window convertPointToScreen:inWindow] : inWindow;
697
+
698
+ HostPlatformViewEventEmitter::MouseEvent mouseEvent = {};
699
+ mouseEvent.clientX = inView.x;
700
+ mouseEvent.clientY = inView.y;
701
+ mouseEvent.pageX = inWindow.x;
702
+ mouseEvent.pageY = inWindow.y;
703
+ mouseEvent.screenX = onScreen.x;
704
+ mouseEvent.screenY = onScreen.y;
705
+ return mouseEvent;
706
+ }
707
+
708
+ - (void)mouseEntered:(NSEvent *)event
709
+ {
710
+ [super mouseEntered:event];
711
+ if (_eventEmitter != nullptr) {
712
+ _eventEmitter->onMouseEnter([self _mouseEventFromNSEvent:event]);
713
+ }
714
+ }
715
+
716
+ - (void)mouseExited:(NSEvent *)event
717
+ {
718
+ [super mouseExited:event];
719
+ if (_eventEmitter != nullptr) {
720
+ _eventEmitter->onMouseLeave([self _mouseEventFromNSEvent:event]);
721
+ }
722
+ }
723
+
724
+ - (void)mouseUp:(NSEvent *)event
725
+ {
726
+ [super mouseUp:event];
727
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
728
+ if (_eventEmitter != nullptr && event.clickCount == 2 &&
729
+ viewProps.hostPlatformEvents[HostPlatformViewEvents::Offset::DoubleClick]) {
730
+ _eventEmitter->onDoubleClick([self _mouseEventFromNSEvent:event]);
731
+ }
732
+ }
733
+
734
+ - (void)rightMouseUp:(NSEvent *)event
735
+ {
736
+ [super rightMouseUp:event];
737
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
738
+ if (_eventEmitter != nullptr && viewProps.hostPlatformEvents[HostPlatformViewEvents::Offset::AuxClick]) {
739
+ _eventEmitter->onAuxClick([self _mouseEventFromNSEvent:event]);
740
+ }
741
+ }
742
+
743
+ - (BOOL)allowsVibrancy
744
+ {
745
+ return _allowsVibrancy;
746
+ }
747
+
748
+ - (BOOL)mouseDownCanMoveWindow
749
+ {
750
+ return _mouseDownCanMoveWindow;
751
+ }
752
+
753
+ - (BOOL)canBecomeKeyView
754
+ {
755
+ return _focusable;
756
+ }
757
+
758
+ - (BOOL)acceptsFirstResponder
759
+ {
760
+ return _focusable || [super acceptsFirstResponder];
761
+ }
762
+ #endif // macOS]
763
+
614
764
  - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
615
765
  {
616
766
  assert(std::dynamic_pointer_cast<const ViewEventEmitter>(eventEmitter));
@@ -102,6 +102,10 @@ Pod::Spec.new do |s|
102
102
  "\"$(PODS_TARGET_SRCROOT)/react/renderer/components/legacyviewmanagerinterop/platform/ios\"",
103
103
  "\"$(PODS_TARGET_SRCROOT)/react/renderer/components/text/platform/cxx\"",
104
104
  "\"$(PODS_TARGET_SRCROOT)/react/renderer/components/textinput/platform/ios\"",
105
+ # [macOS] macOS has its own HostPlatformViewProps -- tooltip, focus ring,
106
+ # first mouse and the rest. Its directory has to come first so it wins
107
+ # over the shared C++ one. macOS]
108
+ "\"$(PODS_TARGET_SRCROOT)/react/renderer/components/view/platform/macos\"",
105
109
  "\"$(PODS_TARGET_SRCROOT)/react/renderer/components/view/platform/cxx\"",
106
110
  ]
107
111
  end
@@ -135,6 +139,11 @@ Pod::Spec.new do |s|
135
139
  sss.dependency "React-renderercss"
136
140
  sss.dependency "Yoga"
137
141
  sss.source_files = podspec_sources(["react/renderer/components/view/*.{m,mm,cpp,h}", "react/renderer/components/view/platform/cxx/**/*.{m,mm,cpp,h}"], ["react/renderer/components/view/*.{h}", "react/renderer/components/view/platform/cxx/**/*.{h}"])
142
+ # [macOS] Swap the shared C++ host-platform view files for the macOS ones.
143
+ # Both define the same symbols, so this is a replacement, not an addition.
144
+ sss.osx.source_files = podspec_sources(["react/renderer/components/view/*.{m,mm,cpp,h}", "react/renderer/components/view/platform/macos/**/*.{m,mm,cpp,h}"], ["react/renderer/components/view/*.{h}", "react/renderer/components/view/platform/macos/**/*.{h}"])
145
+ sss.osx.exclude_files = "react/renderer/components/view/platform/cxx/**/*"
146
+ # macOS]
138
147
  sss.header_dir = "react/renderer/components/view"
139
148
  end
140
149
 
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] Touch is the shared one; a mouse is reported through the same path.
9
+
10
+ #pragma once
11
+
12
+ #include <react/renderer/components/view/BaseTouch.h>
13
+
14
+ namespace facebook::react {
15
+ using HostPlatformTouch = BaseTouch;
16
+ } // namespace facebook::react
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] See HostPlatformViewEventEmitter.h.
9
+
10
+ #include "HostPlatformViewEventEmitter.h"
11
+
12
+ namespace facebook::react {
13
+
14
+ static jsi::Value mouseEventPayload(jsi::Runtime &runtime, const HostPlatformViewEventEmitter::MouseEvent &event)
15
+ {
16
+ auto payload = jsi::Object(runtime);
17
+ payload.setProperty(runtime, "clientX", event.clientX);
18
+ payload.setProperty(runtime, "clientY", event.clientY);
19
+ payload.setProperty(runtime, "screenX", event.screenX);
20
+ payload.setProperty(runtime, "screenY", event.screenY);
21
+ payload.setProperty(runtime, "pageX", event.pageX);
22
+ payload.setProperty(runtime, "pageY", event.pageY);
23
+ return payload;
24
+ }
25
+
26
+ #define RCT_MACOS_MOUSE_EVENT(name) \
27
+ void HostPlatformViewEventEmitter::name(const MouseEvent &event) const \
28
+ { \
29
+ dispatchEvent(#name, [event](jsi::Runtime &runtime) { \
30
+ return mouseEventPayload(runtime, event); \
31
+ }); \
32
+ }
33
+
34
+ RCT_MACOS_MOUSE_EVENT(onMouseEnter)
35
+ RCT_MACOS_MOUSE_EVENT(onMouseLeave)
36
+ RCT_MACOS_MOUSE_EVENT(onDoubleClick)
37
+ RCT_MACOS_MOUSE_EVENT(onAuxClick)
38
+
39
+ } // namespace facebook::react
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] Adds the pointer events AppKit can report and UIKit cannot.
9
+
10
+ #pragma once
11
+
12
+ #include <react/renderer/components/view/BaseViewEventEmitter.h>
13
+
14
+ namespace facebook::react {
15
+
16
+ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
17
+ public:
18
+ using BaseViewEventEmitter::BaseViewEventEmitter;
19
+
20
+ /**
21
+ * A mouse crossing into or out of the view. There is no touch equivalent, so
22
+ * these have no counterpart in BaseViewEventEmitter.
23
+ */
24
+ struct MouseEvent {
25
+ Float clientX{};
26
+ Float clientY{};
27
+ Float screenX{};
28
+ Float screenY{};
29
+ Float pageX{};
30
+ Float pageY{};
31
+ };
32
+
33
+ void onMouseEnter(const MouseEvent &event) const;
34
+ void onMouseLeave(const MouseEvent &event) const;
35
+ void onDoubleClick(const MouseEvent &event) const;
36
+ void onAuxClick(const MouseEvent &event) const;
37
+ };
38
+
39
+ } // namespace facebook::react
@@ -0,0 +1,59 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] Which macOS-only handlers a view actually has.
9
+ //
10
+ // Installing an NSTrackingArea on every view to find out later would cost a
11
+ // mouse-moved dispatch per view per move. The props carry a bitset instead, so
12
+ // the view only tracks the mouse when something is listening -- the same
13
+ // arrangement BaseViewProps uses for pointer and responder events.
14
+
15
+ #pragma once
16
+
17
+ #include <bitset>
18
+ #include <cstddef>
19
+
20
+ namespace facebook::react {
21
+
22
+ struct HostPlatformViewEvents {
23
+ std::bitset<8> bits{};
24
+
25
+ enum class Offset : std::size_t {
26
+ MouseEnter = 0,
27
+ MouseLeave = 1,
28
+ DoubleClick = 2,
29
+ AuxClick = 3,
30
+ };
31
+
32
+ constexpr bool operator[](Offset offset) const
33
+ {
34
+ return bits[static_cast<std::size_t>(offset)];
35
+ }
36
+
37
+ std::bitset<8>::reference operator[](Offset offset)
38
+ {
39
+ return bits[static_cast<std::size_t>(offset)];
40
+ }
41
+
42
+ /** True when any macOS mouse handler is present. */
43
+ bool wantsMouseTracking() const
44
+ {
45
+ return (*this)[Offset::MouseEnter] || (*this)[Offset::MouseLeave];
46
+ }
47
+ };
48
+
49
+ inline bool operator==(const HostPlatformViewEvents &lhs, const HostPlatformViewEvents &rhs)
50
+ {
51
+ return lhs.bits == rhs.bits;
52
+ }
53
+
54
+ inline bool operator!=(const HostPlatformViewEvents &lhs, const HostPlatformViewEvents &rhs)
55
+ {
56
+ return lhs.bits != rhs.bits;
57
+ }
58
+
59
+ } // namespace facebook::react
@@ -0,0 +1,113 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] See HostPlatformViewProps.h.
9
+
10
+ #include "HostPlatformViewProps.h"
11
+
12
+ #include <react/featureflags/ReactNativeFeatureFlags.h>
13
+ #include <react/renderer/core/propsConversions.h>
14
+
15
+ namespace facebook::react {
16
+
17
+ // The whole-map parsing path (feature flag off) needs a converter for the
18
+ // bitset, mirroring the one propsConversions.h provides for ViewEvents.
19
+ static inline HostPlatformViewEvents convertRawProp(
20
+ const PropsParserContext &context,
21
+ const RawProps &rawProps,
22
+ const HostPlatformViewEvents &sourceValue,
23
+ const HostPlatformViewEvents &defaultValue)
24
+ {
25
+ HostPlatformViewEvents result{};
26
+ using Offset = HostPlatformViewEvents::Offset;
27
+
28
+ #define RCT_MACOS_CONVERT_EVENT(name, offset) \
29
+ result[Offset::offset] = convertRawProp( \
30
+ context, rawProps, name, sourceValue[Offset::offset], defaultValue[Offset::offset])
31
+
32
+ RCT_MACOS_CONVERT_EVENT("onMouseEnter", MouseEnter);
33
+ RCT_MACOS_CONVERT_EVENT("onMouseLeave", MouseLeave);
34
+ RCT_MACOS_CONVERT_EVENT("onDoubleClick", DoubleClick);
35
+ RCT_MACOS_CONVERT_EVENT("onAuxClick", AuxClick);
36
+
37
+ #undef RCT_MACOS_CONVERT_EVENT
38
+
39
+ return result;
40
+ }
41
+
42
+ // Two parsing paths exist and both have to be fed. With the iterator setter
43
+ // feature flag on, props arrive one at a time through setProp and the
44
+ // constructor just carries the previous value forward; with it off, the
45
+ // constructor parses the whole RawProps map itself.
46
+ #define RCT_MACOS_PROP(name) \
47
+ name(ReactNativeFeatureFlags::enableCppPropsIteratorSetter() \
48
+ ? sourceProps.name \
49
+ : convertRawProp(context, rawProps, #name, sourceProps.name, {}))
50
+
51
+ HostPlatformViewProps::HostPlatformViewProps(
52
+ const PropsParserContext &context,
53
+ const HostPlatformViewProps &sourceProps,
54
+ const RawProps &rawProps)
55
+ : BaseViewProps(context, sourceProps, rawProps),
56
+ hostPlatformEvents(
57
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
58
+ ? sourceProps.hostPlatformEvents
59
+ : convertRawProp(context, rawProps, sourceProps.hostPlatformEvents, {})),
60
+ RCT_MACOS_PROP(focusable),
61
+ RCT_MACOS_PROP(enableFocusRing),
62
+ RCT_MACOS_PROP(tooltip),
63
+ RCT_MACOS_PROP(acceptsFirstMouse),
64
+ RCT_MACOS_PROP(allowsVibrancy),
65
+ RCT_MACOS_PROP(mouseDownCanMoveWindow)
66
+ {
67
+ }
68
+
69
+ #undef RCT_MACOS_PROP
70
+
71
+ void HostPlatformViewProps::setProp(
72
+ const PropsParserContext &context,
73
+ RawPropsPropNameHash hash,
74
+ const char *propName,
75
+ const RawValue &value)
76
+ {
77
+ // Every Props struct must call super unconditionally: several structs can
78
+ // share the same raw value, and skipping the call drops it for all of them.
79
+ BaseViewProps::setProp(context, hash, propName, value);
80
+
81
+ static auto defaults = HostPlatformViewProps{};
82
+
83
+ // An `onX` prop is stored as a bit rather than a value: all the view needs to
84
+ // know is whether anything is listening.
85
+ #define RCT_MACOS_EVENT_CASE(eventType) \
86
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("on" #eventType): { \
87
+ const auto offset = HostPlatformViewEvents::Offset::eventType; \
88
+ HostPlatformViewEvents defaultEvents{}; \
89
+ bool listening = defaultEvents[offset]; \
90
+ if (value.hasValue()) { \
91
+ fromRawValue(context, value, listening); \
92
+ } \
93
+ hostPlatformEvents[offset] = listening; \
94
+ return; \
95
+ }
96
+
97
+ switch (hash) {
98
+ RCT_MACOS_EVENT_CASE(MouseEnter);
99
+ RCT_MACOS_EVENT_CASE(MouseLeave);
100
+ RCT_MACOS_EVENT_CASE(DoubleClick);
101
+ RCT_MACOS_EVENT_CASE(AuxClick);
102
+ RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
103
+ RAW_SET_PROP_SWITCH_CASE_BASIC(enableFocusRing);
104
+ RAW_SET_PROP_SWITCH_CASE_BASIC(tooltip);
105
+ RAW_SET_PROP_SWITCH_CASE_BASIC(acceptsFirstMouse);
106
+ RAW_SET_PROP_SWITCH_CASE_BASIC(allowsVibrancy);
107
+ RAW_SET_PROP_SWITCH_CASE_BASIC(mouseDownCanMoveWindow);
108
+ }
109
+
110
+ #undef RCT_MACOS_EVENT_CASE
111
+ }
112
+
113
+ } // namespace facebook::react
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] The props a Mac view has and an iOS one does not.
9
+ //
10
+ // React Native reaches platform-specific view props through this class -- see
11
+ // the sibling directories under components/view/platform. Adding them here
12
+ // means no shared C++ changes at all, and the names match
13
+ // microsoft/react-native-macos so code written against that fork type-checks
14
+ // and behaves the same.
15
+
16
+ #pragma once
17
+
18
+ #include <react/renderer/components/view/BaseViewProps.h>
19
+ #include <react/renderer/core/PropsParserContext.h>
20
+
21
+ #include <optional>
22
+ #include <string>
23
+
24
+ #include "HostPlatformViewEvents.h"
25
+
26
+ namespace facebook::react {
27
+
28
+ class HostPlatformViewProps : public BaseViewProps {
29
+ public:
30
+ HostPlatformViewProps() = default;
31
+ HostPlatformViewProps(
32
+ const PropsParserContext &context,
33
+ const HostPlatformViewProps &sourceProps,
34
+ const RawProps &rawProps);
35
+
36
+ void
37
+ setProp(const PropsParserContext &context, RawPropsPropNameHash hash, const char *propName, const RawValue &value);
38
+
39
+ /** Which macOS-only handlers are attached; see HostPlatformViewEvents. */
40
+ HostPlatformViewEvents hostPlatformEvents{};
41
+
42
+ #pragma mark - Props
43
+
44
+ /** Participates in the key view loop, i.e. reachable by Tab. */
45
+ bool focusable{false};
46
+
47
+ /** Draws the focus ring while first responder. On by default, as in AppKit. */
48
+ bool enableFocusRing{true};
49
+
50
+ /** The view's help tag. std::nullopt leaves any inherited tooltip alone. */
51
+ std::optional<std::string> tooltip{};
52
+
53
+ /** Take the click that activates a background window, rather than swallowing it. */
54
+ bool acceptsFirstMouse{false};
55
+
56
+ /** Let the view sit in an NSVisualEffectView and blend with what is behind it. */
57
+ bool allowsVibrancy{false};
58
+
59
+ /**
60
+ * Whether dragging the view moves the window. AppKit's default is true, and
61
+ * that is kept here: a view that opts out is the interesting case.
62
+ */
63
+ bool mouseDownCanMoveWindow{true};
64
+ };
65
+
66
+ } // namespace facebook::react
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
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] Keyboard focus is a real thing on macOS, so unlike the shared C++
9
+ // initializer this reports `focusable` views as keyboard-focusable.
10
+
11
+ #pragma once
12
+
13
+ #include <react/renderer/components/view/ViewProps.h>
14
+ #include <react/renderer/core/ShadowNodeTraits.h>
15
+
16
+ namespace facebook::react::HostPlatformViewTraitsInitializer {
17
+
18
+ inline bool formsStackingContext(const ViewProps & /*props*/)
19
+ {
20
+ return false;
21
+ }
22
+
23
+ inline bool formsView(const ViewProps &props)
24
+ {
25
+ // These all need a backing NSView, so a view carrying any of them must not be
26
+ // flattened away: a focusable view has to join the key view loop, a tooltip
27
+ // has to attach to something, and mouse tracking needs an NSTrackingArea
28
+ // owner. Flattening is otherwise invisible -- the handler simply never fires.
29
+ return props.focusable || props.tooltip.has_value() || props.hostPlatformEvents.bits.any();
30
+ }
31
+
32
+ inline bool isKeyboardFocusable(const ViewProps &props)
33
+ {
34
+ return props.focusable;
35
+ }
36
+
37
+ } // namespace facebook::react::HostPlatformViewTraitsInitializer
@@ -30,6 +30,12 @@ struct Color {
30
30
 
31
31
  static Color createSemanticColor(std::vector<std::string> &semanticItems);
32
32
 
33
+ #if TARGET_OS_OSX // [macOS] Wrap an already-resolved NSColor. AppKit derives some
34
+ // colours from others -- colorWithSystemEffect: -- so the parser needs a way
35
+ // to hand back a colour it built itself. macOS]
36
+ static Color createFromHostPlatformColor(std::shared_ptr<void> hostPlatformColor);
37
+ #endif
38
+
33
39
  std::shared_ptr<void> getUIColor() const
34
40
  {
35
41
  return uiColor_;
@@ -95,6 +95,13 @@ int32_t ColorFromColorComponents(const facebook::react::ColorComponents &compone
95
95
  int32_t ColorFromUIColor(UIColor *color)
96
96
  {
97
97
  CGFloat rgba[4];
98
+ #if TARGET_OS_OSX // [macOS] catalog colours need converting first; see Color::getChannel
99
+ NSColor *rgbColor = [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
100
+ if (rgbColor == nil) {
101
+ return 0;
102
+ }
103
+ color = rgbColor;
104
+ #endif // macOS]
98
105
  [color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
99
106
  return ColorFromColorComponents(
100
107
  {.red = (float)rgba[0], .green = (float)rgba[1], .blue = (float)rgba[2], .alpha = (float)rgba[3]});
@@ -226,6 +233,23 @@ float Color::getChannel(int channelId) const
226
233
  {
227
234
  CGFloat rgba[4];
228
235
  UIColor *color = (__bridge UIColor *)getUIColor().get();
236
+ #if TARGET_OS_OSX // [macOS
237
+ // AppKit's named colours -- everything PlatformColor() resolves to, plus the
238
+ // system palette -- live in the Catalog colour space, and asking one for its
239
+ // RGBA components without converting first raises:
240
+ //
241
+ // -getRed:green:blue:alpha: not valid for the NSColor Catalog color
242
+ //
243
+ // sRGB is the right target: it is what the components are interpreted as
244
+ // everywhere else in the renderer.
245
+ NSColor *rgbColor = [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
246
+ if (rgbColor == nil) {
247
+ // A pattern or otherwise unconvertible colour. Treat it as transparent
248
+ // rather than raising, which would take the whole app down.
249
+ return 0;
250
+ }
251
+ color = rgbColor;
252
+ #endif // macOS]
229
253
  [color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
230
254
  return static_cast<float>(rgba[channelId]);
231
255
  }
@@ -235,6 +259,13 @@ std::size_t Color::getUIColorHash() const
235
259
  return uiColorHashValue_;
236
260
  }
237
261
 
262
+ #if TARGET_OS_OSX // [macOS
263
+ Color Color::createFromHostPlatformColor(std::shared_ptr<void> hostPlatformColor)
264
+ {
265
+ return Color(std::move(hostPlatformColor));
266
+ }
267
+ #endif // macOS]
268
+
238
269
  Color Color::createSemanticColor(std::vector<std::string> &semanticItems)
239
270
  {
240
271
  auto semanticColor = RCTPlatformColorFromSemanticItems(semanticItems);
@@ -63,6 +63,40 @@ SharedColor parsePlatformColor(const ContextContainer &contextContainer, int32_t
63
63
  items.at("dynamic").hasType<std::unordered_map<std::string, RawValue>>()) {
64
64
  auto dynamicItems = (std::unordered_map<std::string, RawValue>)items.at("dynamic");
65
65
  return RCTPlatformColorComponentsFromDynamicItems(contextContainer, surfaceId, dynamicItems);
66
+ #if TARGET_OS_OSX // [macOS
67
+ } else if (
68
+ items.find("colorWithSystemEffect") != items.end() &&
69
+ items.at("colorWithSystemEffect").hasType<std::unordered_map<std::string, RawValue>>()) {
70
+ // AppKit derives the pressed / disabled / rollover variant of a colour
71
+ // rather than making the caller supply it. UIKit has no equivalent, so
72
+ // this arm only exists here.
73
+ auto effectItems = (std::unordered_map<std::string, RawValue>)items.at("colorWithSystemEffect");
74
+ if (effectItems.count("baseColor") == 0u || effectItems.count("systemEffect") == 0u) {
75
+ return clearColor();
76
+ }
77
+
78
+ SharedColor baseSharedColor;
79
+ fromRawValue(contextContainer, surfaceId, effectItems.at("baseColor"), baseSharedColor);
80
+ auto effectName = (std::string)effectItems.at("systemEffect");
81
+
82
+ static const std::unordered_map<std::string, NSColorSystemEffect> effects = {
83
+ {"none", NSColorSystemEffectNone},
84
+ {"pressed", NSColorSystemEffectPressed},
85
+ {"deepPressed", NSColorSystemEffectDeepPressed},
86
+ {"disabled", NSColorSystemEffectDisabled},
87
+ {"rollover", NSColorSystemEffectRollover}};
88
+
89
+ auto effect = effects.find(effectName);
90
+ if (effect == effects.end()) {
91
+ return clearColor();
92
+ }
93
+
94
+ NSColor *baseColor = RCTPlatformColorFromColor(*baseSharedColor);
95
+ if (baseColor == nil) {
96
+ return clearColor();
97
+ }
98
+ return SharedColor(Color::createFromHostPlatformColor(wrapManagedObject([baseColor colorWithSystemEffect:effect->second])));
99
+ #endif // macOS]
66
100
  }
67
101
  }
68
102
 
@@ -183,6 +183,13 @@ static inline NSString *_NSStringFromCString(
183
183
  static inline facebook::react::ColorComponents _ColorComponentsFromUIColor(UIColor *color)
184
184
  {
185
185
  CGFloat rgba[4];
186
+ #if TARGET_OS_OSX // [macOS] catalog colours need converting first
187
+ NSColor *rgbColor = [color colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
188
+ if (rgbColor == nil) {
189
+ return {.red = 0, .green = 0, .blue = 0, .alpha = 0};
190
+ }
191
+ color = rgbColor;
192
+ #endif // macOS]
186
193
  [color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
187
194
  return {.red = (float)rgba[0], .green = (float)rgba[1], .blue = (float)rgba[2], .alpha = (float)rgba[3]};
188
195
  }
@@ -204,6 +211,22 @@ UIColor *RCTPlatformColorFromSemanticItems(std::vector<std::string> &semanticIte
204
211
  if (uiColor != nil) {
205
212
  return uiColor;
206
213
  }
214
+ #if TARGET_OS_OSX // [macOS
215
+ // _UIColorFromSemanticString only knows UIKit's vocabulary. AppKit's own --
216
+ // labelColor, controlAccentColor, windowBackgroundColor and the rest -- are
217
+ // class properties on NSColor, so asking NSColor directly covers all of
218
+ // them without a second table. Without this a macOS-only name silently
219
+ // resolves to clearColor and the view just renders transparent.
220
+ SEL appKitSelector = NSSelectorFromString(semanticNSString);
221
+ if (appKitSelector != nil && [UIColor respondsToSelector:appKitSelector]) {
222
+ IMP imp = [[UIColor class] methodForSelector:appKitSelector];
223
+ id (*getColor)(id, SEL) = (id (*)(id, SEL))imp;
224
+ id candidate = getColor([UIColor class], appKitSelector);
225
+ if ([candidate isKindOfClass:[UIColor class]]) {
226
+ return candidate;
227
+ }
228
+ }
229
+ #endif // macOS]
207
230
  }
208
231
 
209
232
  return UIColor.clearColor;
package/index.js CHANGED
@@ -270,6 +270,16 @@ module.exports = {
270
270
  return require('./Libraries/StyleSheet/PlatformColorValueTypesIOS')
271
271
  .DynamicColorIOS;
272
272
  },
273
+ // [macOS] AppKit's appearance-aware and system-effect colours.
274
+ get DynamicColorMacOS() {
275
+ return require('./Libraries/StyleSheet/PlatformColorValueTypesMacOS')
276
+ .DynamicColorMacOS;
277
+ },
278
+ get ColorWithSystemEffectMacOS() {
279
+ return require('./Libraries/StyleSheet/PlatformColorValueTypesMacOS')
280
+ .ColorWithSystemEffectMacOS;
281
+ },
282
+ // macOS]
273
283
  get Easing() {
274
284
  return require('./Libraries/Animated/Easing').default;
275
285
  },
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  #import "UIColor.h"
10
+ #import "UITraitCollection.h"
10
11
 
11
12
  @implementation NSColor (UIKitCompat)
12
13
 
@@ -14,7 +15,12 @@
14
15
  {
15
16
  return [NSColor colorWithName:nil
16
17
  dynamicProvider:^NSColor *(NSAppearance *appearance) {
17
- return provider(appearance);
18
+ // UIKit hands the provider a UITraitCollection and callers
19
+ // read -userInterfaceStyle and -accessibilityContrast off it.
20
+ // AppKit hands out an NSAppearance, which answers neither, so
21
+ // passing it straight through raises "unrecognized selector"
22
+ // the first time a dynamic colour is resolved.
23
+ return provider([UITraitCollection traitCollectionWithAppearance:appearance]);
18
24
  }];
19
25
  }
20
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "not-react-native-macos",
3
- "version": "0.87.1-rc.3",
3
+ "version": "0.87.1-rc.4",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {