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#import <ABI47_0_0React/ABI47_0_0RCTBaseTextShadowView.h> 9 10#import <ABI47_0_0React/ABI47_0_0RCTShadowView+Layout.h> 11 12#import <ABI47_0_0React/ABI47_0_0RCTRawTextShadowView.h> 13#import <ABI47_0_0React/ABI47_0_0RCTVirtualTextShadowView.h> 14 15NSString *const ABI47_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = @"ABI47_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName"; 16 17static void ABI47_0_0RCTInlineViewYogaNodeDirtied(ABI47_0_0YGNodeRef node) 18{ 19 // An inline view (a view nested inside of a text node) does not have a parent 20 // in the Yoga tree. Consequently, we have to manually propagate the inline 21 // view's dirty signal up through the text nodes. At some point, it'll reach 22 // the outermost text node which has a Yoga node and then Yoga will take over 23 // the dirty signal propagation. 24 ABI47_0_0RCTShadowView *inlineView = (__bridge ABI47_0_0RCTShadowView *)ABI47_0_0YGNodeGetContext(node); 25 ABI47_0_0RCTBaseTextShadowView *baseTextShadowView = 26 (ABI47_0_0RCTBaseTextShadowView *)inlineView.ABI47_0_0ReactSuperview; 27 28 [baseTextShadowView dirtyLayout]; 29} 30 31@implementation ABI47_0_0RCTBaseTextShadowView 32 33- (instancetype)init 34{ 35 if (self = [super init]) { 36 _textAttributes = [ABI47_0_0RCTTextAttributes new]; 37 } 38 39 return self; 40} 41 42- (void)setABI47_0_0ReactTag:(NSNumber *)ABI47_0_0ReactTag 43{ 44 [super setABI47_0_0ReactTag:ABI47_0_0ReactTag]; 45 _textAttributes.tag = ABI47_0_0ReactTag; 46} 47 48#pragma mark - Life Cycle 49 50- (void)insertABI47_0_0ReactSubview:(ABI47_0_0RCTShadowView *)subview atIndex:(NSInteger)index 51{ 52 [super insertABI47_0_0ReactSubview:subview atIndex:index]; 53 54 [self dirtyLayout]; 55 56 if (![subview isKindOfClass:[ABI47_0_0RCTVirtualTextShadowView class]]) { 57 ABI47_0_0YGNodeSetDirtiedFunc(subview.yogaNode, ABI47_0_0RCTInlineViewYogaNodeDirtied); 58 } 59} 60 61- (void)removeABI47_0_0ReactSubview:(ABI47_0_0RCTShadowView *)subview 62{ 63 if (![subview isKindOfClass:[ABI47_0_0RCTVirtualTextShadowView class]]) { 64 ABI47_0_0YGNodeSetDirtiedFunc(subview.yogaNode, NULL); 65 } 66 67 [self dirtyLayout]; 68 69 [super removeABI47_0_0ReactSubview:subview]; 70} 71 72#pragma mark - attributedString 73 74- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable ABI47_0_0RCTTextAttributes *)baseTextAttributes 75{ 76 ABI47_0_0RCTTextAttributes *textAttributes; 77 78 if (baseTextAttributes) { 79 textAttributes = [baseTextAttributes copy]; 80 [textAttributes applyTextAttributes:self.textAttributes]; 81 } else { 82 textAttributes = [self.textAttributes copy]; 83 } 84 85 if (cachedAttributedText && [cachedTextAttributes isEqual:textAttributes]) { 86 return cachedAttributedText; 87 } 88 89 NSMutableAttributedString *attributedText = [NSMutableAttributedString new]; 90 91 [attributedText beginEditing]; 92 93 for (ABI47_0_0RCTShadowView *shadowView in self.ABI47_0_0ReactSubviews) { 94 // Special Case: ABI47_0_0RCTRawTextShadowView 95 if ([shadowView isKindOfClass:[ABI47_0_0RCTRawTextShadowView class]]) { 96 ABI47_0_0RCTRawTextShadowView *rawTextShadowView = (ABI47_0_0RCTRawTextShadowView *)shadowView; 97 NSString *text = rawTextShadowView.text; 98 if (text) { 99 NSAttributedString *rawTextAttributedString = 100 [[NSAttributedString alloc] initWithString:[textAttributes applyTextAttributesToText:text] 101 attributes:textAttributes.effectiveTextAttributes]; 102 [attributedText appendAttributedString:rawTextAttributedString]; 103 } 104 continue; 105 } 106 107 // Special Case: ABI47_0_0RCTBaseTextShadowView 108 if ([shadowView isKindOfClass:[ABI47_0_0RCTBaseTextShadowView class]]) { 109 ABI47_0_0RCTBaseTextShadowView *baseTextShadowView = (ABI47_0_0RCTBaseTextShadowView *)shadowView; 110 NSAttributedString *baseTextAttributedString = 111 [baseTextShadowView attributedTextWithBaseTextAttributes:textAttributes]; 112 [attributedText appendAttributedString:baseTextAttributedString]; 113 continue; 114 } 115 116 // Generic Case: Any ABI47_0_0RCTShadowView 117 NSTextAttachment *attachment = [NSTextAttachment new]; 118 NSMutableAttributedString *embeddedShadowViewAttributedString = [NSMutableAttributedString new]; 119 [embeddedShadowViewAttributedString beginEditing]; 120 [embeddedShadowViewAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]]; 121 [embeddedShadowViewAttributedString addAttribute:ABI47_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName 122 value:shadowView 123 range:(NSRange){0, embeddedShadowViewAttributedString.length}]; 124 [embeddedShadowViewAttributedString endEditing]; 125 [attributedText appendAttributedString:embeddedShadowViewAttributedString]; 126 } 127 128 [attributedText endEditing]; 129 130 [self clearLayout]; 131 132 cachedAttributedText = [attributedText copy]; 133 cachedTextAttributes = textAttributes; 134 135 return cachedAttributedText; 136} 137 138- (void)dirtyLayout 139{ 140 [super dirtyLayout]; 141 cachedAttributedText = nil; 142 cachedTextAttributes = nil; 143} 144 145- (void)didUpdateABI47_0_0ReactSubviews 146{ 147 [super didUpdateABI47_0_0ReactSubviews]; 148 [self dirtyLayout]; 149} 150 151- (void)didSetProps:(NSArray<NSString *> *)changedProps 152{ 153 [super didSetProps:changedProps]; 154 [self dirtyLayout]; 155} 156 157@end 158