Skip to content

Commit 7979c7c

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Remove YogaLayoutableShadowNode::cleanLayout() and Fix ParagraphShadowNode Font Size Invalidation Logic (#51760)
Summary: Pull Request resolved: #51760 This API is evil. Yoga's public API never allows a dirty node to become clean again, until its laid out, but this API requires doing that, since we will otherwise automatically dirty by default. Let's replace it with `YogaLayoutableShadowNode::shouldNewRevisionDirtyMeasurement()`, which lets individual ShadowNodes represent whether a new revision's state and props should cause dirtying, defaulting to true. Changelog: [General][Removed] - Remove `YogaLayoutableShadowNode::cleanLayout()` Reviewed By: lenaic Differential Revision: D75479902 fbshipit-source-id: a40aa531522a76dc49feb7d12bae5a8d877c6c06
1 parent 5c8c538 commit 7979c7c

7 files changed

Lines changed: 49 additions & 31 deletions

File tree

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

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,28 +58,15 @@ ParagraphShadowNode::ParagraphShadowNode(
5858
const ShadowNode& sourceShadowNode,
5959
const ShadowNodeFragment& fragment)
6060
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
61-
auto& sourceParagraphShadowNode =
62-
static_cast<const ParagraphShadowNode&>(sourceShadowNode);
63-
auto& state = getStateData();
64-
const auto& sourceContent = sourceParagraphShadowNode.content_;
65-
66-
if (!fragment.children && !fragment.props &&
67-
sourceParagraphShadowNode.getIsLayoutClean() &&
68-
(!ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout() ||
69-
(sourceContent.has_value() &&
70-
sourceContent.value()
71-
.attributedString.getBaseTextAttributes()
72-
.fontSizeMultiplier ==
73-
state.attributedString.getBaseTextAttributes()
74-
.fontSizeMultiplier))) {
75-
// This ParagraphShadowNode was cloned but did not change
76-
// in a way that affects its layout. Let's mark it clean
77-
// to stop Yoga from traversing it.
78-
cleanLayout();
79-
}
8061
initialize();
8162
}
8263

64+
bool ParagraphShadowNode::shouldNewRevisionDirtyMeasurement(
65+
const ShadowNode& /*sourceShadowNode*/,
66+
const ShadowNodeFragment& fragment) const {
67+
return fragment.props != nullptr;
68+
}
69+
8370
const Content& ParagraphShadowNode::getContent(
8471
const LayoutContext& layoutContext) const {
8572
if (content_.has_value()) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ class ParagraphShadowNode final : public ConcreteViewShadowNode<
9090
Attachments attachments;
9191
};
9292

93+
protected:
94+
bool shouldNewRevisionDirtyMeasurement(
95+
const ShadowNode& sourceShadowNode,
96+
const ShadowNodeFragment& fragment) const override;
97+
9398
private:
9499
void initialize() noexcept;
95100
/*

packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,6 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode(
132132
&yogaNode_, &initializeYogaConfig(yogaConfig_, previousConfig));
133133
updateYogaChildrenOwnersIfNeeded();
134134

135-
// This is the only legit place where we can dirty cloned Yoga node.
136-
// If we do it later, ancestor nodes will not be able to observe this and
137-
// dirty (and clone) themselves as a result.
138-
if (getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode)) {
139-
yogaNode_.setDirty(true);
140-
}
141-
142135
// We do not need to reconfigure this subtree before the next layout pass if
143136
// the previous node with the same props and children has already been
144137
// configured.
@@ -159,8 +152,16 @@ YogaLayoutableShadowNode::YogaLayoutableShadowNode(
159152
ensureConsistency();
160153
}
161154

162-
void YogaLayoutableShadowNode::cleanLayout() {
163-
yogaNode_.setDirty(false);
155+
void YogaLayoutableShadowNode::completeClone(
156+
const ShadowNode& /*sourceShadowNode*/,
157+
const ShadowNodeFragment& fragment) {
158+
if (getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode) &&
159+
// New children means we must always dirty to visit. Otherwise, ask the
160+
// Node if the new revision invalidates measurement.
161+
(fragment.children ||
162+
shouldNewRevisionDirtyMeasurement(*this, fragment))) {
163+
yogaNode_.setDirty(true);
164+
}
164165
}
165166

166167
void YogaLayoutableShadowNode::dirtyLayout() {
@@ -317,6 +318,12 @@ bool YogaLayoutableShadowNode::doesOwn(
317318
return YGNodeGetOwner(&child.yogaNode_) == &yogaNode_;
318319
}
319320

321+
bool YogaLayoutableShadowNode::shouldNewRevisionDirtyMeasurement(
322+
const ShadowNode& /*sourceShadowNode*/,
323+
const ShadowNodeFragment& /*fragment*/) const {
324+
return true;
325+
}
326+
320327
void YogaLayoutableShadowNode::updateYogaChildrenOwnersIfNeeded() {
321328
for (auto& childYogaNode : yogaNode_.getChildren()) {
322329
if (YGNodeGetOwner(childYogaNode) == &yogaNode_) {

packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
3737
const ShadowNode& sourceShadowNode,
3838
const ShadowNodeFragment& fragment);
3939

40+
void completeClone(
41+
const ShadowNode& sourceShadowNode,
42+
const ShadowNodeFragment& fragment) override;
43+
4044
#pragma mark - Mutating Methods
4145

4246
/*
@@ -69,7 +73,6 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
6973

7074
#pragma mark - LayoutableShadowNode
7175

72-
void cleanLayout() override;
7376
void dirtyLayout() override;
7477
bool getIsLayoutClean() const override;
7578

@@ -86,6 +89,14 @@ class YogaLayoutableShadowNode : public LayoutableShadowNode {
8689
Rect getContentBounds() const;
8790

8891
protected:
92+
/**
93+
* Subclasses which provide MeasurableYogaNode may override to signal that a
94+
* new ShadowNode revision does not need to invalidate existing measurements.
95+
*/
96+
virtual bool shouldNewRevisionDirtyMeasurement(
97+
const ShadowNode& sourceShadowNode,
98+
const ShadowNodeFragment& fragment) const;
99+
89100
/*
90101
* Yoga config associated (only) with this particular node.
91102
*/

packages/react-native/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class ConcreteComponentDescriptor : public ComponentDescriptor {
8080
const ShadowNode& sourceShadowNode,
8181
const ShadowNodeFragment& fragment) const override {
8282
auto shadowNode = std::make_shared<ShadowNodeT>(sourceShadowNode, fragment);
83+
shadowNode->completeClone(sourceShadowNode, fragment);
8384
sourceShadowNode.transferRuntimeShadowNodeReference(shadowNode, fragment);
8485

8586
adopt(*shadowNode);

packages/react-native/ReactCommon/react/renderer/core/LayoutableShadowNode.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ class LayoutableShadowNode : public ShadowNode {
146146
* Indicates whether all nodes (and possibly their subtrees) along the path
147147
* to the root node should be re-laid out.
148148
*/
149-
virtual void cleanLayout() = 0;
150149
virtual void dirtyLayout() = 0;
151150
virtual bool getIsLayoutClean() const = 0;
152151

packages/react-native/ReactCommon/react/renderer/core/ShadowNode.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class ShadowNode : public Sealable,
9595
virtual ~ShadowNode() override = default;
9696

9797
/*
98-
* Clones the shadow node using stored `cloneFunction`.
98+
* Clones the shadow node using the ShadowNode's ComponentDescriptor.
9999
*/
100100
std::shared_ptr<ShadowNode> clone(const ShadowNodeFragment& fragment) const;
101101

@@ -124,6 +124,14 @@ class ShadowNode : public Sealable,
124124
const ShadowNode& oldShadowNode,
125125
const ShadowNodeFragment& fragment)>& callback) const;
126126

127+
/**
128+
* Called, once a fully derived ShadowNode clone has been created via
129+
* ComponentDescriptor::cloneShadowNode.
130+
*/
131+
virtual void completeClone(
132+
const ShadowNode& sourceShadowNode,
133+
const ShadowNodeFragment& fragment) {}
134+
127135
#pragma mark - Getters
128136

129137
ComponentName getComponentName() const;

0 commit comments

Comments
 (0)