Fabric: Support for Inline Views in Paragraph component
Summary: Final diff that implements Inline Views in `Paragraph` component. Changelog: [Internal] Fabric-specific internal change. Reviewed By: mdvacca Differential Revision: D20268045 fbshipit-source-id: 9a0093eacb9ae34890f6137b308550547a3bcb38
This commit is contained in:
Родитель
c38318d084
Коммит
507379f6fa
|
@ -7,15 +7,23 @@
|
|||
|
||||
#include "ParagraphShadowNode.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <react/attributedstring/AttributedStringBox.h>
|
||||
#include <react/components/view/ViewShadowNode.h>
|
||||
#include <react/components/view/conversions.h>
|
||||
#include <react/graphics/rounding.h>
|
||||
|
||||
#include "ParagraphState.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using Content = ParagraphShadowNode::Content;
|
||||
|
||||
char const ParagraphComponentName[] = "Paragraph";
|
||||
|
||||
ParagraphShadowNode::Content const &ParagraphShadowNode::getContent() const {
|
||||
Content const &ParagraphShadowNode::getContent() const {
|
||||
if (content_.has_value()) {
|
||||
return content_.value();
|
||||
}
|
||||
|
@ -35,6 +43,48 @@ ParagraphShadowNode::Content const &ParagraphShadowNode::getContent() const {
|
|||
return content_.value();
|
||||
}
|
||||
|
||||
Content ParagraphShadowNode::getContentWithMeasuredAttachments(
|
||||
LayoutContext const &layoutContext,
|
||||
LayoutConstraints const &layoutConstraints) const {
|
||||
auto content = getContent();
|
||||
|
||||
if (content.attachments.empty()) {
|
||||
// Base case: No attachments, nothing to do.
|
||||
return content;
|
||||
}
|
||||
|
||||
auto localLayoutConstraints = layoutConstraints;
|
||||
// Having enforced minimum size for text fragments doesn't make much sense.
|
||||
localLayoutConstraints.minimumSize = Size{0, 0};
|
||||
|
||||
auto &fragments = content.attributedString.getFragments();
|
||||
|
||||
for (auto const &attachment : content.attachments) {
|
||||
auto laytableShadowNode =
|
||||
traitCast<LayoutableShadowNode const *>(attachment.shadowNode);
|
||||
|
||||
if (!laytableShadowNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto size =
|
||||
laytableShadowNode->measure(layoutContext, localLayoutConstraints);
|
||||
|
||||
// Rounding to *next* value on the pixel grid.
|
||||
size.width += 0.01;
|
||||
size.height += 0.01;
|
||||
size = roundToPixel<&ceil>(size, layoutContext.pointScaleFactor);
|
||||
|
||||
auto fragmentLayoutMetrics = LayoutMetrics{};
|
||||
fragmentLayoutMetrics.pointScaleFactor = layoutContext.pointScaleFactor;
|
||||
fragmentLayoutMetrics.frame.size = size;
|
||||
fragments[attachment.fragmentIndex].parentShadowView.layoutMetrics =
|
||||
fragmentLayoutMetrics;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
void ParagraphShadowNode::setTextLayoutManager(
|
||||
SharedTextLayoutManager textLayoutManager) {
|
||||
ensureUnsealed();
|
||||
|
@ -64,7 +114,8 @@ void ParagraphShadowNode::updateStateIfNeeded(Content const &content) {
|
|||
#pragma mark - LayoutableShadowNode
|
||||
|
||||
Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const {
|
||||
auto content = getContent();
|
||||
auto content =
|
||||
getContentWithMeasuredAttachments(LayoutContext{}, layoutConstraints);
|
||||
|
||||
if (content.attributedString.isEmpty()) {
|
||||
return layoutConstraints.clamp({0, 0});
|
||||
|
@ -79,8 +130,88 @@ Size ParagraphShadowNode::measure(LayoutConstraints layoutConstraints) const {
|
|||
}
|
||||
|
||||
void ParagraphShadowNode::layout(LayoutContext layoutContext) {
|
||||
updateStateIfNeeded(getContent());
|
||||
ConcreteViewShadowNode::layout(layoutContext);
|
||||
ensureUnsealed();
|
||||
|
||||
auto layoutMetrics = getLayoutMetrics();
|
||||
auto availableSize = layoutMetrics.getContentFrame().size;
|
||||
|
||||
auto layoutConstraints = LayoutConstraints{
|
||||
availableSize, availableSize, layoutMetrics.layoutDirection};
|
||||
auto content =
|
||||
getContentWithMeasuredAttachments(layoutContext, layoutConstraints);
|
||||
|
||||
updateStateIfNeeded(content);
|
||||
|
||||
if (content.attachments.empty()) {
|
||||
// No attachments, nothing to layout.
|
||||
return;
|
||||
}
|
||||
|
||||
auto measurement = textLayoutManager_->measure(
|
||||
AttributedStringBox{content.attributedString},
|
||||
content.paragraphAttributes,
|
||||
layoutConstraints);
|
||||
|
||||
// Iterating on attachments, we clone shadow nodes and moving
|
||||
// `paragraphShadowNode` that represents clones of `this` object.
|
||||
auto paragraphShadowNode = static_cast<ParagraphShadowNode *>(this);
|
||||
// `paragraphOwningShadowNode` is owning pointer to`paragraphShadowNode`
|
||||
// (besides the initial case when `paragraphShadowNode == this`), we need this
|
||||
// only to keep it in memory for a while.
|
||||
auto paragraphOwningShadowNode = ShadowNode::Unshared{};
|
||||
|
||||
assert(content.attachments.size() == measurement.attachments.size());
|
||||
|
||||
for (auto i = 0; i < content.attachments.size(); i++) {
|
||||
auto &attachment = content.attachments.at(i);
|
||||
|
||||
if (!traitCast<LayoutableShadowNode const *>(attachment.shadowNode)) {
|
||||
// Not a layoutable `ShadowNode`, no need to lay it out.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto clonedShadowNode = ShadowNode::Unshared{};
|
||||
|
||||
paragraphOwningShadowNode = paragraphShadowNode->cloneTree(
|
||||
attachment.shadowNode->getFamily(),
|
||||
[&](ShadowNode const &oldShadowNode) {
|
||||
clonedShadowNode = oldShadowNode.clone({});
|
||||
return clonedShadowNode;
|
||||
});
|
||||
paragraphShadowNode =
|
||||
static_cast<ParagraphShadowNode *>(paragraphOwningShadowNode.get());
|
||||
|
||||
auto &layoutableShadowNode = const_cast<LayoutableShadowNode &>(
|
||||
traitCast<LayoutableShadowNode const &>(*clonedShadowNode));
|
||||
|
||||
auto attachmentFrame = measurement.attachments[i].frame;
|
||||
auto attachmentSize = roundToPixel<&ceil>(
|
||||
attachmentFrame.size, layoutMetrics.pointScaleFactor);
|
||||
auto attachmentOrigin = roundToPixel<&round>(
|
||||
attachmentFrame.origin, layoutMetrics.pointScaleFactor);
|
||||
auto attachmentLayoutContext = layoutContext;
|
||||
attachmentLayoutContext.absolutePosition += attachmentOrigin;
|
||||
auto attachmentLayoutConstrains = LayoutConstraints{
|
||||
attachmentSize, attachmentSize, layoutConstraints.layoutDirection};
|
||||
|
||||
// Laying out the `ShadowNode` and the subtree starting from it.
|
||||
layoutableShadowNode.layoutTree(
|
||||
attachmentLayoutContext, attachmentLayoutConstrains);
|
||||
|
||||
// Altering the origin of the `ShadowNode` (which is defined by text layout,
|
||||
// not by internal styles and state).
|
||||
auto attachmentLayoutMetrics = layoutableShadowNode.getLayoutMetrics();
|
||||
attachmentLayoutMetrics.frame.origin = attachmentOrigin;
|
||||
layoutableShadowNode.setLayoutMetrics(attachmentLayoutMetrics);
|
||||
}
|
||||
|
||||
// If we ended up cloning something, we need to update the list of children to
|
||||
// reflect the changes that we made.
|
||||
if (paragraphShadowNode != this) {
|
||||
this->children_ =
|
||||
static_cast<ParagraphShadowNode const *>(paragraphShadowNode)
|
||||
->children_;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -57,7 +57,6 @@ class ParagraphShadowNode : public ConcreteViewShadowNode<
|
|||
void layout(LayoutContext layoutContext) override;
|
||||
Size measure(LayoutConstraints layoutConstraints) const override;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Internal representation of the nested content of the node in a format
|
||||
* suitable for future processing.
|
||||
|
@ -69,11 +68,19 @@ class ParagraphShadowNode : public ConcreteViewShadowNode<
|
|||
Attachments attachments;
|
||||
};
|
||||
|
||||
private:
|
||||
/*
|
||||
* Builds (if needed) and returns a reference to a `Content` object.
|
||||
*/
|
||||
Content const &getContent() const;
|
||||
|
||||
/*
|
||||
* Builds and returns a `Content` object with given `layoutConstraints`.
|
||||
*/
|
||||
Content getContentWithMeasuredAttachments(
|
||||
LayoutContext const &layoutContext,
|
||||
LayoutConstraints const &layoutConstraints) const;
|
||||
|
||||
/*
|
||||
* Creates a `State` object (with `AttributedText` and
|
||||
* `TextLayoutManager`) if needed.
|
||||
|
|
Загрузка…
Ссылка в новой задаче