Skip to content

Commit

Permalink
Distinguish between embedded views and virtual text subviews
Browse files Browse the repository at this point in the history
  • Loading branch information
Adam Gleitman committed Jun 28, 2024
1 parent 942c7d6 commit ee372a9
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
19 changes: 17 additions & 2 deletions packages/react-native/Libraries/Text/Text/RCTTextShadowView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ - (void)uiManagerWillPerformMounting

NSNumber *tag = self.reactTag;
NSMutableArray<NSNumber *> *descendantViewTags = [NSMutableArray new];
NSMutableArray<NSNumber *> *virtualSubviewTags = [NSMutableArray new];

#if !TARGET_OS_OSX // [macOS]
[textStorage enumerateAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
Expand All @@ -109,7 +110,7 @@ - (void)uiManagerWillPerformMounting

id tagAttribute = attrs[RCTTextAttributesTagAttributeName];
if ([tagAttribute isKindOfClass:[NSNumber class]] && ![tagAttribute isEqualToNumber:tag]) {
[descendantViewTags addObject:tagAttribute];
[virtualSubviewTags addObject:tagAttribute];
}
}];
#endif // macOS]
Expand All @@ -131,11 +132,25 @@ - (void)uiManagerWillPerformMounting
[descendantViews addObject:descendantView];
}];


NSMutableArray<RCTVirtualTextView *> *virtualSubviews = [NSMutableArray arrayWithCapacity:virtualSubviewTags.count]; // [macOS]
[virtualSubviewTags
enumerateObjectsUsingBlock:^(NSNumber *_Nonnull virtualSubviewTag, NSUInteger index, BOOL *_Nonnull stop) {
RCTPlatformView *virtualSubview = viewRegistry[virtualSubviewTag]; // [macOS]
if ([virtualSubview isKindOfClass:[RCTVirtualTextView class]]) {
[virtualSubviews addObject:(RCTVirtualTextView *)virtualSubview];
}
}];

// Removing all references to Shadow Views to avoid unnecessary retaining.
[textStorage removeAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
range:NSMakeRange(0, textStorage.length)];

[textView setTextStorage:textStorage contentFrame:contentFrame descendantViews:descendantViews];
[textView setTextStorage:textStorage contentFrame:contentFrame descendantViews:descendantViews
#if TARGET_OS_OSX // [macOS
virtualSubviews:virtualSubviews
#endif // macOS]
];
}];
}

Expand Down
6 changes: 6 additions & 0 deletions packages/react-native/Libraries/Text/Text/RCTTextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import <React/RCTComponent.h>
#import <React/RCTEventDispatcher.h> // [macOS]
#import <React/RCTVirtualTextView.h>

#import <React/RCTUIKit.h> // [macOS]

Expand All @@ -23,6 +24,11 @@ NS_ASSUME_NONNULL_BEGIN
descendantViews:(NSArray<RCTPlatformView *> *)descendantViews; // [macOS]

#if TARGET_OS_OSX // [macOS
- (void)setTextStorage:(NSTextStorage *)textStorage
contentFrame:(CGRect)contentFrame
descendantViews:(NSArray<RCTPlatformView *> *)descendantViews
virtualSubviews:(NSArray<RCTVirtualTextView *> *_Nullable)virtualSubviews;

- (NSRect)getRectForCharRange:(NSRange)charRange;
#endif // macOS]

Expand Down
33 changes: 29 additions & 4 deletions packages/react-native/Libraries/Text/Text/RCTTextView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ @implementation RCTTextView {

id<RCTEventDispatcherProtocol> _eventDispatcher; // [macOS]
NSArray<RCTUIView *> *_Nullable _descendantViews; // [macOS]
NSArray<RCTVirtualTextView *> *_Nullable _virtualSubviews; // [macOS]
RCTUIView *_Nullable _currentHoveredSubview; // [macOS]
NSTextStorage *_Nullable _textStorage;
CGRect _contentFrame;
Expand Down Expand Up @@ -164,6 +165,19 @@ - (void)didUpdateReactSubviews
- (void)setTextStorage:(NSTextStorage *)textStorage
contentFrame:(CGRect)contentFrame
descendantViews:(NSArray<RCTPlatformView *> *)descendantViews // [macOS]
#if TARGET_OS_OSX // [macOS
{
[self setTextStorage:textStorage
contentFrame:contentFrame
descendantViews:descendantViews
virtualSubviews:nil];
}

- (void)setTextStorage:(NSTextStorage *)textStorage
contentFrame:(CGRect)contentFrame
descendantViews:(NSArray<RCTPlatformView *> *)descendantViews
virtualSubviews:(NSArray<RCTVirtualTextView *> *)virtualSubviews
#endif // macOS]
{
// This lets the textView own its text storage on macOS
// We update and replace the text container `_textView.textStorage.attributedString` when text/layout changes
Expand Down Expand Up @@ -207,6 +221,10 @@ - (void)setTextStorage:(NSTextStorage *)textStorage
[self addSubview:view];
}

#if TARGET_OS_OSX // [macOS
_virtualSubviews = virtualSubviews;
#endif // macOS]

[self setNeedsDisplay];
}

Expand Down Expand Up @@ -424,7 +442,9 @@ - (BOOL)hasMouseHoverEvent
}

// All descendant views of an RCTTextView are RCTVirtualTextViews
NSUInteger indexOfChildWithMouseHoverEvent = [_descendantViews indexOfObjectPassingTest:^BOOL(RCTUIView * _Nonnull childView, NSUInteger idx, BOOL * _Nonnull stop) {
// TODO: This isn't right in the case of embedded views. But maybe we want to keep these separate?
// TODO: How does current RNM handle mouse events in embedded views?
NSUInteger indexOfChildWithMouseHoverEvent = [_virtualSubviews indexOfObjectPassingTest:^BOOL(RCTVirtualTextView *_Nonnull childView, NSUInteger idx, BOOL *_Nonnull stop) {
*stop = [childView hasMouseHoverEvent];
return *stop;
}];
Expand Down Expand Up @@ -478,20 +498,25 @@ - (void)updateHoveredSubviewWithEvent:(NSEvent *)event
{
RCTUIView *hoveredView = nil;

if ([event type] != NSEventTypeMouseExited && _descendantViews != nil) {
if ([event type] != NSEventTypeMouseExited && _virtualSubviews != nil) {
NSNumber *reactTagOfHoveredView = [self reactTagAtMouseLocationFromEvent:event];

if (reactTagOfHoveredView == nil) {
// This happens if we hover over an embedded view, which will handle its own mouse events
return;
}

if ([reactTagOfHoveredView isEqualToNumber:self.reactTag]) {
// We're hovering over the root Text element
hoveredView = self;
} else {
// Maybe we're hovering over a child Text element?
NSUInteger index = [_descendantViews indexOfObjectPassingTest:^BOOL(RCTUIView * _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop) {
NSUInteger index = [_virtualSubviews indexOfObjectPassingTest:^BOOL(RCTVirtualTextView *_Nonnull view, NSUInteger idx, BOOL *_Nonnull stop) {
*stop = [[view reactTag] isEqualToNumber:reactTagOfHoveredView];
return *stop;
}];
if (index != NSNotFound) {
hoveredView = _descendantViews[index];
hoveredView = _virtualSubviews[index];
}
}
}
Expand Down

0 comments on commit ee372a9

Please sign in to comment.