From f6012c09928e4bb07cb06ba5b446367631c3a6ab Mon Sep 17 00:00:00 2001 From: TatianaKapos Date: Thu, 26 Oct 2023 13:33:58 -0700 Subject: [PATCH 1/5] add size animation --- .../CompositionSwitcher.idl | 5 ++-- .../Composition/CompositionContextHelper.cpp | 23 +++++++++++++--- .../Composition/SwitchComponentView.cpp | 26 +++++++++++++++---- .../Fabric/Composition/SwitchComponentView.h | 1 + 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl index 589617a7db7..f54f79094be 100644 --- a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl +++ b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl @@ -115,8 +115,9 @@ namespace Microsoft.ReactNative.Composition interface ISwitchThumbVisual { IVisual InnerVisual { get; }; - void Size(Windows.Foundation.Numerics.Vector2 size); - void Position(Windows.Foundation.Numerics.Vector2 position); + Windows.Foundation.Numerics.Vector2 Size{ get; set; }; + Windows.Foundation.Numerics.Vector2 Position{ get; set; }; + void AnimatePosition(Windows.Foundation.Numerics.Vector2 position); Boolean IsVisible { get; set; }; void Brush(IBrush brush); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp index 71efe139c0d..9994748d880 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp @@ -1114,12 +1114,26 @@ struct CompSwitchThumbVisual : winrt::implements< } void Size(winrt::Windows::Foundation::Numerics::float2 size) noexcept { - m_geometry.Radius(size); - m_spiritShape.Offset(size); - m_compVisual.Size(size); + m_size = size; + m_geometry.Radius(m_size); + m_spiritShape.Offset(m_size); + m_compVisual.Size(m_size); + } + + winrt::Windows::Foundation::Numerics::float2 Size() noexcept { + return m_size; } void Position(winrt::Windows::Foundation::Numerics::float2 position) noexcept { + m_pos = position; + m_compVisual.Offset({position.x, position.y, 0.0f}); + } + + winrt::Windows::Foundation::Numerics::float2 Position() noexcept { + return m_pos; + } + + void AnimatePosition(winrt::Windows::Foundation::Numerics::float2 position) noexcept { if (!isDrawn) { // we don't want to animate if this is the first time the switch is drawn on screen isDrawn = true; @@ -1132,6 +1146,7 @@ struct CompSwitchThumbVisual : winrt::implements< m_compVisual.StartAnimation(L"Offset", animation); } + m_pos = position; } bool IsVisible() const noexcept { @@ -1145,6 +1160,8 @@ struct CompSwitchThumbVisual : winrt::implements< bool isDrawn{false}; typename TTypeRedirects::SpriteVisual m_compVisual; winrt::Microsoft::ReactNative::Composition::IVisual m_visual; + winrt::Windows::Foundation::Numerics::float2 m_size; + winrt::Windows::Foundation::Numerics::float2 m_pos; typename TTypeRedirects::Compositor m_compositor{nullptr}; typename TTypeRedirects::CompositionSpriteShape m_spiritShape{nullptr}; typename TTypeRedirects::CompositionEllipseGeometry m_geometry{nullptr}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 678c5d56898..95c9ce0aacf 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -106,7 +106,7 @@ void SwitchComponentView::Draw() noexcept { float offsetX = static_cast(offset.x / m_layoutMetrics.pointScaleFactor); float offsetY = static_cast(offset.y / m_layoutMetrics.pointScaleFactor); - // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ToggleSwitch_themeresources.xaml + // https://github.com/microsoft/microsoft-ui-xaml/blob/winui2/main/dev/CommonStyles/ToggleSwitch_themeresources.xaml constexpr float thumbMargin = 3.0f; constexpr float thumbRadius = 7.0f; constexpr float trackWidth = 40.0f; @@ -203,9 +203,25 @@ void SwitchComponentView::Draw() noexcept { thumbX = (trackMarginX + trackWidth - thumbRadius - thumbRadius - thumbMargin) * m_layoutMetrics.pointScaleFactor; } - m_thumbVisual.Size( - {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); - m_thumbVisual.Position({thumbX, thumbY}); + // handles various mouse events + if (m_pressed && !switchProps->disabled) { + m_thumbVisual.AnimatePosition({thumbX - 0.8f, thumbY - 0.8f}); + } else if (m_hovered && !switchProps->disabled) { + m_thumbVisual.Size( + {thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f, + thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f}); + m_thumbVisual.Position({m_thumbVisual.Position().x - 0.8f, m_thumbVisual.Position().y - 0.8f}); + } else if (m_pointerReleased && !switchProps->disabled) { + m_pointerReleased = false; + m_thumbVisual.Size( + {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); + m_thumbVisual.Position({thumbX, thumbY}); + } else { // we still want to draw the thumb even if it's disabled + m_thumbVisual.Size( + {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); + m_thumbVisual.AnimatePosition({thumbX, thumbY}); + } + m_thumbVisual.Brush(thumbFill); // Restore old dpi setting @@ -301,7 +317,6 @@ void SwitchComponentView::onPointerReleased( if (!args.GetCurrentPoint(-1).Properties().IsPrimary()) { return; } - m_pressed = false; } @@ -314,6 +329,7 @@ void SwitchComponentView::onPointerEntered( void SwitchComponentView::onPointerExited( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = false; + m_pointerReleased = true; Draw(); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h index 432b785043d..c69551dd7cd 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h @@ -67,6 +67,7 @@ struct SwitchComponentView : CompositionBaseComponentView { bool m_hovered{false}; bool m_pressed{false}; + bool m_pointerReleased{false}; facebook::react::Size m_contentSize; winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr}; facebook::react::SharedViewProps m_props; From 73e3af39bb2a1f10d10c6d4e1998d0bf5a9cda80 Mon Sep 17 00:00:00 2001 From: TatianaKapos Date: Thu, 26 Oct 2023 13:42:25 -0700 Subject: [PATCH 2/5] Change files --- ...ative-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json diff --git a/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json b/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json new file mode 100644 index 00000000000..0c14a77d847 --- /dev/null +++ b/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Adds fabric switch thumb animation", + "packageName": "react-native-windows", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} From c8e1cbc0cefc01342bdaadcdd8b91ec72112e641 Mon Sep 17 00:00:00 2001 From: TatianaKapos Date: Thu, 26 Oct 2023 15:52:58 -0700 Subject: [PATCH 3/5] remove flag from Draw --- .../Fabric/Composition/SwitchComponentView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 95c9ce0aacf..43ac2a59827 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -212,7 +212,6 @@ void SwitchComponentView::Draw() noexcept { thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f}); m_thumbVisual.Position({m_thumbVisual.Position().x - 0.8f, m_thumbVisual.Position().y - 0.8f}); } else if (m_pointerReleased && !switchProps->disabled) { - m_pointerReleased = false; m_thumbVisual.Size( {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); m_thumbVisual.Position({thumbX, thumbY}); @@ -323,6 +322,7 @@ void SwitchComponentView::onPointerReleased( void SwitchComponentView::onPointerEntered( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = true; + m_pointerReleased = false; Draw(); } From 1572c32a97620d1d8724ef9ee36743807ce34ccd Mon Sep 17 00:00:00 2001 From: TatianaKapos Date: Thu, 26 Oct 2023 16:02:33 -0700 Subject: [PATCH 4/5] better variable name --- .../Fabric/Composition/SwitchComponentView.cpp | 6 +++--- .../Fabric/Composition/SwitchComponentView.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 43ac2a59827..67cdfeebc67 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -211,7 +211,7 @@ void SwitchComponentView::Draw() noexcept { {thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f, thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f}); m_thumbVisual.Position({m_thumbVisual.Position().x - 0.8f, m_thumbVisual.Position().y - 0.8f}); - } else if (m_pointerReleased && !switchProps->disabled) { + } else if (m_pointerExited && !switchProps->disabled) { m_thumbVisual.Size( {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); m_thumbVisual.Position({thumbX, thumbY}); @@ -322,14 +322,14 @@ void SwitchComponentView::onPointerReleased( void SwitchComponentView::onPointerEntered( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = true; - m_pointerReleased = false; + m_pointerExited = false; Draw(); } void SwitchComponentView::onPointerExited( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = false; - m_pointerReleased = true; + m_pointerExited = true; Draw(); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h index c69551dd7cd..26c8b1be0bd 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h @@ -67,7 +67,7 @@ struct SwitchComponentView : CompositionBaseComponentView { bool m_hovered{false}; bool m_pressed{false}; - bool m_pointerReleased{false}; + bool m_pointerExited{false}; facebook::react::Size m_contentSize; winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr}; facebook::react::SharedViewProps m_props; From e6a92379f8de6329ef64d4627688f560627897a5 Mon Sep 17 00:00:00 2001 From: TatianaKapos Date: Mon, 30 Oct 2023 14:39:28 -0700 Subject: [PATCH 5/5] handle position in composition --- .../Fabric/Composition/CompositionContextHelper.cpp | 7 ++++++- .../Fabric/Composition/SwitchComponentView.cpp | 9 +-------- .../Fabric/Composition/SwitchComponentView.h | 1 - 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp index 9994748d880..e959614d68b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp @@ -1114,6 +1114,11 @@ struct CompSwitchThumbVisual : winrt::implements< } void Size(winrt::Windows::Foundation::Numerics::float2 size) noexcept { + assert(m_size.x == m_size.y); + // if the size has changed, update the position to the new center + if (m_size.x != 0.0f && m_size.x != size.x) { + Position({m_pos.x + (m_size.x - size.x), m_pos.y + (m_size.x - size.x)}); + } m_size = size; m_geometry.Radius(m_size); m_spiritShape.Offset(m_size); @@ -1160,7 +1165,7 @@ struct CompSwitchThumbVisual : winrt::implements< bool isDrawn{false}; typename TTypeRedirects::SpriteVisual m_compVisual; winrt::Microsoft::ReactNative::Composition::IVisual m_visual; - winrt::Windows::Foundation::Numerics::float2 m_size; + winrt::Windows::Foundation::Numerics::float2 m_size{0.0f, 0.0f}; winrt::Windows::Foundation::Numerics::float2 m_pos; typename TTypeRedirects::Compositor m_compositor{nullptr}; typename TTypeRedirects::CompositionSpriteShape m_spiritShape{nullptr}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 67cdfeebc67..4f8ebfee34f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -210,12 +210,7 @@ void SwitchComponentView::Draw() noexcept { m_thumbVisual.Size( {thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f, thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f}); - m_thumbVisual.Position({m_thumbVisual.Position().x - 0.8f, m_thumbVisual.Position().y - 0.8f}); - } else if (m_pointerExited && !switchProps->disabled) { - m_thumbVisual.Size( - {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); - m_thumbVisual.Position({thumbX, thumbY}); - } else { // we still want to draw the thumb even if it's disabled + } else { m_thumbVisual.Size( {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); m_thumbVisual.AnimatePosition({thumbX, thumbY}); @@ -322,14 +317,12 @@ void SwitchComponentView::onPointerReleased( void SwitchComponentView::onPointerEntered( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = true; - m_pointerExited = false; Draw(); } void SwitchComponentView::onPointerExited( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { m_hovered = false; - m_pointerExited = true; Draw(); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h index 26c8b1be0bd..432b785043d 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h @@ -67,7 +67,6 @@ struct SwitchComponentView : CompositionBaseComponentView { bool m_hovered{false}; bool m_pressed{false}; - bool m_pointerExited{false}; facebook::react::Size m_contentSize; winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr}; facebook::react::SharedViewProps m_props;