MCPcopy Create free account
hub / github.com/Snapchat/Valdi / updateScrollState

Method updateScrollState

valdi/src/valdi/runtime/Context/ViewNode.cpp:2448–2611  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

2446 forceLayout,
2447 /* isFromLazyLayout */ true);
2448
2449 _lazyLayoutData->availableWidth = _calculatedFrame.width;
2450 _lazyLayoutData->availableHeight = _calculatedFrame.height;
2451 return updated;
2452}
2453
2454void ViewNode::updateStickyHeaders(bool refreshCache) {
2455 if (_scrollState == nullptr || !_scrollState->getNativeStickyEnabled()) {
2456 return;
2457 }
2458 if (_viewNodeTree == nullptr) {
2459 return;
2460 }
2461
2462 float scrollY = _scrollState->getDirectionAgnosticContentOffset().y;
2463 // Pixels of visual overhang above sticky headers. Extends the effective header
2464 // height for the clamp so a header rendering a bar above its yoga bounds
2465 // (SectionList.stickyCover) stops sliding before the next section arrives.
2466 // Mirrors SectionList.tsx's `headerHeight = getHeaderHeight() + coverHeight`.
2467 float stickyCover = _scrollState->getNativeStickyCover();
2468 // Pixels below scroll viewport top where headers pin. Matches CSS `top: N`.
2469 // Effectively shifts the pin position down so headers aren't clipped behind
2470 // a floating page header with visual footprint past its Yoga bounds.
2471 float stickyOffset = _scrollState->getNativeStickyOffset();
2472
2473 // We push translationY updates through the attribute-set path so both the C++ state
2474 // and the platform-side transform binder fire in the same frame. withLock acquires
2475 // the tree mutex (recursive -- safe if already held on the Android scroll path) and
2476 // opens a ViewTransactionScope. Nested calls piggyback on the outer transaction; a
2477 // top-level call submits at end-of-scope, pushing transforms in the same VSYNC as
2478 // the scroll gesture.
2479 _viewNodeTree->withLock([&]() {
2480 auto& scope = _viewNodeTree->getCurrentViewTransactionScope();
2481
2482 // Bounded-depth subtree walk. Header nesting in SubscreenSections is deep:
2483 // scroll -> PullToRefresh -> SubscreenContent -> paddingRight layout ->
2484 // SectionList root -> SectionListItem root -> column-reverse layout -> header
2485 // (~8 levels). 16 covers that plus any consumer wrapping without unbounded cost.
2486 // Skip into nested scrolls: each scroll owns its own sticky pass.
2487 static constexpr int kStickyMaxDepth = 16;
2488 auto* owner = AttributeOwner::getNativeOverridenAttributeOwner();
2489 std::function<void(ViewNode*, int)> walk = [&](ViewNode* node, int depth) {
2490 if (depth <= 0) {
2491 return;
2492 }
2493 for (auto* child : *node) {
2494 if (child->getStickyPosition() == StickyPositionTop) {
2495 if (refreshCache) {
2496 auto* parent = child->getParent().get();
2497 // parent Y relative to this scroll node (sum yoga Top edges up the chain).
2498 float parentY = 0.0f;
2499 float parentH = 0.0f;
2500 if (parent == this) {
2501 // Direct child of the scroll: no parent section to clamp against.
2502 // Treat the sticky track as unbounded so the header stays pinned once
2503 // scrolled past its natural Y, matching CSS `position: sticky`
2504 // semantics for elements whose containing block is the scroll itself.
2505 parentH = std::numeric_limits<float>::max();

Callers

nothing calls this directly

Calls 15

resolveYogaNodeFunction · 0.85
ygNodeGetFrameFunction · 0.85
sanitizeYogaValueFunction · 0.85
minFunction · 0.85
maxFunction · 0.85
refreshPreserveAnchorFunction · 0.85
setInScrollModeMethod · 0.80
hadOverflowMethod · 0.80
marginMethod · 0.80
paddingMethod · 0.80

Tested by

no test coverage detected