未验证 提交 e8ad72bf 编写于 作者: X Xiaotian Gu 提交者: GitHub

Merge pull request #12054 from unoplatform/dev/xygu/20230418/toggleswitch

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.UI.RuntimeTests.Helpers;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
using static Private.Infrastructure.TestServices;
namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Media_Animation;
[TestClass]
[RunsOnUIThread]
public class Given_DoubleAnimationUsingKeyFrames
{
[TestMethod]
public async Task When_Quickly_Transitions()
{
// We are mimicking Fluent-style ToggleSwitch's ToggleStates here.
var sut = new TestPages.QuickMultiTransitionsPage();
WindowHelper.WindowContent = sut;
await WindowHelper.WaitForLoaded(sut);
await Task.Delay(1000);
// Quickly play through multiple visual-states to simulate what happens when tapping(*1) on a ToggleSwitch.
/* *1: The full tapping experience is: [InitialState: Off]->Dragging->Off->On or [InitialState: A]->B->A->C
* where A=Off, B=Dragging, C=Off
* SwitchKnobOn.Opacity ToggleStates\AOff->COn 1 @[ControlFasterAnimationDuration] f=Linear
* SwitchKnobOn.Opacity ToggleStates\BDragging->AOff 0 @[ControlFasterAnimationDuration] f=Linear
* SwitchKnobOn.Opacity ToggleStates\BDragging->COn 1 @[ControlFasterAnimationDuration] f=Linear
* SwitchKnobOn.Opacity ToggleStates\COn->AOff 0 @[ControlFasterAnimationDuration] f=Linear
* SwitchKnobOn.Opacity ToggleStates\COn->BDragging 1 @0 f=Linear
* SwitchKnobOn.Opacity ToggleStates\COn 1 @[ControlFasterAnimationDuration] f=Linear
*/
VisualStateManager.GoToState(sut, TestPages.QuickMultiTransitionsPage.TestStateNames.PhaseB, useTransitions: true);
VisualStateManager.GoToState(sut, TestPages.QuickMultiTransitionsPage.TestStateNames.PhaseA, useTransitions: true);
VisualStateManager.GoToState(sut, TestPages.QuickMultiTransitionsPage.TestStateNames.PhaseC, useTransitions: true);
await WindowHelper.WaitForIdle();
await Task.Delay(1000);
// Given that it involves a race condition, we use a matrix of 4x4 to boost the failure rate.
// If everything went smoothly, the end result should be an opacity of 1 for all the borders.
var total = sut.RootGrid.Children.OfType<Border>().Count();
var passed = sut.RootGrid.Children.OfType<Border>()
.Count(x => x.Opacity == 1.0);
Assert.AreEqual(total, passed, $"Only {passed} of {total} Border.Opacity is at expected value of 1.0");
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Media_Animation.TestPages;
public sealed partial class QuickMultiTransitionsPage : Page
{
public static class TestStateNames
{
public const string PhaseA = nameof(PhaseA);
public const string PhaseB = nameof(PhaseB);
public const string PhaseC = nameof(PhaseC);
}
public QuickMultiTransitionsPage()
{
this.InitializeComponent();
this.Loaded += (s, e) => VisualStateManager.GoToState(this, TestStateNames.PhaseA, useTransitions: true);
}
}
......@@ -15,6 +15,8 @@ namespace Windows.UI.Xaml.Media.Animation
partial class ColorAnimationUsingKeyFrames : Timeline, ITimeline
{
private readonly Stopwatch _activeDuration = new Stopwatch();
private bool _wasBeginScheduled;
private bool _wasRequestedToStop;
private int _replayCount = 1;
private ColorOffset? _startingValue;
private ColorOffset _finalValue;
......@@ -79,7 +81,6 @@ namespace Windows.UI.Xaml.Media.Animation
return base.GetCalculatedDuration();
}
bool _wasBeginScheduled;
void ITimeline.Begin()
{
if (!_wasBeginScheduled)
......@@ -87,7 +88,9 @@ namespace Windows.UI.Xaml.Media.Animation
// We dispatch the begin so that we can use bindings on ColorKeyFrame.Value from RelativeParent.
// This works because the template bindings are executed just after the constructor.
// WARNING: This does not allow us to bind ColorKeyFrame.Value with ViewModel properties.
_wasBeginScheduled = true;
_wasRequestedToStop = false;
#if !NET461
#if __ANDROID__
......@@ -97,14 +100,17 @@ namespace Windows.UI.Xaml.Media.Animation
#endif
#endif
{
if (KeyFrames.Count < 1)
_wasBeginScheduled = false;
if (KeyFrames.Count < 1 || // nothing to do
_wasRequestedToStop // was requested to stop, between Begin() and dispatched here
)
{
return; // nothing to do
return;
}
PropertyInfo?.CloneShareableObjectsInPath();
_wasBeginScheduled = false;
_activeDuration.Restart();
_replayCount = 1;
......@@ -207,7 +213,9 @@ namespace Windows.UI.Xaml.Media.Animation
_currentAnimator.Cancel();//Stop the animator if it is running
_startingValue = null;
}
State = TimelineState.Stopped;
_wasRequestedToStop = true;
}
void ITimeline.Stop()
......@@ -215,7 +223,9 @@ namespace Windows.UI.Xaml.Media.Animation
_currentAnimator?.Cancel(); // stop could be called before the initialization
_startingValue = null;
ClearValue();
State = TimelineState.Stopped;
_wasRequestedToStop = true;
}
/// <summary>
......
......@@ -15,6 +15,8 @@ namespace Windows.UI.Xaml.Media.Animation
public partial class DoubleAnimationUsingKeyFrames : Timeline, ITimeline
{
private readonly Stopwatch _activeDuration = new Stopwatch();
private bool _wasBeginScheduled;
private bool _wasRequestedToStop;
private int _replayCount = 1;
private double? _startingValue;
private double _finalValue;
......@@ -55,7 +57,6 @@ namespace Windows.UI.Xaml.Media.Animation
return base.GetCalculatedDuration();
}
bool _wasBeginScheduled;
void ITimeline.Begin()
{
if (!_wasBeginScheduled)
......@@ -63,7 +64,10 @@ namespace Windows.UI.Xaml.Media.Animation
// We dispatch the begin so that we can use bindings on DoubleKeyFrame.Value from RelativeParent.
// This works because the template bindings are executed just after the constructor.
// WARNING: This does not allow us to bind DoubleKeyFrame.Value with ViewModel properties.
_wasBeginScheduled = true;
_wasRequestedToStop = false;
#if !NET461
#if __ANDROID__
_ = Dispatcher.RunAnimation(() =>
......@@ -72,11 +76,14 @@ namespace Windows.UI.Xaml.Media.Animation
#endif
#endif
{
if (KeyFrames.Count < 1)
_wasBeginScheduled = false;
if (KeyFrames.Count < 1 || // nothing to do
_wasRequestedToStop // was requested to stop, between Begin() and dispatched here
)
{
return; // nothing to do
return;
}
_wasBeginScheduled = false;
_activeDuration.Restart();
_replayCount = 1;
......@@ -175,7 +182,9 @@ namespace Windows.UI.Xaml.Media.Animation
_currentAnimator.Cancel();//Stop the animator if it is running
_startingValue = null;
}
State = TimelineState.Stopped;
_wasRequestedToStop = true;
}
void ITimeline.Stop()
......@@ -183,7 +192,9 @@ namespace Windows.UI.Xaml.Media.Animation
_currentAnimator?.Cancel(); // stop could be called before the initialization
_startingValue = null;
ClearValue();
State = TimelineState.Stopped;
_wasRequestedToStop = true;
}
/// <summary>
......
......@@ -114,8 +114,8 @@ namespace Windows.UI.Xaml.Media.Animation
);
}
// We explicitly call the Stop of the _frameScheduler befire teh Reste dispose it,
// so the EndReason will stopped instead of Aborted
// We explicitly call the Stop of the _frameScheduler before the Reset dispose it,
// so the EndReason will be Stopped instead of Aborted.
_frameScheduler?.Stop();
Reset();
......
......@@ -114,6 +114,7 @@ namespace Windows.UI.Xaml.Media.Animation
private void Play()
{
_runningChildren = 0;
if (Children != null && Children.Count > 0)
{
for (int i = 0; i < Children.Count; i++)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册