Skip to content

Commit

Permalink
Breaking: Always use AttributedStringBox instead of AttributedString …
Browse files Browse the repository at this point in the history
…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

Differential Revision: D61484999
  • Loading branch information
NickGerleman authored and facebook-github-bot committed Aug 19, 2024
1 parent 7027eac commit ccf7da7
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ Float ParagraphShadowNode::baseline(
attributedString.appendFragment({string, textAttributes, {}});
}

AttributedStringBox attributedStringBox{attributedString};
return textLayoutManager_->baseline(
attributedString, getConcreteProps().paragraphAttributes, size);
attributedStringBox, getConcreteProps().paragraphAttributes, size);
}

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

AttributedStringBox attributedStringBox{content.attributedString};

if (getConcreteProps().onTextLayout) {
auto linesMeasurements = textLayoutManager_->measureLines(
content.attributedString, content.paragraphAttributes, size);
attributedStringBox, content.paragraphAttributes, size);
getConcreteEventEmitter().onTextLayout(linesMeasurements);
}

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

// Only measure if attachments are not empty.
measurement = textLayoutManager_->measure(
AttributedStringBox{content.attributedString},
attributedStringBox,
content.paragraphAttributes,
textLayoutContext,
layoutConstraints);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,11 @@ Float AndroidTextInputShadowNode::baseline(
auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) +
YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop);

AttributedStringBox attributedStringBox{attributedString};
return textLayoutManager_->baseline(
attributedString, getConcreteProps().paragraphAttributes, size) +
attributedStringBox,
getConcreteProps().paragraphAttributes,
size) +
top;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ Float TextInputShadowNode::baseline(
auto top = YGNodeLayoutGetBorder(&yogaNode_, YGEdgeTop) +
YGNodeLayoutGetPadding(&yogaNode_, YGEdgeTop);

AttributedStringBox attributedStringBox{std::move(attributedString)};
return textLayoutManager_->baseline(
attributedString,
attributedStringBox,
getConcreteProps().getEffectiveParagraphAttributes(),
size) +
top;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ TextMeasurement TextLayoutManager::measure(
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
LayoutConstraints layoutConstraints) const {
const LayoutConstraints& layoutConstraints) const {
auto& attributedString = attributedStringBox.getValue();

auto measurement = textMeasureCache_.get(
Expand Down Expand Up @@ -128,7 +128,7 @@ TextMeasurement TextLayoutManager::measure(
TextMeasurement TextLayoutManager::measureCachedSpannableById(
int64_t cacheId,
const ParagraphAttributes& paragraphAttributes,
LayoutConstraints layoutConstraints) const {
const LayoutConstraints& layoutConstraints) const {
auto env = Environment::current();
auto attachmentPositions = env->NewFloatArray(0);
auto minimumSize = layoutConstraints.minimumSize;
Expand Down Expand Up @@ -162,9 +162,13 @@ TextMeasurement TextLayoutManager::measureCachedSpannableById(
}

LinesMeasurements TextLayoutManager::measureLines(
const AttributedString& attributedString,
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
Size size) const {
const Size& size) const {
react_native_assert(
attributedStringBox.getMode() == AttributedStringBox::Mode::Value);
const auto& attributedString = attributedStringBox.getValue();

auto lineMeasurements = lineMeasureCache_.get(
{attributedString, paragraphAttributes, size},
[&](const LineMeasureCacheKey& /*key*/) {
Expand Down Expand Up @@ -210,10 +214,11 @@ LinesMeasurements TextLayoutManager::measureLines(
}

Float TextLayoutManager::baseline(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const {
auto lines = this->measureLines(attributedString, paragraphAttributes, size);
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const {
auto lines =
this->measureLines(attributedStringBox, paragraphAttributes, size);

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

TextMeasurement TextLayoutManager::doMeasure(
AttributedString attributedString,
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
LayoutConstraints layoutConstraints) const {
layoutConstraints.maximumSize.height = std::numeric_limits<Float>::infinity();

const LayoutConstraints& layoutConstraints) const {
const int attachmentCount = countAttachments(attributedString);
auto env = Environment::current();
auto attachmentPositions = env->NewFloatArray(attachmentCount * 2);

auto minimumSize = layoutConstraints.minimumSize;
auto maximumSize = layoutConstraints.maximumSize;

// We assume max height will have no effect on measurement, so we override it
// with a constant value with no constraints, to enable cache reuse later down
// in the stack.
// TODO: This is suss, and not at the right layer
maximumSize.height = std::numeric_limits<Float>::infinity();

auto attributedStringMap = toMapBuffer(attributedString);
auto paragraphAttributesMap = toMapBuffer(paragraphAttributes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class TextLayoutManager {
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

/**
* Measures an AttributedString on the platform, as identified by some
Expand All @@ -56,25 +56,25 @@ class TextLayoutManager {
TextMeasurement measureCachedSpannableById(
int64_t cacheId,
const ParagraphAttributes& paragraphAttributes,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

/*
* Measures lines of `attributedString` using native text rendering
* infrastructure.
*/
LinesMeasurements measureLines(
const AttributedString& attributedString,
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
Size size) const;
const Size& size) const;

/*
* Calculates baseline of `attributedString` using native text rendering
* infrastructure.
*/
Float baseline(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const;
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;

/*
* Returns an opaque pointer to platform-specific TextLayoutManager.
Expand All @@ -84,9 +84,9 @@ class TextLayoutManager {

private:
TextMeasurement doMeasure(
AttributedString attributedString,
const AttributedString& attributedString,
const ParagraphAttributes& paragraphAttributes,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

void* self_{};
ContextContainer::Shared contextContainer_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ void* TextLayoutManager::getNativeTextLayoutManager() const {
}

TextMeasurement TextLayoutManager::measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& /*paragraphAttributes*/,
const TextLayoutContext& /*layoutContext*/,
LayoutConstraints layoutConstraints) const {
const LayoutConstraints& /*layoutConstraints*/) const {
TextMeasurement::Attachments attachments;
for (const auto& fragment : attributedStringBox.getValue().getFragments()) {
if (fragment.isAttachment()) {
Expand All @@ -31,21 +31,21 @@ TextMeasurement TextLayoutManager::measure(
TextMeasurement TextLayoutManager::measureCachedSpannableById(
int64_t /*cacheId*/,
const ParagraphAttributes& /*paragraphAttributes*/,
LayoutConstraints /*layoutConstraints*/) const {
const LayoutConstraints& /*layoutConstraints*/) const {
return {};
}

LinesMeasurements TextLayoutManager::measureLines(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const {
const AttributedStringBox& /*attributedStringBox*/,
const ParagraphAttributes& /*paragraphAttributes*/,
const Size& /*size*/) const {
return {};
};

Float TextLayoutManager::baseline(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const {
const AttributedStringBox& /*attributedStringBox*/,
const ParagraphAttributes& /*paragraphAttributes*/,
const Size& /*size*/) const {
return 0;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ class TextLayoutManager {
* Measures `attributedStringBox` using native text rendering infrastructure.
*/
virtual TextMeasurement measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

/**
* Measures an AttributedString on the platform, as identified by some
Expand All @@ -48,25 +48,25 @@ class TextLayoutManager {
virtual TextMeasurement measureCachedSpannableById(
int64_t cacheId,
const ParagraphAttributes& paragraphAttributes,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

/*
* Measures lines of `attributedString` using native text rendering
* infrastructure.
*/
virtual LinesMeasurements measureLines(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const;
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;

/*
* Calculates baseline of `attributedString` using native text rendering
* infrastructure.
*/
virtual Float baseline(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const;
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;

/*
* Returns an opaque pointer to platform-specific TextLayoutManager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ class TextLayoutManager {
* Measures `attributedString` using native text rendering infrastructure.
*/
TextMeasurement measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const TextLayoutContext& layoutContext,
LayoutConstraints layoutConstraints) const;
const LayoutConstraints& layoutConstraints) const;

/*
* Measures lines of `attributedString` using native text rendering
* infrastructure.
*/
LinesMeasurements measureLines(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const;
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;

/*
* Calculates baseline of `attributedString` using native text rendering
* infrastructure.
*/
Float baseline(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const;
const AttributedStringBox& attributedStringBox,
const ParagraphAttributes& paragraphAttributes,
const Size& size) const;

/*
* Returns an opaque pointer to platform-specific TextLayoutManager.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
}

TextMeasurement TextLayoutManager::measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
const AttributedStringBox &attributedStringBox,
const ParagraphAttributes &paragraphAttributes,
const TextLayoutContext &layoutContext,
LayoutConstraints layoutConstraints) const
const LayoutConstraints &layoutConstraints) const
{
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);

Expand Down Expand Up @@ -85,10 +85,13 @@
}

LinesMeasurements TextLayoutManager::measureLines(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
Size size) const
const AttributedStringBox &attributedStringBox,
const ParagraphAttributes &paragraphAttributes,
const Size &size) const
{
react_native_assert(attributedStringBox.getMode() == AttributedStringBox::Mode::Value);
const auto &attributedString = attributedStringBox.getValue();

RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);

auto measurement =
Expand All @@ -102,10 +105,12 @@
return measurement;
}

Float TextLayoutManager::baseline(AttributedString attributedString, ParagraphAttributes paragraphAttributes, Size size)
const
Float TextLayoutManager::baseline(
const AttributedStringBox &attributedStringBox,
const ParagraphAttributes &paragraphAttributes,
const Size &size) const
{
auto lines = this->measureLines(attributedString, paragraphAttributes, size);
auto lines = this->measureLines(attributedStringBox, paragraphAttributes, size);

if (!lines.empty()) {
return lines[0].ascender;
Expand Down

0 comments on commit ccf7da7

Please sign in to comment.