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 }}
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'snapshot-artifacts'
|
||||
path: '~/.m2/repository/'
|
||||
|
@@ -23,7 +23,7 @@ jobs:
|
||||
ORG_GRADLE_PROJECT_USE_SNAPSHOT: true
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'snapshot-artifacts'
|
||||
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
|
||||
working-directory: javascript
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: npm-package
|
||||
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(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.
|
||||
|
||||
**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.
|
||||
|
||||
**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
|
||||
space around will result in space being distributed to the beginning of
|
||||
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
|
||||
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.
|
||||
|
@@ -90,3 +90,11 @@ float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge) {
|
||||
return getResolvedLayoutProperty<&LayoutResults::padding>(
|
||||
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 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
|
||||
|
@@ -106,25 +106,25 @@ void roundLayoutResultsToPixelGrid(
|
||||
const bool hasFractionalHeight =
|
||||
!yoga::inexactEquals(round(scaledNodeHeight), scaledNodeHeight);
|
||||
|
||||
node->setLayoutDimension(
|
||||
node->getLayout().setDimension(
|
||||
Dimension::Width,
|
||||
roundValueToPixelGrid(
|
||||
absoluteNodeRight,
|
||||
pointScaleFactor,
|
||||
(textRounding && hasFractionalWidth),
|
||||
(textRounding && !hasFractionalWidth)) -
|
||||
roundValueToPixelGrid(
|
||||
absoluteNodeLeft, pointScaleFactor, false, textRounding),
|
||||
Dimension::Width);
|
||||
absoluteNodeLeft, pointScaleFactor, false, textRounding));
|
||||
|
||||
node->setLayoutDimension(
|
||||
node->getLayout().setDimension(
|
||||
Dimension::Height,
|
||||
roundValueToPixelGrid(
|
||||
absoluteNodeBottom,
|
||||
pointScaleFactor,
|
||||
(textRounding && hasFractionalHeight),
|
||||
(textRounding && !hasFractionalHeight)) -
|
||||
roundValueToPixelGrid(
|
||||
absoluteNodeTop, pointScaleFactor, false, textRounding),
|
||||
Dimension::Height);
|
||||
absoluteNodeTop, pointScaleFactor, false, textRounding));
|
||||
}
|
||||
|
||||
for (yoga::Node* child : node->getChildren()) {
|
||||
|
@@ -27,7 +27,7 @@ enum class SizingMode {
|
||||
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
|
||||
* still fitting around its contents, i.e. minimizing unfilled space while
|
||||
* avoiding overflow.
|
||||
|
@@ -19,6 +19,7 @@ namespace facebook::yoga {
|
||||
#if defined(__cpp_exceptions)
|
||||
throw std::logic_error(message);
|
||||
#else
|
||||
static_cast<void>(message); // Unused
|
||||
std::terminate();
|
||||
#endif
|
||||
}
|
||||
|
@@ -36,12 +36,12 @@ enum struct LayoutPassReason : int {
|
||||
};
|
||||
|
||||
struct LayoutData {
|
||||
int layouts;
|
||||
int measures;
|
||||
uint32_t maxMeasureCache;
|
||||
int cachedLayouts;
|
||||
int cachedMeasures;
|
||||
int measureCallbacks;
|
||||
int layouts = 0;
|
||||
int measures = 0;
|
||||
uint32_t maxMeasureCache = 0;
|
||||
int cachedLayouts = 0;
|
||||
int cachedMeasures = 0;
|
||||
int measureCallbacks = 0;
|
||||
std::array<int, static_cast<uint8_t>(LayoutPassReason::COUNT)>
|
||||
measureCallbackReasonsCount;
|
||||
};
|
||||
|
@@ -66,10 +66,18 @@ struct LayoutResults {
|
||||
return measuredDimensions_[yoga::to_underlying(axis)];
|
||||
}
|
||||
|
||||
float rawDimension(Dimension axis) const {
|
||||
return rawDimensions_[yoga::to_underlying(axis)];
|
||||
}
|
||||
|
||||
void setMeasuredDimension(Dimension axis, float dimension) {
|
||||
measuredDimensions_[yoga::to_underlying(axis)] = dimension;
|
||||
}
|
||||
|
||||
void setRawDimension(Dimension axis, float dimension) {
|
||||
rawDimensions_[yoga::to_underlying(axis)] = dimension;
|
||||
}
|
||||
|
||||
float position(PhysicalEdge physicalEdge) const {
|
||||
return position_[yoga::to_underlying(physicalEdge)];
|
||||
}
|
||||
@@ -113,6 +121,7 @@ struct LayoutResults {
|
||||
|
||||
std::array<float, 2> dimensions_ = {{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> margin_ = {};
|
||||
std::array<float, 4> border_ = {};
|
||||
|
@@ -247,6 +247,7 @@ void Node::setLayoutHadOverflow(bool hadOverflow) {
|
||||
|
||||
void Node::setLayoutDimension(float lengthValue, Dimension dimension) {
|
||||
layout_.setDimension(dimension, lengthValue);
|
||||
layout_.setRawDimension(dimension, lengthValue);
|
||||
}
|
||||
|
||||
// If both left and right are defined, then use left. Otherwise return +left or
|
||||
|
Reference in New Issue
Block a user