Skip to content

Commit

Permalink
chore(fabric, text): Refactor macOS implementation to use contentView
Browse files Browse the repository at this point in the history
  • Loading branch information
Saadnajmi committed Nov 19, 2024
1 parent d2800bd commit 081ac2a
Showing 1 changed file with 42 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@
// ParagraphTextView is an auxiliary view we set as contentView so the drawing
// can happen on top of the layers manipulated by RCTViewComponentView (the parent view)
@interface RCTParagraphTextView : RCTUIView // [macOS]
#else // [macOS
// On macOS, we also defer drawing to an NSTextView,
// in order to get more native behaviors like text selection.
@interface RCTParagraphTextView : NSTextView // [macOS]
#endif // macOS]

@property (nonatomic) ParagraphShadowNode::ConcreteState::Shared state;
@property (nonatomic) ParagraphAttributes paragraphAttributes;
@property (nonatomic) LayoutMetrics layoutMetrics;

#if TARGET_OS_OSX // [macOS]
/// UIKit compatibility shim that simply calls `[self setNeedsDisplay:YES]`
- (void)setNeedsDisplay;
#endif

@end
#else // [macOS
#if TARGET_OS_OSX // [macOS
// On macOS, we defer drawing to an NSTextView rather than a plan NSView, in order
// to get more native behaviors like text selection. We make sure this NSTextView
// does not take focus.
@interface RCTParagraphComponentUnfocusableTextView : NSTextView
@end
#endif // macOS]

#if !TARGET_OS_OSX // [macOS]
@interface RCTParagraphComponentView () <UIEditMenuInteractionDelegate>
Expand All @@ -62,10 +64,8 @@ @implementation RCTParagraphComponentView {
RCTParagraphComponentAccessibilityProvider *_accessibilityProvider;
#if !TARGET_OS_OSX // [macOS]
UILongPressGestureRecognizer *_longPressGestureRecognizer;
RCTParagraphTextView *_textView;
#else // [macOS
RCTParagraphComponentUnfocusableTextView *_textView;
#endif // macOS]
RCTParagraphTextView *_textView;
}

- (instancetype)initWithFrame:(CGRect)frame
Expand All @@ -77,15 +77,14 @@ - (instancetype)initWithFrame:(CGRect)frame
self.opaque = NO;
_textView = [RCTParagraphTextView new];
_textView.backgroundColor = RCTUIColor.clearColor; // [macOS]
self.contentView = _textView;
#else // [macOS
// Make the RCTParagraphComponentView accessible and available in the a11y hierarchy.
self.accessibilityElement = YES;
self.accessibilityRole = NSAccessibilityStaticTextRole;
// Fix blurry text on non-retina displays.
self.canDrawSubviewsIntoLayer = YES;
// The NSTextView is responsible for drawing text and managing selection.
_textView = [[RCTParagraphComponentUnfocusableTextView alloc] initWithFrame:self.bounds];
_textView = [[RCTParagraphTextView alloc] initWithFrame:self.bounds];
// The RCTParagraphComponentUnfocusableTextView is only used for rendering and should not appear in the a11y hierarchy.
_textView.accessibilityElement = NO;
_textView.usesFontPanel = NO;
Expand All @@ -98,6 +97,7 @@ - (instancetype)initWithFrame:(CGRect)frame
self.contentView = _textView;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
#endif // macOS]
self.contentView = _textView;
}

return self;
Expand Down Expand Up @@ -164,12 +164,9 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState
{
_state = std::static_pointer_cast<const ParagraphShadowNode::ConcreteState>(state);
#if !TARGET_OS_OSX // [macOS]
_textView.state = _state;
[_textView setNeedsDisplay];
[self setNeedsLayout];
[self _updateTextView];
#endif // macOS]
}

- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
Expand All @@ -178,53 +175,10 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
// Using stored `_layoutMetrics` as `oldLayoutMetrics` here to avoid
// re-applying individual sub-values which weren't changed.
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:_layoutMetrics];
#if !TARGET_OS_OSX // [macOS]
_textView.layoutMetrics = _layoutMetrics;
[_textView setNeedsDisplay];
[self setNeedsLayout];
#else // [macOS
[self _updateTextView];
#endif // macOS]
}

#if TARGET_OS_OSX // [macOS
- (void)_updateTextView
{
if (!_state) {
return;
}

auto textLayoutManager = _state->getData().paragraphLayoutManager.getTextLayoutManager();

if (!textLayoutManager) {
return;
}

RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());

CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());

NSTextStorage *textStorage = [nativeTextLayoutManager getTextStorageForAttributedString:_state->getData().attributedString paragraphAttributes:_paragraphAttributes frame:frame];

NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;

[_textView replaceTextContainer:textContainer];

NSArray<NSLayoutManager *> *managers = [[textStorage layoutManagers] copy];
for (NSLayoutManager *manager in managers) {
[textStorage removeLayoutManager:manager];
}

_textView.minSize = frame.size;
_textView.maxSize = frame.size;
_textView.frame = frame;
_textView.textStorage.attributedString = textStorage;

[self setNeedsDisplay];
}
#endif // macOS]

- (void)prepareForRecycle
{
Expand Down Expand Up @@ -427,11 +381,13 @@ - (void)copy:(id)sender
return RCTParagraphComponentView.class;
}

#if !TARGET_OS_OSX // [macOS]
@implementation RCTParagraphTextView {
#if !TARGET_OS_OSX // [macOS]
CAShapeLayer *_highlightLayer;
#endif // macOS]
}


- (void)drawRect:(CGRect)rect
{
if (!_state) {
Expand All @@ -449,6 +405,7 @@ - (void)drawRect:(CGRect)rect

CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());

#if !TARGET_OS_OSX // [macOS]
[nativeTextLayoutManager drawAttributedString:_state->getData().attributedString
paragraphAttributes:_paragraphAttributes
frame:frame
Expand All @@ -466,12 +423,33 @@ - (void)drawRect:(CGRect)rect
self->_highlightLayer = nil;
}
}];
#else // [macOS
NSTextStorage *textStorage = [nativeTextLayoutManager getTextStorageForAttributedString:_state->getData().attributedString paragraphAttributes:_paragraphAttributes size:frame.size];

NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;

[self replaceTextContainer:textContainer];

NSArray<NSLayoutManager *> *managers = [[textStorage layoutManagers] copy];
for (NSLayoutManager *manager in managers) {
[textStorage removeLayoutManager:manager];
}

self.minSize = frame.size;
self.maxSize = frame.size;
self.frame = frame;
[[self textStorage] setAttributedString:textStorage];

[super drawRect:rect];
#endif
}

@end
#else // [macOS
#if TARGET_OS_OSX // [macOS
@implementation RCTParagraphComponentUnfocusableTextView
- (void)setNeedsDisplay
{
[self setNeedsDisplay:YES];
}

- (BOOL)canBecomeKeyView
{
Expand All @@ -487,6 +465,6 @@ - (BOOL)resignFirstResponder

return [super resignFirstResponder];
}
#endif // macOS]

@end
#endif // macOS]

0 comments on commit 081ac2a

Please sign in to comment.