Skip to content

Commit ee597bf

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Breaking: Always use AttributedStringBox instead of AttributedString in TextLayoutManager (#46104)
Summary: Pull Request resolved: #46104 We want to use `PrecomputedText` to store glyph-level measurements on underlying Android Spannable. This means we need to consistently reuse the same Spannable, instead of recreating them on measurement. We have an opaque cache ID used by Android, for spannables originating from uncontrolled TextInput on UI-thread side. We also have `AttributedStringBox`, for a kind of similar purpose on iOS, which allows passing opaque pointer to the `TextLayoutManager`. This is only used for the `measure` function. This change makes us consistently use `AttributedStringBox` at the TextLayoutManager boundary, to let us migrate calls across TextLayoutManager to all pass opaque handle to underlying Spannable we will store, instead of passing the AttributedString each time. For now, every place previously passing an AttributedString value still passes one. There were also some egregious cases of accepting very large structures by value, causing unneeded copies. I changed the APIs to accept anything potentially larger than two pointers to pass by reference instead. This change is technically breaking, to any 3p code calling into TextLayoutManager (IIRC live-markdown exposed prefabs for this, but should be able to adapt fairly easily). Changelog: [General][Breaking] - Always use AttributedStringBox instead of AttributedString in TextLayoutManager Reviewed By: joevilches Differential Revision: D61484999 fbshipit-source-id: 07c5600cd917f2dab3d24559a25f27e0872ebddc
1 parent 3cd6d18 commit ee597bf

File tree

9 files changed

+85
-64
lines changed

9 files changed

+85
-64
lines changed

packages/react-native/ReactCommon/react/renderer/components/text/ParagraphShadowNode.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ Float ParagraphShadowNode::baseline(
184184
attributedString.appendFragment({string, textAttributes, {}});
185185
}
186186

187+
AttributedStringBox attributedStringBox{attributedString};
187188
return textLayoutManager_->baseline(
188-
attributedString, getConcreteProps().paragraphAttributes, size);
189+
attributedStringBox, getConcreteProps().paragraphAttributes, size);
189190
}
190191

191192
void ParagraphShadowNode::layout(LayoutContext layoutContext) {
@@ -205,9 +206,11 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
205206
textLayoutContext.pointScaleFactor = layoutContext.pointScaleFactor;
206207
auto measurement = TextMeasurement{};
207208

209+
AttributedStringBox attributedStringBox{content.attributedString};
210+
208211
if (getConcreteProps().onTextLayout) {
209212
auto linesMeasurements = textLayoutManager_->measureLines(
210-
content.attributedString, content.paragraphAttributes, size);
213+
attributedStringBox, content.paragraphAttributes, size);
211214
getConcreteEventEmitter().onTextLayout(linesMeasurements);
212215
}
213216

@@ -218,7 +221,7 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
218221

219222
// Only measure if attachments are not empty.
220223
measurement = textLayoutManager_->measure(
221-
AttributedStringBox{content.attributedString},
224+
attributedStringBox,
222225
content.paragraphAttributes,
223226
textLayoutContext,
224227
layoutConstraints);

packages/react-native/ReactCommon/react/renderer/components/textinput/platform/android/react/renderer/components/androidtextinput/AndroidTextInputShadowNode.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,11 @@ Float AndroidTextInputShadowNode::baseline(
233233
auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) +
234234
YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop);
235235

236+
AttributedStringBox attributedStringBox{attributedString};
236237
return textLayoutManager_->baseline(
237-
attributedString, getConcreteProps().paragraphAttributes, size) +
238+
attributedStringBox,
239+
getConcreteProps().paragraphAttributes,
240+
size) +
238241
top;
239242
}
240243

packages/react-native/ReactCommon/react/renderer/components/textinput/platform/ios/react/renderer/components/iostextinput/TextInputShadowNode.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ Float TextInputShadowNode::baseline(
161161
auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) +
162162
YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop);
163163

164+
AttributedStringBox attributedStringBox{std::move(attributedString)};
164165
return textLayoutManager_->baseline(
165-
attributedString,
166+
attributedStringBox,
166167
getConcreteProps().getEffectiveParagraphAttributes(),
167168
size) +
168169
top;

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.cpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ TextMeasurement TextLayoutManager::measure(
100100
const AttributedStringBox& attributedStringBox,
101101
const ParagraphAttributes& paragraphAttributes,
102102
const TextLayoutContext& layoutContext,
103-
LayoutConstraints layoutConstraints) const {
103+
const LayoutConstraints& layoutConstraints) const {
104104
auto& attributedString = attributedStringBox.getValue();
105105

106106
auto measurement = textMeasureCache_.get(
@@ -128,7 +128,7 @@ TextMeasurement TextLayoutManager::measure(
128128
TextMeasurement TextLayoutManager::measureCachedSpannableById(
129129
int64_t cacheId,
130130
const ParagraphAttributes& paragraphAttributes,
131-
LayoutConstraints layoutConstraints) const {
131+
const LayoutConstraints& layoutConstraints) const {
132132
auto env = Environment::current();
133133
auto attachmentPositions = env->NewFloatArray(0);
134134
auto minimumSize = layoutConstraints.minimumSize;
@@ -162,9 +162,13 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById(
162162
}
163163

164164
LinesMeasurements TextLayoutManager::measureLines(
165-
const AttributedString& attributedString,
165+
const AttributedStringBox& attributedStringBox,
166166
const ParagraphAttributes& paragraphAttributes,
167-
Size size) const {
167+
const Size& size) const {
168+
react_native_assert(
169+
attributedStringBox.getMode() == AttributedStringBox::Mode::Value);
170+
const auto& attributedString = attributedStringBox.getValue();
171+
168172
auto lineMeasurements = lineMeasureCache_.get(
169173
{attributedString, paragraphAttributes, size},
170174
[&](const LineMeasureCacheKey& /*key*/) {
@@ -210,10 +214,11 @@ LinesMeasurements TextLayoutManager::measureLines(
210214
}
211215

212216
Float TextLayoutManager::baseline(
213-
AttributedString attributedString,
214-
ParagraphAttributes paragraphAttributes,
215-
Size size) const {
216-
auto lines = this->measureLines(attributedString, paragraphAttributes, size);
217+
const AttributedStringBox& attributedStringBox,
218+
const ParagraphAttributes& paragraphAttributes,
219+
const Size& size) const {
220+
auto lines =
221+
this->measureLines(attributedStringBox, paragraphAttributes, size);
217222

218223
if (!lines.empty()) {
219224
return lines[0].ascender;
@@ -223,18 +228,22 @@ Float TextLayoutManager::baseline(
223228
}
224229

225230
TextMeasurement TextLayoutManager::doMeasure(
226-
AttributedString attributedString,
231+
const AttributedString& attributedString,
227232
const ParagraphAttributes& paragraphAttributes,
228-
LayoutConstraints layoutConstraints) const {
229-
layoutConstraints.maximumSize.height = std::numeric_limits<Float>::infinity();
230-
233+
const LayoutConstraints& layoutConstraints) const {
231234
const int attachmentCount = countAttachments(attributedString);
232235
auto env = Environment::current();
233236
auto attachmentPositions = env->NewFloatArray(attachmentCount * 2);
234237

235238
auto minimumSize = layoutConstraints.minimumSize;
236239
auto maximumSize = layoutConstraints.maximumSize;
237240

241+
// We assume max height will have no effect on measurement, so we override it
242+
// with a constant value with no constraints, to enable cache reuse later down
243+
// in the stack.
244+
// TODO: This is suss, and not at the right layer
245+
maximumSize.height = std::numeric_limits<Float>::infinity();
246+
238247
auto attributedStringMap = toMapBuffer(attributedString);
239248
auto paragraphAttributesMap = toMapBuffer(paragraphAttributes);
240249

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/android/react/renderer/textlayoutmanager/TextLayoutManager.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class TextLayoutManager {
4747
const AttributedStringBox& attributedStringBox,
4848
const ParagraphAttributes& paragraphAttributes,
4949
const TextLayoutContext& layoutContext,
50-
LayoutConstraints layoutConstraints) const;
50+
const LayoutConstraints& layoutConstraints) const;
5151

5252
/**
5353
* Measures an AttributedString on the platform, as identified by some
@@ -56,25 +56,25 @@ class TextLayoutManager {
5656
TextMeasurement measureCachedSpannableById(
5757
int64_t cacheId,
5858
const ParagraphAttributes& paragraphAttributes,
59-
LayoutConstraints layoutConstraints) const;
59+
const LayoutConstraints& layoutConstraints) const;
6060

6161
/*
6262
* Measures lines of `attributedString` using native text rendering
6363
* infrastructure.
6464
*/
6565
LinesMeasurements measureLines(
66-
const AttributedString& attributedString,
66+
const AttributedStringBox& attributedStringBox,
6767
const ParagraphAttributes& paragraphAttributes,
68-
Size size) const;
68+
const Size& size) const;
6969

7070
/*
7171
* Calculates baseline of `attributedString` using native text rendering
7272
* infrastructure.
7373
*/
7474
Float baseline(
75-
AttributedString attributedString,
76-
ParagraphAttributes paragraphAttributes,
77-
Size size) const;
75+
const AttributedStringBox& attributedStringBox,
76+
const ParagraphAttributes& paragraphAttributes,
77+
const Size& size) const;
7878

7979
/*
8080
* Returns an opaque pointer to platform-specific TextLayoutManager.
@@ -84,9 +84,9 @@ class TextLayoutManager {
8484

8585
private:
8686
TextMeasurement doMeasure(
87-
AttributedString attributedString,
87+
const AttributedString& attributedString,
8888
const ParagraphAttributes& paragraphAttributes,
89-
LayoutConstraints layoutConstraints) const;
89+
const LayoutConstraints& layoutConstraints) const;
9090

9191
void* self_{};
9292
ContextContainer::Shared contextContainer_;

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ void* TextLayoutManager::getNativeTextLayoutManager() const {
1414
}
1515

1616
TextMeasurement TextLayoutManager::measure(
17-
AttributedStringBox attributedStringBox,
18-
ParagraphAttributes paragraphAttributes,
17+
const AttributedStringBox& attributedStringBox,
18+
const ParagraphAttributes& /*paragraphAttributes*/,
1919
const TextLayoutContext& /*layoutContext*/,
20-
LayoutConstraints layoutConstraints) const {
20+
const LayoutConstraints& /*layoutConstraints*/) const {
2121
TextMeasurement::Attachments attachments;
2222
for (const auto& fragment : attributedStringBox.getValue().getFragments()) {
2323
if (fragment.isAttachment()) {
@@ -31,21 +31,21 @@ TextMeasurement TextLayoutManager::measure(
3131
TextMeasurement TextLayoutManager::measureCachedSpannableById(
3232
int64_t /*cacheId*/,
3333
const ParagraphAttributes& /*paragraphAttributes*/,
34-
LayoutConstraints /*layoutConstraints*/) const {
34+
const LayoutConstraints& /*layoutConstraints*/) const {
3535
return {};
3636
}
3737

3838
LinesMeasurements TextLayoutManager::measureLines(
39-
AttributedString attributedString,
40-
ParagraphAttributes paragraphAttributes,
41-
Size size) const {
39+
const AttributedStringBox& /*attributedStringBox*/,
40+
const ParagraphAttributes& /*paragraphAttributes*/,
41+
const Size& /*size*/) const {
4242
return {};
4343
};
4444

4545
Float TextLayoutManager::baseline(
46-
AttributedString attributedString,
47-
ParagraphAttributes paragraphAttributes,
48-
Size size) const {
46+
const AttributedStringBox& /*attributedStringBox*/,
47+
const ParagraphAttributes& /*paragraphAttributes*/,
48+
const Size& /*size*/) const {
4949
return 0;
5050
}
5151

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/TextLayoutManager.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ class TextLayoutManager {
3636
* Measures `attributedStringBox` using native text rendering infrastructure.
3737
*/
3838
virtual TextMeasurement measure(
39-
AttributedStringBox attributedStringBox,
40-
ParagraphAttributes paragraphAttributes,
39+
const AttributedStringBox& attributedStringBox,
40+
const ParagraphAttributes& paragraphAttributes,
4141
const TextLayoutContext& layoutContext,
42-
LayoutConstraints layoutConstraints) const;
42+
const LayoutConstraints& layoutConstraints) const;
4343

4444
/**
4545
* Measures an AttributedString on the platform, as identified by some
@@ -48,25 +48,25 @@ class TextLayoutManager {
4848
virtual TextMeasurement measureCachedSpannableById(
4949
int64_t cacheId,
5050
const ParagraphAttributes& paragraphAttributes,
51-
LayoutConstraints layoutConstraints) const;
51+
const LayoutConstraints& layoutConstraints) const;
5252

5353
/*
5454
* Measures lines of `attributedString` using native text rendering
5555
* infrastructure.
5656
*/
5757
virtual LinesMeasurements measureLines(
58-
AttributedString attributedString,
59-
ParagraphAttributes paragraphAttributes,
60-
Size size) const;
58+
const AttributedStringBox& attributedStringBox,
59+
const ParagraphAttributes& paragraphAttributes,
60+
const Size& size) const;
6161

6262
/*
6363
* Calculates baseline of `attributedString` using native text rendering
6464
* infrastructure.
6565
*/
6666
virtual Float baseline(
67-
AttributedString attributedString,
68-
ParagraphAttributes paragraphAttributes,
69-
Size size) const;
67+
const AttributedStringBox& attributedStringBox,
68+
const ParagraphAttributes& paragraphAttributes,
69+
const Size& size) const;
7070

7171
/*
7272
* Returns an opaque pointer to platform-specific TextLayoutManager.

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,28 @@ class TextLayoutManager {
3131
* Measures `attributedString` using native text rendering infrastructure.
3232
*/
3333
TextMeasurement measure(
34-
AttributedStringBox attributedStringBox,
35-
ParagraphAttributes paragraphAttributes,
34+
const AttributedStringBox& attributedStringBox,
35+
const ParagraphAttributes& paragraphAttributes,
3636
const TextLayoutContext& layoutContext,
37-
LayoutConstraints layoutConstraints) const;
37+
const LayoutConstraints& layoutConstraints) const;
3838

3939
/*
4040
* Measures lines of `attributedString` using native text rendering
4141
* infrastructure.
4242
*/
4343
LinesMeasurements measureLines(
44-
AttributedString attributedString,
45-
ParagraphAttributes paragraphAttributes,
46-
Size size) const;
44+
const AttributedStringBox& attributedStringBox,
45+
const ParagraphAttributes& paragraphAttributes,
46+
const Size& size) const;
4747

4848
/*
4949
* Calculates baseline of `attributedString` using native text rendering
5050
* infrastructure.
5151
*/
5252
Float baseline(
53-
AttributedString attributedString,
54-
ParagraphAttributes paragraphAttributes,
55-
Size size) const;
53+
const AttributedStringBox& attributedStringBox,
54+
const ParagraphAttributes& paragraphAttributes,
55+
const Size& size) const;
5656

5757
/*
5858
* Returns an opaque pointer to platform-specific TextLayoutManager.

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
}
2626

2727
TextMeasurement TextLayoutManager::measure(
28-
AttributedStringBox attributedStringBox,
29-
ParagraphAttributes paragraphAttributes,
28+
const AttributedStringBox &attributedStringBox,
29+
const ParagraphAttributes &paragraphAttributes,
3030
const TextLayoutContext &layoutContext,
31-
LayoutConstraints layoutConstraints) const
31+
const LayoutConstraints &layoutConstraints) const
3232
{
3333
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);
3434

@@ -85,10 +85,13 @@
8585
}
8686

8787
LinesMeasurements TextLayoutManager::measureLines(
88-
AttributedString attributedString,
89-
ParagraphAttributes paragraphAttributes,
90-
Size size) const
88+
const AttributedStringBox &attributedStringBox,
89+
const ParagraphAttributes &paragraphAttributes,
90+
const Size &size) const
9191
{
92+
react_native_assert(attributedStringBox.getMode() == AttributedStringBox::Mode::Value);
93+
const auto &attributedString = attributedStringBox.getValue();
94+
9295
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);
9396

9497
auto measurement =
@@ -102,10 +105,12 @@
102105
return measurement;
103106
}
104107

105-
Float TextLayoutManager::baseline(AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size)
106-
const
108+
Float TextLayoutManager::baseline(
109+
const AttributedStringBox &attributedStringBox,
110+
const ParagraphAttributes &paragraphAttributes,
111+
const Size &size) const
107112
{
108-
auto lines = this->measureLines(attributedString, paragraphAttributes, size);
113+
auto lines = this->measureLines(attributedStringBox, paragraphAttributes, size);
109114

110115
if (!lines.empty()) {
111116
return lines[0].ascender;

0 commit comments

Comments
 (0)