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 <ABI48_0_0React/ABI48_0_0RCTBaseTextShadowView.h> 9 10#import <ABI48_0_0React/ABI48_0_0RCTShadowView+Layout.h> 11 12#import <ABI48_0_0React/ABI48_0_0RCTRawTextShadowView.h> 13#import <ABI48_0_0React/ABI48_0_0RCTVirtualTextShadowView.h> 14 15NSString *const ABI48_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = 16 @"ABI48_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName"; 17 18static void ABI48_0_0RCTInlineViewYogaNodeDirtied(ABI48_0_0YGNodeRef node) 19{ 20 // An inline view (a view nested inside of a text node) does not have a parent 21 // in the Yoga tree. Consequently, we have to manually propagate the inline 22 // view's dirty signal up through the text nodes. At some point, it'll reach 23 // the outermost text node which has a Yoga node and then Yoga will take over 24 // the dirty signal propagation. 25 ABI48_0_0RCTShadowView *inlineView = (__bridge ABI48_0_0RCTShadowView *)ABI48_0_0YGNodeGetContext(node); 26 ABI48_0_0RCTBaseTextShadowView *baseTextShadowView = (ABI48_0_0RCTBaseTextShadowView *)inlineView.ABI48_0_0ReactSuperview; 27 28 [baseTextShadowView dirtyLayout]; 29} 30 31@implementation ABI48_0_0RCTBaseTextShadowView 32 33- (instancetype)init 34{ 35 if (self = [super init]) { 36 _textAttributes = [ABI48_0_0RCTTextAttributes new]; 37 } 38 39 return self; 40} 41 42- (void)setABI48_0_0ReactTag:(NSNumber *)ABI48_0_0ReactTag 43{ 44 [super setABI48_0_0ReactTag:ABI48_0_0ReactTag]; 45 _textAttributes.tag = ABI48_0_0ReactTag; 46} 47 48#pragma mark - Life Cycle 49 50- (void)insertABI48_0_0ReactSubview:(ABI48_0_0RCTShadowView *)subview atIndex:(NSInteger)index 51{ 52 [super insertABI48_0_0ReactSubview:subview atIndex:index]; 53 54 [self dirtyLayout]; 55 56 if (![subview isKindOfClass:[ABI48_0_0RCTVirtualTextShadowView class]]) { 57 ABI48_0_0YGNodeSetDirtiedFunc(subview.yogaNode, ABI48_0_0RCTInlineViewYogaNodeDirtied); 58 } 59} 60 61- (void)removeABI48_0_0ReactSubview:(ABI48_0_0RCTShadowView *)subview 62{ 63 if (![subview isKindOfClass:[ABI48_0_0RCTVirtualTextShadowView class]]) { 64 ABI48_0_0YGNodeSetDirtiedFunc(subview.yogaNode, NULL); 65 } 66 67 [self dirtyLayout]; 68 69 [super removeABI48_0_0ReactSubview:subview]; 70} 71 72#pragma mark - attributedString 73 74- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable ABI48_0_0RCTTextAttributes *)baseTextAttributes 75{ 76 ABI48_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 (ABI48_0_0RCTShadowView *shadowView in self.ABI48_0_0ReactSubviews) { 94 // Special Case: ABI48_0_0RCTRawTextShadowView 95 if ([shadowView isKindOfClass:[ABI48_0_0RCTRawTextShadowView class]]) { 96 ABI48_0_0RCTRawTextShadowView *rawTextShadowView = (ABI48_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: ABI48_0_0RCTBaseTextShadowView 108 if ([shadowView isKindOfClass:[ABI48_0_0RCTBaseTextShadowView class]]) { 109 ABI48_0_0RCTBaseTextShadowView *baseTextShadowView = (ABI48_0_0RCTBaseTextShadowView *)shadowView; 110 NSAttributedString *baseTextAttributedString = 111 [baseTextShadowView attributedTextWithBaseTextAttributes:textAttributes]; 112 [attributedText appendAttributedString:baseTextAttributedString]; 113 continue; 114 } 115 116 // Generic Case: Any ABI48_0_0RCTShadowView 117 NSTextAttachment *attachment = [NSTextAttachment new]; 118 NSMutableAttributedString *embeddedShadowViewAttributedString = [NSMutableAttributedString new]; 119 [embeddedShadowViewAttributedString beginEditing]; 120 [embeddedShadowViewAttributedString 121 appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]]; 122 [embeddedShadowViewAttributedString addAttribute:ABI48_0_0RCTBaseTextShadowViewEmbeddedShadowViewAttributeName 123 value:shadowView 124 range:(NSRange){0, embeddedShadowViewAttributedString.length}]; 125 [embeddedShadowViewAttributedString endEditing]; 126 [attributedText appendAttributedString:embeddedShadowViewAttributedString]; 127 } 128 129 [attributedText endEditing]; 130 131 [self clearLayout]; 132 133 cachedAttributedText = [attributedText copy]; 134 cachedTextAttributes = textAttributes; 135 136 return cachedAttributedText; 137} 138 139- (void)dirtyLayout 140{ 141 [super dirtyLayout]; 142 cachedAttributedText = nil; 143 cachedTextAttributes = nil; 144} 145 146- (void)didUpdateABI48_0_0ReactSubviews 147{ 148 [super didUpdateABI48_0_0ReactSubviews]; 149 [self dirtyLayout]; 150} 151 152- (void)didSetProps:(NSArray<NSString *> *)changedProps 153{ 154 [super didSetProps:changedProps]; 155 [self dirtyLayout]; 156} 157 158@end 159