From 560707bfe2bfcab7ac7871232db105aeddb3f093 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 4 May 2015 22:53:02 +0200 Subject: [PATCH] Added page slide transition. --- Perspex-Mono.sln | 14 ++-- Perspex.Controls/Deck.cs | 6 +- Perspex.Controls/Presenters/DeckPresenter.cs | 11 ++- Perspex.Controls/TabControl.cs | 4 +- .../{CrossFadeTransition.cs => CrossFade.cs} | 9 +-- ...bilityTransition.cs => IPageTransition.cs} | 6 +- Perspex.SceneGraph/Animation/PageSlide.cs | 78 +++++++++++++++++++ .../Media/TranslateTransform.cs | 49 ++++++++++++ Perspex.SceneGraph/Perspex.SceneGraph.csproj | 6 +- TestApplication/Program.cs | 18 ++--- 10 files changed, 167 insertions(+), 34 deletions(-) rename Perspex.SceneGraph/Animation/{CrossFadeTransition.cs => CrossFade.cs} (85%) rename Perspex.SceneGraph/Animation/{IVisibilityTransition.cs => IPageTransition.cs} (66%) create mode 100644 Perspex.SceneGraph/Animation/PageSlide.cs create mode 100644 Perspex.SceneGraph/Media/TranslateTransform.cs diff --git a/Perspex-Mono.sln b/Perspex-Mono.sln index 725b3bf205..f93381257c 100644 --- a/Perspex-Mono.sln +++ b/Perspex-Mono.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.22609.0 +VisualStudioVersion = 14.0.22823.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Base", "Perspex.Base\Perspex.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject @@ -53,7 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{596AF7 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo.RenderTests", "Tests\Perspex.RenderTests\Perspex.Cairo.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Cairo.RenderTests", "Tests\Perspex.RenderTests\Perspex.Cairo.RenderTests.csproj", "{E106CF37-4066-4615-B684-172A6D30B058}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -141,10 +141,10 @@ Global {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.Build.0 = Debug|Any CPU {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.ActiveCfg = Release|Any CPU {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.Build.0 = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|Any CPU.Build.0 = Release|Any CPU + {E106CF37-4066-4615-B684-172A6D30B058}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E106CF37-4066-4615-B684-172A6D30B058}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E106CF37-4066-4615-B684-172A6D30B058}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E106CF37-4066-4615-B684-172A6D30B058}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -158,6 +158,6 @@ Global {2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {415E048E-4611-4815-9CF2-D774E29079AC} = {2BAFBE53-7FA4-4BB9-976F-9AFCC4F9847D} {DB070A10-BF39-4752-8456-86E9D5928478} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} + {E106CF37-4066-4615-B684-172A6D30B058} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} EndGlobalSection EndGlobal diff --git a/Perspex.Controls/Deck.cs b/Perspex.Controls/Deck.cs index b40f7ba2c7..4c44d66c6a 100644 --- a/Perspex.Controls/Deck.cs +++ b/Perspex.Controls/Deck.cs @@ -18,8 +18,8 @@ namespace Perspex.Controls /// public class Deck : SelectingItemsControl { - public static readonly PerspexProperty TransitionProperty = - PerspexProperty.Register("Transition"); + public static readonly PerspexProperty TransitionProperty = + PerspexProperty.Register("Transition"); private static readonly ItemsPanelTemplate PanelTemplate = new ItemsPanelTemplate(() => new Panel()); @@ -29,7 +29,7 @@ namespace Perspex.Controls ItemsPanelProperty.OverrideDefaultValue(typeof(Deck), PanelTemplate); } - public IVisibilityTransition Transition + public IPageTransition Transition { get { return this.GetValue(TransitionProperty); } set { this.SetValue(TransitionProperty, value); } diff --git a/Perspex.Controls/Presenters/DeckPresenter.cs b/Perspex.Controls/Presenters/DeckPresenter.cs index 71af424279..adfd6de028 100644 --- a/Perspex.Controls/Presenters/DeckPresenter.cs +++ b/Perspex.Controls/Presenters/DeckPresenter.cs @@ -9,6 +9,7 @@ namespace Perspex.Controls.Presenters using Perspex.Animation; using Perspex.Controls.Generators; using Perspex.Controls.Primitives; + using Perspex.Controls.Utils; using Perspex.Input; using Perspex.Styling; using System; @@ -29,7 +30,7 @@ namespace Perspex.Controls.Presenters public static readonly PerspexProperty SelectedItemProperty = SelectingItemsControl.SelectedItemProperty.AddOwner(); - public static readonly PerspexProperty TransitionProperty = + public static readonly PerspexProperty TransitionProperty = Deck.TransitionProperty.AddOwner(); private bool createdPanel; @@ -69,7 +70,7 @@ namespace Perspex.Controls.Presenters private set; } - public IVisibilityTransition Transition + public IPageTransition Transition { get { return this.GetValue(TransitionProperty); } set { this.SetValue(TransitionProperty, value); } @@ -125,21 +126,25 @@ namespace Perspex.Controls.Presenters var generator = this.GetGenerator(); Control from = null; Control to = null; + int fromIndex = -1; + int toIndex = -1; if (value.Item1 != null) { from = generator.GetContainerForItem(value.Item1); + fromIndex = this.Items.IndexOf(value.Item1); } if (value.Item2 != null) { to = generator.Generate(new[] { value.Item2 }).Single(); this.Panel.Children.Add(to); + toIndex = this.Items.IndexOf(value.Item2); } if (this.Transition != null) { - await this.Transition.Start(from, to); + await this.Transition.Start(from, to, fromIndex < toIndex); } if (from != null) diff --git a/Perspex.Controls/TabControl.cs b/Perspex.Controls/TabControl.cs index 8b39cd501a..ee4086c508 100644 --- a/Perspex.Controls/TabControl.cs +++ b/Perspex.Controls/TabControl.cs @@ -25,7 +25,7 @@ namespace Perspex.Controls public static readonly PerspexProperty SelectedTabProperty = PerspexProperty.Register("SelectedTab"); - public static readonly PerspexProperty TransitionProperty = + public static readonly PerspexProperty TransitionProperty = Deck.TransitionProperty.AddOwner(); private PerspexReadOnlyListView logicalChildren = @@ -60,7 +60,7 @@ namespace Perspex.Controls set { this.SetValue(SelectedTabProperty, value); } } - public IVisibilityTransition Transition + public IPageTransition Transition { get { return this.GetValue(TransitionProperty); } set { this.SetValue(TransitionProperty, value); } diff --git a/Perspex.SceneGraph/Animation/CrossFadeTransition.cs b/Perspex.SceneGraph/Animation/CrossFade.cs similarity index 85% rename from Perspex.SceneGraph/Animation/CrossFadeTransition.cs rename to Perspex.SceneGraph/Animation/CrossFade.cs index f61145da67..6d128a65f1 100644 --- a/Perspex.SceneGraph/Animation/CrossFadeTransition.cs +++ b/Perspex.SceneGraph/Animation/CrossFade.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright 2015 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- @@ -8,20 +8,19 @@ namespace Perspex.Animation { using System; using System.Collections.Generic; - using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading.Tasks; - public class CrossFadeTransition : IVisibilityTransition + public class CrossFade : IPageTransition { - public CrossFadeTransition(TimeSpan duration) + public CrossFade(TimeSpan duration) { this.Duration = duration; } public TimeSpan Duration { get; } - public async Task Start(Visual from, Visual to) + public async Task Start(Visual from, Visual to, bool forward) { var tasks = new List(); diff --git a/Perspex.SceneGraph/Animation/IVisibilityTransition.cs b/Perspex.SceneGraph/Animation/IPageTransition.cs similarity index 66% rename from Perspex.SceneGraph/Animation/IVisibilityTransition.cs rename to Perspex.SceneGraph/Animation/IPageTransition.cs index 7e7e0a46af..7a3239bcd2 100644 --- a/Perspex.SceneGraph/Animation/IVisibilityTransition.cs +++ b/Perspex.SceneGraph/Animation/IPageTransition.cs @@ -1,5 +1,5 @@ // ----------------------------------------------------------------------- -// +// // Copyright 2015 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- @@ -8,8 +8,8 @@ namespace Perspex.Animation { using System.Threading.Tasks; - public interface IVisibilityTransition + public interface IPageTransition { - Task Start(Visual from, Visual to); + Task Start(Visual from, Visual to, bool forward); } } diff --git a/Perspex.SceneGraph/Animation/PageSlide.cs b/Perspex.SceneGraph/Animation/PageSlide.cs new file mode 100644 index 0000000000..28aedecd50 --- /dev/null +++ b/Perspex.SceneGraph/Animation/PageSlide.cs @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Animation +{ + using Perspex.Media; + using Perspex.VisualTree; + using System; + using System.Collections.Generic; + using System.Reactive.Threading.Tasks; + using System.Threading.Tasks; + + public class PageSlide : IPageTransition + { + public PageSlide(TimeSpan duration) + { + this.Duration = duration; + } + + public TimeSpan Duration { get; } + + public async Task Start(Visual from, Visual to, bool forward) + { + var tasks = new List(); + var parent = GetVisualParent(from, to); + var distance = parent.Bounds.Width; + + if (from != null) + { + var transform = new TranslateTransform(); + from.RenderTransform = transform; + tasks.Add(Animate.Property( + transform, + TranslateTransform.XProperty, + 0, + forward ? -distance : distance, + LinearEasing.For(), + this.Duration).ToTask()); + } + + if (to != null) + { + var transform = new TranslateTransform(); + to.RenderTransform = transform; + tasks.Add(Animate.Property( + transform, + TranslateTransform.XProperty, + forward ? distance : -distance, + 0, + LinearEasing.For(), + this.Duration).ToTask()); + } + + await Task.WhenAll(tasks.ToArray()); + + if (from != null) + { + from.IsVisible = false; + } + } + + private static IVisual GetVisualParent(IVisual from, IVisual to) + { + var p1 = (from ?? to).VisualParent; + var p2 = (to ?? from).VisualParent; + + if (p1 != null && p2 != null && p1 != p2) + { + throw new ArgumentException("Controls for PageSlide must have same parent."); + } + + return p1; + } + } +} diff --git a/Perspex.SceneGraph/Media/TranslateTransform.cs b/Perspex.SceneGraph/Media/TranslateTransform.cs new file mode 100644 index 0000000000..65c5ecf8ae --- /dev/null +++ b/Perspex.SceneGraph/Media/TranslateTransform.cs @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2013 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Media +{ + using System; + + public class TranslateTransform : Transform + { + public static readonly PerspexProperty XProperty = + PerspexProperty.Register("X"); + + public static readonly PerspexProperty YProperty = + PerspexProperty.Register("Y"); + + public TranslateTransform() + { + this.GetObservable(XProperty).Subscribe(_ => this.RaiseChanged()); + this.GetObservable(YProperty).Subscribe(_ => this.RaiseChanged()); + } + + public TranslateTransform(double x, double y) + : this() + { + this.X = x; + this.Y = y; + } + + public double X + { + get { return this.GetValue(XProperty); } + set { this.SetValue(XProperty, value); } + } + + public double Y + { + get { return this.GetValue(YProperty); } + set { this.SetValue(YProperty, value); } + } + + public override Matrix Value + { + get { return Matrix.Translation(this.X, this.Y); } + } + } +} diff --git a/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/Perspex.SceneGraph/Perspex.SceneGraph.csproj index bc0c481581..1bd22ce7f7 100644 --- a/Perspex.SceneGraph/Perspex.SceneGraph.csproj +++ b/Perspex.SceneGraph/Perspex.SceneGraph.csproj @@ -45,8 +45,9 @@ - - + + + @@ -63,6 +64,7 @@ + diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs index 06993479ff..833e05619f 100644 --- a/TestApplication/Program.cs +++ b/TestApplication/Program.cs @@ -155,7 +155,7 @@ namespace TestApplication LayoutTab(), AnimationsTab(), }, - Transition = new CrossFadeTransition(TimeSpan.FromSeconds(0.25)), + Transition = new PageSlide(TimeSpan.FromSeconds(0.25)), [Grid.ColumnSpanProperty] = 2, }, (fps = new TextBlock @@ -178,14 +178,14 @@ namespace TestApplication DevTools.Attach(window); - //var renderer = ((IRenderRoot)window).Renderer; - //var last = renderer.RenderCount; - //DispatcherTimer.Run(() => - //{ - // fps.Text = "FPS: " + (renderer.RenderCount - last); - // last = renderer.RenderCount; - // return true; - //}, TimeSpan.FromSeconds(1)); + var renderer = ((IRenderRoot)window).Renderer; + var last = renderer.RenderCount; + DispatcherTimer.Run(() => + { + fps.Text = "FPS: " + (renderer.RenderCount - last); + last = renderer.RenderCount; + return true; + }, TimeSpan.FromSeconds(1)); window.Show(); Application.Current.Run(window);