From b74f502029b08987f55dce7183435780d847eaac Mon Sep 17 00:00:00 2001 From: Christian Falch Date: Sun, 14 Sep 2025 18:19:54 +0200 Subject: [PATCH] [fabric] Fixed updating font size after system change When updating accessibility text size there is an issue in the current code that would cut the tree enumeration and not update all the shadownodes in the tree - causing the layout in parent elements (of the text element) to not update its layout. This commit fixes this by simplifying the code a lot - we don't need any optimizations here - we can update the whole tree each time the system font size is changed. Tested in RN Tester and Expo Go (with the `enableFontScaleChangesUpdatingLayout` feature flag enabled) --- .../renderer/scheduler/SurfaceHandler.cpp | 75 ++----------------- .../react/renderer/scheduler/SurfaceHandler.h | 6 +- 2 files changed, 9 insertions(+), 72 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp index 2f8a69cf892..d368253aa20 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp @@ -233,75 +233,14 @@ Size SurfaceHandler::measure( return rootShadowNode->getLayoutMetrics().frame.size; } -std::shared_ptr SurfaceHandler::dirtyMeasurableNodesRecursive( - std::shared_ptr node) const { - const auto nodeHasChildren = !node->getChildren().empty(); - const auto isMeasurableYogaNode = - node->getTraits().check(ShadowNodeTraits::Trait::MeasurableYogaNode); - - // Node is not measurable and has no children, its layout will not be affected - if (!nodeHasChildren && !isMeasurableYogaNode) { - return nullptr; +void SurfaceHandler::dirtyMeasurableNodes(std::shared_ptr node) const { + if (auto* layoutableNode = dynamic_cast(node.get())) { + // Only cast away const where needed + const_cast(layoutableNode)->dirtyLayout(); } - ShadowNode::SharedListOfShared newChildren = - ShadowNodeFragment::childrenPlaceholder(); - - if (nodeHasChildren) { - std::shared_ptr>> - newChildrenMutable = nullptr; - for (size_t i = 0; i < node->getChildren().size(); i++) { - const auto& child = node->getChildren()[i]; - - if (const auto& layoutableNode = - std::dynamic_pointer_cast( - child)) { - auto newChild = dirtyMeasurableNodesRecursive(layoutableNode); - - if (newChild != nullptr) { - if (newChildrenMutable == nullptr) { - newChildrenMutable = std::make_shared< - std::vector>>( - node->getChildren()); - newChildren = newChildrenMutable; - } - - (*newChildrenMutable)[i] = newChild; - } - } - } - - // Node is not measurable and its children were not dirtied, its layout will - // not be affected - if (!isMeasurableYogaNode && newChildrenMutable == nullptr) { - return nullptr; - } - } - - const auto newNode = node->getComponentDescriptor().cloneShadowNode( - *node, - { - .children = newChildren, - // Preserve the original state of the node - .state = node->getState(), - }); - - if (isMeasurableYogaNode) { - std::static_pointer_cast(newNode)->dirtyLayout(); - } - - return newNode; -} - -void SurfaceHandler::dirtyMeasurableNodes(ShadowNode& root) const { - for (const auto& child : root.getChildren()) { - if (const auto& layoutableNode = - std::dynamic_pointer_cast(child)) { - const auto newChild = dirtyMeasurableNodesRecursive(layoutableNode); - if (newChild != nullptr) { - root.replaceChild(*child, newChild); - } - } + for (const auto& child : node->getChildren()) { + dirtyMeasurableNodes(child); } } @@ -344,7 +283,7 @@ void SurfaceHandler::constraintLayout( layoutContext.fontSizeMultiplier != oldRootShadowNode.getConcreteProps() .layoutContext.fontSizeMultiplier) { - dirtyMeasurableNodes(*newRoot); + dirtyMeasurableNodes(newRoot); } return newRoot; diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h index 9a4f0195eb0..69352013218 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h @@ -158,11 +158,9 @@ class SurfaceHandler { void applyDisplayMode(DisplayMode displayMode) const; /* - * An utility for dirtying all measurable shadow nodes present in the tree. + * Marks all measurable shadow nodes in the given tree as dirty */ - void dirtyMeasurableNodes(ShadowNode& root) const; - std::shared_ptr dirtyMeasurableNodesRecursive( - std::shared_ptr node) const; + void dirtyMeasurableNodes(std::shared_ptr node) const; #pragma mark - Link & Parameters