mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fabric: Calculating positions of attachments as part of text measurement
Summary: This diff changes API we use to measure text. Previously, a platform-specific measure infra returned just the size of the text, now it returns the size and an array of frames that describe where attachments are placed. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D20268041 fbshipit-source-id: 7c065607b6af18a36318db0aab24dad0f171d33a
This commit is contained in:
committed by
Facebook Github Bot
parent
d1ad29b7db
commit
90ada5abb1
@@ -70,10 +70,12 @@ Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const {
|
||||
return layoutConstraints.clamp({0, 0});
|
||||
}
|
||||
|
||||
return textLayoutManager_->measure(
|
||||
AttributedStringBox{content.attributedString},
|
||||
content.paragraphAttributes,
|
||||
layoutConstraints);
|
||||
return textLayoutManager_
|
||||
->measure(
|
||||
AttributedStringBox{content.attributedString},
|
||||
content.paragraphAttributes,
|
||||
layoutConstraints)
|
||||
.size;
|
||||
}
|
||||
|
||||
void ParagraphShadowNode::layout(LayoutContext layoutContext) {
|
||||
|
||||
+6
-4
@@ -180,10 +180,12 @@ Size AndroidTextInputShadowNode::measure(
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
return textLayoutManager_->measure(
|
||||
AttributedStringBox{attributedString},
|
||||
getConcreteProps().paragraphAttributes,
|
||||
layoutConstraints);
|
||||
return textLayoutManager_
|
||||
->measure(
|
||||
AttributedStringBox{attributedString},
|
||||
getConcreteProps().paragraphAttributes,
|
||||
layoutConstraints)
|
||||
.size;
|
||||
}
|
||||
|
||||
void AndroidTextInputShadowNode::layout(LayoutContext layoutContext) {
|
||||
|
||||
@@ -81,10 +81,12 @@ void TextInputShadowNode::updateStateIfNeeded() {
|
||||
#pragma mark - LayoutableShadowNode
|
||||
|
||||
Size TextInputShadowNode::measure(LayoutConstraints layoutConstraints) const {
|
||||
return textLayoutManager_->measure(
|
||||
attributedStringBoxToMeasure(),
|
||||
getConcreteProps().getEffectiveParagraphAttributes(),
|
||||
layoutConstraints);
|
||||
return textLayoutManager_
|
||||
->measure(
|
||||
attributedStringBoxToMeasure(),
|
||||
getConcreteProps().getEffectiveParagraphAttributes(),
|
||||
layoutConstraints)
|
||||
.size;
|
||||
}
|
||||
|
||||
void TextInputShadowNode::layout(LayoutContext layoutContext) {
|
||||
|
||||
@@ -16,6 +16,23 @@
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Describes a result of text measuring.
|
||||
*/
|
||||
class TextMeasurement final {
|
||||
public:
|
||||
class Attachment final {
|
||||
public:
|
||||
Rect frame;
|
||||
bool isClipped;
|
||||
};
|
||||
|
||||
using Attachments = std::vector<Attachment>;
|
||||
|
||||
Size size;
|
||||
Attachments attachments;
|
||||
};
|
||||
|
||||
// The Key type that is used for Text Measure Cache.
|
||||
// The equivalence and hashing operations of this are defined to respect the
|
||||
// nature of text measuring.
|
||||
@@ -39,7 +56,7 @@ constexpr auto kSimpleThreadSafeCacheSizeCap = size_t{256};
|
||||
*/
|
||||
using TextMeasureCache = SimpleThreadSafeCache<
|
||||
TextMeasureCacheKey,
|
||||
Size,
|
||||
TextMeasurement,
|
||||
kSimpleThreadSafeCacheSizeCap>;
|
||||
|
||||
inline bool areTextAttributesEquivalentLayoutWise(
|
||||
|
||||
@@ -22,7 +22,7 @@ void *TextLayoutManager::getNativeTextLayoutManager() const {
|
||||
return self_;
|
||||
}
|
||||
|
||||
Size TextLayoutManager::measure(
|
||||
TextMeasurement TextLayoutManager::measure(
|
||||
AttributedStringBox attributedStringBox,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const {
|
||||
@@ -36,7 +36,7 @@ Size TextLayoutManager::measure(
|
||||
});
|
||||
}
|
||||
|
||||
Size TextLayoutManager::doMeasure(
|
||||
TextMeasurement TextLayoutManager::doMeasure(
|
||||
AttributedString attributedString,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const {
|
||||
@@ -69,7 +69,7 @@ Size TextLayoutManager::doMeasure(
|
||||
reinterpret_cast<ReadableMap::javaobject>(attributedStringRNM.get()));
|
||||
local_ref<ReadableMap::javaobject> paragraphAttributesRM = make_local(
|
||||
reinterpret_cast<ReadableMap::javaobject>(paragraphAttributesRNM.get()));
|
||||
return yogaMeassureToSize(measure(
|
||||
auto size = yogaMeassureToSize(measure(
|
||||
fabricUIManager,
|
||||
-1,
|
||||
componentName.get(),
|
||||
@@ -80,6 +80,8 @@ Size TextLayoutManager::doMeasure(
|
||||
maximumSize.width,
|
||||
minimumSize.height,
|
||||
maximumSize.height));
|
||||
|
||||
return TextMeasurement{size, {}};
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
||||
@@ -32,7 +32,7 @@ class TextLayoutManager {
|
||||
/*
|
||||
* Measures `attributedString` using native text rendering infrastructure.
|
||||
*/
|
||||
Size measure(
|
||||
TextMeasurement measure(
|
||||
AttributedStringBox attributedStringBox,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const;
|
||||
@@ -44,7 +44,7 @@ class TextLayoutManager {
|
||||
void *getNativeTextLayoutManager() const;
|
||||
|
||||
private:
|
||||
Size doMeasure(
|
||||
TextMeasurement doMeasure(
|
||||
AttributedString attributedString,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#import <react/attributedstring/ParagraphAttributes.h>
|
||||
#import <react/core/LayoutConstraints.h>
|
||||
#import <react/graphics/Geometry.h>
|
||||
#import <react/textlayoutmanager/TextMeasureCache.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -19,13 +20,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface RCTTextLayoutManager : NSObject
|
||||
|
||||
- (facebook::react::Size)measureAttributedString:(facebook::react::AttributedString)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
|
||||
- (facebook::react::TextMeasurement)measureAttributedString:(facebook::react::AttributedString)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
|
||||
|
||||
- (facebook::react::Size)measureNSAttributedString:(NSAttributedString *)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
|
||||
- (facebook::react::TextMeasurement)measureNSAttributedString:(NSAttributedString *)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(facebook::react::LayoutConstraints)layoutConstraints;
|
||||
|
||||
- (void)drawAttributedString:(facebook::react::AttributedString)attributedString
|
||||
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
||||
|
||||
@@ -32,15 +32,15 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
|
||||
}
|
||||
}
|
||||
|
||||
- (facebook::react::Size)measureNSAttributedString:(NSAttributedString *)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(LayoutConstraints)layoutConstraints
|
||||
- (TextMeasurement)measureNSAttributedString:(NSAttributedString *)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(LayoutConstraints)layoutConstraints
|
||||
{
|
||||
if (attributedString.length == 0) {
|
||||
// This is not really an optimization because that should be checked much earlier on the call stack.
|
||||
// Sometimes, very irregularly, measuring an empty string crashes/freezes iOS internal text infrastructure.
|
||||
// This is our last line of defense.
|
||||
return {0, 0};
|
||||
return {};
|
||||
}
|
||||
|
||||
CGSize maximumSize = CGSize{layoutConstraints.maximumSize.width, CGFLOAT_MAX};
|
||||
@@ -55,12 +55,38 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
|
||||
|
||||
CGSize size = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
return {size.width, size.height};
|
||||
__block auto attachments = TextMeasurement::Attachments{};
|
||||
|
||||
[textStorage
|
||||
enumerateAttribute:NSAttachmentAttributeName
|
||||
inRange:NSMakeRange(0, textStorage.length)
|
||||
options:0
|
||||
usingBlock:^(NSTextAttachment *attachment, NSRange range, BOOL *stop) {
|
||||
if (!attachment) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize attachmentSize = attachment.bounds.size;
|
||||
CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range inTextContainer:textContainer];
|
||||
|
||||
UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil];
|
||||
|
||||
CGRect frame = {{glyphRect.origin.x,
|
||||
glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender},
|
||||
attachmentSize};
|
||||
|
||||
auto rect = facebook::react::Rect{facebook::react::Point{frame.origin.x, frame.origin.y},
|
||||
facebook::react::Size{frame.size.width, frame.size.height}};
|
||||
|
||||
attachments.push_back(TextMeasurement::Attachment{rect, false});
|
||||
}];
|
||||
|
||||
return TextMeasurement{{size.width, size.height}, attachments};
|
||||
}
|
||||
|
||||
- (facebook::react::Size)measureAttributedString:(AttributedString)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(LayoutConstraints)layoutConstraints
|
||||
- (TextMeasurement)measureAttributedString:(AttributedString)attributedString
|
||||
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
||||
layoutConstraints:(LayoutConstraints)layoutConstraints
|
||||
{
|
||||
return [self measureNSAttributedString:[self _nsAttributedStringFromAttributedString:attributedString]
|
||||
paragraphAttributes:paragraphAttributes
|
||||
|
||||
@@ -34,7 +34,7 @@ class TextLayoutManager {
|
||||
/*
|
||||
* Measures `attributedString` using native text rendering infrastructure.
|
||||
*/
|
||||
Size measure(
|
||||
TextMeasurement measure(
|
||||
AttributedStringBox attributedStringBox,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const;
|
||||
|
||||
@@ -25,20 +25,20 @@ std::shared_ptr<void> TextLayoutManager::getNativeTextLayoutManager() const
|
||||
return self_;
|
||||
}
|
||||
|
||||
Size TextLayoutManager::measure(
|
||||
TextMeasurement TextLayoutManager::measure(
|
||||
AttributedStringBox attributedStringBox,
|
||||
ParagraphAttributes paragraphAttributes,
|
||||
LayoutConstraints layoutConstraints) const
|
||||
{
|
||||
RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(self_);
|
||||
|
||||
auto size = Size{};
|
||||
auto measurement = TextMeasurement{};
|
||||
|
||||
switch (attributedStringBox.getMode()) {
|
||||
case AttributedStringBox::Mode::Value: {
|
||||
auto &attributedString = attributedStringBox.getValue();
|
||||
|
||||
size = measureCache_.get(
|
||||
measurement = measureCache_.get(
|
||||
{attributedString, paragraphAttributes, layoutConstraints}, [&](TextMeasureCacheKey const &key) {
|
||||
return [textLayoutManager measureAttributedString:attributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
@@ -51,14 +51,16 @@ Size TextLayoutManager::measure(
|
||||
NSAttributedString *nsAttributedString =
|
||||
(NSAttributedString *)unwrapManagedObject(attributedStringBox.getOpaquePointer());
|
||||
|
||||
size = [textLayoutManager measureNSAttributedString:nsAttributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
layoutConstraints:layoutConstraints];
|
||||
measurement = [textLayoutManager measureNSAttributedString:nsAttributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
layoutConstraints:layoutConstraints];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return layoutConstraints.clamp(size);
|
||||
measurement.size = layoutConstraints.clamp(measurement.size);
|
||||
|
||||
return measurement;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
||||
Reference in New Issue
Block a user