Cache NSTextStorage

Summary:
changelog: [internal]

This diff introduces a mechanism to cache NSTextStorage on Paragraph's state. The old renderer already has caching for NSTextStorage: https://github.com/facebook/react-native/blob/main/Libraries/Text/Text/RCTTextShadowView.m#L21

Fabric will store `NSTextStorage` inside `ParagraphLayoutManager` which is owned by state.

There is one notable change which is not gated: Paragraph's state strongly owns `TextLayoutManager`, not `ParagraphShadowNode` like previously. This shouldn't change anything

Reviewed By: mdvacca

Differential Revision: D43692171

fbshipit-source-id: efe6077222a30ab6d89abd9916534b9a96e745d4
This commit is contained in:
Samuel Susla authored and Facebook GitHub Bot committed 2023-03-08 08:36:08 -08:00
1 parent a5bc6f0574
commit d41e95fb1a
20 files changed
+279 -90

No files matched your search

@@ -116,11 +116,8 @@ using namespace facebook::react;
return;
}
auto textLayoutManager = _state->getData().layoutManager.lock();
if (!textLayoutManager) {
return;
}
auto textLayoutManager = _state->getData().paragraphLayoutManager.getTextLayoutManager();
auto nsTextStorage = _state->getData().paragraphLayoutManager.getHostTextStorage();
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
@@ -129,7 +126,8 @@ using namespace facebook::react;
[nativeTextLayoutManager drawAttributedString:_state->getData().attributedString
paragraphAttributes:_paragraphAttributes
frame:frame];
frame:frame
textStorage:unwrapManagedObject(nsTextStorage)];
}
#pragma mark - Accessibility
@@ -161,18 +159,15 @@ using namespace facebook::react;
auto &data = _state->getData();
if (![_accessibilityProvider isUpToDate:data.attributedString]) {
auto textLayoutManager = data.layoutManager.lock();
if (textLayoutManager) {
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
_accessibilityProvider =
[[RCTParagraphComponentAccessibilityProvider alloc] initWithString:data.attributedString
layoutManager:nativeTextLayoutManager
paragraphAttributes:data.paragraphAttributes
frame:frame
view:self];
}
auto textLayoutManager = data.paragraphLayoutManager.getTextLayoutManager();
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
CGRect frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
_accessibilityProvider = [[RCTParagraphComponentAccessibilityProvider alloc] initWithString:data.attributedString
layoutManager:nativeTextLayoutManager
paragraphAttributes:data.paragraphAttributes
frame:frame
view:self];
}
return _accessibilityProvider.accessibilityElements;
@@ -191,11 +186,7 @@ using namespace facebook::react;
return _eventEmitter;
}
auto textLayoutManager = _state->getData().layoutManager.lock();
if (!textLayoutManager) {
return _eventEmitter;
}
auto textLayoutManager = _state->getData().paragraphLayoutManager.getTextLayoutManager();
RCTTextLayoutManager *nativeTextLayoutManager =
(RCTTextLayoutManager *)unwrapManagedObject(textLayoutManager->getNativeTextLayoutManager());
+4
View File
@@ -281,6 +281,10 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
CoreFeatures::useNativeState = true;
}
if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:enable_nstextstorage_caching")) {
CoreFeatures::cacheNSTextStorage = true;
}
auto componentRegistryFactory =
[factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)](
EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) {
@@ -0,0 +1,59 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "ParagraphLayoutManager.h"
#include <folly/Hash.h>
#include <react/renderer/core/CoreFeatures.h>
namespace facebook::react {
TextMeasurement ParagraphLayoutManager::measure(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
LayoutConstraints layoutConstraints) const {
if (CoreFeatures::cacheNSTextStorage) {
size_t newHash = folly::hash::hash_combine(
0,
textAttributedStringHashLayoutWise(attributedString),
paragraphAttributes);
if (!hostTextStorage_ || newHash != hash_) {
hostTextStorage_ = textLayoutManager_->getHostTextStorage(
attributedString, paragraphAttributes, layoutConstraints);
hash_ = newHash;
}
}
return textLayoutManager_->measure(
AttributedStringBox(attributedString),
paragraphAttributes,
layoutConstraints,
hostTextStorage_);
}
LinesMeasurements ParagraphLayoutManager::measureLines(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
Size size) const {
return textLayoutManager_->measureLines(
attributedString, paragraphAttributes, size);
}
void ParagraphLayoutManager::setTextLayoutManager(
std::shared_ptr<TextLayoutManager const> textLayoutManager) const {
textLayoutManager_ = std::move(textLayoutManager);
}
std::shared_ptr<TextLayoutManager const>
ParagraphLayoutManager::getTextLayoutManager() const {
return textLayoutManager_;
}
std::shared_ptr<void> ParagraphLayoutManager::getHostTextStorage() const {
return hostTextStorage_;
}
} // namespace facebook::react
@@ -0,0 +1,59 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/core/LayoutConstraints.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
namespace facebook::react {
/*
* Serves as a middle man between `ParagraphShadowNode` and `TextLayoutManager`.
* On iOS, caches `NSTextStorage` for individual `ParagraphShadowNode` to make
* sure only one `NSTextStorage` is created for every string. `NSTextStorage`
* can be re created on native views layer but it is expensive. On Android, this
* class does not cache anything.
*/
class ParagraphLayoutManager {
public:
TextMeasurement measure(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
LayoutConstraints layoutConstraints) const;
LinesMeasurements measureLines(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
Size size) const;
void setTextLayoutManager(
std::shared_ptr<TextLayoutManager const> textLayoutManager) const;
/*
* Returns an opaque pointer to platform-specific `TextLayoutManager`.
* Is used on a native views layer to delegate text rendering to the manager.
*/
std::shared_ptr<TextLayoutManager const> getTextLayoutManager() const;
/*
* Returns opaque shared_ptr holding `NSTextStorage`.
* May be nullptr.
* Is used on a native views layer to prevent `NSTextStorage` from being
* created twice.
*/
std::shared_ptr<void> getHostTextStorage() const;
private:
std::shared_ptr<TextLayoutManager const> mutable textLayoutManager_{};
std::shared_ptr<void> mutable hostTextStorage_{};
size_t mutable hash_{};
};
} // namespace facebook::react
@@ -95,7 +95,8 @@ Content ParagraphShadowNode::getContentWithMeasuredAttachments(
void ParagraphShadowNode::setTextLayoutManager(
std::shared_ptr<TextLayoutManager const> textLayoutManager) {
ensureUnsealed();
textLayoutManager_ = std::move(textLayoutManager);
getStateData().paragraphLayoutManager.setTextLayoutManager(
std::move(textLayoutManager));
}
void ParagraphShadowNode::updateStateIfNeeded(Content const &content) {
@@ -103,7 +104,7 @@ void ParagraphShadowNode::updateStateIfNeeded(Content const &content) {
auto &state = getStateData();
react_native_assert(textLayoutManager_);
react_native_assert(state.paragraphLayoutManager.getTextLayoutManager());
if (state.attributedString == content.attributedString) {
return;
@@ -112,7 +113,7 @@ void ParagraphShadowNode::updateStateIfNeeded(Content const &content) {
setStateData(ParagraphState{
content.attributedString,
content.paragraphAttributes,
textLayoutManager_});
state.paragraphLayoutManager});
}
#pragma mark - LayoutableShadowNode
@@ -136,11 +137,9 @@ Size ParagraphShadowNode::measureContent(
attributedString.appendFragment({string, textAttributes, {}});
}
return textLayoutManager_
->measure(
AttributedStringBox{attributedString},
content.paragraphAttributes,
layoutConstraints)
return getStateData()
.paragraphLayoutManager
.measure(attributedString, content.paragraphAttributes, layoutConstraints)
.size;
}
@@ -157,13 +156,11 @@ void ParagraphShadowNode::layout(LayoutContext layoutContext) {
updateStateIfNeeded(content);
auto measurement = textLayoutManager_->measure(
AttributedStringBox{content.attributedString},
content.paragraphAttributes,
layoutConstraints);
auto measurement = getStateData().paragraphLayoutManager.measure(
content.attributedString, content.paragraphAttributes, layoutConstraints);
if (getConcreteProps().onTextLayout) {
auto linesMeasurements = textLayoutManager_->measureLines(
auto linesMeasurements = getStateData().paragraphLayoutManager.measureLines(
content.attributedString,
content.paragraphAttributes,
measurement.size);
@@ -7,15 +7,14 @@
#pragma once
#include <react/renderer/components/text/BaseTextShadowNode.h>
#include <react/renderer/components/text/ParagraphEventEmitter.h>
#include <react/renderer/components/text/ParagraphLayoutManager.h>
#include <react/renderer/components/text/ParagraphProps.h>
#include <react/renderer/components/text/ParagraphState.h>
#include <react/renderer/components/text/TextShadowNode.h>
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
#include <react/renderer/core/ConcreteShadowNode.h>
#include <react/renderer/core/LayoutContext.h>
#include <react/renderer/core/ShadowNode.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
namespace facebook {
namespace react {
@@ -96,8 +95,6 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode<
*/
void updateStateIfNeeded(Content const &content);
std::shared_ptr<TextLayoutManager const> textLayoutManager_;
/*
* Cached content of the subtree started from the node.
*/
@@ -10,15 +10,14 @@
#include <react/debug/react_native_assert.h>
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
#include <react/renderer/components/text/ParagraphLayoutManager.h>
#ifdef ANDROID
#include <folly/dynamic.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#endif
namespace facebook {
namespace react {
namespace facebook::react {
#ifdef ANDROID
// constants for Text State serialization
@@ -33,8 +32,7 @@ constexpr static MapBuffer::Key TX_STATE_KEY_MOST_RECENT_EVENT_COUNT = 3;
* State for <Paragraph> component.
* Represents what to render and how to render.
*/
class ParagraphState final {
public:
struct ParagraphState {
/*
* All content of <Paragraph> component represented as an `AttributedString`.
*/
@@ -47,22 +45,22 @@ class ParagraphState final {
ParagraphAttributes paragraphAttributes;
/*
* `TextLayoutManager` provides a connection to platform-specific
* `ParagraphLayoutManager` provides a connection to platform-specific
* text rendering infrastructure which is capable to render the
* `AttributedString`.
* This is not on every platform. This is not used on Android, but is
* used on the iOS mounting layer.
*/
std::weak_ptr<TextLayoutManager const> layoutManager;
ParagraphLayoutManager paragraphLayoutManager;
#ifdef ANDROID
ParagraphState(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
std::weak_ptr<const TextLayoutManager> const &layoutManager)
ParagraphLayoutManager const &paragraphLayoutManager)
: attributedString(attributedString),
paragraphAttributes(paragraphAttributes),
layoutManager(layoutManager) {}
paragraphLayoutManager(paragraphLayoutManager) {}
ParagraphState() = default;
ParagraphState(
ParagraphState const &previousState,
@@ -74,5 +72,4 @@ class ParagraphState final {
#endif
};
} // namespace react
} // namespace facebook
} // namespace facebook::react
@@ -197,7 +197,8 @@ Size AndroidTextInputShadowNode::measureContent(
->measure(
AttributedStringBox{attributedString},
getConcreteProps().paragraphAttributes,
layoutConstraints)
layoutConstraints,
nullptr)
.size;
}
@@ -110,7 +110,8 @@ Size TextInputShadowNode::measureContent(
->measure(
attributedStringBoxToMeasure(layoutContext),
getConcreteProps().getEffectiveParagraphAttributes(),
layoutConstraints)
layoutConstraints,
nullptr)
.size;
}
@@ -14,6 +14,7 @@ bool CoreFeatures::enablePropIteratorSetter = false;
bool CoreFeatures::enableMapBuffer = false;
bool CoreFeatures::blockPaintForUseLayoutEffect = false;
bool CoreFeatures::useNativeState = false;
bool CoreFeatures::cacheNSTextStorage = false;
} // namespace react
} // namespace facebook
@@ -34,6 +34,11 @@ class CoreFeatures {
// Whether to use Hermes' NativeState instead of HostObject
// in simple data passing scenarios with JS
static bool useNativeState;
// Creating NSTextStorage is relatively expensive operation and we were
// creating it twice. Once when measuring text and once when rendering it.
// This flag caches it inside ParagraphState.
static bool cacheNSTextStorage;
};
} // namespace react
@@ -7,6 +7,7 @@
#pragma once
#include <folly/Hash.h>
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/core/LayoutConstraints.h>
@@ -154,8 +154,9 @@ void *TextLayoutManager::getNativeTextLayoutManager() const {
TextMeasurement TextLayoutManager::measure(
AttributedStringBox const &attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const {
ParagraphAttributes const &paragraphAttributes,
LayoutConstraints layoutConstraints,
std::shared_ptr<void> /* hostTextStorage */) const {
auto &attributedString = attributedStringBox.getValue();
auto measurement = measureCache_.get(
@@ -179,6 +180,12 @@ TextMeasurement TextLayoutManager::measure(
measurement.size = layoutConstraints.clamp(measurement.size);
return measurement;
}
std::shared_ptr<void> TextLayoutManager::getHostTextStorage(
AttributedString const & /* attributedStringBox */,
ParagraphAttributes const & /* paragraphAttributes */,
LayoutConstraints /* layoutConstraints */) const {
return nullptr;
}
TextMeasurement TextLayoutManager::measureCachedSpannableById(
int64_t cacheId,
@@ -45,7 +45,13 @@ class TextLayoutManager {
*/
TextMeasurement measure(
AttributedStringBox const &attributedStringBox,
ParagraphAttributes paragraphAttributes,
ParagraphAttributes const &paragraphAttributes,
LayoutConstraints layoutConstraints,
std::shared_ptr<void> /* hostTextStorage */) const;
std::shared_ptr<void> getHostTextStorage(
AttributedString const &attributedString,
ParagraphAttributes const &paragraphAttributes,
LayoutConstraints layoutConstraints) const;
/**
@@ -17,7 +17,8 @@ void *TextLayoutManager::getNativeTextLayoutManager() const {
TextMeasurement TextLayoutManager::measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const {
LayoutConstraints layoutConstraints,
std::shared_ptr<void>) const {
TextMeasurement::Attachments attachments;
for (auto const &fragment : attributedStringBox.getValue().getFragments()) {
if (fragment.isAttachment()) {
@@ -35,5 +36,12 @@ LinesMeasurements TextLayoutManager::measureLines(
return {};
};
std::shared_ptr<void> TextLayoutManager::getHostTextStorage(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const {
return nullptr;
}
} // namespace react
} // namespace facebook
@@ -36,7 +36,8 @@ class TextLayoutManager {
TextMeasurement measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const;
LayoutConstraints layoutConstraints,
std::shared_ptr<void>) const;
/*
* Measures lines of `attributedString` using native text rendering
@@ -52,6 +53,11 @@ class TextLayoutManager {
* Is used on a native views layer to delegate text rendering to the manager.
*/
void *getNativeTextLayoutManager() const;
std::shared_ptr<void> getHostTextStorage(
AttributedString attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const;
};
} // namespace react
@@ -28,15 +28,22 @@ using RCTTextLayoutFragmentEnumerationBlock =
- (facebook::react::TextMeasurement)measureAttributedString:(facebook::react::AttributedString)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints
textStorage:(NSTextStorage *_Nullable)textStorage;
- (facebook::react::TextMeasurement)measureNSAttributedString:(NSAttributedString *)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints
textStorage:(NSTextStorage *_Nullable)textStorage;
- (NSTextStorage *)textStorageForAttributesString:(facebook::react::AttributedString)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
size:(CGSize)size;
- (void)drawAttributedString:(facebook::react::AttributedString)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
frame:(CGRect)frame;
frame:(CGRect)frame
textStorage:(NSTextStorage *_Nullable)textStorage;
- (facebook::react::LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString
paragraphAttributes:
@@ -37,6 +37,7 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
- (TextMeasurement)measureNSAttributedString:(NSAttributedString *)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
layoutConstraints:(LayoutConstraints)layoutConstraints
textStorage:(NSTextStorage *_Nullable)textStorage
{
if (attributedString.length == 0) {
// This is not really an optimization because that should be checked much earlier on the call stack.
@@ -47,9 +48,13 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
CGSize maximumSize = CGSize{layoutConstraints.maximumSize.width, CGFLOAT_MAX};
NSTextStorage *textStorage = [self _textStorageAndLayoutManagerWithAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:maximumSize];
if (!textStorage) {
textStorage = [self _textStorageForNSAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:maximumSize];
} else {
textStorage.layoutManagers.firstObject.textContainers.firstObject.size = maximumSize;
}
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -93,20 +98,24 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
- (TextMeasurement)measureAttributedString:(AttributedString)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
layoutConstraints:(LayoutConstraints)layoutConstraints
textStorage:(NSTextStorage *_Nullable)textStorage
{
return [self measureNSAttributedString:[self _nsAttributedStringFromAttributedString:attributedString]
paragraphAttributes:paragraphAttributes
layoutConstraints:layoutConstraints];
layoutConstraints:layoutConstraints
textStorage:textStorage];
}
- (void)drawAttributedString:(AttributedString)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
frame:(CGRect)frame
textStorage:(NSTextStorage *_Nullable)textStorage
{
NSTextStorage *textStorage = [self
_textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString]
if (!textStorage) {
textStorage = [self textStorageForAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:frame.size];
}
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -125,14 +134,13 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
#endif
}
- (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedString)attributedString
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
- (LinesMeasurements)getLinesForAttributedString:(AttributedString)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
{
NSTextStorage *textStorage = [self
_textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString]
paragraphAttributes:paragraphAttributes
size:size];
NSTextStorage *textStorage = [self textStorageForAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:size];
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -170,9 +178,9 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
return paragraphLines;
}
- (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
- (NSTextStorage *)_textStorageForNSAttributesString:(NSAttributedString *)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
{
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:size];
@@ -199,15 +207,23 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
return textStorage;
}
- (NSTextStorage *)textStorageForAttributesString:(AttributedString)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
{
return [self _textStorageForNSAttributesString:[self _nsAttributedStringFromAttributedString:attributedString]
paragraphAttributes:paragraphAttributes
size:size];
}
- (SharedEventEmitter)getEventEmitterWithAttributeString:(AttributedString)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
frame:(CGRect)frame
atPoint:(CGPoint)point
{
NSTextStorage *textStorage = [self
_textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString]
paragraphAttributes:paragraphAttributes
size:frame.size];
NSTextStorage *textStorage = [self textStorageForAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:frame.size];
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -245,10 +261,9 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
frame:(CGRect)frame
usingBlock:(RCTTextLayoutFragmentEnumerationBlock)block
{
NSTextStorage *textStorage = [self
_textStorageAndLayoutManagerWithAttributesString:[self _nsAttributedStringFromAttributedString:attributedString]
paragraphAttributes:paragraphAttributes
size:frame.size];
NSTextStorage *textStorage = [self textStorageForAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:frame.size];
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
@@ -33,7 +33,8 @@ class TextLayoutManager {
TextMeasurement measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const;
LayoutConstraints layoutConstraints,
std::shared_ptr<void> hostTextStorage) const;
/*
* Measures lines of `attributedString` using native text rendering
@@ -44,6 +45,11 @@ class TextLayoutManager {
ParagraphAttributes paragraphAttributes,
Size size) const;
std::shared_ptr<void> getHostTextStorage(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const;
/*
* Returns an opaque pointer to platform-specific TextLayoutManager.
* Is used on a native views layer to delegate text rendering to the manager.
@@ -25,12 +25,31 @@ std::shared_ptr<void> TextLayoutManager::getNativeTextLayoutManager() const
return self_;
}
TextMeasurement TextLayoutManager::measure(
AttributedStringBox attributedStringBox,
std::shared_ptr<void> TextLayoutManager::getHostTextStorage(
AttributedString attributedString,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints) const
{
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);
CGSize maximumSize = CGSize{layoutConstraints.maximumSize.width, CGFLOAT_MAX};
NSTextStorage *textStorage = [textLayoutManager textStorageForAttributesString:attributedString
paragraphAttributes:paragraphAttributes
size:maximumSize];
return wrapManagedObject(textStorage);
}
TextMeasurement TextLayoutManager::measure(
AttributedStringBox attributedStringBox,
ParagraphAttributes paragraphAttributes,
LayoutConstraints layoutConstraints,
std::shared_ptr<void> hostTextStorage) const
{
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);
NSTextStorage *textStorage;
if (hostTextStorage) {
textStorage = unwrapManagedObject(hostTextStorage);
}
auto measurement = TextMeasurement{};
@@ -47,7 +66,8 @@ TextMeasurement TextLayoutManager::measure(
auto measurement = [textLayoutManager measureAttributedString:attributedString
paragraphAttributes:paragraphAttributes
layoutConstraints:layoutConstraints];
layoutConstraints:layoutConstraints
textStorage:textStorage];
if (telemetry) {
telemetry->didMeasureText();
@@ -69,7 +89,8 @@ TextMeasurement TextLayoutManager::measure(
measurement = [textLayoutManager measureNSAttributedString:nsAttributedString
paragraphAttributes:paragraphAttributes
layoutConstraints:layoutConstraints];
layoutConstraints:layoutConstraints
textStorage:textStorage];
if (telemetry) {
telemetry->didMeasureText();