Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix visual transition support #6792

Merged
merged 7 commits into from
Aug 19, 2021
Merged
197 changes: 101 additions & 96 deletions build/PackageDiffIgnore.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3832,105 +3832,110 @@
</IgnoreSet>

<IgnoreSet baseVersion="3.9.7">
<Types>
</Types>
<Methods>
<!-- Focus management -->
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorBrush..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStop..ctor()"
reason="Should not be public" />
<Member
fullName="System.UInt32 Windows.UI.Composition.CompositionColorGradientStopCollection.get_Size()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection.set_Count(System.Int32 value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection.set_IsReadOnly(System.Boolean value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionEllipseGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Numerics.Vector2 Windows.UI.Composition.CompositionGradientBrush.get_AnchorPoint()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionGradientBrush.set_AnchorPoint(System.Numerics.Vector2 value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionLinearGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionLineGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRadialGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRectangleGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRoundedRectangleGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionSurfaceBrush..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionSurfaceBrush..ctor(Windows.UI.Composition.Compositor compositor, Windows.UI.Composition.ICompositionSurface surface)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionViewBox..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.InsetClip..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaExpansionBehavior..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaRotationBehavior..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaTranslationBehavior..ctor()"
reason="Should not be public" />

<Member
fullName="Windows.UI.Xaml.Controls.Primitives.PlacementMode Windows.UI.Xaml.Controls.ToolTipService.GetPlacement(Windows.UI.Xaml.DependencyObject element)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Void Windows.UI.Xaml.Controls.ToolTipService.SetPlacement(Windows.UI.Xaml.DependencyObject element, Windows.UI.Xaml.Controls.Primitives.PlacementMode value)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Object Windows.UI.Xaml.Controls.ToolTipService.GetToolTip(Windows.UI.Xaml.DependencyObject element)"
reason="INVALID REMOVE, TO ADJUST" />

<Member
fullName="System.Void Windows.UI.Xaml.Controls.ToolTipService.SetToolTip(Windows.UI.Xaml.DependencyObject element, System.Object value)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Boolean Windows.UI.Xaml.Controls.VirtualizingPanelLayout.ShouldInsertReorderingView(System.Double physicalExtentOffset)"
reason="INVALID REMOVE, TO ADJUST" />
<Methods>
<Member
fullName="Windows.UI.Xaml.Media.Animation.Timeline/TimelineState Windows.UI.Xaml.Media.Animation.Timeline.get_State()"
reason="Not part of the UWP API" />
<Member
fullName="System.Void Windows.UI.Xaml.Media.Animation.Timeline.set_State(Windows.UI.Xaml.Media.Animation.Timeline/TimelineState value)"
reason="Not part of the UWP API" />
<!-- Focus management -->
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorBrush..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStop..ctor()"
reason="Should not be public" />
<Member
fullName="System.UInt32 Windows.UI.Composition.CompositionColorGradientStopCollection.get_Size()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection.set_Count(System.Int32 value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection.set_IsReadOnly(System.Boolean value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionColorGradientStopCollection..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionEllipseGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Numerics.Vector2 Windows.UI.Composition.CompositionGradientBrush.get_AnchorPoint()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionGradientBrush.set_AnchorPoint(System.Numerics.Vector2 value)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionLinearGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionLineGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRadialGradientBrush..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRectangleGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionRoundedRectangleGeometry..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionSurfaceBrush..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionSurfaceBrush..ctor(Windows.UI.Composition.Compositor compositor, Windows.UI.Composition.ICompositionSurface surface)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.CompositionViewBox..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Composition.InsetClip..ctor(Windows.UI.Composition.Compositor compositor)"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaExpansionBehavior..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaRotationBehavior..ctor()"
reason="Should not be public" />
<Member
fullName="System.Void Windows.UI.Xaml.Input.InertiaTranslationBehavior..ctor()"
reason="Should not be public" />

<Member
fullName="Windows.UI.Xaml.Controls.Primitives.PlacementMode Windows.UI.Xaml.Controls.ToolTipService.GetPlacement(Windows.UI.Xaml.DependencyObject element)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Void Windows.UI.Xaml.Controls.ToolTipService.SetPlacement(Windows.UI.Xaml.DependencyObject element, Windows.UI.Xaml.Controls.Primitives.PlacementMode value)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Object Windows.UI.Xaml.Controls.ToolTipService.GetToolTip(Windows.UI.Xaml.DependencyObject element)"
reason="INVALID REMOVE, TO ADJUST" />

<Member
fullName="System.Void Windows.UI.Xaml.Controls.ToolTipService.SetToolTip(Windows.UI.Xaml.DependencyObject element, System.Object value)"
reason="INVALID REMOVE, TO ADJUST" />
<Member
fullName="System.Boolean Windows.UI.Xaml.Controls.VirtualizingPanelLayout.ShouldInsertReorderingView(System.Double physicalExtentOffset)"
reason="INVALID REMOVE, TO ADJUST" />

</Methods>
<Fields>
</Fields>
<Properties>
<Member
</Methods>
<Properties>
<Member
fullName="Windows.UI.Xaml.Media.Animation.Timeline/TimelineState Windows.UI.Xaml.Media.Animation.Timeline::State()"
reason="Not part of the UWP API" />
<Member
fullName="System.UInt32 Windows.UI.Composition.CompositionColorGradientStopCollection::Size()"
reason="Method not in UWP contract" />
<Member
fullName="System.Numerics.Vector2 Windows.UI.Composition.CompositionGradientBrush::AnchorPoint()"
reason="Method not in UWP contract" />
</Properties>
<Member
fullName="System.Numerics.Vector2 Windows.UI.Composition.CompositionGradientBrush::AnchorPoint()"
reason="Method not in UWP contract" />
</Properties>
</IgnoreSet>
<!--
Supported nodes (please keep at the bottom of this file):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Uno.Extensions;

namespace Uno.UI.Tests.Windows_UI_Xaml.VisualStateManagerTests
{
Expand Down Expand Up @@ -137,6 +139,155 @@ public void When_UsingStateTriggerAndStoryboard()
completed2Count.Should().Be(1);
}

[TestMethod]
public void When_SetterAffectsSameProperty_Then_OriginalValueNotRestored()
{
var (control, group) = Setup();

var tags = new List<string> { (string)control.Tag };
control.RegisterPropertyChangedCallback(Control.TagProperty, (_, __) => tags.Add((string)control.Tag));

VisualStateManager.GoToState(control, "state1", true);
VisualStateManager.GoToState(control, "state2", true);

Assert.IsTrue(new[] { "initial", "state1", "state2" }.SequenceEqual(tags));
}

[TestMethod]
public void When_NoSetter_Then_OriginalValueRestored()
{
var (control, group) = Setup();
group.States[1].Setters.Clear();

var tags = new List<string> { (string)control.Tag };
control.RegisterPropertyChangedCallback(Control.TagProperty, (_, __) => tags.Add((string)control.Tag));

VisualStateManager.GoToState(control, "state1", true);
VisualStateManager.GoToState(control, "state2", true);

Assert.IsTrue(new[] { "initial", "state1", "initial" }.SequenceEqual(tags));
}

[TestMethod]
public void When_GoToStateWhileTransitionning_Then_StopTransition()
{
var (control, group) = Setup();
var transition = Transition(control, to: 1, frames: 5);
group.Transitions.Add(transition);

VisualStateManager.GoToState(control, "state1", true);
Assert.IsTrue(transition.Storyboard.State == Timeline.TimelineState.Active);

VisualStateManager.GoToState(control, "state2", true);
Assert.IsTrue(transition.Storyboard.State == Timeline.TimelineState.Stopped);
}

[TestMethod]
public void When_GoToStateWhileAnimating_Then_StopAnimation()
{
var (control, group) = Setup();
var animation = group.States[0].Storyboard = AnimateTag(control, "state1", 5);

VisualStateManager.GoToState(control, "state1", true);
Assert.IsTrue(animation.State == Timeline.TimelineState.Active);

VisualStateManager.GoToState(control, "state2", true);
Assert.IsTrue(animation.State == Timeline.TimelineState.Stopped);
}

[TestMethod]
public void When_TransitionAndSetter_Then_SetterAppliedAfter()
{
var (control, group) = Setup();
group.Transitions.Add(Transition(control, from: 1, to: 2, frames: 0));

var tags = new List<string> { (string)control.Tag };
control.RegisterPropertyChangedCallback(Control.TagProperty, (_, __) => tags.Add((string)control.Tag));

VisualStateManager.GoToState(control, "state1", true);
VisualStateManager.GoToState(control, "state2", true);

Assert.IsTrue(new [] { "initial", "state1", "transition_from_state1_to_state2_frame_0", "state2" }.SequenceEqual(tags));
}

[TestMethod]
public void When_TransitionAndSetter_InCompatibilityMode_Then_SetterAppliedBefore()
{
try
{
FeatureConfiguration.VisualState.ApplySettersBeforeTransition = true;

var (control, group) = Setup();
group.Transitions.Add(Transition(control, from: 1, to: 2, frames: 0));

var tags = new List<string> { (string)control.Tag };
control.RegisterPropertyChangedCallback(Control.TagProperty, (_, __) => tags.Add((string)control.Tag));

VisualStateManager.GoToState(control, "state1", true);
VisualStateManager.GoToState(control, "state2", true);

Assert.IsTrue(new[] { "initial", "state1", "state2", "transition_from_state1_to_state2_frame_0" }.SequenceEqual(tags));
}
finally
{
FeatureConfiguration.VisualState.ApplySettersBeforeTransition = false;
}
}

private static (Control control, VisualStateGroup states) Setup()
{
var control = new Control { Name = "control", Tag = "initial", Template = new ControlTemplate(() => new Grid()) };
var group = new VisualStateGroup { States = { State(1), State(2) } };

VisualStateManager.SetVisualStateGroups((FrameworkElement)control.TemplatedRoot, new List<VisualStateGroup> { group });

control.ApplyTemplate();

return (control, group);
}

private static VisualState State(int id)
=> new VisualState
{
Name = "state" + id,
Setters = {new Setter(new TargetPropertyPath("control", "Tag"), "state" + id)}
};

private static VisualTransition Transition(Control control, int? @from = null, int? to = null, params int[] frames)
{
var transition = new VisualTransition();
var animationName = "transition";

if (@from is int f)
{
transition.From = "state" + f;
animationName += "_from_state" + f;
}

if (to is int t)
{
transition.To = "state" + t;
animationName += "_to_state" + t;
}

transition.Storyboard = AnimateTag(control, animationName, frames);

return transition;
}

private static Storyboard AnimateTag(Control target, string name, params int[] frames)
{
var anim = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTarget(anim, target);
Storyboard.SetTargetProperty(anim, "Tag");
foreach (var frame in frames)
{
anim.KeyFrames.Add(new DiscreteObjectKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(frame)), Value = $"{name}_frame_{frame}" });
}

return new Storyboard { Children = { anim } };
}

private class CustomStateTrigger : StateTrigger
{
internal CustomStateTrigger(bool initialState = true)
Expand Down
10 changes: 10 additions & 0 deletions src/Uno.UI/FeatureConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,16 @@ public static class UIElement
#endif
}

public static class VisualState
{
/// <summary>
/// When this is set, the <see cref="Windows.UI.Xaml.VisualState.Setters"/> will be applied synchronously when changing state,
/// unlike UWP which waits the for the end of the <see cref="VisualTransition.Storyboard"/> (if any) to apply them.
/// </summary>
/// <remarks>This flag is for backward compatibility with old versions of uno and should not be turned on.</remarks>
dr1rrb marked this conversation as resolved.
Show resolved Hide resolved
public static bool ApplySettersBeforeTransition { get; set; } = false;
}

public static class WebView
{
#if __ANDROID__
Expand Down
Loading