Skip to content

Commit c8ef8e3

Browse files
authored
[Fabric] Add keyboard handlers to ScrollView (#12190)
* [Fabric] Add keyboard handlers to ScrollView * Change files * format * Fix scrollwheeling while scrollwheel animation in progress not adding to exising target position * fix
1 parent c380c1a commit c8ef8e3

9 files changed

+219
-66
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Add keyboard handlers to ScrollView",
4+
"packageName": "react-native-windows",
5+
"email": "30809111+acoates-ms@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/CompositionSwitcher.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ namespace Microsoft.ReactNative.Composition
8888
event Windows.Foundation.EventHandler<IScrollPositionChangedArgs> ScrollPositionChanged;
8989
void ContentSize(Windows.Foundation.Numerics.Vector2 size);
9090
Windows.Foundation.Numerics.Vector3 ScrollPosition { get; };
91-
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset);
91+
void ScrollBy(Windows.Foundation.Numerics.Vector3 offset, Boolean animate);
9292
void TryUpdatePosition(Windows.Foundation.Numerics.Vector3 position, Boolean animate);
9393
}
9494

vnext/Microsoft.ReactNative/Fabric/ComponentView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct IComponentView {
7575
virtual void onKeyUp(
7676
const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source,
7777
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept = 0;
78+
virtual bool ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept = 0;
7879
virtual bool focusable() const noexcept = 0;
7980
virtual facebook::react::SharedViewEventEmitter eventEmitterAtPoint(facebook::react::Point pt) noexcept = 0;
8081
virtual facebook::react::SharedViewEventEmitter eventEmitter() noexcept = 0;

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct CompositionTypeTraits<WindowsTypeTag> {
3434
using AnimationDelayBehavior = winrt::Windows::UI::Composition::AnimationDelayBehavior;
3535
using AnimationDirection = winrt::Windows::UI::Composition::AnimationDirection;
3636
using AnimationIterationBehavior = winrt::Windows::UI::Composition::AnimationIterationBehavior;
37+
using CompositionAnimation = winrt::Windows::UI::Composition::CompositionAnimation;
3738
using CompositionBackfaceVisibility = winrt::Windows::UI::Composition::CompositionBackfaceVisibility;
3839
using CompositionBrush = winrt::Windows::UI::Composition::CompositionBrush;
3940
using CompositionDrawingSurface = winrt::Windows::UI::Composition::CompositionDrawingSurface;
@@ -89,6 +90,7 @@ struct CompositionTypeTraits<MicrosoftTypeTag> {
8990
using AnimationDelayBehavior = winrt::Microsoft::UI::Composition::AnimationDelayBehavior;
9091
using AnimationDirection = winrt::Microsoft::UI::Composition::AnimationDirection;
9192
using AnimationIterationBehavior = winrt::Microsoft::UI::Composition::AnimationIterationBehavior;
93+
using CompositionAnimation = winrt::Microsoft::UI::Composition::CompositionAnimation;
9294
using CompositionBackfaceVisibility = winrt::Microsoft::UI::Composition::CompositionBackfaceVisibility;
9395
using CompositionBrush = winrt::Microsoft::UI::Composition::CompositionBrush;
9496
using CompositionDrawingSurface = winrt::Microsoft::UI::Composition::CompositionDrawingSurface;
@@ -536,13 +538,22 @@ struct CompScrollerVisual : winrt::implements<
536538

537539
void CustomAnimationStateEntered(
538540
typename TTypeRedirects::InteractionTracker sender,
539-
typename TTypeRedirects::InteractionTrackerCustomAnimationStateEnteredArgs args) noexcept {}
541+
typename TTypeRedirects::InteractionTrackerCustomAnimationStateEnteredArgs args) noexcept {
542+
m_outer->m_custom = true;
543+
m_outer->m_inertia = false;
544+
}
540545
void IdleStateEntered(
541546
typename TTypeRedirects::InteractionTracker sender,
542-
typename TTypeRedirects::InteractionTrackerIdleStateEnteredArgs args) noexcept {}
547+
typename TTypeRedirects::InteractionTrackerIdleStateEnteredArgs args) noexcept {
548+
m_outer->m_custom = false;
549+
m_outer->m_inertia = false;
550+
}
543551
void InertiaStateEntered(
544552
typename TTypeRedirects::InteractionTracker sender,
545-
typename TTypeRedirects::InteractionTrackerInertiaStateEnteredArgs args) noexcept {}
553+
typename TTypeRedirects::InteractionTrackerInertiaStateEnteredArgs args) noexcept {
554+
m_outer->m_custom = false;
555+
m_outer->m_inertia = true;
556+
}
546557
void InteractingStateEntered(
547558
typename TTypeRedirects::InteractionTracker sender,
548559
typename TTypeRedirects::InteractionTrackerInteractingStateEnteredArgs args) noexcept {}
@@ -552,6 +563,7 @@ struct CompScrollerVisual : winrt::implements<
552563
void ValuesChanged(
553564
typename TTypeRedirects::InteractionTracker sender,
554565
typename TTypeRedirects::InteractionTrackerValuesChangedArgs args) noexcept {
566+
m_outer->m_currentPosition = args.Position();
555567
m_outer->FireScrollPositionChanged({args.Position().x, args.Position().y});
556568
}
557569

@@ -715,20 +727,54 @@ struct CompScrollerVisual : winrt::implements<
715727
return m_interactionTracker.Position();
716728
}
717729

718-
void ScrollBy(winrt::Windows::Foundation::Numerics::float3 const &offset) noexcept {
719-
m_interactionTracker.TryUpdatePositionBy(offset);
730+
// ChangeOffsets scrolling constants
731+
static constexpr int64_t s_offsetsChangeMsPerUnit{5};
732+
static constexpr int64_t s_offsetsChangeMinMs{50};
733+
static constexpr int64_t s_offsetsChangeMaxMs{1000};
734+
735+
typename TTypeRedirects::CompositionAnimation GetPositionAnimation(float x, float y) {
736+
const int64_t distance =
737+
static_cast<int64_t>(std::sqrt(std::pow(x - m_currentPosition.x, 2.0f) + pow(y - m_currentPosition.y, 2.0f)));
738+
auto compositor = m_visual.Compositor();
739+
auto positionAnimation = compositor.CreateVector3KeyFrameAnimation();
740+
741+
positionAnimation.InsertKeyFrame(1.0f, {x, y, 0.0f});
742+
positionAnimation.Duration(std::chrono::milliseconds(
743+
std::clamp(distance * s_offsetsChangeMsPerUnit, s_offsetsChangeMinMs, s_offsetsChangeMaxMs)));
744+
745+
return positionAnimation;
746+
}
747+
748+
void ScrollBy(winrt::Windows::Foundation::Numerics::float3 const &offset, bool animate) noexcept {
749+
auto restingPosition = m_inertia ? m_interactionTracker.NaturalRestingPosition() : m_interactionTracker.Position();
750+
if (m_custom) {
751+
restingPosition = m_targetPosition;
752+
}
753+
if (animate) {
754+
auto maxPosition = m_interactionTracker.MaxPosition();
755+
m_custom = true;
756+
m_targetPosition = {
757+
std::clamp(restingPosition.x + offset.x, 0.0f, maxPosition.x),
758+
std::clamp(restingPosition.y + offset.y, 0.0f, maxPosition.y),
759+
std::clamp(restingPosition.z + offset.z, 0.0f, maxPosition.z)};
760+
761+
auto kfa = GetPositionAnimation(m_targetPosition.x, m_targetPosition.y);
762+
m_interactionTracker.TryUpdatePositionWithAnimation(kfa);
763+
} else {
764+
m_interactionTracker.TryUpdatePositionBy(offset);
765+
}
720766
};
721767

722768
void TryUpdatePosition(winrt::Windows::Foundation::Numerics::float3 const &position, bool animate) noexcept {
769+
auto maxPosition = m_interactionTracker.MaxPosition();
723770
if (animate) {
724-
auto compositor = m_visual.Compositor();
725-
auto cubicBezier = compositor.CreateCubicBezierEasingFunction({0.17f, 0.67f}, {1.0f, 1.0f});
726-
auto kfa = compositor.CreateVector3KeyFrameAnimation();
727-
kfa.Duration(std::chrono::seconds{1});
728-
kfa.InsertKeyFrame(1.0f, position, cubicBezier);
771+
auto kfa = GetPositionAnimation(std::min(maxPosition.x, position.x), std::min(maxPosition.y, position.y));
729772
m_interactionTracker.TryUpdatePositionWithAnimation(kfa);
730773
} else {
731-
m_interactionTracker.TryUpdatePosition(position);
774+
m_interactionTracker.TryUpdatePosition(
775+
{std::min(maxPosition.x, position.x),
776+
std::min(maxPosition.y, position.y),
777+
std::min(maxPosition.z, position.z)});
732778
}
733779
}
734780

@@ -744,6 +790,10 @@ struct CompScrollerVisual : winrt::implements<
744790
0});
745791
}
746792

793+
bool m_inertia{false};
794+
bool m_custom{false};
795+
winrt::Windows::Foundation::Numerics::float3 m_targetPosition;
796+
winrt::Windows::Foundation::Numerics::float3 m_currentPosition;
747797
winrt::Windows::Foundation::Numerics::float2 m_contentSize{0};
748798
winrt::Windows::Foundation::Numerics::float2 m_visualSize{0};
749799
winrt::event<

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,18 @@ void CompositionEventHandler::ScrollWheel(facebook::react::Point pt, uint32_t de
155155
::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) {
156156
facebook::react::Point ptLocal;
157157

158-
RootComponentView().ScrollWheel(
159-
{static_cast<float>(pt.x / m_compRootView.ScaleFactor()),
160-
static_cast<float>(pt.y / m_compRootView.ScaleFactor())},
161-
delta);
158+
facebook::react::Point ptScaled = {
159+
static_cast<float>(pt.x / m_compRootView.ScaleFactor()),
160+
static_cast<float>(pt.y / m_compRootView.ScaleFactor())};
161+
auto tag = RootComponentView().hitTest(ptScaled, ptLocal);
162+
163+
if (tag == -1) {
164+
return;
165+
}
166+
167+
IComponentView *targetComponentView =
168+
fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view.get();
169+
static_cast<CompositionBaseComponentView *>(targetComponentView)->ScrollWheel(ptLocal, delta);
162170
}
163171
}
164172

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ const facebook::react::SharedViewEventEmitter &CompositionBaseComponentView::Get
176176
}
177177

178178
bool CompositionBaseComponentView::ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept {
179+
if (m_parent) {
180+
pt.x += m_layoutMetrics.frame.origin.x * m_layoutMetrics.pointScaleFactor;
181+
pt.y += m_layoutMetrics.frame.origin.y * m_layoutMetrics.pointScaleFactor;
182+
return m_parent->ScrollWheel(pt, delta);
183+
}
179184
return false;
180185
}
181186

@@ -1381,19 +1386,6 @@ facebook::react::Tag CompositionViewComponentView::hitTest(
13811386
return -1;
13821387
}
13831388

1384-
bool CompositionViewComponentView::ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept {
1385-
facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y};
1386-
1387-
facebook::react::Tag tag;
1388-
if (std::any_of(m_children.rbegin(), m_children.rend(), [ptLocal, delta](auto child) {
1389-
return const_cast<CompositionBaseComponentView *>(static_cast<const CompositionBaseComponentView *>(child))
1390-
->ScrollWheel(ptLocal, delta);
1391-
}))
1392-
return true;
1393-
1394-
return false;
1395-
}
1396-
13971389
void CompositionViewComponentView::onKeyDown(
13981390
const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source,
13991391
const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept {

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct CompositionBaseComponentView : public IComponentView,
4949
facebook::react::Tag tag() const noexcept override;
5050
int64_t sendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept override;
5151

52-
virtual bool ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept;
52+
bool ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept override;
5353
RECT getClientRect() const noexcept override;
5454

5555
void indexOffsetForBorder(uint32_t &index) const noexcept;
@@ -148,7 +148,6 @@ struct CompositionViewComponentView : public CompositionBaseComponentView {
148148
facebook::react::Point pt,
149149
facebook::react::Point &localPt,
150150
bool ignorePointerEvents = false) const noexcept override;
151-
bool ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept override;
152151

153152
winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override;
154153

0 commit comments

Comments
 (0)