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

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 (25) hide show
  1. package/Libraries/Components/View/ViewPropTypes.js +91 -0
  2. package/Libraries/NativeComponent/BaseViewConfig.macos.js +68 -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/Libraries/Types/CoreEventTypes.js +11 -0
  8. package/React/Base/RCTConvert.mm +49 -0
  9. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +294 -0
  10. package/ReactCommon/React-Fabric.podspec +9 -0
  11. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformTouch.h +16 -0
  12. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.cpp +74 -0
  13. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEventEmitter.h +52 -0
  14. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewEvents.h +61 -0
  15. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.cpp +121 -0
  16. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewProps.h +76 -0
  17. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/HostPlatformViewTraitsInitializer.h +38 -0
  18. package/ReactCommon/react/renderer/components/view/platform/macos/react/renderer/components/view/KeyEvent.h +100 -0
  19. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.h +6 -0
  20. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.mm +31 -0
  21. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/PlatformColorParser.mm +34 -0
  22. package/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm +23 -0
  23. package/index.js +10 -0
  24. package/macos/UIKitCompat/UIKit/UIColor.m +7 -1
  25. package/package.json +1 -1
@@ -10,6 +10,7 @@
10
10
 
11
11
  #import <CoreGraphics/CoreGraphics.h>
12
12
  #import <QuartzCore/QuartzCore.h>
13
+ #import <algorithm> // [macOS] std::find, for matching a press against keyDownEvents
13
14
  #import <objc/runtime.h>
14
15
  #import <ranges>
15
16
 
@@ -55,6 +56,13 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
55
56
  NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
56
57
  RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
57
58
  BOOL _focusable;
59
+ #if TARGET_OS_OSX // [macOS
60
+ // AppKit reads these rather than being told them, so they are answered from
61
+ // stored values by the overrides further down.
62
+ BOOL _allowsVibrancy;
63
+ BOOL _mouseDownCanMoveWindow;
64
+ NSTrackingArea *_mouseTrackingArea;
65
+ #endif // macOS]
58
66
  }
59
67
 
60
68
  #ifdef RCT_DYNAMIC_FRAMEWORKS
@@ -69,6 +77,10 @@ const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f;
69
77
  if (self = [super initWithFrame:frame]) {
70
78
  _props = ViewShadowNode::defaultSharedProps();
71
79
  _reactSubviews = [NSMutableArray new];
80
+ #if TARGET_OS_OSX // [macOS] mirror HostPlatformViewProps' defaults
81
+ _mouseDownCanMoveWindow = YES;
82
+ _allowsVibrancy = NO;
83
+ #endif // macOS]
72
84
  #if !TARGET_OS_TV
73
85
  self.multipleTouchEnabled = YES;
74
86
  #endif
@@ -608,9 +620,275 @@ static BOOL RCTLayerTransformCollapsesAxis(CALayer *layer)
608
620
 
609
621
  _needsInvalidateLayer = _needsInvalidateLayer || needsInvalidateLayer;
610
622
 
623
+ #if TARGET_OS_OSX // [macOS
624
+ [self _updateMacOSProps:oldViewProps newProps:newViewProps];
625
+ #endif // macOS]
626
+
611
627
  _props = std::static_pointer_cast<const ViewProps>(props);
612
628
  }
613
629
 
630
+ #if TARGET_OS_OSX // [macOS
631
+ /**
632
+ * The props AppKit has and UIKit does not. They are declared in
633
+ * HostPlatformViewProps, so `ViewProps` already carries them on this platform.
634
+ */
635
+ - (void)_updateMacOSProps:(const ViewProps &)oldViewProps newProps:(const ViewProps &)newViewProps
636
+ {
637
+ if (oldViewProps.tooltip != newViewProps.tooltip) {
638
+ self.toolTip = newViewProps.tooltip.has_value() ? RCTNSStringFromString(*newViewProps.tooltip) : nil;
639
+ }
640
+
641
+ if (oldViewProps.acceptsFirstMouse != newViewProps.acceptsFirstMouse) {
642
+ self.acceptsFirstMouse = newViewProps.acceptsFirstMouse;
643
+ }
644
+
645
+ if (oldViewProps.mouseDownCanMoveWindow != newViewProps.mouseDownCanMoveWindow) {
646
+ _mouseDownCanMoveWindow = newViewProps.mouseDownCanMoveWindow;
647
+ }
648
+
649
+ if (oldViewProps.enableFocusRing != newViewProps.enableFocusRing) {
650
+ self.enableFocusRing = newViewProps.enableFocusRing;
651
+ }
652
+
653
+ // -allowsVibrancy and -canBecomeKeyView are read by AppKit rather than set,
654
+ // so the values are stored and the overrides below answer from them.
655
+ if (oldViewProps.allowsVibrancy != newViewProps.allowsVibrancy) {
656
+ _allowsVibrancy = newViewProps.allowsVibrancy;
657
+ self.needsDisplay = YES;
658
+ }
659
+
660
+ if (oldViewProps.focusable != newViewProps.focusable) {
661
+ _focusable = newViewProps.focusable;
662
+ }
663
+
664
+ if (oldViewProps.hostPlatformEvents != newViewProps.hostPlatformEvents) {
665
+ [self _updateMouseTracking:newViewProps.hostPlatformEvents.wantsMouseTracking()];
666
+ }
667
+ }
668
+
669
+ /**
670
+ * A view only tracks the mouse while something is listening. Tracking every
671
+ * view would cost a dispatch per view per mouse move.
672
+ */
673
+ - (void)_updateMouseTracking:(BOOL)wanted
674
+ {
675
+ if (wanted == (_mouseTrackingArea != nil)) {
676
+ return;
677
+ }
678
+
679
+ if (!wanted) {
680
+ [self removeTrackingArea:_mouseTrackingArea];
681
+ _mouseTrackingArea = nil;
682
+ return;
683
+ }
684
+
685
+ _mouseTrackingArea = [[NSTrackingArea alloc]
686
+ initWithRect:NSZeroRect
687
+ options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect
688
+ owner:self
689
+ userInfo:nil];
690
+ [self addTrackingArea:_mouseTrackingArea];
691
+ }
692
+
693
+ - (HostPlatformViewEventEmitter::MouseEvent)_mouseEventFromNSEvent:(NSEvent *)event
694
+ {
695
+ NSPoint inWindow = event.locationInWindow;
696
+ NSPoint inView = [self convertPoint:inWindow fromView:nil];
697
+ NSPoint onScreen = self.window != nil ? [self.window convertPointToScreen:inWindow] : inWindow;
698
+
699
+ HostPlatformViewEventEmitter::MouseEvent mouseEvent = {};
700
+ mouseEvent.clientX = inView.x;
701
+ mouseEvent.clientY = inView.y;
702
+ mouseEvent.pageX = inWindow.x;
703
+ mouseEvent.pageY = inWindow.y;
704
+ mouseEvent.screenX = onScreen.x;
705
+ mouseEvent.screenY = onScreen.y;
706
+ return mouseEvent;
707
+ }
708
+
709
+ - (void)mouseEntered:(NSEvent *)event
710
+ {
711
+ [super mouseEntered:event];
712
+ if (_eventEmitter != nullptr) {
713
+ _eventEmitter->onMouseEnter([self _mouseEventFromNSEvent:event]);
714
+ }
715
+ }
716
+
717
+ - (void)mouseExited:(NSEvent *)event
718
+ {
719
+ [super mouseExited:event];
720
+ if (_eventEmitter != nullptr) {
721
+ _eventEmitter->onMouseLeave([self _mouseEventFromNSEvent:event]);
722
+ }
723
+ }
724
+
725
+ - (void)mouseUp:(NSEvent *)event
726
+ {
727
+ [super mouseUp:event];
728
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
729
+ if (_eventEmitter != nullptr && event.clickCount == 2 &&
730
+ viewProps.hostPlatformEvents[HostPlatformViewEvents::Offset::DoubleClick]) {
731
+ _eventEmitter->onDoubleClick([self _mouseEventFromNSEvent:event]);
732
+ }
733
+ }
734
+
735
+ - (void)rightMouseUp:(NSEvent *)event
736
+ {
737
+ [super rightMouseUp:event];
738
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
739
+ if (_eventEmitter != nullptr && viewProps.hostPlatformEvents[HostPlatformViewEvents::Offset::AuxClick]) {
740
+ _eventEmitter->onAuxClick([self _mouseEventFromNSEvent:event]);
741
+ }
742
+ }
743
+
744
+ #pragma mark - Keyboard Events
745
+
746
+ /**
747
+ * The W3C `key` name for a press, per https://www.w3.org/TR/uievents-key/.
748
+ *
749
+ * `charactersIgnoringModifiers` already gives the right answer for anything
750
+ * printable. The cases below are the ones where it gives a private-use unichar
751
+ * (the arrows, the function keys) or a control character (Return, Delete)
752
+ * instead of a name. Tab and Escape are matched on keyCode rather than
753
+ * character because AppKit reports them as \t and \e, which would otherwise
754
+ * arrive as those literal characters.
755
+ *
756
+ * Naming follows the cross-platform reconciliation react-native-windows and
757
+ * react-native-macos both use, so a key handler is portable between them.
758
+ */
759
+ static NSString *RCTKeyFromNSEvent(NSEvent *event)
760
+ {
761
+ NSString *characters = event.charactersIgnoringModifiers;
762
+ unichar code = characters.length > 0 ? [characters characterAtIndex:0] : 0;
763
+
764
+ switch (event.keyCode) {
765
+ case 48:
766
+ return @"Tab";
767
+ case 53:
768
+ return @"Escape";
769
+ default:
770
+ break;
771
+ }
772
+
773
+ switch (code) {
774
+ case NSEnterCharacter:
775
+ case NSNewlineCharacter:
776
+ case NSCarriageReturnCharacter:
777
+ return @"Enter";
778
+ case NSLeftArrowFunctionKey:
779
+ return @"ArrowLeft";
780
+ case NSRightArrowFunctionKey:
781
+ return @"ArrowRight";
782
+ case NSUpArrowFunctionKey:
783
+ return @"ArrowUp";
784
+ case NSDownArrowFunctionKey:
785
+ return @"ArrowDown";
786
+ case NSBackspaceCharacter:
787
+ case NSDeleteCharacter:
788
+ return @"Backspace";
789
+ case NSDeleteFunctionKey:
790
+ return @"Delete";
791
+ case NSHomeFunctionKey:
792
+ return @"Home";
793
+ case NSEndFunctionKey:
794
+ return @"End";
795
+ case NSPageUpFunctionKey:
796
+ return @"PageUp";
797
+ case NSPageDownFunctionKey:
798
+ return @"PageDown";
799
+ default:
800
+ break;
801
+ }
802
+
803
+ if (code >= NSF1FunctionKey && code <= NSF12FunctionKey) {
804
+ return [NSString stringWithFormat:@"F%u", (unsigned)(code - NSF1FunctionKey + 1)];
805
+ }
806
+
807
+ return characters;
808
+ }
809
+
810
+ /**
811
+ * Emits the press and reports whether the view claimed it.
812
+ *
813
+ * Claiming matters because AppKit interprets an unclaimed key itself once the
814
+ * responder chain is done with it: Tab moves focus, Escape cancels, anything
815
+ * else beeps. A view says which keys it means to act on through `keyDownEvents`
816
+ * / `keyUpEvents`, and only those suppress the default behaviour. Listening via
817
+ * `onKeyDown` alone deliberately does not, so observing a key does not change
818
+ * what it does.
819
+ */
820
+ - (BOOL)_handleKeyboardEvent:(NSEvent *)event
821
+ {
822
+ NSEventModifierFlags flags = event.modifierFlags;
823
+ KeyEvent keyEvent = {
824
+ .key = RCTStringFromNSString(RCTKeyFromNSEvent(event)),
825
+ .altKey = static_cast<bool>(flags & NSEventModifierFlagOption),
826
+ .ctrlKey = static_cast<bool>(flags & NSEventModifierFlagControl),
827
+ .shiftKey = static_cast<bool>(flags & NSEventModifierFlagShift),
828
+ .metaKey = static_cast<bool>(flags & NSEventModifierFlagCommand),
829
+ .capsLockKey = static_cast<bool>(flags & NSEventModifierFlagCapsLock),
830
+ .numericPadKey = static_cast<bool>(flags & NSEventModifierFlagNumericPad),
831
+ .helpKey = static_cast<bool>(flags & NSEventModifierFlagHelp),
832
+ .functionKey = static_cast<bool>(flags & NSEventModifierFlagFunction),
833
+ };
834
+
835
+ BOOL isKeyDown = event.type == NSEventTypeKeyDown;
836
+ const auto &viewProps = static_cast<const ViewProps &>(*_props);
837
+
838
+ // Calling super walks the responder chain, which is the view hierarchy, so
839
+ // every ancestor view would emit the same press. Fabric bubbles the event
840
+ // through the shadow tree on its own, so only the innermost view should emit.
841
+ // The flag rides on the NSEvent because that is the one object the whole
842
+ // chain shares.
843
+ static const char kEmittedKey = 0;
844
+ if (_eventEmitter != nullptr && !((NSNumber *)objc_getAssociatedObject(event, &kEmittedKey)).boolValue) {
845
+ if (isKeyDown) {
846
+ _eventEmitter->onKeyDown(keyEvent);
847
+ } else {
848
+ _eventEmitter->onKeyUp(keyEvent);
849
+ }
850
+ objc_setAssociatedObject(event, &kEmittedKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
851
+ }
852
+
853
+ const auto &handled = isKeyDown ? viewProps.keyDownEvents : viewProps.keyUpEvents;
854
+ return std::find(handled.cbegin(), handled.cend(), keyEvent) != handled.cend();
855
+ }
856
+
857
+ - (void)keyDown:(NSEvent *)event
858
+ {
859
+ if (![self _handleKeyboardEvent:event]) {
860
+ [super keyDown:event];
861
+ }
862
+ }
863
+
864
+ - (void)keyUp:(NSEvent *)event
865
+ {
866
+ if (![self _handleKeyboardEvent:event]) {
867
+ [super keyUp:event];
868
+ }
869
+ }
870
+
871
+ - (BOOL)allowsVibrancy
872
+ {
873
+ return _allowsVibrancy;
874
+ }
875
+
876
+ - (BOOL)mouseDownCanMoveWindow
877
+ {
878
+ return _mouseDownCanMoveWindow;
879
+ }
880
+
881
+ - (BOOL)canBecomeKeyView
882
+ {
883
+ return _focusable;
884
+ }
885
+
886
+ - (BOOL)acceptsFirstResponder
887
+ {
888
+ return _focusable || [super acceptsFirstResponder];
889
+ }
890
+ #endif // macOS]
891
+
614
892
  - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
615
893
  {
616
894
  assert(std::dynamic_pointer_cast<const ViewEventEmitter>(eventEmitter));
@@ -1772,7 +2050,16 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
1772
2050
 
1773
2051
  - (void)focus
1774
2052
  {
2053
+ #if TARGET_OS_OSX // [macOS] On AppKit -becomeFirstResponder is the window
2054
+ // notifying the view that it happened, not the view asking. Sending it
2055
+ // directly changes nothing: -makeFirstResponder: is the request. Without
2056
+ // this, `focusable` views never receive key events, because nothing ever
2057
+ // makes them first responder -- AppKit moves focus on Tab only, and only
2058
+ // when Full Keyboard Access is on.
2059
+ [self.window makeFirstResponder:self];
2060
+ #else // [macOS]
1775
2061
  [self becomeFirstResponder];
2062
+ #endif // [macOS]
1776
2063
 
1777
2064
  #if TARGET_OS_TV
1778
2065
  RCTSurfaceHostingProxyRootView *rootView = [self containingRootView];
@@ -1788,7 +2075,14 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
1788
2075
 
1789
2076
  - (void)blur
1790
2077
  {
2078
+ #if TARGET_OS_OSX // [macOS] Symmetrically: resigning is granted by the window,
2079
+ // and only if this view still holds the focus.
2080
+ if (self.window.firstResponder == self) {
2081
+ [self.window makeFirstResponder:nil];
2082
+ }
2083
+ #else // [macOS]
1791
2084
  [self resignFirstResponder];
2085
+ #endif // [macOS]
1792
2086
  }
1793
2087
 
1794
2088
  - (BOOL)becomeFirstResponder
@@ -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,74 @@
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
+ payload.setProperty(runtime, "altKey", event.altKey);
24
+ payload.setProperty(runtime, "ctrlKey", event.ctrlKey);
25
+ payload.setProperty(runtime, "shiftKey", event.shiftKey);
26
+ payload.setProperty(runtime, "metaKey", event.metaKey);
27
+ payload.setProperty(runtime, "button", event.button);
28
+ // Pressability's click guard reads this to tell a real mouse click apart from
29
+ // one synthesised by the responder system, which would otherwise fire onPress
30
+ // twice.
31
+ payload.setProperty(runtime, "pointerType", "mouse");
32
+ return payload;
33
+ }
34
+
35
+ static jsi::Value keyEventPayload(jsi::Runtime &runtime, const KeyEvent &event)
36
+ {
37
+ auto payload = jsi::Object(runtime);
38
+ payload.setProperty(runtime, "key", jsi::String::createFromUtf8(runtime, event.key));
39
+ payload.setProperty(runtime, "altKey", event.altKey);
40
+ payload.setProperty(runtime, "ctrlKey", event.ctrlKey);
41
+ payload.setProperty(runtime, "shiftKey", event.shiftKey);
42
+ payload.setProperty(runtime, "metaKey", event.metaKey);
43
+ payload.setProperty(runtime, "capsLockKey", event.capsLockKey);
44
+ payload.setProperty(runtime, "numericPadKey", event.numericPadKey);
45
+ payload.setProperty(runtime, "helpKey", event.helpKey);
46
+ payload.setProperty(runtime, "functionKey", event.functionKey);
47
+ return payload;
48
+ }
49
+
50
+ #define RCT_MACOS_MOUSE_EVENT(name) \
51
+ void HostPlatformViewEventEmitter::name(const MouseEvent &event) const \
52
+ { \
53
+ dispatchEvent(#name, [event](jsi::Runtime &runtime) { \
54
+ return mouseEventPayload(runtime, event); \
55
+ }); \
56
+ }
57
+
58
+ RCT_MACOS_MOUSE_EVENT(onMouseEnter)
59
+ RCT_MACOS_MOUSE_EVENT(onMouseLeave)
60
+ RCT_MACOS_MOUSE_EVENT(onDoubleClick)
61
+ RCT_MACOS_MOUSE_EVENT(onAuxClick)
62
+
63
+ #define RCT_MACOS_KEY_EVENT(name) \
64
+ void HostPlatformViewEventEmitter::name(const KeyEvent &event) const \
65
+ { \
66
+ dispatchEvent(#name, [event](jsi::Runtime &runtime) { \
67
+ return keyEventPayload(runtime, event); \
68
+ }); \
69
+ }
70
+
71
+ RCT_MACOS_KEY_EVENT(onKeyDown)
72
+ RCT_MACOS_KEY_EVENT(onKeyUp)
73
+
74
+ } // namespace facebook::react
@@ -0,0 +1,52 @@
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 and key events AppKit can report and UIKit cannot.
9
+
10
+ #pragma once
11
+
12
+ #include <react/renderer/components/view/BaseViewEventEmitter.h>
13
+
14
+ #include "KeyEvent.h"
15
+
16
+ namespace facebook::react {
17
+
18
+ class HostPlatformViewEventEmitter : public BaseViewEventEmitter {
19
+ public:
20
+ using BaseViewEventEmitter::BaseViewEventEmitter;
21
+
22
+ /**
23
+ * A mouse crossing into or out of the view. There is no touch equivalent, so
24
+ * these have no counterpart in BaseViewEventEmitter.
25
+ */
26
+ struct MouseEvent {
27
+ Float clientX{};
28
+ Float clientY{};
29
+ Float screenX{};
30
+ Float screenY{};
31
+ Float pageX{};
32
+ Float pageY{};
33
+
34
+ bool altKey{false};
35
+ bool ctrlKey{false};
36
+ bool shiftKey{false};
37
+ bool metaKey{false};
38
+
39
+ /** DOM button numbering: 0 left, 1 middle, 2 right. */
40
+ int button{0};
41
+ };
42
+
43
+ void onMouseEnter(const MouseEvent &event) const;
44
+ void onMouseLeave(const MouseEvent &event) const;
45
+ void onDoubleClick(const MouseEvent &event) const;
46
+ void onAuxClick(const MouseEvent &event) const;
47
+
48
+ void onKeyDown(const KeyEvent &event) const;
49
+ void onKeyUp(const KeyEvent &event) const;
50
+ };
51
+
52
+ } // namespace facebook::react
@@ -0,0 +1,61 @@
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
+ KeyDown = 4,
31
+ KeyUp = 5,
32
+ };
33
+
34
+ constexpr bool operator[](Offset offset) const
35
+ {
36
+ return bits[static_cast<std::size_t>(offset)];
37
+ }
38
+
39
+ std::bitset<8>::reference operator[](Offset offset)
40
+ {
41
+ return bits[static_cast<std::size_t>(offset)];
42
+ }
43
+
44
+ /** True when any macOS mouse handler is present. */
45
+ bool wantsMouseTracking() const
46
+ {
47
+ return (*this)[Offset::MouseEnter] || (*this)[Offset::MouseLeave];
48
+ }
49
+ };
50
+
51
+ inline bool operator==(const HostPlatformViewEvents &lhs, const HostPlatformViewEvents &rhs)
52
+ {
53
+ return lhs.bits == rhs.bits;
54
+ }
55
+
56
+ inline bool operator!=(const HostPlatformViewEvents &lhs, const HostPlatformViewEvents &rhs)
57
+ {
58
+ return lhs.bits != rhs.bits;
59
+ }
60
+
61
+ } // namespace facebook::react
@@ -0,0 +1,121 @@
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
+ RCT_MACOS_CONVERT_EVENT("onKeyDown", KeyDown);
37
+ RCT_MACOS_CONVERT_EVENT("onKeyUp", KeyUp);
38
+
39
+ #undef RCT_MACOS_CONVERT_EVENT
40
+
41
+ return result;
42
+ }
43
+
44
+ // Two parsing paths exist and both have to be fed. With the iterator setter
45
+ // feature flag on, props arrive one at a time through setProp and the
46
+ // constructor just carries the previous value forward; with it off, the
47
+ // constructor parses the whole RawProps map itself.
48
+ #define RCT_MACOS_PROP(name) \
49
+ name(ReactNativeFeatureFlags::enableCppPropsIteratorSetter() \
50
+ ? sourceProps.name \
51
+ : convertRawProp(context, rawProps, #name, sourceProps.name, {}))
52
+
53
+ HostPlatformViewProps::HostPlatformViewProps(
54
+ const PropsParserContext &context,
55
+ const HostPlatformViewProps &sourceProps,
56
+ const RawProps &rawProps)
57
+ : BaseViewProps(context, sourceProps, rawProps),
58
+ hostPlatformEvents(
59
+ ReactNativeFeatureFlags::enableCppPropsIteratorSetter()
60
+ ? sourceProps.hostPlatformEvents
61
+ : convertRawProp(context, rawProps, sourceProps.hostPlatformEvents, {})),
62
+ RCT_MACOS_PROP(focusable),
63
+ RCT_MACOS_PROP(enableFocusRing),
64
+ RCT_MACOS_PROP(keyDownEvents),
65
+ RCT_MACOS_PROP(keyUpEvents),
66
+ RCT_MACOS_PROP(tooltip),
67
+ RCT_MACOS_PROP(acceptsFirstMouse),
68
+ RCT_MACOS_PROP(allowsVibrancy),
69
+ RCT_MACOS_PROP(mouseDownCanMoveWindow)
70
+ {
71
+ }
72
+
73
+ #undef RCT_MACOS_PROP
74
+
75
+ void HostPlatformViewProps::setProp(
76
+ const PropsParserContext &context,
77
+ RawPropsPropNameHash hash,
78
+ const char *propName,
79
+ const RawValue &value)
80
+ {
81
+ // Every Props struct must call super unconditionally: several structs can
82
+ // share the same raw value, and skipping the call drops it for all of them.
83
+ BaseViewProps::setProp(context, hash, propName, value);
84
+
85
+ static auto defaults = HostPlatformViewProps{};
86
+
87
+ // An `onX` prop is stored as a bit rather than a value: all the view needs to
88
+ // know is whether anything is listening.
89
+ #define RCT_MACOS_EVENT_CASE(eventType) \
90
+ case CONSTEXPR_RAW_PROPS_KEY_HASH("on" #eventType): { \
91
+ const auto offset = HostPlatformViewEvents::Offset::eventType; \
92
+ HostPlatformViewEvents defaultEvents{}; \
93
+ bool listening = defaultEvents[offset]; \
94
+ if (value.hasValue()) { \
95
+ fromRawValue(context, value, listening); \
96
+ } \
97
+ hostPlatformEvents[offset] = listening; \
98
+ return; \
99
+ }
100
+
101
+ switch (hash) {
102
+ RCT_MACOS_EVENT_CASE(MouseEnter);
103
+ RCT_MACOS_EVENT_CASE(MouseLeave);
104
+ RCT_MACOS_EVENT_CASE(DoubleClick);
105
+ RCT_MACOS_EVENT_CASE(AuxClick);
106
+ RCT_MACOS_EVENT_CASE(KeyDown);
107
+ RCT_MACOS_EVENT_CASE(KeyUp);
108
+ RAW_SET_PROP_SWITCH_CASE_BASIC(focusable);
109
+ RAW_SET_PROP_SWITCH_CASE_BASIC(enableFocusRing);
110
+ RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents);
111
+ RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents);
112
+ RAW_SET_PROP_SWITCH_CASE_BASIC(tooltip);
113
+ RAW_SET_PROP_SWITCH_CASE_BASIC(acceptsFirstMouse);
114
+ RAW_SET_PROP_SWITCH_CASE_BASIC(allowsVibrancy);
115
+ RAW_SET_PROP_SWITCH_CASE_BASIC(mouseDownCanMoveWindow);
116
+ }
117
+
118
+ #undef RCT_MACOS_EVENT_CASE
119
+ }
120
+
121
+ } // namespace facebook::react