11
11
#if !TARGET_OS_OSX // [macOS]
12
12
#import < MobileCoreServices/UTCoreTypes.h>
13
13
#endif // [macOS]
14
+ #if TARGET_OS_OSX // [macOS
15
+ #import < QuartzCore/CAShapeLayer.h>
16
+ #endif // macOS]
14
17
15
18
#import < react/renderer/components/text/ParagraphComponentDescriptor.h>
16
19
#import < react/renderer/components/text/ParagraphProps.h>
25
28
#import " RCTConversions.h"
26
29
#import " RCTFabricComponentsPlugins.h"
27
30
28
- #import < QuartzCore/QuartzCore.h> // [macOS]
29
-
30
31
using namespace facebook ::react;
31
32
32
- #if !TARGET_OS_OSX // [macOS]
33
33
// ParagraphTextView is an auxiliary view we set as contentView so the drawing
34
34
// can happen on top of the layers manipulated by RCTViewComponentView (the parent view)
35
35
@interface RCTParagraphTextView : RCTUIView // [macOS]
36
- #else // [macOS
37
- // On macOS, we also defer drawing to an NSTextView,
38
- // in order to get more native behaviors like text selection.
39
- @interface RCTParagraphTextView : NSTextView // [macOS]
40
- #endif // macOS]
41
36
42
37
@property (nonatomic ) ParagraphShadowNode::ConcreteState::Shared state;
43
38
@property (nonatomic ) ParagraphAttributes paragraphAttributes;
44
39
@property (nonatomic ) LayoutMetrics layoutMetrics;
45
40
46
- #if TARGET_OS_OSX // [macOS]
47
- // / UIKit compatibility shim that simply calls `[self setNeedsDisplay:YES]`
48
- - (void )setNeedsDisplay ;
49
- #endif
50
-
51
41
@end
52
42
53
43
#if !TARGET_OS_OSX // [macOS]
54
44
@interface RCTParagraphComponentView () <UIEditMenuInteractionDelegate>
55
45
56
46
@property (nonatomic , nullable ) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE (ios(16.0 ));
57
47
48
+ @end
49
+ #else // [macOS
50
+ @interface RCTParagraphComponentView ()
58
51
@end
59
52
#endif // [macOS]
60
53
@@ -64,7 +57,7 @@ @implementation RCTParagraphComponentView {
64
57
RCTParagraphComponentAccessibilityProvider *_accessibilityProvider;
65
58
#if !TARGET_OS_OSX // [macOS]
66
59
UILongPressGestureRecognizer *_longPressGestureRecognizer;
67
- #endif // macOS]
60
+ #endif // [ macOS]
68
61
RCTParagraphTextView *_textView;
69
62
}
70
63
@@ -73,30 +66,11 @@ - (instancetype)initWithFrame:(CGRect)frame
73
66
if (self = [super initWithFrame: frame]) {
74
67
_props = ParagraphShadowNode::defaultSharedProps ();
75
68
76
- #if !TARGET_OS_OSX // [macOS]
69
+ #if !TARGET_OS_OSX // [macOS]
77
70
self.opaque = NO ;
71
+ #endif // [macOS]
78
72
_textView = [RCTParagraphTextView new ];
79
73
_textView.backgroundColor = RCTUIColor.clearColor ; // [macOS]
80
- #else // [macOS
81
- // Make the RCTParagraphComponentView accessible and available in the a11y hierarchy.
82
- self.accessibilityElement = YES ;
83
- self.accessibilityRole = NSAccessibilityStaticTextRole ;
84
- // Fix blurry text on non-retina displays.
85
- self.canDrawSubviewsIntoLayer = YES ;
86
- // The NSTextView is responsible for drawing text and managing selection.
87
- _textView = [[RCTParagraphTextView alloc ] initWithFrame: self .bounds];
88
- // The RCTParagraphComponentUnfocusableTextView is only used for rendering and should not appear in the a11y hierarchy.
89
- _textView.accessibilityElement = NO ;
90
- _textView.usesFontPanel = NO ;
91
- _textView.drawsBackground = NO ;
92
- _textView.linkTextAttributes = @{};
93
- _textView.editable = NO ;
94
- _textView.selectable = NO ;
95
- _textView.verticallyResizable = NO ;
96
- _textView.layoutManager .usesFontLeading = NO ;
97
- self.contentView = _textView;
98
- self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
99
- #endif // macOS]
100
74
self.contentView = _textView;
101
75
}
102
76
@@ -153,9 +127,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
153
127
} else {
154
128
[self disableContextMenu ];
155
129
}
156
- #else // [macOS
157
- _textView.selectable = newParagraphProps.isSelectable ;
158
- #endif // macOS]
130
+ #endif // [macOS]
159
131
}
160
132
161
133
[super updateProps: props oldProps: oldProps];
@@ -213,7 +185,7 @@ - (BOOL)isAccessibilityElement
213
185
return NO ;
214
186
}
215
187
216
- #if !TARGET_OS_OSX // [macOS]
188
+ #if !TARGET_OS_OSX // [macOS
217
189
- (NSArray *)accessibilityElements
218
190
{
219
191
const auto ¶graphProps = static_cast <const ParagraphProps &>(*_props);
@@ -249,7 +221,12 @@ - (UIAccessibilityTraits)accessibilityTraits
249
221
{
250
222
return [super accessibilityTraits ] | UIAccessibilityTraitStaticText;
251
223
}
252
- #endif // [macOS]
224
+ #else // [macOS
225
+ - (NSAccessibilityRole )accessibilityRole
226
+ {
227
+ return [super accessibilityRole ] ?: NSAccessibilityStaticTextRole ;
228
+ }
229
+ #endif // macOS]
253
230
254
231
#pragma mark - RCTTouchableComponentViewProtocol
255
232
@@ -333,6 +310,7 @@ - (BOOL)canBecomeFirstResponder
333
310
return paragraphProps.isSelectable ;
334
311
}
335
312
313
+ #if !TARGET_OS_OSX // [macOS]
336
314
- (BOOL )canPerformAction : (SEL )action withSender : (id )sender
337
315
{
338
316
const auto ¶graphProps = static_cast <const ParagraphProps &>(*_props);
@@ -341,12 +319,9 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
341
319
return YES ;
342
320
}
343
321
344
- #if !TARGET_OS_OSX // [macOS]
345
322
return [self .nextResponder canPerformAction: action withSender: sender];
346
- #else // [macOS
347
- return NO ;
348
- #endif // macOS]
349
323
}
324
+ #endif // [macOS]
350
325
351
326
- (void )copy : (id )sender
352
327
{
@@ -382,12 +357,10 @@ - (void)copy:(id)sender
382
357
}
383
358
384
359
@implementation RCTParagraphTextView {
385
- #if !TARGET_OS_OSX // [macOS]
386
360
CAShapeLayer *_highlightLayer;
387
- #endif // macOS]
388
361
}
389
362
390
- - (RCTUIView *)hitTest : (CGPoint)point withEvent : (UIEvent *)event // [macOS]
363
+ - (RCTUIView *)hitTest : (CGPoint)point withEvent : (UIEvent *)event
391
364
{
392
365
return nil ;
393
366
}
@@ -409,7 +382,6 @@ - (void)drawRect:(CGRect)rect
409
382
410
383
CGRect frame = RCTCGRectFromRect (_layoutMetrics.getContentFrame ());
411
384
412
- #if !TARGET_OS_OSX // [macOS]
413
385
[nativeTextLayoutManager drawAttributedString: _state->getData ().attributedString
414
386
paragraphAttributes: _paragraphAttributes
415
387
frame: frame
@@ -421,54 +393,70 @@ - (void)drawRect:(CGRect)rect
421
393
[self .layer addSublayer: self ->_highlightLayer];
422
394
}
423
395
self->_highlightLayer .position = frame.origin ;
396
+
397
+ #if !TARGET_OS_OSX // [macOS]
424
398
self->_highlightLayer .path = highlightPath.CGPath ;
399
+ #else // [macOS Update once our minimum is macOS 14
400
+ self->_highlightLayer .path = UIBezierPathCreateCGPathRef (highlightPath);
401
+ #endif // macOS]
425
402
} else {
426
403
[self ->_highlightLayer removeFromSuperlayer ];
427
404
self->_highlightLayer = nil ;
428
405
}
429
406
}];
430
- #else // [macOS
431
- NSTextStorage *textStorage = [nativeTextLayoutManager getTextStorageForAttributedString: _state->getData ().attributedString paragraphAttributes: _paragraphAttributes size: frame.size];
432
-
433
- NSLayoutManager *layoutManager = textStorage.layoutManagers .firstObject ;
434
- NSTextContainer *textContainer = layoutManager.textContainers .firstObject ;
435
-
436
- [self replaceTextContainer: textContainer];
437
-
438
- NSArray <NSLayoutManager *> *managers = [[textStorage layoutManagers ] copy ];
439
- for (NSLayoutManager *manager in managers) {
440
- [textStorage removeLayoutManager: manager];
441
- }
442
-
443
- self.minSize = frame.size ;
444
- self.maxSize = frame.size ;
445
- self.frame = frame;
446
- [[self textStorage ] setAttributedString: textStorage];
447
-
448
- [super drawRect: rect];
449
- #endif
450
407
}
451
408
452
- #if TARGET_OS_OSX // [macOS
453
- - (void )setNeedsDisplay
454
- {
455
- [self setNeedsDisplay: YES ];
456
- }
457
-
458
- - (BOOL )canBecomeKeyView
459
- {
460
- return NO ;
461
- }
409
+ @end
462
410
463
- - (BOOL )resignFirstResponder
411
+ #if TARGET_OS_OSX // [macOS
412
+ // Copied from RCTUIKit
413
+ CGPathRef UIBezierPathCreateCGPathRef (UIBezierPath *bezierPath)
464
414
{
465
- // Don't relinquish first responder while selecting text.
466
- if (self.selectable && NSRunLoop .currentRunLoop .currentMode == NSEventTrackingRunLoopMode ) {
467
- return NO ;
415
+ CGPathRef immutablePath = NULL ;
416
+
417
+ // Draw the path elements.
418
+ NSInteger numElements = [bezierPath elementCount ];
419
+ if (numElements > 0 )
420
+ {
421
+ CGMutablePathRef path = CGPathCreateMutable ();
422
+ NSPoint points[3 ];
423
+ BOOL didClosePath = YES ;
424
+
425
+ for (NSInteger i = 0 ; i < numElements; i++)
426
+ {
427
+ switch ([bezierPath elementAtIndex: i associatedPoints: points])
428
+ {
429
+ case NSBezierPathElementMoveTo:
430
+ CGPathMoveToPoint (path, NULL , points[0 ].x , points[0 ].y );
431
+ break ;
432
+
433
+ case NSBezierPathElementLineTo:
434
+ CGPathAddLineToPoint (path, NULL , points[0 ].x , points[0 ].y );
435
+ didClosePath = NO ;
436
+ break ;
437
+
438
+ case NSBezierPathElementCurveTo:
439
+ CGPathAddCurveToPoint (path, NULL , points[0 ].x , points[0 ].y ,
440
+ points[1 ].x , points[1 ].y ,
441
+ points[2 ].x , points[2 ].y );
442
+ didClosePath = NO ;
443
+ break ;
444
+
445
+ case NSBezierPathElementClosePath:
446
+ CGPathCloseSubpath (path);
447
+ didClosePath = YES ;
448
+ break ;
449
+ }
450
+ }
451
+
452
+ // Be sure the path is closed or Quartz may not do valid hit detection.
453
+ if (!didClosePath)
454
+ CGPathCloseSubpath (path);
455
+
456
+ immutablePath = CGPathCreateCopy (path);
457
+ CGPathRelease (path);
468
458
}
469
-
470
- return [ super resignFirstResponder ] ;
459
+
460
+ return immutablePath ;
471
461
}
472
462
#endif // macOS]
473
-
474
- @end
0 commit comments