diff --git a/tests/YGDirtyMarkingTest.cpp b/tests/YGDirtyMarkingTest.cpp index b7da4fcaae..5a50b8deae 100644 --- a/tests/YGDirtyMarkingTest.cpp +++ b/tests/YGDirtyMarkingTest.cpp @@ -249,3 +249,58 @@ TEST(YogaTest, dirty_node_only_if_undefined_values_gets_set_to_undefined) { YGNodeFreeRecursive(root); } + +TEST(YogaTest, dirty_removed_child_node) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + YGNodeRef child = YGNodeNew(); + YGNodeStyleSetWidth(child, 50); + YGNodeStyleSetHeight(child, 50); + YGNodeInsertChild(root, child, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FALSE(YGNodeIsDirty(child)); + + YGNodeRemoveChild(root, child); + + // Child should be marked dirty after removal so layout is recalculated + // when the child is reused (e.g., in a recycling view system) + EXPECT_TRUE(YGNodeIsDirty(child)); + + YGNodeFree(child); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dirty_removed_child_nodes_when_removing_all) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + YGNodeRef child0 = YGNodeNew(); + YGNodeStyleSetWidth(child0, 50); + YGNodeStyleSetHeight(child0, 25); + YGNodeInsertChild(root, child0, 0); + + YGNodeRef child1 = YGNodeNew(); + YGNodeStyleSetWidth(child1, 50); + YGNodeStyleSetHeight(child1, 25); + YGNodeInsertChild(root, child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FALSE(YGNodeIsDirty(child0)); + EXPECT_FALSE(YGNodeIsDirty(child1)); + + YGNodeRemoveAllChildren(root); + + // All children should be marked dirty after removal + EXPECT_TRUE(YGNodeIsDirty(child0)); + EXPECT_TRUE(YGNodeIsDirty(child1)); + + YGNodeFree(child0); + YGNodeFree(child1); + YGNodeFreeRecursive(root); +} diff --git a/tests/YGNodeChildTest.cpp b/tests/YGNodeChildTest.cpp index b61c1322fe..0b7148dd52 100644 --- a/tests/YGNodeChildTest.cpp +++ b/tests/YGNodeChildTest.cpp @@ -33,3 +33,36 @@ TEST(YogaTest, reset_layout_when_child_removed) { YGNodeFreeRecursive(root); YGNodeFreeRecursive(root_child0); } + +TEST(YogaTest, removed_child_can_be_reused_with_valid_layout) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef child = YGNodeNew(); + YGNodeStyleSetWidth(child, 100); + YGNodeStyleSetHeight(child, 100); + YGNodeInsertChild(root, child, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(child)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(child)); + + // Remove child - layout should be cleared and child marked dirty + YGNodeRemoveChild(root, child); + + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetWidth(child))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetHeight(child))); + ASSERT_TRUE(YGNodeIsDirty(child)); + + // Reinsert the child and recalculate - layout should be valid again + YGNodeInsertChild(root, child, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(child)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(child)); + ASSERT_FALSE(YGNodeIsDirty(child)); + + YGNodeFreeRecursive(root); +} diff --git a/yoga/YGNode.cpp b/yoga/YGNode.cpp index a34685eee6..9903478391 100644 --- a/yoga/YGNode.cpp +++ b/yoga/YGNode.cpp @@ -177,6 +177,7 @@ void YGNodeRemoveChild( if (owner == childOwner) { excludedChild->setLayout({}); // layout is no longer valid excludedChild->setOwner(nullptr); + excludedChild->setDirty(true); // invalidate cache } owner->markDirtyAndPropagate(); } @@ -198,6 +199,7 @@ void YGNodeRemoveAllChildren(const YGNodeRef ownerRef) { yoga::Node* oldChild = owner->getChild(i); oldChild->setLayout({}); // layout is no longer valid oldChild->setOwner(nullptr); + oldChild->setDirty(true); // invalidate cache } owner->clearChildren(); owner->markDirtyAndPropagate();