Files
yoga/yoga/YGNode.cpp
Nick Gerleman a121483e81 Use CSS terminology for sizing rules (#1460)
Summary:
X-link: https://github.com/facebook/react-native/pull/41390

Pull Request resolved: https://github.com/facebook/yoga/pull/1460

Yoga passes `MeasureMode`/`YGMeasureMode` to express constraints in how a box should be measured, given definite or indefinite available space.

This is modeled after Android [MeasureSpec](https://developer.android.com/reference/android/view/View.MeasureSpec), with a table above `calculateLayoutImpl()` explaining the CSS terms they map to. This can be confusing when flipping between the spec, and code.

This switches internal usages to the CSS terms, but leaves around `YGMeasureMode` since it is the public API passed to measure functions.

Reviewed By: joevilches

Differential Revision: D51068417

fbshipit-source-id: 0a76266a4e7e0cc39996164607229c3c41de2818
2023-11-25 20:41:22 -08:00

368 lines
9.9 KiB
C++

/*
* 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 <yoga/Yoga.h>
#include <yoga/algorithm/Cache.h>
#include <yoga/algorithm/CalculateLayout.h>
#include <yoga/debug/AssertFatal.h>
#include <yoga/debug/Log.h>
#include <yoga/debug/NodeToString.h>
#include <yoga/event/event.h>
#include <yoga/node/Node.h>
using namespace facebook;
using namespace facebook::yoga;
YGNodeRef YGNodeNew(void) {
return YGNodeNewWithConfig(YGConfigGetDefault());
}
YGNodeRef YGNodeNewWithConfig(const YGConfigConstRef config) {
auto* node = new yoga::Node{resolveRef(config)};
yoga::assertFatal(
config != nullptr, "Tried to construct YGNode with null config");
Event::publish<Event::NodeAllocation>(node, {config});
return node;
}
YGNodeRef YGNodeClone(YGNodeConstRef oldNodeRef) {
auto oldNode = resolveRef(oldNodeRef);
const auto node = new yoga::Node(*oldNode);
Event::publish<Event::NodeAllocation>(node, {node->getConfig()});
node->setOwner(nullptr);
return node;
}
void YGNodeFree(const YGNodeRef nodeRef) {
const auto node = resolveRef(nodeRef);
if (auto owner = node->getOwner()) {
owner->removeChild(node);
node->setOwner(nullptr);
}
const size_t childCount = node->getChildCount();
for (size_t i = 0; i < childCount; i++) {
auto child = node->getChild(i);
child->setOwner(nullptr);
}
node->clearChildren();
Event::publish<Event::NodeDeallocation>(node, {YGNodeGetConfig(node)});
delete resolveRef(node);
}
void YGNodeFreeRecursive(YGNodeRef rootRef) {
const auto root = resolveRef(rootRef);
size_t skipped = 0;
while (root->getChildCount() > skipped) {
const auto child = root->getChild(skipped);
if (child->getOwner() != root) {
// Don't free shared nodes that we don't own.
skipped += 1;
} else {
YGNodeRemoveChild(root, child);
YGNodeFreeRecursive(child);
}
}
YGNodeFree(root);
}
void YGNodeFinalize(const YGNodeRef node) {
Event::publish<Event::NodeDeallocation>(node, {YGNodeGetConfig(node)});
delete resolveRef(node);
}
void YGNodeReset(YGNodeRef node) {
resolveRef(node)->reset();
}
void YGNodeCalculateLayout(
const YGNodeRef node,
const float ownerWidth,
const float ownerHeight,
const YGDirection ownerDirection) {
yoga::calculateLayout(
resolveRef(node), ownerWidth, ownerHeight, scopedEnum(ownerDirection));
}
bool YGNodeGetHasNewLayout(YGNodeConstRef node) {
return resolveRef(node)->getHasNewLayout();
}
void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
resolveRef(node)->setHasNewLayout(hasNewLayout);
}
bool YGNodeIsDirty(YGNodeConstRef node) {
return resolveRef(node)->isDirty();
}
void YGNodeMarkDirty(const YGNodeRef nodeRef) {
const auto node = resolveRef(nodeRef);
yoga::assertFatalWithNode(
node,
node->hasMeasureFunc(),
"Only leaf nodes with custom measure functions "
"should manually mark themselves as dirty");
node->markDirtyAndPropagate();
}
void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) {
resolveRef(node)->setDirtiedFunc(dirtiedFunc);
}
YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeConstRef node) {
return resolveRef(node)->getDirtiedFunc();
}
void YGNodeInsertChild(
const YGNodeRef ownerRef,
const YGNodeRef childRef,
const size_t index) {
auto owner = resolveRef(ownerRef);
auto child = resolveRef(childRef);
yoga::assertFatalWithNode(
owner,
child->getOwner() == nullptr,
"Child already has a owner, it must be removed first.");
yoga::assertFatalWithNode(
owner,
!owner->hasMeasureFunc(),
"Cannot add child: Nodes with measure functions cannot have children.");
owner->insertChild(child, index);
child->setOwner(owner);
owner->markDirtyAndPropagate();
}
void YGNodeSwapChild(
const YGNodeRef ownerRef,
const YGNodeRef childRef,
const size_t index) {
auto owner = resolveRef(ownerRef);
auto child = resolveRef(childRef);
owner->replaceChild(child, index);
child->setOwner(owner);
}
void YGNodeRemoveChild(
const YGNodeRef ownerRef,
const YGNodeRef excludedChildRef) {
auto owner = resolveRef(ownerRef);
auto excludedChild = resolveRef(excludedChildRef);
if (owner->getChildCount() == 0) {
// This is an empty set. Nothing to remove.
return;
}
// Children may be shared between parents, which is indicated by not having an
// owner. We only want to reset the child completely if it is owned
// exclusively by one node.
auto childOwner = excludedChild->getOwner();
if (owner->removeChild(excludedChild)) {
if (owner == childOwner) {
excludedChild->setLayout({}); // layout is no longer valid
excludedChild->setOwner(nullptr);
}
owner->markDirtyAndPropagate();
}
}
void YGNodeRemoveAllChildren(const YGNodeRef ownerRef) {
auto owner = resolveRef(ownerRef);
const size_t childCount = owner->getChildCount();
if (childCount == 0) {
// This is an empty set already. Nothing to do.
return;
}
auto* firstChild = owner->getChild(0);
if (firstChild->getOwner() == owner) {
// If the first child has this node as its owner, we assume that this child
// set is unique.
for (size_t i = 0; i < childCount; i++) {
yoga::Node* oldChild = owner->getChild(i);
oldChild->setLayout({}); // layout is no longer valid
oldChild->setOwner(nullptr);
}
owner->clearChildren();
owner->markDirtyAndPropagate();
return;
}
// Otherwise, we are not the owner of the child set. We don't have to do
// anything to clear it.
owner->setChildren({});
owner->markDirtyAndPropagate();
}
void YGNodeSetChildren(
const YGNodeRef ownerRef,
const YGNodeRef* childrenRefs,
const size_t count) {
auto owner = resolveRef(ownerRef);
auto children = reinterpret_cast<yoga::Node* const*>(childrenRefs);
if (!owner) {
return;
}
const std::vector<yoga::Node*> childrenVector = {children, children + count};
if (childrenVector.size() == 0) {
if (owner->getChildCount() > 0) {
for (auto* child : owner->getChildren()) {
child->setLayout({});
child->setOwner(nullptr);
}
owner->setChildren({});
owner->markDirtyAndPropagate();
}
} else {
if (owner->getChildCount() > 0) {
for (auto* oldChild : owner->getChildren()) {
// Our new children may have nodes in common with the old children. We
// don't reset these common nodes.
if (std::find(childrenVector.begin(), childrenVector.end(), oldChild) ==
childrenVector.end()) {
oldChild->setLayout({});
oldChild->setOwner(nullptr);
}
}
}
owner->setChildren(childrenVector);
for (yoga::Node* child : childrenVector) {
child->setOwner(owner);
}
owner->markDirtyAndPropagate();
}
}
YGNodeRef YGNodeGetChild(const YGNodeRef nodeRef, const size_t index) {
const auto node = resolveRef(nodeRef);
if (index < node->getChildren().size()) {
return node->getChild(index);
}
return nullptr;
}
size_t YGNodeGetChildCount(const YGNodeConstRef node) {
return resolveRef(node)->getChildren().size();
}
YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
return resolveRef(node)->getOwner();
}
YGNodeRef YGNodeGetParent(const YGNodeRef node) {
return resolveRef(node)->getOwner();
}
void YGNodeSetConfig(YGNodeRef node, YGConfigRef config) {
resolveRef(node)->setConfig(resolveRef(config));
}
YGConfigConstRef YGNodeGetConfig(YGNodeRef node) {
return resolveRef(node)->getConfig();
}
void YGNodeSetContext(YGNodeRef node, void* context) {
return resolveRef(node)->setContext(context);
}
void* YGNodeGetContext(YGNodeConstRef node) {
return resolveRef(node)->getContext();
}
void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
resolveRef(node)->setMeasureFunc(measureFunc);
}
bool YGNodeHasMeasureFunc(YGNodeConstRef node) {
return resolveRef(node)->hasMeasureFunc();
}
void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) {
resolveRef(node)->setBaselineFunc(baselineFunc);
}
bool YGNodeHasBaselineFunc(YGNodeConstRef node) {
return resolveRef(node)->hasBaselineFunc();
}
void YGNodeSetIsReferenceBaseline(YGNodeRef nodeRef, bool isReferenceBaseline) {
const auto node = resolveRef(nodeRef);
if (node->isReferenceBaseline() != isReferenceBaseline) {
node->setIsReferenceBaseline(isReferenceBaseline);
node->markDirtyAndPropagate();
}
}
bool YGNodeIsReferenceBaseline(YGNodeConstRef node) {
return resolveRef(node)->isReferenceBaseline();
}
void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
return resolveRef(node)->setNodeType(scopedEnum(nodeType));
}
YGNodeType YGNodeGetNodeType(YGNodeConstRef node) {
return unscopedEnum(resolveRef(node)->getNodeType());
}
void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
resolveRef(node)->setPrintFunc(printFunc);
}
#ifdef DEBUG
void YGNodePrint(const YGNodeConstRef node, const YGPrintOptions options) {
yoga::print(resolveRef(node), scopedEnum(options));
}
#endif
// TODO: This leaks internal details to the public API. Remove after removing
// ComponentKit usage of it.
bool YGNodeCanUseCachedMeasurement(
YGMeasureMode widthMode,
float availableWidth,
YGMeasureMode heightMode,
float availableHeight,
YGMeasureMode lastWidthMode,
float lastAvailableWidth,
YGMeasureMode lastHeightMode,
float lastAvailableHeight,
float lastComputedWidth,
float lastComputedHeight,
float marginRow,
float marginColumn,
YGConfigRef config) {
return yoga::canUseCachedMeasurement(
sizingMode(scopedEnum(widthMode)),
availableWidth,
sizingMode(scopedEnum(heightMode)),
availableHeight,
sizingMode(scopedEnum(lastWidthMode)),
lastAvailableWidth,
sizingMode(scopedEnum(lastHeightMode)),
lastAvailableHeight,
lastComputedWidth,
lastComputedHeight,
marginRow,
marginColumn,
resolveRef(config));
}