Browse Source

Added Transitions to TabControl.

Currently only one transition available: CrossFadeTransition, which is
demonstrated in the test app.
pull/58/head
Steven Kirk 12 years ago
parent
commit
08aa54e16f
  1. 12
      Perspex.Animation/Animate.cs
  2. 62
      Perspex.Animation/Animation.cs
  3. 1
      Perspex.Animation/Perspex.Animation.csproj
  4. 10
      Perspex.Controls/Deck.cs
  5. 33
      Perspex.Controls/Presenters/DeckPresenter.cs
  6. 10
      Perspex.Controls/TabControl.cs
  7. 70
      Perspex.SceneGraph/Animation/CrossFadeTransition.cs
  8. 15
      Perspex.SceneGraph/Animation/IVisibilityTransition.cs
  9. 7
      Perspex.SceneGraph/Media/IDrawingContext.cs
  10. 2
      Perspex.SceneGraph/Perspex.SceneGraph.csproj
  11. 5
      Perspex.SceneGraph/Rendering/RendererBase.cs
  12. 1
      Perspex.SceneGraph/Visual.cs
  13. 1
      Perspex.Themes.Default/DeckStyle.cs
  14. 3
      Perspex.Themes.Default/TabControlStyle.cs
  15. 1
      TestApplication/Program.cs
  16. 31
      Windows/Perspex.Direct2D1/Media/DrawingContext.cs

12
Perspex.Animation/Animate.cs

@ -96,8 +96,8 @@ namespace Perspex.Animation
/// <param name="finish">The value of the property at the end of the animation.</param>
/// <param name="easing">The easing function to use.</param>
/// <param name="duration">The duration of the animation.</param>
/// <returns>An <see cref="IDisposable"/> that can be used to stop the animation.</returns>
public static IDisposable Property(
/// <returns>An <see cref="Animation"/> that can be used to track or stop the animation.</returns>
public static Animation Property(
PerspexObject target,
PerspexProperty property,
object start,
@ -106,7 +106,7 @@ namespace Perspex.Animation
TimeSpan duration)
{
var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish));
return target.Bind(property, o, BindingPriority.Animation);
return new Animation(o, target.Bind(property, o, BindingPriority.Animation));
}
/// <summary>
@ -119,8 +119,8 @@ namespace Perspex.Animation
/// <param name="finish">The value of the property at the end of the animation.</param>
/// <param name="easing">The easing function to use.</param>
/// <param name="duration">The duration of the animation.</param>
/// <returns>An <see cref="IDisposable"/> that can be used to stop the animation.</returns>
public static IDisposable Property<T>(
/// <returns>An <see cref="Animation"/> that can be used to track or stop the animation.</returns>
public static Animation<T> Property<T>(
PerspexObject target,
PerspexProperty<T> property,
T start,
@ -129,7 +129,7 @@ namespace Perspex.Animation
TimeSpan duration)
{
var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish));
return target.Bind(property, o, BindingPriority.Animation);
return new Animation<T>(o, target.Bind(property, o, BindingPriority.Animation));
}
}
}

62
Perspex.Animation/Animation.cs

@ -0,0 +1,62 @@
// -----------------------------------------------------------------------
// <copyright file="Animation.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Animation
{
using System;
/// <summary>
/// Tracks the progress of an animation.
/// </summary>
public class Animation : IObservable<object>, IDisposable
{
private IObservable<object> inner;
private IDisposable subscription;
public Animation(IObservable<object> inner, IDisposable subscription)
{
this.inner = inner;
this.subscription = subscription;
}
public void Dispose()
{
this.subscription.Dispose();
}
public IDisposable Subscribe(IObserver<object> observer)
{
return this.inner.Subscribe(observer);
}
}
/// <summary>
/// Tracks the progress of an animation.
/// </summary>
public class Animation<T> : IObservable<T>, IDisposable
{
private IObservable<T> inner;
private IDisposable subscription;
public Animation(IObservable<T> inner, IDisposable subscription)
{
this.inner = inner;
this.subscription = subscription;
}
public void Dispose()
{
this.subscription.Dispose();
}
public IDisposable Subscribe(IObserver<T> observer)
{
return this.inner.Subscribe(observer);
}
}
}

1
Perspex.Animation/Perspex.Animation.csproj

@ -41,6 +41,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Animation.cs" />
<Compile Include="Animate.cs" />
<Compile Include="Animatable.cs" />
<Compile Include="AnimationExtensions.cs" />

10
Perspex.Controls/Deck.cs

@ -11,12 +11,16 @@ namespace Perspex.Controls
using Perspex.Controls.Primitives;
using Perspex.Controls.Utils;
using Perspex.Input;
using Perspex.Animation;
/// <summary>
/// A selecting items control that displays a single item that fills the control.
/// </summary>
public class Deck : SelectingItemsControl
{
public static readonly PerspexProperty<IVisibilityTransition> TransitionProperty =
PerspexProperty.Register<Deck, IVisibilityTransition>("Transition");
private static readonly ItemsPanelTemplate PanelTemplate =
new ItemsPanelTemplate(() => new Panel());
@ -25,6 +29,12 @@ namespace Perspex.Controls
ItemsPanelProperty.OverrideDefaultValue(typeof(Deck), PanelTemplate);
}
public IVisibilityTransition Transition
{
get { return this.GetValue(TransitionProperty); }
set { this.SetValue(TransitionProperty, value); }
}
protected override ItemContainerGenerator CreateItemContainerGenerator()
{
return new TypedItemContainerGenerator<DeckItem>(this);

33
Perspex.Controls/Presenters/DeckPresenter.cs

@ -6,6 +6,7 @@
namespace Perspex.Controls.Presenters
{
using Perspex.Animation;
using Perspex.Controls.Generators;
using Perspex.Controls.Primitives;
using Perspex.Input;
@ -13,7 +14,9 @@ namespace Perspex.Controls.Presenters
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
public class DeckPresenter : Control, IVisual, IPresenter, ITemplatedControl
{
@ -26,6 +29,9 @@ namespace Perspex.Controls.Presenters
public static readonly PerspexProperty<object> SelectedItemProperty =
SelectingItemsControl.SelectedItemProperty.AddOwner<DeckPresenter>();
public static readonly PerspexProperty<IVisibilityTransition> TransitionProperty =
Deck.TransitionProperty.AddOwner<DeckPresenter>();
private bool createdPanel;
public DeckPresenter()
@ -63,6 +69,12 @@ namespace Perspex.Controls.Presenters
private set;
}
public IVisibilityTransition Transition
{
get { return this.GetValue(TransitionProperty); }
set { this.SetValue(TransitionProperty, value); }
}
public override sealed void ApplyTemplate()
{
if (!this.createdPanel)
@ -106,21 +118,34 @@ namespace Perspex.Controls.Presenters
return this.ItemContainerGenerator;
}
private void SelectedItemChanged(Tuple<object, object> value)
private async void SelectedItemChanged(Tuple<object, object> value)
{
if (this.createdPanel)
{
var generator = this.GetGenerator();
Control from = null;
Control to = null;
if (value.Item1 != null)
{
generator.RemoveAll();
this.Panel.Children.Clear();
from = generator.GetContainerForItem(value.Item1);
}
if (value.Item2 != null)
{
this.Panel.Children.AddRange(generator.Generate(new[] { value.Item2 }));
to = generator.Generate(new[] { value.Item2 }).Single();
this.Panel.Children.Add(to);
}
if (this.Transition != null)
{
await this.Transition.Start(from, to);
}
if (from != null)
{
this.Panel.Children.Remove(from);
generator.Remove(new[] { value.Item1 });
}
}
}

10
Perspex.Controls/TabControl.cs

@ -15,6 +15,7 @@ namespace Perspex.Controls
using Perspex.Controls.Primitives;
using Perspex.Controls.Templates;
using Perspex.Input;
using Perspex.Animation;
public class TabControl : SelectingItemsControl, ILogical
{
@ -24,6 +25,9 @@ namespace Perspex.Controls
public static readonly PerspexProperty<TabItem> SelectedTabProperty =
PerspexProperty.Register<TabControl, TabItem>("SelectedTab");
public static readonly PerspexProperty<IVisibilityTransition> TransitionProperty =
Deck.TransitionProperty.AddOwner<TabControl>();
private PerspexReadOnlyListView<ILogical> logicalChildren =
new PerspexReadOnlyListView<ILogical>();
@ -56,6 +60,12 @@ namespace Perspex.Controls
set { this.SetValue(SelectedTabProperty, value); }
}
public IVisibilityTransition Transition
{
get { return this.GetValue(TransitionProperty); }
set { this.SetValue(TransitionProperty, value); }
}
IPerspexReadOnlyList<ILogical> ILogical.LogicalChildren
{
get { return this.logicalChildren; }

70
Perspex.SceneGraph/Animation/CrossFadeTransition.cs

@ -0,0 +1,70 @@
// -----------------------------------------------------------------------
// <copyright file="CrossFadeTransition.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
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 CrossFadeTransition(TimeSpan duration)
{
this.Duration = duration;
}
public TimeSpan Duration { get; }
public async Task Start(Visual from, Visual to)
{
var tasks = new List<Task>();
if (to != null)
{
to.Opacity = 0;
}
if (from != null)
{
tasks.Add(Animate.Property(
from,
Visual.OpacityProperty,
from.Opacity,
0,
LinearEasing.For<double>(),
this.Duration).ToTask());
}
if (to != null)
{
to.Opacity = 0;
to.IsVisible = true;
tasks.Add(Animate.Property(
to,
Visual.OpacityProperty,
0,
1,
LinearEasing.For<double>(),
this.Duration).ToTask());
}
await Task.WhenAll(tasks.ToArray());
if (from != null)
{
from.IsVisible = false;
from.Opacity = 1;
}
to.Opacity = 1;
}
}
}

15
Perspex.SceneGraph/Animation/IVisibilityTransition.cs

@ -0,0 +1,15 @@
// -----------------------------------------------------------------------
// <copyright file="IVisibilityTransition.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Animation
{
using System.Threading.Tasks;
public interface IVisibilityTransition
{
Task Start(Visual from, Visual to);
}
}

7
Perspex.SceneGraph/Media/IDrawingContext.cs

@ -63,6 +63,13 @@ namespace Perspex.Media
/// <returns>A disposable used to undo the clip rectangle.</returns>
IDisposable PushClip(Rect clip);
/// <summary>
/// Pushes an opacity value.
/// </summary>
/// <param name="opacity">The opacity.</param>
/// <returns>A disposable used to undo the opacity.</returns>
IDisposable PushOpacity(double opacity);
/// <summary>
/// Pushes a matrix transformation.
/// </summary>

2
Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -45,6 +45,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Animation\CrossFadeTransition.cs" />
<Compile Include="Animation\IVisibilityTransition.cs" />
<Compile Include="ILogical.cs" />
<Compile Include="Media\Brush.cs" />
<Compile Include="Media\Brushes.cs" />

5
Perspex.SceneGraph/Rendering/RendererBase.cs

@ -64,7 +64,9 @@ namespace Perspex.Rendering
/// <param name="context">The drawing context.</param>
protected virtual void Render(IVisual visual, IDrawingContext context, Matrix translation, Matrix transform)
{
if (visual.IsVisible && visual.Opacity > 0)
var opacity = visual.Opacity;
if (visual.IsVisible && opacity > 0)
{
// Translate any existing transform into this controls coordinate system.
Matrix offset = Matrix.Translation(visual.Bounds.Position);
@ -84,6 +86,7 @@ namespace Perspex.Rendering
var m = transform * translation;
var d = context.PushTransform(m);
using (context.PushOpacity(opacity))
using (visual.ClipToBounds ? context.PushClip(visual.Bounds) : null)
{
visual.Render(context);

1
Perspex.SceneGraph/Visual.cs

@ -49,6 +49,7 @@ namespace Perspex
static Visual()
{
AffectsRender(IsVisibleProperty);
AffectsRender(OpacityProperty);
RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
}

1
Perspex.Themes.Default/DeckStyle.cs

@ -35,6 +35,7 @@ namespace Perspex.Themes.Default
[~ItemsPresenter.ItemsProperty] = control[~Deck.ItemsProperty],
[~ItemsPresenter.ItemsPanelProperty] = control[~Deck.ItemsPanelProperty],
[~DeckPresenter.SelectedItemProperty] = control[~Deck.SelectedItemProperty],
[~DeckPresenter.TransitionProperty] = control[~Deck.TransitionProperty],
};
}
}

3
Perspex.Themes.Default/TabControlStyle.cs

@ -13,6 +13,8 @@ namespace Perspex.Themes.Default
using Perspex.Controls.Primitives;
using Perspex.Styling;
using Perspex.Controls.Templates;
using Perspex.Animation;
using System;
public class TabControlStyle : Styles
{
@ -56,6 +58,7 @@ namespace Perspex.Themes.Default
},
[~Deck.ItemsProperty] = control[~TabControl.ItemsProperty],
[!Deck.SelectedItemProperty] = control[!TabControl.SelectedItemProperty],
[~Deck.TransitionProperty] = control[~TabControl.TransitionProperty],
[Grid.RowProperty] = 1,
}
}

1
TestApplication/Program.cs

@ -155,6 +155,7 @@ namespace TestApplication
LayoutTab(),
AnimationsTab(),
},
Transition = new CrossFadeTransition(TimeSpan.FromSeconds(0.25)),
[Grid.ColumnSpanProperty] = 2,
},
(fps = new TextBlock

31
Windows/Perspex.Direct2D1/Media/DrawingContext.cs

@ -191,6 +191,37 @@ namespace Perspex.Direct2D1.Media
});
}
/// <summary>
/// Pushes an opacity value.
/// </summary>
/// <param name="opacity">The opacity.</param>
/// <returns>A disposable used to undo the opacity.</returns>
public IDisposable PushOpacity(double opacity)
{
if (opacity < 1)
{
var parameters = new LayerParameters
{
ContentBounds = RectangleF.Infinite,
MaskTransform = Matrix3x2.Identity,
Opacity = (float)opacity,
};
var layer = new Layer(this.renderTarget);
this.renderTarget.PushLayer(ref parameters, layer);
return Disposable.Create(() =>
{
this.renderTarget.PopLayer();
});
}
else
{
return Disposable.Empty;
}
}
/// <summary>
/// Pushes a matrix transformation.
/// </summary>

Loading…
Cancel
Save