diff --git a/apple/RNGestureHandlerButton.h b/apple/RNGestureHandlerButton.h index 9b31823969..3514c3cb26 100644 --- a/apple/RNGestureHandlerButton.h +++ b/apple/RNGestureHandlerButton.h @@ -9,6 +9,11 @@ #import "RNGestureHandler.h" #if TARGET_OS_OSX + +#include + +@protocol RCTComponentViewProtocol; + @interface RNGestureHandlerButton : NSControl #else @interface RNGestureHandlerButton : UIControl @@ -19,4 +24,11 @@ @property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets; @property (nonatomic) BOOL userEnabled; +#if TARGET_OS_OSX +- (void)mountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index; +- (void)unmountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index; +- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics + oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics; +#endif + @end diff --git a/apple/RNGestureHandlerButton.m b/apple/RNGestureHandlerButton.mm similarity index 54% rename from apple/RNGestureHandlerButton.m rename to apple/RNGestureHandlerButton.mm index e16f07e8e3..a48317fad8 100644 --- a/apple/RNGestureHandlerButton.m +++ b/apple/RNGestureHandlerButton.mm @@ -10,8 +10,13 @@ #if !TARGET_OS_OSX #import +#else +#import #endif +#import +#import + /** * Gesture Handler Button components overrides standard mechanism used by RN * to determine touch target, which normally would reurn the UIView that is placed @@ -82,4 +87,59 @@ - (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event } #endif +#if TARGET_OS_OSX +- (void)mountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index +{ + if (childComponentView.superview != nil) { + return; + } + + if (index < [[self subviews] count]) { + // Get the view currently at your desired index + NSView *existingView = [[self subviews] objectAtIndex:index]; + + // Now use this to insert your new view above the existing one + [self addSubview:childComponentView positioned:NSWindowAbove relativeTo:existingView]; + } else { + // if the index is out of bounds, add the new subview at the end + [self addSubview:childComponentView]; + } +} + +- (void)unmountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index +{ + [childComponentView removeFromSuperview]; +} + +- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics + oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics +{ + bool forceUpdate = oldLayoutMetrics == facebook::react::EmptyLayoutMetrics; + + if (forceUpdate || (layoutMetrics.frame != oldLayoutMetrics.frame)) { + CGRect frame = RCTCGRectFromRect(layoutMetrics.frame); + + if (!std::isfinite(frame.origin.x) || !std::isfinite(frame.origin.y) || !std::isfinite(frame.size.width) || + !std::isfinite(frame.size.height)) { + // CALayer will crash if we pass NaN or Inf values. + // It's unclear how to detect this case on cross-platform manner holistically, so we have to do it on the mounting + // layer as well. NaN/Inf is a kinda valid result of some math operations. Even if we can (and should) detect (and + // report early) incorrect (NaN and Inf) values which come from JavaScript side, we sometimes cannot backtrace the + // sources of a calculation that produced an incorrect/useless result. + RCTLogWarn( + @"-[UIView(ComponentViewProtocol) updateLayoutMetrics:oldLayoutMetrics:]: Received invalid layout metrics (%@) for a view (%@).", + NSStringFromCGRect(frame), + self); + } else { + self.frame = frame; + self.bounds = CGRect{CGPointZero, frame.size}; + } + } + + if (forceUpdate || (layoutMetrics.displayType != oldLayoutMetrics.displayType)) { + self.hidden = layoutMetrics.displayType == facebook::react::DisplayType::None; + } +} +#endif + @end diff --git a/apple/RNGestureHandlerButtonComponentView.h b/apple/RNGestureHandlerButtonComponentView.h index f1cbdd9ed5..642f3d9f1a 100644 --- a/apple/RNGestureHandlerButtonComponentView.h +++ b/apple/RNGestureHandlerButtonComponentView.h @@ -1,6 +1,10 @@ #ifdef RCT_NEW_ARCH_ENABLED +#if !TARGET_OS_OSX #import +#else +#import +#endif #import diff --git a/apple/RNGestureHandlerButtonComponentView.mm b/apple/RNGestureHandlerButtonComponentView.mm index b593c8be52..32a8641ac5 100644 --- a/apple/RNGestureHandlerButtonComponentView.mm +++ b/apple/RNGestureHandlerButtonComponentView.mm @@ -21,6 +21,15 @@ @implementation RNGestureHandlerButtonComponentView { RNGestureHandlerButton *_buttonView; } +#if TARGET_OS_OSX +// Here we want to disable view recycling on buttons. Listeners are not removed from views when they're being unmounted, +// therefore after navigating through other screens buttons may have different actions then they are supposed to have. ++ (BOOL)shouldBeRecycled +{ + return NO; +} +#endif + // Needed because of this: https://github.com/facebook/react-native/pull/37274 + (void)load { @@ -40,12 +49,12 @@ - (instancetype)initWithFrame:(CGRect)frame return self; } -- (void)mountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +- (void)mountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index { [_buttonView mountChildComponentView:childComponentView index:index]; } -- (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index +- (void)unmountChildComponentView:(RNGHUIView *)childComponentView index:(NSInteger)index { [_buttonView unmountChildComponentView:childComponentView index:index]; } @@ -97,7 +106,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & const auto &newProps = *std::static_pointer_cast(props); _buttonView.userEnabled = newProps.enabled; -#if !TARGET_OS_TV +#if !TARGET_OS_TV && !TARGET_OS_OSX _buttonView.exclusiveTouch = newProps.exclusive; #endif _buttonView.hitTestEdgeInsets = UIEdgeInsetsMake( diff --git a/apple/RNGestureHandlerButtonManager.m b/apple/RNGestureHandlerButtonManager.mm similarity index 94% rename from apple/RNGestureHandlerButtonManager.m rename to apple/RNGestureHandlerButtonManager.mm index 8b4f827a39..68345982cf 100644 --- a/apple/RNGestureHandlerButtonManager.m +++ b/apple/RNGestureHandlerButtonManager.mm @@ -30,7 +30,7 @@ @implementation RNGestureHandlerButtonManager - (RNGHUIView *)view { - return [RNGestureHandlerButton new]; + return (RNGHUIView *)[RNGestureHandlerButton new]; } @end