Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Implement onMomentumScrollEnd and onMomentumScrollBegin for Fabric ScrollView",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
6 changes: 6 additions & 0 deletions packages/playground/Samples/scrollViewSnapSample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ export default class Bootstrap extends React.Component<{}, any> {
onScrollEndDrag={() => {
console.log('onScrollEndDrag');
}}
onMomentumScrollBegin={() => {
console.log('onMomentumScrollBegin');
}}
onMomentumScrollEnd={() => {
console.log('onMomentumScrollEnd');
}}
onScroll={() => {
console.log('onScroll');
}}
Expand Down
2 changes: 2 additions & 0 deletions vnext/Microsoft.ReactNative/CompositionSwitcher.idl
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ namespace Microsoft.ReactNative.Composition.Experimental
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollPositionChanged;
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollBeginDrag;
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollEndDrag;
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumBegin;
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollMomentumEnd;
void ContentSize(Windows.Foundation.Numerics.Vector2 size);
Windows.Foundation.Numerics.Vector3 ScrollPosition { get; };
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,24 +711,50 @@ struct CompScrollerVisual : winrt::implements<
void IdleStateEntered(
typename TTypeRedirects::InteractionTracker sender,
typename TTypeRedirects::InteractionTrackerIdleStateEnteredArgs args) noexcept {
// If we were in inertia and are now idle, momentum has ended
if (m_outer->m_inertia) {
m_outer->FireScrollMomentumEnd({sender.Position().x, sender.Position().y});
}

// If we were interacting but never entered inertia (Interacting -> Idle),
// and the interaction was user-driven (requestId == 0), fire end-drag here.
// Note: if the interactionRequestId was non-zero it was caused by a Try* call
// (programmatic), so we should not fire onScrollEndDrag.
if (m_outer->m_interacting && args.RequestId() == 0) {
m_outer->FireScrollEndDrag({sender.Position().x, sender.Position().y});
}

// Clear state flags
m_outer->m_custom = false;
m_outer->m_inertia = false;
m_outer->m_interacting = false;
}
void InertiaStateEntered(
typename TTypeRedirects::InteractionTracker sender,
typename TTypeRedirects::InteractionTrackerInertiaStateEnteredArgs args) noexcept {
m_outer->m_custom = false;
m_outer->m_inertia = true;
m_outer->m_currentPosition = args.NaturalRestingPosition();
// When the user stops interacting with the object, tracker can go into two paths:
// 1. tracker goes into idle state immediately
// 2. tracker has just started gliding into Inertia state
// Fire ScrollEndDrag
m_outer->FireScrollEndDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});

if (!m_outer->m_interacting && args.RequestId() == 0) {
m_outer->FireScrollBeginDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
}

// If interaction was user-driven (requestId == 0),
// fire ScrollEndDrag here (Interacting -> Inertia caused by user lift).
if (m_outer->m_interacting && args.RequestId() == 0) {
m_outer->FireScrollEndDrag({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
}

// Fire momentum scroll begin when we enter inertia (user or programmatic)
m_outer->FireScrollMomentumBegin({args.NaturalRestingPosition().x, args.NaturalRestingPosition().y});
}
void InteractingStateEntered(
typename TTypeRedirects::InteractionTracker sender,
typename TTypeRedirects::InteractionTrackerInteractingStateEnteredArgs args) noexcept {
// Mark that we're now interacting and remember the requestId (user manipulations => 0)
m_outer->m_interacting = true;

// Fire when the user starts dragging the object
m_outer->FireScrollBeginDrag({sender.Position().x, sender.Position().y});
}
Expand All @@ -738,6 +764,10 @@ struct CompScrollerVisual : winrt::implements<
void ValuesChanged(
typename TTypeRedirects::InteractionTracker sender,
typename TTypeRedirects::InteractionTrackerValuesChangedArgs args) noexcept {
if (!m_outer->m_interacting && args.RequestId() == 0) {
m_outer->FireScrollBeginDrag({args.Position().x, args.Position().y});
}
m_outer->m_interacting = true;
m_outer->m_currentPosition = args.Position();
m_outer->FireScrollPositionChanged({args.Position().x, args.Position().y});
}
Expand Down Expand Up @@ -811,7 +841,7 @@ struct CompScrollerVisual : winrt::implements<
m_horizontal ? TTypeRedirects::InteractionSourceMode::Disabled
: TTypeRedirects::InteractionSourceMode::EnabledWithInertia);
m_visualInteractionSource.ManipulationRedirectionMode(
TTypeRedirects::VisualInteractionSourceRedirectionMode::CapableTouchpadOnly);
TTypeRedirects::VisualInteractionSourceRedirectionMode::CapableTouchpadAndPointerWheel);
} else {
m_visualInteractionSource.PositionXSourceMode(TTypeRedirects::InteractionSourceMode::Disabled);
m_visualInteractionSource.PositionYSourceMode(TTypeRedirects::InteractionSourceMode::Disabled);
Expand Down Expand Up @@ -985,6 +1015,20 @@ struct CompScrollerVisual : winrt::implements<
return m_scrollEndDragEvent.add(handler);
}

winrt::event_token ScrollMomentumBegin(
winrt::Windows::Foundation::EventHandler<
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs> const
&handler) noexcept {
return m_scrollMomentumBeginEvent.add(handler);
}

winrt::event_token ScrollMomentumEnd(
winrt::Windows::Foundation::EventHandler<
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs> const
&handler) noexcept {
return m_scrollMomentumEndEvent.add(handler);
}

void ScrollPositionChanged(winrt::event_token const &token) noexcept {
m_scrollPositionChangedEvent.remove(token);
}
Expand All @@ -997,6 +1041,14 @@ struct CompScrollerVisual : winrt::implements<
m_scrollEndDragEvent.remove(token);
}

void ScrollMomentumBegin(winrt::event_token const &token) noexcept {
m_scrollMomentumBeginEvent.remove(token);
}

void ScrollMomentumEnd(winrt::event_token const &token) noexcept {
m_scrollMomentumEndEvent.remove(token);
}

void ContentSize(winrt::Windows::Foundation::Numerics::float2 const &size) noexcept {
m_contentSize = size;
m_contentVisual.Size(size);
Expand Down Expand Up @@ -1075,6 +1127,14 @@ struct CompScrollerVisual : winrt::implements<
m_scrollEndDragEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
}

void FireScrollMomentumBegin(winrt::Windows::Foundation::Numerics::float2 position) noexcept {
m_scrollMomentumBeginEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
}

void FireScrollMomentumEnd(winrt::Windows::Foundation::Numerics::float2 position) noexcept {
m_scrollMomentumEndEvent(*this, winrt::make<CompScrollPositionChangedArgs>(position));
}

void UpdateMaxPosition() noexcept {
m_interactionTracker.MaxPosition(
{std::max<float>(m_contentSize.x - m_visualSize.x, 0),
Expand Down Expand Up @@ -1250,6 +1310,7 @@ struct CompScrollerVisual : winrt::implements<
SnapAlignment m_snapToAlignment{SnapAlignment::Start};
bool m_inertia{false};
bool m_custom{false};
bool m_interacting{false};
winrt::Windows::Foundation::Numerics::float3 m_targetPosition;
winrt::Windows::Foundation::Numerics::float3 m_currentPosition;
winrt::Windows::Foundation::Numerics::float2 m_contentSize{0};
Expand All @@ -1263,6 +1324,12 @@ struct CompScrollerVisual : winrt::implements<
winrt::event<winrt::Windows::Foundation::EventHandler<
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
m_scrollEndDragEvent;
winrt::event<winrt::Windows::Foundation::EventHandler<
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
m_scrollMomentumBeginEvent;
winrt::event<winrt::Windows::Foundation::EventHandler<
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs>>
m_scrollMomentumEndEvent;
typename TTypeRedirects::SpriteVisual m_visual{nullptr};
typename TTypeRedirects::SpriteVisual m_contentVisual{nullptr};
typename TTypeRedirects::InteractionTracker m_interactionTracker{nullptr};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,32 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
}
});

m_scrollMomentumBeginRevoker = m_scrollVisual.ScrollMomentumBegin(
winrt::auto_revoke,
[this](
winrt::IInspectable const & /*sender*/,
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
auto scrollMetrics = getScrollMetrics(eventEmitter, args);
std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const>(eventEmitter)
->onMomentumScrollBegin(scrollMetrics);
}
});

m_scrollMomentumEndRevoker = m_scrollVisual.ScrollMomentumEnd(
winrt::auto_revoke,
[this](
winrt::IInspectable const & /*sender*/,
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
auto scrollMetrics = getScrollMetrics(eventEmitter, args);
std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const>(eventEmitter)
->onMomentumScrollEnd(scrollMetrics);
}
});

return visual;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,12 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
m_scrollPositionChangedRevoker{};
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollBeginDrag_revoker
m_scrollBeginDragRevoker{};

winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollEndDrag_revoker
m_scrollEndDragRevoker{};
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollMomentumBegin_revoker
m_scrollMomentumBeginRevoker{};
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollVisual::ScrollMomentumEnd_revoker
m_scrollMomentumEndRevoker{};

float m_zoomFactor{1.0f};
bool m_isScrollingFromInertia = false;
Expand Down
Loading