Compare commits
9 Commits
export-D70
...
potato520h
Author | SHA1 | Date | |
---|---|---|---|
|
b7421b1270 | ||
|
1232761571 | ||
|
c935fd5e10 | ||
|
624325302c | ||
|
37a94a86de | ||
|
4abc1a7d5f | ||
|
51e6095005 | ||
|
79cef614ce | ||
|
6455a848a7 |
@@ -24,7 +24,7 @@ jobs:
|
|||||||
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
|
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
|
||||||
|
|
||||||
- name: Upload Build Artifacts
|
- name: Upload Build Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: 'snapshot-artifacts'
|
name: 'snapshot-artifacts'
|
||||||
path: '~/.m2/repository/'
|
path: '~/.m2/repository/'
|
||||||
|
@@ -23,7 +23,7 @@ jobs:
|
|||||||
ORG_GRADLE_PROJECT_USE_SNAPSHOT: true
|
ORG_GRADLE_PROJECT_USE_SNAPSHOT: true
|
||||||
|
|
||||||
- name: Upload Build Artifacts
|
- name: Upload Build Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: 'snapshot-artifacts'
|
name: 'snapshot-artifacts'
|
||||||
path: '~/.m2/repository/'
|
path: '~/.m2/repository/'
|
||||||
|
2
.github/workflows/validate-js.yml
vendored
2
.github/workflows/validate-js.yml
vendored
@@ -110,7 +110,7 @@ jobs:
|
|||||||
run: yarn pack --filename yoga-layout.tar.gz
|
run: yarn pack --filename yoga-layout.tar.gz
|
||||||
working-directory: javascript
|
working-directory: javascript
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: npm-package
|
name: npm-package
|
||||||
path: javascript/yoga-layout.tar.gz
|
path: javascript/yoga-layout.tar.gz
|
||||||
|
184
tests/YGPersistentNodeCloningTest.cpp
Normal file
184
tests/YGPersistentNodeCloningTest.cpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* 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 <gtest/gtest.h>
|
||||||
|
#include <yoga/Yoga.h>
|
||||||
|
#include <yoga/config/Config.h>
|
||||||
|
#include <yoga/node/Node.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace facebook::yoga {
|
||||||
|
|
||||||
|
struct YGPersistentNodeCloningTest : public ::testing::Test {
|
||||||
|
struct NodeWrapper {
|
||||||
|
explicit NodeWrapper(
|
||||||
|
YGConfigRef config,
|
||||||
|
std::vector<std::shared_ptr<NodeWrapper>> children = {})
|
||||||
|
: node{YGNodeNewWithConfig(config)}, children{std::move(children)} {
|
||||||
|
YGNodeSetContext(node, this);
|
||||||
|
|
||||||
|
auto privateNode = resolveRef(node);
|
||||||
|
for (const auto& child : this->children) {
|
||||||
|
auto privateChild = resolveRef(child->node);
|
||||||
|
// Claim first ownership of not yet owned nodes, to avoid immediately
|
||||||
|
// cloning them
|
||||||
|
if (YGNodeGetOwner(child->node) == nullptr) {
|
||||||
|
privateChild->setOwner(privateNode);
|
||||||
|
}
|
||||||
|
privateNode->insertChild(privateChild, privateNode->getChildCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone, with current children, for mutation
|
||||||
|
NodeWrapper(const NodeWrapper& other)
|
||||||
|
: node{YGNodeClone(other.node)}, children{other.children} {
|
||||||
|
YGNodeSetContext(node, this);
|
||||||
|
|
||||||
|
auto privateNode = resolveRef(node);
|
||||||
|
privateNode->setOwner(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone, with new children
|
||||||
|
NodeWrapper(
|
||||||
|
const NodeWrapper& other,
|
||||||
|
std::vector<std::shared_ptr<NodeWrapper>> children)
|
||||||
|
: node{YGNodeClone(other.node)}, children{std::move(children)} {
|
||||||
|
YGNodeSetContext(node, this);
|
||||||
|
|
||||||
|
auto privateNode = resolveRef(node);
|
||||||
|
privateNode->setOwner(nullptr);
|
||||||
|
privateNode->setChildren({});
|
||||||
|
privateNode->setDirty(true);
|
||||||
|
|
||||||
|
for (const auto& child : this->children) {
|
||||||
|
auto privateChild = resolveRef(child->node);
|
||||||
|
// Claim first ownership of not yet owned nodes, to avoid immediately
|
||||||
|
// cloning them
|
||||||
|
if (YGNodeGetOwner(child->node) == nullptr) {
|
||||||
|
privateChild->setOwner(privateNode);
|
||||||
|
}
|
||||||
|
privateNode->insertChild(privateChild, privateNode->getChildCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeWrapper(NodeWrapper&&) = delete;
|
||||||
|
|
||||||
|
~NodeWrapper() {
|
||||||
|
YGNodeFree(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeWrapper& operator=(const NodeWrapper& other) = delete;
|
||||||
|
NodeWrapper& operator=(NodeWrapper&& other) = delete;
|
||||||
|
|
||||||
|
YGNodeRef node;
|
||||||
|
std::vector<std::shared_ptr<NodeWrapper>> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConfigWrapper {
|
||||||
|
ConfigWrapper() {
|
||||||
|
YGConfigSetCloneNodeFunc(
|
||||||
|
config,
|
||||||
|
[](YGNodeConstRef oldNode, YGNodeConstRef owner, size_t childIndex) {
|
||||||
|
onClone(oldNode, owner, childIndex);
|
||||||
|
auto wrapper = static_cast<NodeWrapper*>(YGNodeGetContext(owner));
|
||||||
|
auto old = static_cast<NodeWrapper*>(YGNodeGetContext(oldNode));
|
||||||
|
|
||||||
|
wrapper->children[childIndex] = std::make_shared<NodeWrapper>(*old);
|
||||||
|
return wrapper->children[childIndex]->node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigWrapper(const ConfigWrapper&) = delete;
|
||||||
|
ConfigWrapper(ConfigWrapper&&) = delete;
|
||||||
|
|
||||||
|
~ConfigWrapper() {
|
||||||
|
YGConfigFree(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigWrapper& operator=(const ConfigWrapper&) = delete;
|
||||||
|
ConfigWrapper& operator=(ConfigWrapper&&) = delete;
|
||||||
|
|
||||||
|
YGConfigRef config{YGConfigNew()};
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigWrapper configWrapper;
|
||||||
|
YGConfigRef config{configWrapper.config};
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
onClone = [](...) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static inline std::function<void(YGNodeConstRef, YGNodeConstRef, size_t)>
|
||||||
|
onClone;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(
|
||||||
|
YGPersistentNodeCloningTest,
|
||||||
|
changing_sibling_height_does_not_clone_neighbors) {
|
||||||
|
// <ScrollView>
|
||||||
|
// <View id="Sibling" style={{ height: 1 }} />
|
||||||
|
// <View id="A" style={{ height: 1 }}>
|
||||||
|
// <View id="B">
|
||||||
|
// <View id="C">
|
||||||
|
// <View id="D"/>
|
||||||
|
// </View>
|
||||||
|
// </View>
|
||||||
|
// </View>
|
||||||
|
// </ScrollView>
|
||||||
|
|
||||||
|
auto sibling = std::make_shared<NodeWrapper>(config);
|
||||||
|
YGNodeStyleSetHeight(sibling->node, 1);
|
||||||
|
|
||||||
|
auto d = std::make_shared<NodeWrapper>(config);
|
||||||
|
auto c = std::make_shared<NodeWrapper>(config, std::vector{d});
|
||||||
|
auto b = std::make_shared<NodeWrapper>(config, std::vector{c});
|
||||||
|
auto a = std::make_shared<NodeWrapper>(config, std::vector{b});
|
||||||
|
YGNodeStyleSetHeight(a->node, 1);
|
||||||
|
|
||||||
|
auto scrollContentView =
|
||||||
|
std::make_shared<NodeWrapper>(config, std::vector{sibling, a});
|
||||||
|
YGNodeStyleSetPositionType(scrollContentView->node, YGPositionTypeAbsolute);
|
||||||
|
|
||||||
|
auto scrollView =
|
||||||
|
std::make_shared<NodeWrapper>(config, std::vector{scrollContentView});
|
||||||
|
YGNodeStyleSetWidth(scrollView->node, 100);
|
||||||
|
YGNodeStyleSetHeight(scrollView->node, 100);
|
||||||
|
|
||||||
|
// We don't expect any cloning during the first layout
|
||||||
|
onClone = [](...) { FAIL(); };
|
||||||
|
|
||||||
|
YGNodeCalculateLayout(
|
||||||
|
scrollView->node, YGUndefined, YGUndefined, YGDirectionLTR);
|
||||||
|
|
||||||
|
auto siblingPrime = std::make_shared<NodeWrapper>(config);
|
||||||
|
YGNodeStyleSetHeight(siblingPrime->node, 2);
|
||||||
|
|
||||||
|
auto scrollContentViewPrime = std::make_shared<NodeWrapper>(
|
||||||
|
*scrollContentView, std::vector{siblingPrime, a});
|
||||||
|
auto scrollViewPrime = std::make_shared<NodeWrapper>(
|
||||||
|
*scrollView, std::vector{scrollContentViewPrime});
|
||||||
|
|
||||||
|
std::vector<NodeWrapper*> nodesCloned;
|
||||||
|
// We should only need to clone "A"
|
||||||
|
onClone = [&](YGNodeConstRef oldNode,
|
||||||
|
YGNodeConstRef /*owner*/,
|
||||||
|
size_t /*childIndex*/) {
|
||||||
|
nodesCloned.push_back(static_cast<NodeWrapper*>(YGNodeGetContext(oldNode)));
|
||||||
|
};
|
||||||
|
|
||||||
|
YGNodeCalculateLayout(
|
||||||
|
scrollViewPrime->node, YGUndefined, YGUndefined, YGDirectionLTR);
|
||||||
|
|
||||||
|
EXPECT_EQ(nodesCloned.size(), 1);
|
||||||
|
EXPECT_EQ(nodesCloned[0], a.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace facebook::yoga
|
@@ -161,3 +161,23 @@ TEST(YogaTest, per_node_point_scale_factor) {
|
|||||||
YGConfigFree(config2);
|
YGConfigFree(config2);
|
||||||
YGConfigFree(config3);
|
YGConfigFree(config3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(YogaTest, raw_layout_dimensions) {
|
||||||
|
YGConfigRef config = YGConfigNew();
|
||||||
|
YGConfigSetPointScaleFactor(config, 0.5f);
|
||||||
|
|
||||||
|
YGNodeRef root = YGNodeNewWithConfig(config);
|
||||||
|
YGNodeStyleSetWidth(root, 11.5f);
|
||||||
|
YGNodeStyleSetHeight(root, 9.5f);
|
||||||
|
|
||||||
|
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
|
||||||
|
|
||||||
|
ASSERT_EQ(YGNodeLayoutGetWidth(root), 12.0f);
|
||||||
|
ASSERT_EQ(YGNodeLayoutGetHeight(root), 10.0f);
|
||||||
|
ASSERT_EQ(YGNodeLayoutGetRawWidth(root), 11.5f);
|
||||||
|
ASSERT_EQ(YGNodeLayoutGetRawHeight(root), 9.5f);
|
||||||
|
|
||||||
|
YGNodeFreeRecursive(root);
|
||||||
|
|
||||||
|
YGConfigFree(config);
|
||||||
|
}
|
||||||
|
@@ -17,15 +17,15 @@ has effect when items are wrapped to multiple lines using [flex wrap](/docs/styl
|
|||||||
|
|
||||||
**Center**: Align wrapped lines in the center of the container's cross axis.
|
**Center**: Align wrapped lines in the center of the container's cross axis.
|
||||||
|
|
||||||
**Space between**: Evenly space wrapped lines across the container's main axis, distributing
|
**Space between**: Evenly space wrapped lines across the container's cross axis, distributing
|
||||||
remaining space between the lines.
|
remaining space between the lines.
|
||||||
|
|
||||||
**Space around**: Evenly space wrapped lines across the container's main axis, distributing
|
**Space around**: Evenly space wrapped lines across the container's cross axis, distributing
|
||||||
remaining space around the lines. Compared to space between using
|
remaining space around the lines. Compared to space between using
|
||||||
space around will result in space being distributed to the beginning of
|
space around will result in space being distributed to the beginning of
|
||||||
the first lines and end of the last line.
|
the first lines and end of the last line.
|
||||||
|
|
||||||
**Space evenly**: Evenly space wrapped lines across the container's main axis, distributing
|
**Space evenly**: Evenly space wrapped lines across the container's cross axis, distributing
|
||||||
remaining space around the lines. Compared to space around, space evenly will not
|
remaining space around the lines. Compared to space around, space evenly will not
|
||||||
double the gaps between children. The size of gaps between children and between
|
double the gaps between children. The size of gaps between children and between
|
||||||
the parent's edges and the first/last child will all be equal.
|
the parent's edges and the first/last child will all be equal.
|
||||||
|
@@ -90,3 +90,11 @@ float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge) {
|
|||||||
return getResolvedLayoutProperty<&LayoutResults::padding>(
|
return getResolvedLayoutProperty<&LayoutResults::padding>(
|
||||||
node, scopedEnum(edge));
|
node, scopedEnum(edge));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float YGNodeLayoutGetRawHeight(YGNodeConstRef node) {
|
||||||
|
return resolveRef(node)->getLayout().rawDimension(Dimension::Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
float YGNodeLayoutGetRawWidth(YGNodeConstRef node) {
|
||||||
|
return resolveRef(node)->getLayout().rawDimension(Dimension::Width);
|
||||||
|
}
|
||||||
|
@@ -32,4 +32,14 @@ YG_EXPORT float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge);
|
|||||||
YG_EXPORT float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge);
|
YG_EXPORT float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge);
|
||||||
YG_EXPORT float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge);
|
YG_EXPORT float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the measured height of the node, before layout rounding
|
||||||
|
*/
|
||||||
|
YG_EXPORT float YGNodeLayoutGetRawHeight(YGNodeConstRef node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the measured width of the node, before layout rounding
|
||||||
|
*/
|
||||||
|
YG_EXPORT float YGNodeLayoutGetRawWidth(YGNodeConstRef node);
|
||||||
|
|
||||||
YG_EXTERN_C_END
|
YG_EXTERN_C_END
|
||||||
|
@@ -106,25 +106,25 @@ void roundLayoutResultsToPixelGrid(
|
|||||||
const bool hasFractionalHeight =
|
const bool hasFractionalHeight =
|
||||||
!yoga::inexactEquals(round(scaledNodeHeight), scaledNodeHeight);
|
!yoga::inexactEquals(round(scaledNodeHeight), scaledNodeHeight);
|
||||||
|
|
||||||
node->setLayoutDimension(
|
node->getLayout().setDimension(
|
||||||
|
Dimension::Width,
|
||||||
roundValueToPixelGrid(
|
roundValueToPixelGrid(
|
||||||
absoluteNodeRight,
|
absoluteNodeRight,
|
||||||
pointScaleFactor,
|
pointScaleFactor,
|
||||||
(textRounding && hasFractionalWidth),
|
(textRounding && hasFractionalWidth),
|
||||||
(textRounding && !hasFractionalWidth)) -
|
(textRounding && !hasFractionalWidth)) -
|
||||||
roundValueToPixelGrid(
|
roundValueToPixelGrid(
|
||||||
absoluteNodeLeft, pointScaleFactor, false, textRounding),
|
absoluteNodeLeft, pointScaleFactor, false, textRounding));
|
||||||
Dimension::Width);
|
|
||||||
|
|
||||||
node->setLayoutDimension(
|
node->getLayout().setDimension(
|
||||||
|
Dimension::Height,
|
||||||
roundValueToPixelGrid(
|
roundValueToPixelGrid(
|
||||||
absoluteNodeBottom,
|
absoluteNodeBottom,
|
||||||
pointScaleFactor,
|
pointScaleFactor,
|
||||||
(textRounding && hasFractionalHeight),
|
(textRounding && hasFractionalHeight),
|
||||||
(textRounding && !hasFractionalHeight)) -
|
(textRounding && !hasFractionalHeight)) -
|
||||||
roundValueToPixelGrid(
|
roundValueToPixelGrid(
|
||||||
absoluteNodeTop, pointScaleFactor, false, textRounding),
|
absoluteNodeTop, pointScaleFactor, false, textRounding));
|
||||||
Dimension::Height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (yoga::Node* child : node->getChildren()) {
|
for (yoga::Node* child : node->getChildren()) {
|
||||||
|
@@ -27,7 +27,7 @@ enum class SizingMode {
|
|||||||
StretchFit,
|
StretchFit,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A box’s “ideal” size in a given axis when given infinite available space.
|
* A box's "ideal" size in a given axis when given infinite available space.
|
||||||
* Usually this is the smallest size the box could take in that axis while
|
* Usually this is the smallest size the box could take in that axis while
|
||||||
* still fitting around its contents, i.e. minimizing unfilled space while
|
* still fitting around its contents, i.e. minimizing unfilled space while
|
||||||
* avoiding overflow.
|
* avoiding overflow.
|
||||||
|
@@ -19,6 +19,7 @@ namespace facebook::yoga {
|
|||||||
#if defined(__cpp_exceptions)
|
#if defined(__cpp_exceptions)
|
||||||
throw std::logic_error(message);
|
throw std::logic_error(message);
|
||||||
#else
|
#else
|
||||||
|
static_cast<void>(message); // Unused
|
||||||
std::terminate();
|
std::terminate();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -36,12 +36,12 @@ enum struct LayoutPassReason : int {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct LayoutData {
|
struct LayoutData {
|
||||||
int layouts;
|
int layouts = 0;
|
||||||
int measures;
|
int measures = 0;
|
||||||
uint32_t maxMeasureCache;
|
uint32_t maxMeasureCache = 0;
|
||||||
int cachedLayouts;
|
int cachedLayouts = 0;
|
||||||
int cachedMeasures;
|
int cachedMeasures = 0;
|
||||||
int measureCallbacks;
|
int measureCallbacks = 0;
|
||||||
std::array<int, static_cast<uint8_t>(LayoutPassReason::COUNT)>
|
std::array<int, static_cast<uint8_t>(LayoutPassReason::COUNT)>
|
||||||
measureCallbackReasonsCount;
|
measureCallbackReasonsCount;
|
||||||
};
|
};
|
||||||
|
@@ -66,10 +66,18 @@ struct LayoutResults {
|
|||||||
return measuredDimensions_[yoga::to_underlying(axis)];
|
return measuredDimensions_[yoga::to_underlying(axis)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float rawDimension(Dimension axis) const {
|
||||||
|
return rawDimensions_[yoga::to_underlying(axis)];
|
||||||
|
}
|
||||||
|
|
||||||
void setMeasuredDimension(Dimension axis, float dimension) {
|
void setMeasuredDimension(Dimension axis, float dimension) {
|
||||||
measuredDimensions_[yoga::to_underlying(axis)] = dimension;
|
measuredDimensions_[yoga::to_underlying(axis)] = dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setRawDimension(Dimension axis, float dimension) {
|
||||||
|
rawDimensions_[yoga::to_underlying(axis)] = dimension;
|
||||||
|
}
|
||||||
|
|
||||||
float position(PhysicalEdge physicalEdge) const {
|
float position(PhysicalEdge physicalEdge) const {
|
||||||
return position_[yoga::to_underlying(physicalEdge)];
|
return position_[yoga::to_underlying(physicalEdge)];
|
||||||
}
|
}
|
||||||
@@ -113,6 +121,7 @@ struct LayoutResults {
|
|||||||
|
|
||||||
std::array<float, 2> dimensions_ = {{YGUndefined, YGUndefined}};
|
std::array<float, 2> dimensions_ = {{YGUndefined, YGUndefined}};
|
||||||
std::array<float, 2> measuredDimensions_ = {{YGUndefined, YGUndefined}};
|
std::array<float, 2> measuredDimensions_ = {{YGUndefined, YGUndefined}};
|
||||||
|
std::array<float, 2> rawDimensions_ = {{YGUndefined, YGUndefined}};
|
||||||
std::array<float, 4> position_ = {};
|
std::array<float, 4> position_ = {};
|
||||||
std::array<float, 4> margin_ = {};
|
std::array<float, 4> margin_ = {};
|
||||||
std::array<float, 4> border_ = {};
|
std::array<float, 4> border_ = {};
|
||||||
|
@@ -247,6 +247,7 @@ void Node::setLayoutHadOverflow(bool hadOverflow) {
|
|||||||
|
|
||||||
void Node::setLayoutDimension(float lengthValue, Dimension dimension) {
|
void Node::setLayoutDimension(float lengthValue, Dimension dimension) {
|
||||||
layout_.setDimension(dimension, lengthValue);
|
layout_.setDimension(dimension, lengthValue);
|
||||||
|
layout_.setRawDimension(dimension, lengthValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both left and right are defined, then use left. Otherwise return +left or
|
// If both left and right are defined, then use left. Otherwise return +left or
|
||||||
|
Reference in New Issue
Block a user