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