diff --git a/README.md b/README.md
index 6c3808f..0cfeb80 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,6 @@ It also has a collection of helper methods to load Win2D images, dispatch code t
- [Quick start](#quick-start)
- [Animations](#animations)
- [`UI.Composition` effects](#uicomposition-effects)
- - [Reveal highlight effect](#reveal-highlight-effect)
- [Misc](#misc)
- [Requirements](#requirements)
@@ -37,108 +36,60 @@ More details available [here](https://www.nuget.org/packages/UICompositionAnimat
## Animations
-The package exposes synchronous and asynchronous APIs (from the `CompositionExtensions` and `XAMLTransformExtensions` classes) to quickly setup and start animations. There are different kinds of available animation types to choose from:
-- **Fade** (Opacity)
-- **Slide** (Translation)
-- **Scale**
-- **Color** (for a `SolidColorBrush` object)
+The available animation APIs use the fluent pattern and support combining multiple animations togetger. The main entry point is the ` UIElementExtensions.Animation` method, that returns an `IAnimationBuilder` object targeting the input `UIElement`. This object exposes all the available animation APIs.
-The library also has APIs to automatically combine different kinds of animations, like Fade + Slide or Fade + Scale, and various helper methods to change UI-related parameters of a target object. Here are some animation exaples:
-
-#### Synchronous fade animation
+You can use it like this:
```C#
-MyControl.StartCompositionFadeAnimation(
- null, // Using null will make the fade animation start from the current value
- 1, // End opacity
- 200, // Duration in ms
- null, // Optional delay in ms
- EasingFunctionNames.CircleEaseOut, // Easing function,
- () => Foo()); // Optional callback
+MyControl.Animation()
+ .Opacity(0)
+ .Translation(Axis.X, 60)
+ .Duration(250)
+ .Start();
```
-#### Asynchronous fade and scale animation
+It is also possible to set an initial delay, and to wait for the animation to be completed. Also, should you need to do so in a particular situation, it is also possible to choose between the `Windows.UI.Composition` and `Windows.UI.Xaml.Media.Animation` APIs to run the animations. To toggle between the two, just pass a `FrameworkLayer` value to the `Animation` method. Furthermore, each animation API has two overloads: one that just takes the target value, and one that also sets the initial value for the animation. It is also possible to specify an easing function for each individual animation. Here is another, more complex example:
```C#
-await MyControl.StartCompositionFadeScaleAnimationAsync(
- null, // Initial opacity (use current value)
- 1, // Target opacity
- 1.1f, // Initial scale
- 1, // End scale
- 250, // Animation duration
- null, // Optional scale animation duration (if null, the base animation duration will be used)
- null, // Optional delay
- EasingFunctionNames.Linear); // Easing function
+await MyControl.Animation(FrameworkLayer.Xaml)
+ .Opacity(0, 1, Easing.CircleEaseOut)
+ .Scale(1.2, 1, Easing.QuadratincEaseInOut)
+ .Duration(500)
+ .Delay(250)
+ .StartAsync();
```
## `UI.Composition` effects
-The library provides several ways to use `UI.Composition` effects. There are ready to use XAML brushes, a `CompositionBrushBuilder` class to create complex composition effect pipelines, an `AttachedCompositionEffectsFactory` class that provides an alternative way to attach commonly used effects to visual elements, and much more.
+The library provides several ways to use `UI.Composition` effects: there are both ready to use XAML brushes (like a customizable acrylic brush), a `CompositionBrushBuilder` class to create complex composition effect pipelines, and more.
-#### Declare a shared acrylic brush in XAML
+#### Declare an acrylic brush in XAML
```XAML
-
+xmlns:brushes="using:UICompositionAnimations.Brushes">
-
-
- ...
-
+
+
```
**Note**: the `NoiseTextureUri` parameter must be set to a .png image with a noise texture. It is up to the developer to create his own noise texture and to import it into the app. An easy plugin to create one is [NoiseChoice](https://forums.getpaint.net/topic/22500-red-ochre-plug-in-pack-v9-updated-30th-july-2014/) for [Paint.NET](https://www.getpaint.net/).
-#### Create and assign an acrylic brush in C#
-
-```C#
-control.Background = CompositionBrushBuilder.FromHostBackdropAcrylic(Colors.DarkOrange, 0.6f, new Uri("ms-appx:///Assets/noise.png")).AsBrush();
-```
-
-#### Build an acrylic effect pipeline from scratch:
-
-```C#
-Brush brush = CompositionBrushBuilder.FromHostBackdropBrush()
- .Effect(source => new LuminanceToAlphaEffect { Source = source })
- .Opacity(0.4f)
- .Blend(CompositionBrushBuilder.FromHostBackdropBrush(), BlendEffectMode.Multiply)
- .Tint(Color.FromArgb(0xFF, 0x14, 0x14, 0x14), 0.8f)
- .Blend(CompositionBrushBuilder.FromTiles(new Uri("ms-appx:///Assets/noise.png")), BlendEffectMode.Overlay, EffectPlacement.Background)
- .AsBrush();
-```
-
-The `CompositionBrushBuilder` class can also be used to quickly implement custom XAML brushes with an arbitrary effects pipeline. To do so, just inherit from `XamlCompositionEffectBrushBase` and setup your own effects pipeline in the `OnBrushRequested` method.
-
-#### Get a custom effect that can be animated:
-
-```C#
-// Build the effects pipeline
-XamlCompositionBrush acrylic = CompositionBrushBuilder.FromHostBackdropAcrylic(Colors.Orange, 0.6f, new Uri("ms-appx:///Assets/noise.png"))
- .Saturation(1, out EffectAnimation animation)
- .AsBrush();
-acrylic.Bind(animation, out XamlEffectAnimation saturation); // Bind the effect animation to the target brush
-
-// Later on, when needed
-saturation(0.2f, 250); // Animate the opacity to 0.2 in 250ms
-```
-
#### Create custom effects in XAML:
-Using the APIs in `UICompositionAnimations.Behaviours.Xaml` it is also possible to build complex Composition/Win2D pipelines directly from XAML, in a declarative way. This is how to define a custom host backdrop acrylic brush:
+Using the APIs in `UICompositionAnimations.Brushes.Effects` it is also possible to build complex Composition/Win2D pipelines directly from XAML, in a declarative way. This is how to define a custom host backdrop acrylic brush:
```xml
-xmlns:xaml="using:UICompositionAnimations.Behaviours.Xaml"
-xmlns:effects="using:UICompositionAnimations.Behaviours.Xaml.Effects"
+xmlns:brushes="using:UICompositionAnimations.Brushes"
+xmlns:effects="using:UICompositionAnimations.Brushes.Effects"
-
-
+
+
@@ -153,62 +104,51 @@ xmlns:effects="using:UICompositionAnimations.Behaviours.Xaml.Effects"
-
-
+
+
```
-## Reveal highlight effect
-
-Part of the Fluent Design System introduced with Windows 10 Fall Creators Update, this effect can actually already be used with Windows 10 Creators Update (build 15063.x). The library exposes APIs to easily use the effect in an application.
-
-#### Setup the lights
+#### Create and assign an acrylic brush in C#
```C#
-// In App.xaml.cs, before loading the application UI
-LightsSourceHelper.Initialize(
- () => new PointerPositionSpotLight { Shade = 0x60 }, // Example light
- () => new PointerPositionSpotLight
- {
- IdAppendage = "[Wide]", // This ID is used to specify the target brushes for the specific light
- Z = 30,
- Shade = 0x10
- }); // It is possible to add an arbitrary number of lights here
-Window.Current.Content = new Frame(); // Sample UI initialization
-LightsSourceHelper.SetIsLightsContainer(Window.Current.Content, true); // Assign the lights to the app main UI
+control.Background = PipelineBuilder.FromHostBackdropAcrylic(Colors.DarkOrange, 0.6f, new Uri("ms-appx:///Assets/noise.png"))
+ .AsBrush();
```
-#### Setup the target brushes for the lights
-
-````XAML
-
-
-
- ...
-
-````
+#### Build an acrylic effect pipeline from scratch:
```C#
-// Since the second light has a special ID, it is necessary to manually set its target brush
-LightingBrush brush = Application.Current.Resources["BorderWideLightBrush"] as LightingBrush;
-XamlLight.AddTargetBrush($"{PointerPositionSpotLight.GetIdStatic()}[Wide]", brush);
+Brush brush = PipelineBuilder.FromHostBackdropBrush()
+ .Effect(source => new LuminanceToAlphaEffect { Source = source })
+ .Opacity(0.4f)
+ .Blend(CompositionBrushBuilder.FromHostBackdropBrush(), BlendEffectMode.Multiply)
+ .Tint(Color.FromArgb(0xFF, 0x14, 0x14, 0x14), 0.8f)
+ .Blend(CompositionBrushBuilder.FromTiles(new Uri("ms-appx:///Assets/noise.png")), BlendEffectMode.Overlay, Placement.Background)
+ .AsBrush();
```
-At this point we have a series of lights, each targeting an arbitrary number of brushes. The library takes care of managing the pointer events for the lights as well. It is now possible to use those brushes in any `UIElement` to see the reveal highlight effect in action.
+The `PipelineBuilder` class can also be used to quickly implement custom XAML brushes with an arbitrary effects pipeline. To do so, just inherit from `XamlCompositionEffectBrushBase` and setup your own effects pipeline in the `OnBrushRequested` method.
-**Note**: the lights only work on `UIElement`s in their visual tree. This means that in order for the `LightingBrush` objects to work correctly in popups or flyouts, the lights must be added to their visual tree too. To do this, just call the `LightsSourceHelper.SetIsLightsContainer` method on the root element of the new object being displayed (for example, the root `Grid` inside a new `Popup`).
+#### Get a custom effect that can be animated:
+
+```C#
+// Build the effects pipeline
+XamlCompositionBrush acrylic = PipelineBuilder.FromHostBackdropAcrylic(Colors.Orange, 0.6f, new Uri("ms-appx:///Assets/noise.png"))
+ .Saturation(1, out EffectAnimation animation)
+ .AsBrush();
+acrylic.Bind(animation, out XamlEffectAnimation saturation); // Bind the effect animation to the target brush
+
+// Later on, when needed
+saturation(0.2f, 250); // Animate the opacity to 0.2 in 250ms
+```
# Misc
Many utility methods are also available, here are some useful classes:
-- `XAMLTransformToolkit`: exposes methods to manually create, start and wait for `DoubleAnimation`(s) and `Storyboard`(s), as well as for quickly assigning a certain `RenderTransform` object to a `UIElement`.
- `DispatcherHelper`: exposes methods to easily execute code on the UI thread or on a target `CoreDispatcher` object
- `Win2DImageHelper`: exposes APIs to quickly load a Win2D image on a `CompositionSurfaceBrush` object
- `PointerHelper`: exposes APIs to quickly setup pointer event handlers for `UIElement`s
+- `AsyncMutex`: an async mutex included into `System.Threading.Tasks` that can be used to asynchronously acquire a lock with a `using` block.
# Requirements
At least Windows 10 April Update (17134.x)
diff --git a/UICompositionAnimations/Animations/Abstract/AnimationBuilderBase.cs b/UICompositionAnimations/Animations/Abstract/AnimationBuilderBase.cs
new file mode 100644
index 0000000..2c49970
--- /dev/null
+++ b/UICompositionAnimations/Animations/Abstract/AnimationBuilderBase.cs
@@ -0,0 +1,223 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Threading.Tasks;
+using Windows.UI.Xaml;
+using JetBrains.Annotations;
+using UICompositionAnimations.Animations.Interfaces;
+using UICompositionAnimations.Enums;
+
+namespace UICompositionAnimations.Animations.Abstract
+{
+ ///
+ /// An used as base by all the animation builder classes
+ ///
+ internal abstract class AnimationBuilderBase : IAnimationBuilder
+ {
+ ///
+ /// A constructor that initializes the target
+ ///
+ /// The target to animate
+ protected AnimationBuilderBase([NotNull] UIElement target) => TargetElement = target;
+
+ ///
+ /// The target to animate
+ ///
+ [NotNull]
+ protected UIElement TargetElement { get; }
+
+ ///
+ /// Gets the list of instances used to create the animations to run
+ ///
+ [NotNull, ItemNotNull]
+ protected IList AnimationFactories { get; } = new List();
+
+ ///
+ /// Gets the that defines the duration of the animation
+ ///
+ protected TimeSpan DurationInterval { get; private set; }
+
+ ///
+ /// The that defines the initial delay of the animation
+ ///
+ private TimeSpan? _Delay;
+
+ #region Animations
+
+ ///
+ public IAnimationBuilder Opacity(double to, Easing ease = Easing.Linear) => OnOpacity(null, to, ease);
+
+ ///
+ public IAnimationBuilder Opacity(double from, double to, Easing ease = Easing.Linear) => OnOpacity(from, to, ease);
+
+ ///
+ /// Schedules an opacity animation on a single axis
+ ///
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnOpacity(double? from, double to, Easing ease);
+
+ ///
+ public IAnimationBuilder Translation(Axis axis, double to, Easing ease = Easing.Linear) => OnTranslation(axis, null, to, ease);
+
+ ///
+ public IAnimationBuilder Translation(Axis axis, double from, double to, Easing ease = Easing.Linear) => OnTranslation(axis, from, to, ease);
+
+ ///
+ /// Schedules a translation animation on a single axis
+ ///
+ /// The target axis to animate
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnTranslation(Axis axis, double? from, double to, Easing ease);
+
+ ///
+ public IAnimationBuilder Translation(Vector2 to, Easing ease = Easing.Linear) => OnTranslation(null, to, ease);
+
+ ///
+ public IAnimationBuilder Translation(Vector2 from, Vector2 to, Easing ease = Easing.Linear) => OnTranslation(from, to, ease);
+
+ ///
+ /// Schedules a 2D translation animation
+ ///
+ /// The optional starting position
+ /// The target position
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnTranslation(Vector2? from, Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ public IAnimationBuilder Offset(Axis axis, double to, Easing ease = Easing.Linear) => OnOffset(axis, null, to, ease);
+
+ ///
+ public IAnimationBuilder Offset(Axis axis, double from, double to, Easing ease = Easing.Linear) => OnOffset(axis, from, to, ease);
+
+ ///
+ /// Schedules an offset animation on a single axis
+ ///
+ /// The target axis to animate
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnOffset(Axis axis, double? from, double to, Easing ease);
+
+ ///
+ public IAnimationBuilder Offset(Vector2 to, Easing ease = Easing.Linear) => OnOffset(null, to, ease);
+
+ ///
+ public IAnimationBuilder Offset(Vector2 from, Vector2 to, Easing ease = Easing.Linear) => OnOffset(from, to, ease);
+
+ ///
+ /// Schedules a 2D offset animation
+ ///
+ /// The optional starting position
+ /// The target position
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnOffset(Vector2? from, Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ public IAnimationBuilder Scale(double to, Easing ease = Easing.Linear) => OnScale(null, to, ease);
+
+ ///
+ public IAnimationBuilder Scale(double from, double to, Easing ease = Easing.Linear) => OnScale(from, to, ease);
+
+ ///
+ /// Schedules a scale animation
+ ///
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the scale animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnScale(double? from, double to, Easing ease);
+
+ ///
+ public IAnimationBuilder Rotate(double to, Easing ease = Easing.Linear) => OnRotate(null, to, ease);
+
+ ///
+ public IAnimationBuilder Rotate(double from, double to, Easing ease = Easing.Linear) => OnRotate(from, to, ease);
+
+ ///
+ /// Schedules a rotation animation
+ ///
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the rotation animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnRotate(double? from, double to, Easing ease);
+
+ ///
+ public IAnimationBuilder Clip(Side side, double to, Easing ease = Easing.Linear) => OnClip(side, null, to, ease);
+
+ ///
+ public IAnimationBuilder Clip(Side side, double from, double to, Easing ease = Easing.Linear) => OnClip(side, from, to, ease);
+
+ ///
+ /// Schedules a clip animation
+ ///
+ /// The clip side to animate
+ /// The optional starting value
+ /// The target value
+ /// The easing function to use for the clip animation
+ [MustUseReturnValue, NotNull]
+ protected abstract IAnimationBuilder OnClip(Side side, double? from, double to, Easing ease);
+
+ #endregion
+
+ ///
+ public IAnimationBuilder Duration(int ms) => Duration(TimeSpan.FromMilliseconds(ms));
+
+ ///
+ public IAnimationBuilder Duration(TimeSpan duration)
+ {
+ DurationInterval = duration;
+ return this;
+ }
+
+ ///
+ public IAnimationBuilder Delay(int ms) => Delay(TimeSpan.FromMilliseconds(ms));
+
+ ///
+ public IAnimationBuilder Delay(TimeSpan interval)
+ {
+ _Delay = interval;
+ return this;
+ }
+
+ ///
+ public async void Start()
+ {
+ if (_Delay != null) await Task.Delay(_Delay.Value);
+ OnStart();
+ }
+
+ ///
+ /// Starts the animation represented by the current instance
+ ///
+ protected abstract void OnStart();
+
+ ///
+ public async void Start(Action callback)
+ {
+ await StartAsync();
+ callback();
+ }
+
+ ///
+ public async Task StartAsync()
+ {
+ if (_Delay != null) await Task.Delay(_Delay.Value);
+ await OnStartAsync();
+ }
+
+ ///
+ /// Starts the animation represented by the current instance, and returns a to track it
+ ///
+ protected abstract Task OnStartAsync();
+ }
+}
diff --git a/UICompositionAnimations/Animations/CompositionAnimationBuilder.cs b/UICompositionAnimations/Animations/CompositionAnimationBuilder.cs
new file mode 100644
index 0000000..7abc3a1
--- /dev/null
+++ b/UICompositionAnimations/Animations/CompositionAnimationBuilder.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+using Windows.UI.Composition;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Hosting;
+using JetBrains.Annotations;
+using UICompositionAnimations.Animations.Abstract;
+using UICompositionAnimations.Animations.Interfaces;
+using UICompositionAnimations.Enums;
+
+namespace UICompositionAnimations.Animations
+{
+ ///
+ /// A that implements the using composition APIs
+ ///
+ internal sealed class CompositionAnimationBuilder : AnimationBuilderBase>
+ {
+ ///
+ /// The target to animate
+ ///
+ [NotNull]
+ private readonly Visual TargetVisual;
+
+ public CompositionAnimationBuilder([NotNull] UIElement target) : base(target)
+ {
+ TargetVisual = target.GetVisual();
+ ElementCompositionPreview.SetIsTranslationEnabled(TargetElement, true);
+ }
+
+ ///
+ protected override IAnimationBuilder OnOpacity(double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ TargetVisual.StopAnimation(nameof(Visual.Opacity));
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+ ScalarKeyFrameAnimation animation = TargetVisual.Compositor.CreateScalarKeyFrameAnimation((float?)from, (float)to, duration, null, easingFunction);
+ TargetVisual.StartAnimation(nameof(Visual.Opacity), animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnTranslation(Axis axis, double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ string property = $"Translation.{axis}";
+ TargetVisual.StopAnimation(property);
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+
+ // Create and return the animation
+ ScalarKeyFrameAnimation animation = TargetVisual.Compositor.CreateScalarKeyFrameAnimation((float?)from, (float)to, duration, null, easingFunction);
+ TargetVisual.StartAnimation(property, animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnTranslation(Vector2? from, Vector2 to, Easing ease = Easing.Linear)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ TargetVisual.StopAnimation("Translation");
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+
+ // Create and return the animation
+ Vector3? from3 = from == null ? default : new Vector3(from.Value, 0);
+ Vector3 to3 = new Vector3(to, 0);
+ Vector3KeyFrameAnimation animation = TargetVisual.Compositor.CreateVector3KeyFrameAnimation(from3, to3, duration, null, easingFunction);
+ TargetVisual.StartAnimation("Translation", animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnOffset(Axis axis, double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ TargetVisual.StopAnimation(nameof(Visual.Offset));
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+
+ // Create and return the animation
+ ScalarKeyFrameAnimation animation = TargetVisual.Compositor.CreateScalarKeyFrameAnimation((float?)from, (float)to, duration, null, easingFunction);
+ TargetVisual.StartAnimation(nameof(Visual.Offset), animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnOffset(Vector2? from, Vector2 to, Easing ease = Easing.Linear)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ TargetVisual.StopAnimation(nameof(Visual.Offset));
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+
+ // Create and return the animation
+ Vector3? from3 = from == null ? default : new Vector3(from.Value, 0);
+ Vector3 to3 = new Vector3(to, 0);
+ Vector3KeyFrameAnimation animation = TargetVisual.Compositor.CreateVector3KeyFrameAnimation(from3, to3, duration, null, easingFunction);
+ TargetVisual.StartAnimation(nameof(Visual.Offset), animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnScale(double? from, double to, Easing ease)
+ {
+ // Center the visual center point
+ if (!(TargetElement is FrameworkElement element)) throw new InvalidOperationException("The scale animation needs a framework element");
+ element.GetVisual().CenterPoint = new Vector3((float)(element.ActualWidth / 2), (float)(element.ActualHeight / 2), 0);
+
+ // Add the scale animation
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ TargetVisual.StopAnimation(nameof(Visual.Scale));
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+
+ // Create and return the animation
+ Vector3? from3 = from == null ? default : new Vector3((float)from.Value, (float)from.Value, 0);
+ Vector3 to3 = new Vector3((float)to, (float)to, 0);
+ Vector3KeyFrameAnimation animation = TargetVisual.Compositor.CreateVector3KeyFrameAnimation(from3, to3, duration, null, easingFunction);
+ TargetVisual.StartAnimation(nameof(Visual.Scale), animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnRotate(double? from, double to, Easing ease)
+ {
+ // Center the visual center point
+ if (!(TargetElement is FrameworkElement element)) throw new InvalidOperationException("The rotation animation needs a framework element");
+ element.GetVisual().CenterPoint = new Vector3((float)(element.ActualWidth / 2), (float)(element.ActualHeight / 2), 0);
+
+ // Add the rotation animation
+ AnimationFactories.Add(duration =>
+ {
+ TargetVisual.StopAnimation(nameof(Visual.RotationAngleInDegrees));
+ CompositionEasingFunction easingFunction = TargetVisual.GetEasingFunction(ease);
+ ScalarKeyFrameAnimation animation = TargetVisual.Compositor.CreateScalarKeyFrameAnimation((float?)from, (float)to, duration, null, easingFunction);
+ TargetVisual.StartAnimation(nameof(Visual.RotationAngleInDegrees), animation);
+ });
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnClip(Side side, double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration =>
+ {
+ // Stop the animation and get the easing function
+ string property;
+ switch (side)
+ {
+ case Side.Top: property = nameof(InsetClip.TopInset); break;
+ case Side.Bottom: property = nameof(InsetClip.BottomInset); break;
+ case Side.Right: property = nameof(InsetClip.RightInset); break;
+ case Side.Left: property = nameof(InsetClip.LeftInset); break;
+ default: throw new ArgumentException("Invalid side", nameof(side));
+ }
+ InsetClip clip = TargetVisual.Clip as InsetClip ?? (TargetVisual.Clip = TargetVisual.Compositor.CreateInsetClip()).To();
+ clip.StopAnimation(property);
+ CompositionEasingFunction easingFunction = clip.GetEasingFunction(ease);
+
+ // Create and return the animation
+ ScalarKeyFrameAnimation animation = clip.Compositor.CreateScalarKeyFrameAnimation((float?)from, (float)to, duration, null, easingFunction);
+ clip.StartAnimation(property, animation);
+ });
+
+ return this;
+ }
+
+ ///
+ /// Creates and starts the pending animations
+ ///
+ private void StartAnimations()
+ {
+ foreach (var animation in AnimationFactories)
+ animation(DurationInterval);
+ }
+
+ ///
+ protected override void OnStart() => StartAnimations();
+
+ ///
+ protected override Task OnStartAsync()
+ {
+ CompositionScopedBatch batch = TargetVisual.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ batch.Completed += (s, e) => tcs.SetResult();
+ StartAnimations();
+ batch.End();
+ return tcs.Task;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Animations/Interfaces/IAnimationBuilder.cs b/UICompositionAnimations/Animations/Interfaces/IAnimationBuilder.cs
new file mode 100644
index 0000000..7bf99d4
--- /dev/null
+++ b/UICompositionAnimations/Animations/Interfaces/IAnimationBuilder.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using UICompositionAnimations.Enums;
+
+namespace UICompositionAnimations.Animations.Interfaces
+{
+ ///
+ /// An that represents an animation builder that uses a specific set of APIs
+ ///
+ [PublicAPI]
+ public interface IAnimationBuilder
+ {
+ ///
+ /// Schedules an opacity animation
+ ///
+ /// The target opacity value to animate to
+ /// The easing function to use for the opacity animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Opacity(double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules an opacity animation
+ ///
+ /// The opacity value to animate from
+ /// The target opacity value to animate to
+ /// The easing function to use for the opacity animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Opacity(double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a translation animation
+ ///
+ /// The translation axis to animate
+ /// The target translation value to animate to
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Translation(Axis axis, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a translation animation
+ ///
+ /// The translation axis to animate
+ /// The initial translation value to animate from
+ /// The target translation value to animate to
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Translation(Axis axis, double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a translation animation
+ ///
+ /// The target translation position to animate to
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Translation(Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a translation animation
+ ///
+ /// The initial translation position to animate from
+ /// The target translation position to animate to
+ /// The easing function to use for the translation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Translation(Vector2 from, Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules an offset animation
+ ///
+ /// The offset axis to animate
+ /// The target offset value to animate to
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Offset(Axis axis, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules an offset animation
+ ///
+ /// The offset axis to animate
+ /// The initial offset value to animate from
+ /// The target offset value to animate to
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Offset(Axis axis, double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules an offset animation
+ ///
+ /// The target offset position to animate to
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Offset(Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules an offset animation
+ ///
+ /// The initial offset position to animate from
+ /// The target offset position to animate to
+ /// The easing function to use for the offset animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Offset(Vector2 from, Vector2 to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a scale animation
+ ///
+ /// The target scale value to animate to
+ /// The easing function to use for the scale animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Scale(double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a scale animation
+ ///
+ /// The scale value to animate from
+ /// The target scale value to animate to
+ /// The easing function to use for the scale animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Scale(double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a rotation animation
+ ///
+ /// The target rotation value to animate to
+ /// The easing function to use for the rotation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Rotate(double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a rotation animation
+ ///
+ /// The rotation value to animate from
+ /// The target rotation value to animate to
+ /// The easing function to use for the rotation animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Rotate(double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a clip animation
+ ///
+ /// The clip side to animate
+ /// The target clip value to animate to
+ /// The easing function to use for the clip animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Clip(Side side, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Schedules a clip animation
+ ///
+ /// The clip side to animate
+ /// The clip value to animate from
+ /// The target clip value to animate to
+ /// The easing function to use for the clip animation
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Clip(Side side, double from, double to, Easing ease = Easing.Linear);
+
+ ///
+ /// Sets the duration of the animation
+ ///
+ /// The animation duration, in milliseconds
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Duration(int ms);
+
+ ///
+ /// Sets the duration of the animation
+ ///
+ /// The value indicating the animation duration
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Duration(TimeSpan duration);
+
+ ///
+ /// Sets the optional initial delay for the animation
+ ///
+ /// The delay duration, in milliseconds
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Delay(int ms);
+
+ ///
+ /// Sets the optional initial delay for the animation
+ ///
+ /// The value indicating the animation delay duration
+ [MustUseReturnValue, NotNull]
+ IAnimationBuilder Delay(TimeSpan duration);
+
+ ///
+ /// Starts the animation
+ ///
+ void Start();
+
+ ///
+ /// Starts the animation and executes a callback when it completes
+ ///
+ /// The callback to execute when the animation completes
+ void Start([NotNull] Action callback);
+
+ ///
+ /// Starts the animation and returns a to track its completion
+ ///
+ Task StartAsync();
+ }
+}
diff --git a/UICompositionAnimations/Animations/XamlAnimationBuilder.cs b/UICompositionAnimations/Animations/XamlAnimationBuilder.cs
new file mode 100644
index 0000000..f219ff1
--- /dev/null
+++ b/UICompositionAnimations/Animations/XamlAnimationBuilder.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Linq;
+using System.Numerics;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Animation;
+using JetBrains.Annotations;
+using UICompositionAnimations.Animations.Abstract;
+using UICompositionAnimations.Animations.Interfaces;
+using UICompositionAnimations.Enums;
+
+namespace UICompositionAnimations.Animations
+{
+ ///
+ /// A that implements the using composition APIs
+ ///
+ internal sealed class XamlAnimationBuilder : AnimationBuilderBase>
+ {
+ ///
+ /// The target to animate
+ ///
+ [NotNull]
+ private readonly CompositeTransform TargetTransform;
+
+ public XamlAnimationBuilder([NotNull] UIElement target) : base(target)
+ {
+ TargetTransform = target.GetTransform(false);
+ target.RenderTransformOrigin = new Point(0.5, 0.5);
+ }
+
+ ///
+ protected override IAnimationBuilder OnOpacity(double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration => TargetElement.CreateDoubleAnimation(nameof(UIElement.Opacity), from, to, duration, ease));
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnTranslation(Axis axis, double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation($"Translate{axis}", from, to, duration, ease));
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnTranslation(Vector2? from, Vector2 to, Easing ease = Easing.Linear)
+ {
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation(nameof(CompositeTransform.TranslateX), from?.X, to.X, duration, ease));
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation(nameof(CompositeTransform.TranslateY), from?.Y, to.Y, duration, ease));
+
+ return this;
+ }
+
+ protected override IAnimationBuilder OnOffset(Axis axis, double? from, double to, Easing ease) => throw new NotSupportedException("Can't animate the offset property from XAML");
+
+ protected override IAnimationBuilder OnOffset(Vector2? from, Vector2 to, Easing ease = Easing.Linear) => throw new NotSupportedException("Can't animate the offset property from XAML");
+
+ ///
+ protected override IAnimationBuilder OnScale(double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation(nameof(CompositeTransform.ScaleX), from, to, duration, ease));
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation(nameof(CompositeTransform.ScaleY), from, to, duration, ease));
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnRotate(double? from, double to, Easing ease)
+ {
+ AnimationFactories.Add(duration => TargetTransform.CreateDoubleAnimation(nameof(CompositeTransform.Rotation), from, to, duration, ease));
+
+ return this;
+ }
+
+ ///
+ protected override IAnimationBuilder OnClip(Side side, double? @from, double to, Easing ease) => throw new NotSupportedException("Can't animate the clip property from XAML");
+
+ ///
+ /// Gets the represented by the current instance
+ ///
+ [NotNull]
+ private Storyboard Storyboard => AnimationFactories.Select(f => f(DurationInterval)).ToStoryboard();
+
+ ///
+ protected override void OnStart() => Storyboard.Begin();
+
+ ///
+ protected override Task OnStartAsync() => Storyboard.BeginAsync();
+ }
+}
diff --git a/UICompositionAnimations/AttachedProperties/ClipHelper.cs b/UICompositionAnimations/AttachedProperties/ClipHelper.cs
new file mode 100644
index 0000000..05294d2
--- /dev/null
+++ b/UICompositionAnimations/AttachedProperties/ClipHelper.cs
@@ -0,0 +1,43 @@
+using System;
+using Windows.UI.Composition;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Hosting;
+using JetBrains.Annotations;
+
+namespace UICompositionAnimations.AttachedProperties
+{
+ ///
+ /// A with an attached property to set a composition clip to instances
+ ///
+ [PublicAPI]
+ public sealed class ClipHelper
+ {
+ ///
+ /// Gets the value of
+ ///
+ /// The to read the property value from
+ public static bool GetClipToBounds(UIElement element) => element.GetValue(ClipToBoundsProperty).To();
+
+ ///
+ /// Sets the value of
+ ///
+ /// The to set the property to
+ /// The new value of the attached property
+ public static void SetClipToBounds(UIElement element, bool value) => element.SetValue(ClipToBoundsProperty, value);
+
+ ///
+ /// A property that indicates whether or not the contents of the target should always be clipped to their parent's bounds
+ ///
+ public static readonly DependencyProperty ClipToBoundsProperty = DependencyProperty.RegisterAttached(
+ "ClipToBounds",
+ typeof(bool),
+ typeof(ClipHelper),
+ new PropertyMetadata(false, OnClipToBoundsPropertyChanged));
+
+ private static void OnClipToBoundsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ Visual visual = ElementCompositionPreview.GetElementVisual(d.To());
+ visual.Clip = e.NewValue.To() ? visual.Compositor.CreateInsetClip() : null;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs b/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs
deleted file mode 100644
index f0f9ccb..0000000
--- a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Windows.Graphics.Effects;
-using Windows.UI;
-using Windows.UI.Composition;
-using JetBrains.Annotations;
-using Microsoft.Graphics.Canvas;
-using Microsoft.Graphics.Canvas.Effects;
-using Microsoft.Graphics.Canvas.UI.Xaml;
-using UICompositionAnimations.Enums;
-using UICompositionAnimations.Helpers;
-
-namespace UICompositionAnimations.Behaviours
-{
- ///
- /// An helper class that manages the tint and noise layers on the custom acrylic brush effect
- ///
- internal static class AcrylicEffectHelper
- {
- ///
- /// Concatenates the source effect with a tint overlay and a border effect
- ///
- /// The current object to use
- /// The source effect to insert in the pipeline
- /// A dictionary to use to keep track of reference parameters to add when creating a
- /// The tint color
- /// The amount of tint color to apply
- /// The optional to use to generate the image for the
- /// The path to the source image to use for the
- /// The resulting effect through the pipeline
- /// The method does side effect on the variable
- [MustUseReturnValue, ItemNotNull]
- public static async Task ConcatenateEffectWithTintAndBorderAsync(
- [NotNull] Compositor compositor,
- [NotNull] IGraphicsEffectSource source, [NotNull] IDictionary parameters,
- Color color, float colorMix,
- [CanBeNull] CanvasControl canvas, [NotNull] Uri uri)
- {
- // Setup the tint effect
- ArithmeticCompositeEffect tint = new ArithmeticCompositeEffect
- {
- MultiplyAmount = 0,
- Source1Amount = 1 - colorMix,
- Source2Amount = colorMix, // Mix the background with the desired tint color
- Source1 = source,
- Source2 = new ColorSourceEffect { Color = color }
- };
-
- // Get the noise brush using Win2D
- CompositionSurfaceBrush noiseBitmap = canvas == null
- ? await Win2DImageHelper.LoadImageAsync(compositor, uri, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode.Default)
- : await Win2DImageHelper.LoadImageAsync(compositor, canvas, uri, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode.Default);
-
- // Make sure the Win2D brush was loaded correctly
- if (noiseBitmap != null)
- {
- // Layer 4 - Noise effect
- BorderEffect borderEffect = new BorderEffect
- {
- ExtendX = CanvasEdgeBehavior.Wrap,
- ExtendY = CanvasEdgeBehavior.Wrap,
- Source = new CompositionEffectSourceParameter(nameof(noiseBitmap))
- };
- BlendEffect blendEffect = new BlendEffect
- {
- Background = tint,
- Foreground = borderEffect,
- Mode = BlendEffectMode.Overlay
- };
- parameters.Add(nameof(noiseBitmap), noiseBitmap);
- return blendEffect;
- }
- return tint;
- }
-
- ///
- /// Overlays a texture on a tint effect
- ///
- /// The current object to use
- /// A dictionary to use to keep track of reference parameters to add when creating a
- /// The tint color
- /// The path to the source image to use for the
- /// The resulting effect through the pipeline
- /// The method does side effect on the variable
- [MustUseReturnValue, ItemNotNull]
- public static async Task LoadTextureEffectWithTintAsync(
- [NotNull] Compositor compositor, [NotNull] IDictionary parameters,
- Color color, [NotNull] Uri uri)
- {
- // Initial setup
- ColorSourceEffect colorEffect = new ColorSourceEffect { Color = color };
- CompositionSurfaceBrush noiseBitmap = await Win2DImageHelper.LoadImageAsync(compositor, uri, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode.Default);
- if (noiseBitmap == null) return colorEffect;
-
- // Blend the effects
- BorderEffect borderEffect = new BorderEffect
- {
- ExtendX = CanvasEdgeBehavior.Wrap,
- ExtendY = CanvasEdgeBehavior.Wrap,
- Source = new CompositionEffectSourceParameter(nameof(noiseBitmap))
- };
- BlendEffect blendEffect = new BlendEffect
- {
- Background = colorEffect,
- Foreground = borderEffect,
- Mode = BlendEffectMode.Overlay
- };
- parameters.Add(nameof(noiseBitmap), noiseBitmap);
- return blendEffect;
- }
- }
-}
\ No newline at end of file
diff --git a/UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs b/UICompositionAnimations/Behaviours/BrushProvider.cs
similarity index 74%
rename from UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs
rename to UICompositionAnimations/Behaviours/BrushProvider.cs
index 7ab97a6..06ce3a3 100644
--- a/UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs
+++ b/UICompositionAnimations/Behaviours/BrushProvider.cs
@@ -9,7 +9,7 @@ namespace UICompositionAnimations.Behaviours
/// A simple container used to store info on a custom composition effect to create
///
[PublicAPI]
- public sealed class CompositionSourceInitializer
+ public sealed class BrushProvider
{
///
/// Gets the stored effect initializer
@@ -23,7 +23,7 @@ public sealed class CompositionSourceInitializer
[NotNull]
internal string Name { get; }
- private CompositionSourceInitializer([NotNull] string name, [NotNull] Func> initializer)
+ private BrushProvider([NotNull] string name, [NotNull] Func> initializer)
{
Name = name;
Initializer = initializer;
@@ -35,7 +35,7 @@ private CompositionSourceInitializer([NotNull] string name, [NotNull] FuncThe target effect name
/// A instance that will produce the to use to initialize the effect
[Pure, NotNull]
- public static CompositionSourceInitializer New([NotNull] string name, [NotNull] Func initializer) => new CompositionSourceInitializer(name, () => Task.FromResult(initializer()));
+ public static BrushProvider New([NotNull] string name, [NotNull] Func initializer) => new BrushProvider(name, () => Task.FromResult(initializer()));
///
/// Creates a new instance with the info on a given to initialize
@@ -43,6 +43,6 @@ private CompositionSourceInitializer([NotNull] string name, [NotNull] FuncThe target effect name
/// An asynchronous instance that will produce the to use to initialize the effect
[Pure, NotNull]
- public static CompositionSourceInitializer New([NotNull] string name, [NotNull] Func> initializer) => new CompositionSourceInitializer(name, initializer);
+ public static BrushProvider New([NotNull] string name, [NotNull] Func> initializer) => new BrushProvider(name, initializer);
}
}
diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/PipelineBuilder.cs
similarity index 73%
rename from UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs
rename to UICompositionAnimations/Behaviours/PipelineBuilder.cs
index 87f0069..2aa121a 100644
--- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs
+++ b/UICompositionAnimations/Behaviours/PipelineBuilder.cs
@@ -12,9 +12,9 @@
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using UICompositionAnimations.Brushes;
-using UICompositionAnimations.Brushes.Cache;
using UICompositionAnimations.Enums;
using UICompositionAnimations.Helpers;
+using UICompositionAnimations.Helpers.Cache;
namespace UICompositionAnimations.Behaviours
{
@@ -30,7 +30,7 @@ namespace UICompositionAnimations.Behaviours
/// A that allows to build custom effects pipelines and create instances from them
///
[PublicAPI]
- public sealed class CompositionBrushBuilder
+ public sealed class PipelineBuilder
{
///
/// The instance used to produce the output for this pipeline
@@ -56,7 +56,7 @@ public sealed class CompositionBrushBuilder
/// Constructor used to initialize a pipeline from a , for example using the method
///
/// A instance that will return the initial
- private CompositionBrushBuilder([NotNull] Func> factory)
+ private PipelineBuilder([NotNull] Func> factory)
{
string
guid = Guid.NewGuid().ToString("N"),
@@ -73,7 +73,7 @@ private CompositionBrushBuilder([NotNull] Func> factory)
/// A instance that will produce the new to add to the pipeline
/// The collection of animation properties for the new effect
/// The collection of instances that needs to be initialized for the new effect
- private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] [ItemNotNull] IReadOnlyCollection animations, [NotNull] IReadOnlyDictionary>> lazy)
+ private PipelineBuilder([NotNull] Func> factory, [NotNull] [ItemNotNull] IReadOnlyCollection animations, [NotNull] IReadOnlyDictionary>> lazy)
{
SourceProducer = factory;
AnimationProperties = animations;
@@ -84,7 +84,7 @@ private CompositionBrushBuilder([NotNull] Func> fact
/// Constructor used to initialize a pipeline from a custom instance
///
/// A instance that will return the initial
- private CompositionBrushBuilder([NotNull] Func> factory)
+ private PipelineBuilder([NotNull] Func> factory)
: this(factory, new string[0], new Dictionary>>())
{ }
@@ -95,7 +95,7 @@ private CompositionBrushBuilder([NotNull] Func> fact
/// A instance that will produce the new to add to the pipeline
/// The collection of animation properties for the new effect
/// The collection of instances that needs to be initialized for the new effect
- private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source,
+ private PipelineBuilder([NotNull] PipelineBuilder source,
[NotNull] Func> factory,
[CanBeNull] [ItemNotNull] IReadOnlyCollection animations = null, [CanBeNull] IReadOnlyDictionary>> lazy = null)
: this(factory, animations?.Merge(source.AnimationProperties) ?? source.AnimationProperties, lazy?.Merge(source.LazyParameters) ?? source.LazyParameters)
@@ -109,8 +109,8 @@ private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source,
/// The second pipeline to merge
/// The collection of animation properties for the new effect
/// The collection of instances that needs to be initialized for the new effect
- private CompositionBrushBuilder([NotNull] Func> factory,
- [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b,
+ private PipelineBuilder([NotNull] Func> factory,
+ [NotNull] PipelineBuilder a, [NotNull] PipelineBuilder b,
[CanBeNull] [ItemNotNull] IReadOnlyCollection animations = null, [CanBeNull] IReadOnlyDictionary>> lazy = null)
: this(factory, animations?.Merge(a.AnimationProperties.Merge(b.AnimationProperties)) ?? a.AnimationProperties.Merge(b.AnimationProperties), lazy?.Merge(a.LazyParameters.Merge(b.LazyParameters)) ?? a.LazyParameters.Merge(b.LazyParameters))
{ }
@@ -124,78 +124,78 @@ private CompositionBrushBuilder([NotNull] Func> fact
private static readonly ThreadSafeCompositionCache BackdropBrushCache = new ThreadSafeCompositionCache();
///
- /// Starts a new pipeline from the returned by
+ /// Starts a new pipeline from the returned by
///
[Pure, NotNull]
- public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => BackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateBackdropBrush));
+ public static PipelineBuilder FromBackdropBrush() => new PipelineBuilder(() => BackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateBackdropBrush));
// The cache manager for host backdrop brushes
[NotNull]
private static readonly ThreadSafeCompositionCache HostBackdropBrushCache = new ThreadSafeCompositionCache();
///
- /// Starts a new pipeline from the returned by
+ /// Starts a new pipeline from the returned by
///
[Pure, NotNull]
- public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => HostBackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateHostBackdropBrush));
+ public static PipelineBuilder FromHostBackdropBrush() => new PipelineBuilder(() => HostBackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateHostBackdropBrush));
///
- /// Starts a new pipeline from a solid with the specified color
+ /// Starts a new pipeline from a solid with the specified color
///
/// The desired color for the initial
[Pure, NotNull]
- public static CompositionBrushBuilder FromColor(Color color) => new CompositionBrushBuilder(() => Task.FromResult(new ColorSourceEffect { Color = color }.To()));
+ public static PipelineBuilder FromColor(Color color) => new PipelineBuilder(() => Task.FromResult(new ColorSourceEffect { Color = color }.To()));
///
- /// Starts a new pipeline from the input instance
+ /// Starts a new pipeline from the input instance
///
/// A that synchronously returns a instance to start the pipeline
[Pure, NotNull]
- public static CompositionBrushBuilder FromBrush(Func factory) => new CompositionBrushBuilder(() => Task.FromResult(factory()));
+ public static PipelineBuilder FromBrush(Func factory) => new PipelineBuilder(() => Task.FromResult(factory()));
///
- /// Starts a new pipeline from the input instance
+ /// Starts a new pipeline from the input instance
///
/// A that asynchronously returns a instance to start the pipeline
[Pure, NotNull]
- public static CompositionBrushBuilder FromBrush(Func> factory) => new CompositionBrushBuilder(factory);
+ public static PipelineBuilder FromBrush(Func> factory) => new PipelineBuilder(factory);
///
- /// Starts a new pipeline from the input instance
+ /// Starts a new pipeline from the input instance
///
/// A that synchronously returns a instance to start the pipeline
[Pure, NotNull]
- public static CompositionBrushBuilder FromEffect(Func factory) => new CompositionBrushBuilder(() => Task.FromResult(factory()));
+ public static PipelineBuilder FromEffect(Func factory) => new PipelineBuilder(() => Task.FromResult(factory()));
///
- /// Starts a new pipeline from the input instance
+ /// Starts a new pipeline from the input instance
///
/// A that asynchronously returns a instance to start the pipeline
[Pure, NotNull]
- public static CompositionBrushBuilder FromEffect(Func> factory) => new CompositionBrushBuilder(factory);
+ public static PipelineBuilder FromEffect(Func> factory) => new PipelineBuilder(factory);
///
- /// Starts a new pipeline from a Win2D image
+ /// Starts a new pipeline from a Win2D image
///
/// The path for the image to load
/// Indicates the desired DPI mode to use when loading the image
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromImage([NotNull] Uri uri, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static PipelineBuilder FromImage([NotNull] Uri uri, DpiMode dpiMode = DpiMode.DisplayDpiWith96AsLowerBound, CacheMode cache = CacheMode.Default)
{
- return new CompositionBrushBuilder(() => Win2DImageHelper.LoadImageAsync(Window.Current.Compositor, uri, dpiMode, cache).ContinueWith(t => t.Result as CompositionBrush));
+ return new PipelineBuilder(() => Win2DImageHelper.LoadImageAsync(Window.Current.Compositor, uri, dpiMode, cache).ContinueWith(t => t.Result as CompositionBrush));
}
///
- /// Starts a new pipeline from a Win2D image tiled to cover the available space
+ /// Starts a new pipeline from a Win2D image tiled to cover the available space
///
/// The path for the image to load
/// Indicates the desired DPI mode to use when loading the image
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static PipelineBuilder FromTiles([NotNull] Uri uri, DpiMode dpiMode = DpiMode.DisplayDpiWith96AsLowerBound, CacheMode cache = CacheMode.Default)
{
- CompositionBrushBuilder image = FromImage(uri, dpiMode, cache);
+ PipelineBuilder image = FromImage(uri, dpiMode, cache);
async Task Factory() => new BorderEffect
{
@@ -204,17 +204,17 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode
Source = await image.SourceProducer()
};
- return new CompositionBrushBuilder(image, Factory);
+ return new PipelineBuilder(image, Factory);
}
///
- /// Starts a new pipeline from the returned by on the input
+ /// Starts a new pipeline from the returned by on the input
///
/// The source to use to create the pipeline
[Pure, NotNull]
- public static CompositionBrushBuilder FromUIElement([NotNull] UIElement element)
+ public static PipelineBuilder FromUIElement([NotNull] UIElement element)
{
- return new CompositionBrushBuilder(() => Task.FromResult(element.GetVisual().Compositor.CreateBackdropBrush().To()));
+ return new PipelineBuilder(() => Task.FromResult(element.GetVisual().Compositor.CreateBackdropBrush().To()));
}
#endregion
@@ -222,25 +222,25 @@ public static CompositionBrushBuilder FromUIElement([NotNull] UIElement element)
#region Prebuilt pipelines
///
- /// Returns a new instance that implements the host backdrop acrylic effect
+ /// Returns a new instance that implements the host backdrop acrylic effect
///
/// The tint color to use
/// The amount of tint to apply over the current effect
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float mix, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static PipelineBuilder FromHostBackdropAcrylic(Color tint, float mix, [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromHostBackdropBrush()
.Effect(source => new LuminanceToAlphaEffect { Source = source })
.Opacity(0.4f)
.Blend(FromHostBackdropBrush(), BlendEffectMode.Multiply)
.Tint(tint, mix)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
///
- /// Returns a new instance that implements the host backdrop acrylic effect
+ /// Returns a new instance that implements the host backdrop acrylic effect
///
/// The tint color to use
/// The animation to apply on the tint color of the effect
@@ -248,18 +248,18 @@ public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float mix, out EffectAnimation tintAnimation, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static PipelineBuilder FromHostBackdropAcrylic(Color tint, float mix, out EffectAnimation tintAnimation, [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromHostBackdropBrush()
.Effect(source => new LuminanceToAlphaEffect { Source = source })
.Opacity(0.4f)
.Blend(FromHostBackdropBrush(), BlendEffectMode.Multiply)
.Tint(tint, mix, out tintAnimation)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
///
- /// Returns a new instance that implements the in-app backdrop acrylic effect
+ /// Returns a new instance that implements the in-app backdrop acrylic effect
///
/// The tint color to use
/// The amount of tint to apply over the current effect
@@ -267,16 +267,16 @@ public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromBackdropAcrylic(Color tint, float mix, float blur, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static PipelineBuilder FromBackdropAcrylic(Color tint, float mix, float blur, [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromBackdropBrush()
.Tint(tint, mix)
.Blur(blur)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
///
- /// Returns a new instance that implements the in-app backdrop acrylic effect
+ /// Returns a new instance that implements the in-app backdrop acrylic effect
///
/// The tint color to use
/// The amount of tint to apply over the current effect
@@ -285,19 +285,19 @@ public static CompositionBrushBuilder FromBackdropAcrylic(Color tint, float mix,
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromBackdropAcrylic(
+ public static PipelineBuilder FromBackdropAcrylic(
Color tint, float mix, out EffectAnimation tintAnimation,
float blur,
- [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromBackdropBrush()
.Tint(tint, mix, out tintAnimation)
.Blur(blur)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
///
- /// Returns a new instance that implements the in-app backdrop acrylic effect
+ /// Returns a new instance that implements the in-app backdrop acrylic effect
///
/// The tint color to use
/// The amount of tint to apply over the current effect
@@ -306,19 +306,19 @@ public static CompositionBrushBuilder FromBackdropAcrylic(
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromBackdropAcrylic(
+ public static PipelineBuilder FromBackdropAcrylic(
Color tint, float mix,
float blur, out EffectAnimation blurAnimation,
- [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromBackdropBrush()
.Tint(tint, mix)
.Blur(blur, out blurAnimation)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
///
- /// Returns a new instance that implements the in-app backdrop acrylic effect
+ /// Returns a new instance that implements the in-app backdrop acrylic effect
///
/// The tint color to use
/// The amount of tint to apply over the current effect
@@ -328,15 +328,15 @@ public static CompositionBrushBuilder FromBackdropAcrylic(
/// The for the noise texture to load for the acrylic effect
/// The cache mode to use to load the image
[Pure, NotNull]
- public static CompositionBrushBuilder FromBackdropAcrylic(
+ public static PipelineBuilder FromBackdropAcrylic(
Color tint, float mix, out EffectAnimation tintAnimation,
float blur, out EffectAnimation blurAnimation,
- [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default)
+ [NotNull] Uri noiseUri, CacheMode cache = CacheMode.Default)
{
return FromBackdropBrush()
.Tint(tint, mix, out tintAnimation)
.Blur(blur, out blurAnimation)
- .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background);
+ .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, Placement.Background);
}
#endregion
@@ -346,13 +346,13 @@ public static CompositionBrushBuilder FromBackdropAcrylic(
///
/// Blends two pipelines using a instance with the specified mode
///
- /// The second instance to blend
+ /// The second instance to blend
/// The desired to use to blend the input pipelines
/// The sorting mode to use with the two input pipelines
[Pure, NotNull]
- public CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder pipeline, BlendEffectMode mode, EffectPlacement sorting = EffectPlacement.Foreground)
+ public PipelineBuilder Blend([NotNull] PipelineBuilder pipeline, BlendEffectMode mode, Placement sorting = Placement.Foreground)
{
- (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this);
+ var (foreground, background) = sorting == Placement.Foreground ? (this, pipeline) : (pipeline, this);
async Task Factory() => new BlendEffect
{
@@ -361,20 +361,20 @@ public CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder pipeline,
Mode = mode
};
- return new CompositionBrushBuilder(Factory, foreground, background);
+ return new PipelineBuilder(Factory, foreground, background);
}
///
/// Blends two pipelines using an instance
///
- /// The second instance to blend
+ /// The second instance to blend
/// The cross fade factor to blend the input effects
/// The sorting mode to use with the two input pipelines
[Pure, NotNull]
- public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float factor = 0.5f, EffectPlacement sorting = EffectPlacement.Foreground)
+ public PipelineBuilder Mix([NotNull] PipelineBuilder pipeline, float factor = 0.5f, Placement sorting = Placement.Foreground)
{
if (factor < 0 || factor > 1) throw new ArgumentOutOfRangeException(nameof(factor), "The factor must be in the [0,1] range");
- (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this);
+ var (foreground, background) = sorting == Placement.Foreground ? (this, pipeline) : (pipeline, this);
async Task Factory() => new CrossFadeEffect
{
@@ -383,22 +383,22 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f
Source2 = await background.SourceProducer()
};
- return new CompositionBrushBuilder(Factory, foreground, background);
+ return new PipelineBuilder(Factory, foreground, background);
}
///
/// Blends two pipelines using an instance
///
- /// The second instance to blend
+ /// The second instance to blend
/// The cross fade factor to blend the input effects
/// The optional blur animation for the effect
/// The sorting mode to use with the two input pipelines
/// Note that each pipeline can only contain a single instance of any of the built-in effects with animation support
[Pure, NotNull]
- public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float factor, out EffectAnimation animation, EffectPlacement sorting = EffectPlacement.Foreground)
+ public PipelineBuilder Mix([NotNull] PipelineBuilder pipeline, float factor, out EffectAnimation animation, Placement sorting = Placement.Foreground)
{
if (factor < 0 || factor > 1) throw new ArgumentOutOfRangeException(nameof(factor), "The factor must be in the [0,1] range");
- (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this);
+ var (foreground, background) = sorting == Placement.Foreground ? (this, pipeline) : (pipeline, this);
async Task Factory() => new CrossFadeEffect
{
@@ -414,7 +414,7 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f
return brush.StartAnimationAsync("Fade.CrossFade", value, TimeSpan.FromMilliseconds(ms));
};
- return new CompositionBrushBuilder(Factory, foreground, background, new[] { "Fade.CrossFade" });
+ return new PipelineBuilder(Factory, foreground, background, new[] { "Fade.CrossFade" });
}
///
@@ -425,14 +425,14 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f
/// The list of optional animatable properties in the returned effect
/// The list of source parameters that require deferred initialization (see for more info)
[Pure, NotNull]
- public CompositionBrushBuilder Merge(
+ public PipelineBuilder Merge(
[NotNull] Func factory,
- [NotNull] CompositionBrushBuilder background,
- IEnumerable animations = null, IEnumerable initializers = null)
+ [NotNull] PipelineBuilder background,
+ IEnumerable animations = null, IEnumerable initializers = null)
{
async Task Factory() => factory(await SourceProducer(), await background.SourceProducer());
- return new CompositionBrushBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
+ return new PipelineBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
}
///
@@ -443,14 +443,14 @@ public CompositionBrushBuilder Merge(
/// The list of optional animatable properties in the returned effect
/// The list of source parameters that require deferred initialization (see for more info)
[Pure, NotNull]
- public CompositionBrushBuilder Merge(
+ public PipelineBuilder Merge(
[NotNull] Func> factory,
- [NotNull] CompositionBrushBuilder background,
- IEnumerable animations = null, IEnumerable initializers = null)
+ [NotNull] PipelineBuilder background,
+ IEnumerable animations = null, IEnumerable initializers = null)
{
async Task Factory() => await factory(await SourceProducer(), await background.SourceProducer());
- return new CompositionBrushBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
+ return new PipelineBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
}
#endregion
@@ -464,7 +464,7 @@ public CompositionBrushBuilder Merge(
/// The parameter for the effect, defaults to
/// The parameter to use, defaults to
[Pure, NotNull]
- public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBorderMode.Hard, EffectOptimization optimization = EffectOptimization.Balanced)
+ public PipelineBuilder Blur(float blur, EffectBorderMode mode = EffectBorderMode.Hard, EffectOptimization optimization = EffectOptimization.Balanced)
{
// Blur effect
async Task Factory() => new GaussianBlurEffect
@@ -475,7 +475,7 @@ public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBo
Source = await SourceProducer()
};
- return new CompositionBrushBuilder(this, Factory);
+ return new PipelineBuilder(this, Factory);
}
///
@@ -487,7 +487,7 @@ public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBo
/// The parameter to use, defaults to
/// Note that each pipeline can only contain a single instance of any of the built-in effects with animation support
[Pure, NotNull]
- public CompositionBrushBuilder Blur(float blur, out EffectAnimation animation, EffectBorderMode mode = EffectBorderMode.Hard, EffectOptimization optimization = EffectOptimization.Balanced)
+ public PipelineBuilder Blur(float blur, out EffectAnimation animation, EffectBorderMode mode = EffectBorderMode.Hard, EffectOptimization optimization = EffectOptimization.Balanced)
{
// Blur effect
async Task Factory() => new GaussianBlurEffect
@@ -501,7 +501,7 @@ public CompositionBrushBuilder Blur(float blur, out EffectAnimation animation, E
animation = (brush, value, ms) => brush.StartAnimationAsync("Blur.BlurAmount", value, TimeSpan.FromMilliseconds(ms));
- return new CompositionBrushBuilder(this, Factory, new[] { "Blur.BlurAmount" });
+ return new PipelineBuilder(this, Factory, new[] { "Blur.BlurAmount" });
}
///
@@ -509,7 +509,7 @@ public CompositionBrushBuilder Blur(float blur, out EffectAnimation animation, E
///
/// The saturation amount for the new effect
[Pure, NotNull]
- public CompositionBrushBuilder Saturation(float saturation)
+ public PipelineBuilder Saturation(float saturation)
{
if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException(nameof(saturation), "The saturation must be in the [0,1] range");
async Task Factory() => new SaturationEffect
@@ -518,7 +518,7 @@ public CompositionBrushBuilder Saturation(float saturation)
Source = await SourceProducer()
};
- return new CompositionBrushBuilder(this, Factory);
+ return new PipelineBuilder(this, Factory);
}
///
@@ -528,7 +528,7 @@ public CompositionBrushBuilder Saturation(float saturation)
/// The optional saturation animation for the effect
/// Note that each pipeline can only contain a single instance of any of the built-in effects with animation support
[Pure, NotNull]
- public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation animation)
+ public PipelineBuilder Saturation(float saturation, out EffectAnimation animation)
{
if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException(nameof(saturation), "The saturation must be in the [0,1] range");
async Task Factory() => new SaturationEffect
@@ -544,7 +544,7 @@ public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation
return brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms));
};
- return new CompositionBrushBuilder(this, Factory, new[] { "Saturation.Saturation" });
+ return new PipelineBuilder(this, Factory, new[] { "Saturation.Saturation" });
}
///
@@ -552,7 +552,7 @@ public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation
///
/// The opacity value to apply to the pipeline
[Pure, NotNull]
- public CompositionBrushBuilder Opacity(float opacity)
+ public PipelineBuilder Opacity(float opacity)
{
if (opacity < 0 || opacity > 1) throw new ArgumentOutOfRangeException(nameof(opacity), "The opacity must be in the [0,1] range");
async Task Factory() => new OpacityEffect
@@ -561,7 +561,7 @@ public CompositionBrushBuilder Opacity(float opacity)
Source = await SourceProducer()
};
- return new CompositionBrushBuilder(this, Factory);
+ return new PipelineBuilder(this, Factory);
}
///
@@ -571,7 +571,7 @@ public CompositionBrushBuilder Opacity(float opacity)
/// The optional opacity animation for the effect
/// Note that each pipeline can only contain a single instance of any of the built-in effects with animation support
[Pure, NotNull]
- public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animation)
+ public PipelineBuilder Opacity(float opacity, out EffectAnimation animation)
{
if (opacity < 0 || opacity > 1) throw new ArgumentOutOfRangeException(nameof(opacity), "The opacity must be in the [0,1] range");
async Task Factory() => new OpacityEffect
@@ -587,7 +587,7 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat
return brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms));
};
- return new CompositionBrushBuilder(this, Factory, new[] { "Opacity.Opacity" });
+ return new PipelineBuilder(this, Factory, new[] { "Opacity.Opacity" });
}
///
@@ -596,7 +596,7 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat
/// The tint color to use
/// The amount of tint to apply over the current effect
[Pure, NotNull]
- public CompositionBrushBuilder Tint(Color color, float mix) => FromColor(color).Mix(this, mix);
+ public PipelineBuilder Tint(Color color, float mix) => FromColor(color).Mix(this, mix);
///
/// Applies a tint color on the current pipeline
@@ -606,7 +606,7 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat
/// The optional tint animation for the effect
/// Note that each pipeline can only contain a single instance of any of the built-in effects with animation support
[Pure, NotNull]
- public CompositionBrushBuilder Tint(Color color, float mix, out EffectAnimation animation) => FromColor(color).Mix(this, mix, out animation);
+ public PipelineBuilder Tint(Color color, float mix, out EffectAnimation animation) => FromColor(color).Mix(this, mix, out animation);
#endregion
@@ -619,11 +619,11 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat
/// The list of optional animatable properties in the returned effect
/// The list of source parameters that require deferred initialization (see for more info)
[Pure, NotNull]
- public CompositionBrushBuilder Effect([NotNull] Func factory, IEnumerable animations = null, IEnumerable initializers = null)
+ public PipelineBuilder Effect([NotNull] Func factory, IEnumerable animations = null, IEnumerable initializers = null)
{
async Task Factory() => factory(await SourceProducer());
- return new CompositionBrushBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
+ return new PipelineBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
}
///
@@ -633,11 +633,11 @@ public CompositionBrushBuilder Effect([NotNull] FuncThe list of optional animatable properties in the returned effect
/// The list of source parameters that require deferred initialization (see for more info)
[Pure, NotNull]
- public CompositionBrushBuilder Effect([NotNull] Func> factory, IEnumerable animations = null, IEnumerable initializers = null)
+ public PipelineBuilder Effect([NotNull] Func> factory, IEnumerable animations = null, IEnumerable initializers = null)
{
async Task Factory() => await factory(await SourceProducer());
- return new CompositionBrushBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
+ return new PipelineBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer));
}
#endregion
diff --git a/UICompositionAnimations/Brushes/AcrylicBrush.cs b/UICompositionAnimations/Brushes/AcrylicBrush.cs
new file mode 100644
index 0000000..8e4bdc0
--- /dev/null
+++ b/UICompositionAnimations/Brushes/AcrylicBrush.cs
@@ -0,0 +1,51 @@
+using System;
+using Windows.UI;
+using Windows.UI.Xaml.Media;
+using UICompositionAnimations.Behaviours;
+using UICompositionAnimations.Brushes.Base;
+
+namespace UICompositionAnimations.Brushes
+{
+ ///
+ /// A that implements an acrylic effect with customizable parameters
+ ///
+ public sealed class AcrylicBrush : XamlCompositionEffectBrushBase
+ {
+ ///
+ /// Gets or sets the source mode for the effect
+ ///
+ public AcrylicBackgroundSource Source { get; set; }
+
+ ///
+ /// Gets or sets the blur amount for the effect
+ ///
+ /// This property is ignored when the active mode is
+ public double BlurAmount { get; set; }
+
+ ///
+ /// Gets or sets the tint for the effect
+ ///
+ public Color Tint { get; set; }
+
+ ///
+ /// Gets or sets the color for the tint effect
+ ///
+ public double TintMix { get; set; }
+
+ ///
+ /// Gets or sets the to the texture to use
+ ///
+ public Uri TextureUri { get; set; }
+
+ ///
+ protected override PipelineBuilder OnBrushRequested()
+ {
+ switch (Source)
+ {
+ case AcrylicBackgroundSource.Backdrop: return PipelineBuilder.FromBackdropAcrylic(Tint, (float)TintMix, (float)BlurAmount, TextureUri);
+ case AcrylicBackgroundSource.HostBackdrop: return PipelineBuilder.FromHostBackdropAcrylic(Tint, (float)TintMix, TextureUri);
+ default: throw new ArgumentOutOfRangeException(nameof(Source), $"Invalid acrylic source: {Source}");
+ }
+ }
+ }
+}
diff --git a/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs b/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs
index 685bd9a..a967254 100644
--- a/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs
+++ b/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs
@@ -6,7 +6,7 @@
namespace UICompositionAnimations.Brushes.Base
{
///
- /// A custom that's ready to be used with a custom pipeline.
+ /// A custom that's ready to be used with a custom pipeline.
///
[PublicAPI]
public abstract class XamlCompositionEffectBrushBase : XamlCompositionBrushBase
@@ -16,11 +16,11 @@ public abstract class XamlCompositionEffectBrushBase : XamlCompositionBrushBase
private readonly AsyncMutex ConnectedMutex = new AsyncMutex();
///
- /// A method that builds and returns the pipeline to use in the current instance.
+ /// A method that builds and returns the pipeline to use in the current instance.
/// This method can also be used to store any needed instances in local fields, for later use (they will need to be called upon ).
///
[MustUseReturnValue, NotNull]
- protected abstract CompositionBrushBuilder OnBrushRequested();
+ protected abstract PipelineBuilder OnBrushRequested();
///
protected override async void OnConnected()
diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs
deleted file mode 100644
index df67cc5..0000000
--- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs
+++ /dev/null
@@ -1,441 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Windows.Graphics.Effects;
-using Windows.UI;
-using Microsoft.Graphics.Canvas.Effects;
-using Windows.UI.Composition;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Media;
-using UICompositionAnimations.Behaviours;
-using UICompositionAnimations.Brushes.Cache;
-using UICompositionAnimations.Enums;
-using UICompositionAnimations.Helpers;
-using Windows.ApplicationModel;
-using JetBrains.Annotations;
-
-namespace UICompositionAnimations.Brushes
-{
- ///
- /// A custom XAML brush that includes an acrylic effect that blurs the in-app content
- ///
- public sealed class CustomAcrylicBrush : XamlCompositionBrushBase
- {
- #region Constants
-
- // The name of the animatable blur amount property
- private const string BlurAmountParameterName = "Blur.BlurAmount";
-
- // The name of the animatable source 1 property (the brush) of the tint effect
- private const string TintColor1ParameterName = "Tint.Source1Amount";
-
- // The name of the animatable source 2 property (the tint color) of the tint effect
- private const string TintColor2ParameterName = "Tint.Source2Amount";
-
- // The name of the animatable color property of the color effect
- private const string ColorSourceParameterName = "ColorSource.Color";
-
- // The name of the animatable color property of the fallback color effect
- private const string FallbackColorParameterName = "FallbackColor.Color";
-
- #endregion
-
- #region Properties
-
- ///
- /// Gets or sets the source mode for the custom acrylic effect
- ///
- public AcrylicEffectMode Mode
- {
- get => GetValue(ModeProperty).To();
- set => SetValue(ModeProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty ModeProperty =
- DependencyProperty.Register(nameof(Mode), typeof(AcrylicEffectMode), typeof(CustomAcrylicBrush), new PropertyMetadata(AcrylicEffectMode.InAppBlur, OnModePropertyChanged));
-
- private static async void OnModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- CustomAcrylicBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- if (@this.CompositionBrush != null)
- {
- // Rebuild the effects pipeline when needed
- @this.CompositionBrush.Dispose();
- @this.CompositionBrush = null;
- @this._State = AcrylicBrushEffectState.Default;
- await @this.SetupEffectAsync();
- }
- @this.ConnectedSemaphore.Release();
- }
-
- ///
- /// Gets or sets the blur amount for the effect
- ///
- /// This property is ignored when the active mode is
- public double BlurAmount
- {
- get => GetValue(BlurAmountProperty).To();
- set => SetValue(BlurAmountProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty BlurAmountProperty =
- DependencyProperty.Register(nameof(BlurAmount), typeof(double), typeof(CustomAcrylicBrush), new PropertyMetadata(8d, OnBlurAmountPropertyChanged));
-
- private static async void OnBlurAmountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- CustomAcrylicBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- if (@this.Mode == AcrylicEffectMode.InAppBlur)
- {
- // Update the blur value with or without an animation
- if (@this.BlurAnimationDuration == 0)
- {
- @this._EffectBrush?.Properties.InsertScalar(BlurAmountParameterName, (float)e.NewValue.To());
- }
- else @this._EffectBrush?.Properties.StartAnimationAsync(BlurAmountParameterName, (float)e.NewValue, TimeSpan.FromMilliseconds(@this.BlurAnimationDuration));
- }
- @this.ConnectedSemaphore.Release();
- }
-
- ///
- /// Gets or sets the duration of the optional animation played when changing the property
- ///
- /// This property is ignored when the active mode is
- public int BlurAnimationDuration
- {
- get => GetValue(BlurAnimationDurationProperty).To();
- set => SetValue(BlurAnimationDurationProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty BlurAnimationDurationProperty =
- DependencyProperty.Register(nameof(BlurAnimationDuration), typeof(int), typeof(CustomAcrylicBrush), new PropertyMetadata(0));
-
- ///
- /// Gets or sets the color for the tint effect
- ///
- public Color Tint
- {
- get => GetValue(TintProperty).To();
- set => SetValue(TintProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty TintProperty =
- DependencyProperty.Register(nameof(Tint), typeof(Color), typeof(CustomAcrylicBrush), new PropertyMetadata(Colors.Transparent, OnTintPropertyChanged));
-
- private static async void OnTintPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- CustomAcrylicBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- if (@this.CompositionBrush != null && @this._State != AcrylicBrushEffectState.FallbackMode)
- {
- @this._EffectBrush?.Properties.InsertColor(ColorSourceParameterName, e.NewValue.To());
- }
- @this.ConnectedSemaphore.Release();
- }
-
- ///
- /// Gets or sets the color for the tint effect (NOTE: this value must be in the [0..1) range)
- ///
- public double TintMix
- {
- get => GetValue(TintMixProperty).To();
- set => SetValue(TintMixProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty TintMixProperty =
- DependencyProperty.Register(nameof(TintMix), typeof(double), typeof(CustomAcrylicBrush), new PropertyMetadata(0d, OnTintMixPropertyChanged));
-
- private static async void OnTintMixPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- double value = e.NewValue.To();
- float fvalue = (float)value;
- if (value < 0 || value >= 1) throw new ArgumentOutOfRangeException(nameof(value), "The tint mix must be in the [0..1) range");
- CustomAcrylicBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- if (@this.CompositionBrush != null && @this._State != AcrylicBrushEffectState.FallbackMode)
- {
- @this._EffectBrush.Properties.InsertScalar(TintColor1ParameterName, 1 - fvalue);
- @this._EffectBrush.Properties.InsertScalar(TintColor2ParameterName, fvalue);
- }
- @this.ConnectedSemaphore.Release();
- }
-
- ///
- /// Gets or sets the optional color to use for the brush, if the effect can't be loaded
- ///
- public Color UnsupportedEffectFallbackColor
- {
- get => GetValue(UnsupportedEffectFallbackColorProperty).To();
- set => SetValue(UnsupportedEffectFallbackColorProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty UnsupportedEffectFallbackColorProperty =
- DependencyProperty.Register(nameof(UnsupportedEffectFallbackColor), typeof(Color), typeof(CustomAcrylicBrush),
- new PropertyMetadata(Colors.Transparent, OnUnsupportedEffectFallbackColorPropertyChanged));
-
- private static async void OnUnsupportedEffectFallbackColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- // Unpack and lock
- CustomAcrylicBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- Color value = e.NewValue.To();
-
- // The fallback mode is currently enabled
- if (@this._State == AcrylicBrushEffectState.FallbackMode)
- {
- // New value is null, reset the current effect
- if (value.Equals(Colors.Transparent))
- {
- @this.CompositionBrush.Dispose();
- @this.CompositionBrush = null;
- @this._State = AcrylicBrushEffectState.Default;
- }
- else
- {
- // Otherwise, update the fallback color effect
- @this._EffectBrush?.Properties.InsertColor(FallbackColorParameterName, value);
- }
- }
- @this.ConnectedSemaphore.Release();
- }
-
- ///
- /// Gets or sets the to the noise texture to use
- ///
- /// This property must be initialized before using the brush
- public Uri NoiseTextureUri { get; set; }
-
- ///
- /// Indicates whether or not to enable an additional safety procedure when loading the acrylic brush.
- /// This property should be used for brushes being rendered on secondary windows that use a given acrylic mode for the first
- /// time in the application, to prevent the internal cache from storing a brush instance that would cause the app to crash if
- /// reused in the app primary window, due to the different associated with that window.
- /// This property can stay disabled in most cases and should only be turned on when dealing with particular edge cases or issues
- /// with secondary app windows.
- ///
- /// Turning this property on disables the internal cache system for instances.
- /// This property, like the property, must be initialized before using the brush
- public bool DispatchProtectionEnabled { get; set; }
-
- #endregion
-
- // Initialization semaphore (due to the Win2D image loading being asynchronous)
- private readonly SemaphoreSlim ConnectedSemaphore = new SemaphoreSlim(1);
-
- // The composition brush used to render the effect
- private CompositionEffectBrush _EffectBrush;
-
- // Gets the current brush shate
- private AcrylicBrushEffectState _State = AcrylicBrushEffectState.Default;
-
- ///
- protected override async void OnConnected()
- {
- if (CompositionBrush == null)
- {
- await ConnectedSemaphore.WaitAsync();
- if (CompositionBrush == null) // It could have been initialized while waiting on the semaphore
- {
- await SetupEffectAsync();
- }
- ConnectedSemaphore.Release();
- }
- base.OnConnected();
- }
-
- ///
- protected override async void OnDisconnected()
- {
- if (CompositionBrush != null)
- {
- await ConnectedSemaphore.WaitAsync();
- if (CompositionBrush != null)
- {
- CompositionBrush.Dispose();
- CompositionBrush = null;
- _State = AcrylicBrushEffectState.Default;
- }
- ConnectedSemaphore.Release();
- }
- base.OnDisconnected();
- }
-
- #region CompositionBackdropBrush cache
-
- // The synchronization semaphore for the in-app backdrop brush
- private static readonly SemaphoreSlim BackdropSemaphore = new SemaphoreSlim(1);
-
- // The cached in-app backdrop brush
- private static CompositionBackdropBrush _BackdropInstance;
-
- // The name to use for the in-app backdrop reference parameter
- private const string BackdropReferenceParameterName = "BackdropBrush";
-
- // The synchronization semaphore for the host backdrop brush
- private static readonly SemaphoreSlim HostBackdropSemaphore = new SemaphoreSlim(1);
-
- // The cached host backdrop effect and partial pipeline to reuse
- private static HostBackdropInstanceWrapper _HostBackdropCache;
-
- // The name to use for the host backdrop reference parameter
- private const string HostBackdropReferenceParameterName = "HostBackdropBrush";
-
- ///
- /// Clears the internal cache of instances
- ///
- [PublicAPI]
- public static async Task ClearCacheAsync(AcrylicEffectMode targets)
- {
- // In-app backdrop brush
- if (targets.HasFlag(AcrylicEffectMode.InAppBlur))
- {
- await BackdropSemaphore.WaitAsync();
- _BackdropInstance = null;
- BackdropSemaphore.Release();
- }
-
- // Host backdrop brush
- if (targets.HasFlag(AcrylicEffectMode.HostBackdrop))
- {
- await HostBackdropSemaphore.WaitAsync();
- _HostBackdropCache = null;
- HostBackdropSemaphore.Release();
- }
- }
-
- #endregion
-
- ///
- /// Initializes the appropriate acrylic effect for the current instance
- ///
- private async Task SetupEffectAsync()
- {
- // Designer check
- if (DesignMode.DesignMode2Enabled) return;
-
- // Dictionary to track the reference and animatable parameters
- IDictionary sourceParameters = new Dictionary();
- List animatableParameters = new List
- {
- TintColor1ParameterName,
- TintColor2ParameterName,
- ColorSourceParameterName
- };
-
- // Setup the base effect
- IGraphicsEffectSource baseEffect;
- if (Mode == AcrylicEffectMode.InAppBlur)
- {
- // Manage the cache
- await BackdropSemaphore.WaitAsync();
- if (_BackdropInstance == null && !DispatchProtectionEnabled)
- {
- _BackdropInstance = Window.Current.Compositor.CreateBackdropBrush();
- }
-
- // Prepare the blur effect for the backdrop brush
- baseEffect = new GaussianBlurEffect
- {
- Name = "Blur",
- BlurAmount = 0f, // The blur value is inserted later on as it isn't applied correctly when set from here
- BorderMode = EffectBorderMode.Hard,
- Optimization = EffectOptimization.Balanced,
- Source = new CompositionEffectSourceParameter(nameof(BackdropReferenceParameterName))
- };
- animatableParameters.Add(BlurAmountParameterName);
- sourceParameters.Add(nameof(BackdropReferenceParameterName),
- _BackdropInstance?.Dispatcher.HasThreadAccess == true
- ? _BackdropInstance
- : Window.Current.Compositor.CreateBackdropBrush()); // Create a new instance when on a secondary window
- BackdropSemaphore.Release();
- }
- else
- {
- // Manage the cache
- await HostBackdropSemaphore.WaitAsync();
- if (_HostBackdropCache == null || // Cache not initialized yet
- !_HostBackdropCache.Brush.Dispatcher.HasThreadAccess) // Cache initialized on another UI thread (different window)
- {
- // Prepare a luminosity to alpha effect to adjust the background contrast
- CompositionBackdropBrush hostBackdropBrush = Window.Current.Compositor.CreateHostBackdropBrush();
- CompositionEffectSourceParameter backgroundParameter = new CompositionEffectSourceParameter(nameof(hostBackdropBrush));
- LuminanceToAlphaEffect alphaEffect = new LuminanceToAlphaEffect { Source = backgroundParameter };
- OpacityEffect opacityEffect = new OpacityEffect
- {
- Source = alphaEffect,
- Opacity = 0.4f // Reduce the amount of the effect to avoid making bright areas completely black
- };
-
- // Layer [0,1,3] - Desktop background with blur and opacity mask
- baseEffect = new BlendEffect
- {
- Background = backgroundParameter,
- Foreground = opacityEffect,
- Mode = BlendEffectMode.Multiply
- };
- sourceParameters.Add(HostBackdropReferenceParameterName, hostBackdropBrush);
-
- // Update the cache when needed
- if (_HostBackdropCache == null && !DispatchProtectionEnabled)
- {
- _HostBackdropCache = new HostBackdropInstanceWrapper(baseEffect, hostBackdropBrush);
- }
- }
- else
- {
- // Reuse the cached pipeline and effect
- baseEffect = _HostBackdropCache.Pipeline;
- sourceParameters.Add(HostBackdropReferenceParameterName, _HostBackdropCache.Brush);
- }
- HostBackdropSemaphore.Release();
- }
-
- // Get the noise brush using Win2D
- IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(Window.Current.Compositor,
- baseEffect, sourceParameters, Tint, (float)TintMix, null, NoiseTextureUri);
-
- // Extract and setup the tint and color effects
- ArithmeticCompositeEffect tint = source as ArithmeticCompositeEffect ?? source.To().Background as ArithmeticCompositeEffect;
- if (tint == null) throw new InvalidOperationException("Error while retrieving the tint effect");
- tint.Name = "Tint";
- if (!(tint.Source2 is ColorSourceEffect color)) throw new InvalidOperationException("Error while retrieving the color effect");
- color.Name = "ColorSource";
-
- // Make sure the Win2D brush was loaded correctly
- CompositionEffectFactory factory = Window.Current.Compositor.CreateEffectFactory(source, animatableParameters);
-
- // Create the effect factory and apply the final effect
- _EffectBrush = factory.CreateBrush();
- foreach (KeyValuePair pair in sourceParameters)
- {
- _EffectBrush.SetSourceParameter(pair.Key, pair.Value);
- }
-
- // Update the blur amount and store the effect
- if (Mode == AcrylicEffectMode.InAppBlur)
- _EffectBrush.Properties.InsertScalar(BlurAmountParameterName, (float)BlurAmount);
- CompositionBrush = _EffectBrush;
- _State = AcrylicBrushEffectState.EffectEnabled;
- }
- }
-}
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ImageEffectBase.cs b/UICompositionAnimations/Brushes/Effects/Abstract/ImageEffectBase.cs
similarity index 60%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ImageEffectBase.cs
rename to UICompositionAnimations/Brushes/Effects/Abstract/ImageEffectBase.cs
index 381ada6..f2cd40a 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ImageEffectBase.cs
+++ b/UICompositionAnimations/Brushes/Effects/Abstract/ImageEffectBase.cs
@@ -1,7 +1,8 @@
using System;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
using UICompositionAnimations.Enums;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects.Abstract
+namespace UICompositionAnimations.Brushes.Effects.Abstract
{
///
/// An image based effect that loads an image at the specified location
@@ -14,13 +15,13 @@ public abstract class ImageEffectBase : IPipelineEffect
public Uri Uri { get; set; }
///
- /// Gets or sets the DPI mode used to render the image (the default is )
+ /// Gets or sets the DPI mode used to render the image (the default is )
///
- public BitmapDPIMode DPIMode { get; set; } = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound;
+ public DpiMode DPIMode { get; set; } = DpiMode.DisplayDpiWith96AsLowerBound;
///
- /// Gets or sets the cache mode to use when loading the image (the default is )
+ /// Gets or sets the cache mode to use when loading the image (the default is )
///
- public BitmapCacheMode CacheMode { get; set; } = BitmapCacheMode.Default;
+ public CacheMode CacheMode { get; set; } = CacheMode.Default;
}
}
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ValueEffectBase.cs b/UICompositionAnimations/Brushes/Effects/Abstract/ValueEffectBase.cs
similarity index 75%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ValueEffectBase.cs
rename to UICompositionAnimations/Brushes/Effects/Abstract/ValueEffectBase.cs
index 63bfba1..6bcb00d 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/Abstract/ValueEffectBase.cs
+++ b/UICompositionAnimations/Brushes/Effects/Abstract/ValueEffectBase.cs
@@ -1,4 +1,6 @@
-namespace UICompositionAnimations.Behaviours.Xaml.Effects.Abstract
+using UICompositionAnimations.Brushes.Effects.Interfaces;
+
+namespace UICompositionAnimations.Brushes.Effects.Abstract
{
///
/// A base for an effect that exposes a single parameter
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/BackdropEffect.cs b/UICompositionAnimations/Brushes/Effects/BackdropEffect.cs
similarity index 78%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/BackdropEffect.cs
rename to UICompositionAnimations/Brushes/Effects/BackdropEffect.cs
index f614801..adefa5f 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/BackdropEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/BackdropEffect.cs
@@ -1,6 +1,7 @@
using Windows.UI.Xaml.Media;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A backdrop effect that can sample from a specified source
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/BlendEffect.cs b/UICompositionAnimations/Brushes/Effects/BlendEffect.cs
similarity index 78%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/BlendEffect.cs
rename to UICompositionAnimations/Brushes/Effects/BlendEffect.cs
index aafaf9c..397a8d3 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/BlendEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/BlendEffect.cs
@@ -1,9 +1,10 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.Graphics.Canvas.Effects;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
using UICompositionAnimations.Enums;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A blend effect that merges the current pipeline with an input one
@@ -22,8 +23,8 @@ public sealed class BlendEffect : IPipelineEffect
public BlendEffectMode Mode { get; set; }
///
- /// Gets or sets the placement of the input pipeline with respect to the current one (the default is )
+ /// Gets or sets the placement of the input pipeline with respect to the current one (the default is )
///
- public EffectPlacement Placement { get; set; } = EffectPlacement.Foreground;
+ public Placement Placement { get; set; } = Placement.Foreground;
}
}
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/BlurEffect.cs b/UICompositionAnimations/Brushes/Effects/BlurEffect.cs
similarity index 50%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/BlurEffect.cs
rename to UICompositionAnimations/Brushes/Effects/BlurEffect.cs
index 5d25528..c9323f5 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/BlurEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/BlurEffect.cs
@@ -1,6 +1,6 @@
-using UICompositionAnimations.Behaviours.Xaml.Effects.Abstract;
+using UICompositionAnimations.Brushes.Effects.Abstract;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A gaussian blur effect
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/ImageEffect.cs b/UICompositionAnimations/Brushes/Effects/ImageEffect.cs
similarity index 58%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/ImageEffect.cs
rename to UICompositionAnimations/Brushes/Effects/ImageEffect.cs
index 5f159f3..8b6325b 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/ImageEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/ImageEffect.cs
@@ -1,6 +1,6 @@
-using UICompositionAnimations.Behaviours.Xaml.Effects.Abstract;
+using UICompositionAnimations.Brushes.Effects.Abstract;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// An image effect, which displays an image loaded as a Win2D surface
diff --git a/UICompositionAnimations/Behaviours/Xaml/IPipelineEffect.cs b/UICompositionAnimations/Brushes/Effects/Interfaces/IPipelineEffect.cs
similarity index 75%
rename from UICompositionAnimations/Behaviours/Xaml/IPipelineEffect.cs
rename to UICompositionAnimations/Brushes/Effects/Interfaces/IPipelineEffect.cs
index 6f8c954..b1479ec 100644
--- a/UICompositionAnimations/Behaviours/Xaml/IPipelineEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/Interfaces/IPipelineEffect.cs
@@ -1,4 +1,4 @@
-namespace UICompositionAnimations.Behaviours.Xaml
+namespace UICompositionAnimations.Brushes.Effects.Interfaces
{
///
/// The base for all the pipeline effects to be used in a
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/LuminanceEffect.cs b/UICompositionAnimations/Brushes/Effects/LuminanceEffect.cs
similarity index 66%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/LuminanceEffect.cs
rename to UICompositionAnimations/Brushes/Effects/LuminanceEffect.cs
index f98cce9..5fb2002 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/LuminanceEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/LuminanceEffect.cs
@@ -1,4 +1,6 @@
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+using UICompositionAnimations.Brushes.Effects.Interfaces;
+
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A luminance effect which directly replicates
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/OpacityEffect.cs b/UICompositionAnimations/Brushes/Effects/OpacityEffect.cs
similarity index 51%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/OpacityEffect.cs
rename to UICompositionAnimations/Brushes/Effects/OpacityEffect.cs
index 04f49f9..ca5cc92 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/OpacityEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/OpacityEffect.cs
@@ -1,6 +1,6 @@
-using UICompositionAnimations.Behaviours.Xaml.Effects.Abstract;
+using UICompositionAnimations.Brushes.Effects.Abstract;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A simple opacity effect
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/SaturationEffect.cs b/UICompositionAnimations/Brushes/Effects/SaturationEffect.cs
similarity index 51%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/SaturationEffect.cs
rename to UICompositionAnimations/Brushes/Effects/SaturationEffect.cs
index 78a7efc..6141712 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/SaturationEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/SaturationEffect.cs
@@ -1,6 +1,6 @@
-using UICompositionAnimations.Behaviours.Xaml.Effects.Abstract;
+using UICompositionAnimations.Brushes.Effects.Abstract;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A saturation effect
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/SolidColorEffect.cs b/UICompositionAnimations/Brushes/Effects/SolidColorEffect.cs
similarity index 76%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/SolidColorEffect.cs
rename to UICompositionAnimations/Brushes/Effects/SolidColorEffect.cs
index 05a1003..fbb1284 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/SolidColorEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/SolidColorEffect.cs
@@ -1,6 +1,7 @@
using Windows.UI;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A simple effect that renders a solid color on the available surface
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/TileEffect.cs b/UICompositionAnimations/Brushes/Effects/TileEffect.cs
similarity index 60%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/TileEffect.cs
rename to UICompositionAnimations/Brushes/Effects/TileEffect.cs
index ca9d942..a4fd228 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/TileEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/TileEffect.cs
@@ -1,6 +1,6 @@
-using UICompositionAnimations.Behaviours.Xaml.Effects.Abstract;
+using UICompositionAnimations.Brushes.Effects.Abstract;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// An effect that loads an image and replicates it to cover all the available surface area
diff --git a/UICompositionAnimations/Behaviours/Xaml/Effects/TintEffect.cs b/UICompositionAnimations/Brushes/Effects/TintEffect.cs
similarity index 80%
rename from UICompositionAnimations/Behaviours/Xaml/Effects/TintEffect.cs
rename to UICompositionAnimations/Brushes/Effects/TintEffect.cs
index 404c465..811de70 100644
--- a/UICompositionAnimations/Behaviours/Xaml/Effects/TintEffect.cs
+++ b/UICompositionAnimations/Brushes/Effects/TintEffect.cs
@@ -1,6 +1,7 @@
using Windows.UI;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
-namespace UICompositionAnimations.Behaviours.Xaml.Effects
+namespace UICompositionAnimations.Brushes.Effects
{
///
/// A tint effect with a customizable opacity
diff --git a/UICompositionAnimations/Brushes/LightingBrush.cs b/UICompositionAnimations/Brushes/LightingBrush.cs
deleted file mode 100644
index 0ce927b..0000000
--- a/UICompositionAnimations/Brushes/LightingBrush.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using Microsoft.Graphics.Canvas.Effects;
-using Windows.UI.Composition;
-using Windows.UI.Composition.Effects;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Media;
-using UICompositionAnimations.Helpers;
-
-namespace UICompositionAnimations.Brushes
-{
- ///
- /// A custom XAML brush that includes a lighting effect
- ///
- public sealed class LightingBrush : XamlCompositionBrushBase
- {
- ///
- /// Gets or sets the diffuse property for the brush
- ///
- public double DiffuseAmount
- {
- get => (double)GetValue(DiffuseAmountProperty);
- set => SetValue(DiffuseAmountProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty DiffuseAmountProperty =
- DependencyProperty.Register(nameof(DiffuseAmount), typeof(double), typeof(LightingBrush), new PropertyMetadata(1d, OnDiffuseAmountChanged));
-
- private static void OnDiffuseAmountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- d.To()?.CompositionBrush?.Properties.InsertScalar("Light.DiffuseAmount", (float)(double)e.NewValue);
- }
-
-
- ///
- /// Gets or sets the specular shine of the light. The default value is 16 and it must be between 1 and 128
- ///
- public double SpecularShine
- {
- get => (double)GetValue(SpecularShineProperty);
- set => SetValue(SpecularShineProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty SpecularShineProperty =
- DependencyProperty.Register(nameof(SpecularShine), typeof(double), typeof(LightingBrush), new PropertyMetadata(16d, OnSpecularShineChanged));
-
- private static void OnSpecularShineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- d.To()?.CompositionBrush?.Properties.InsertScalar("Light.SpecularShine", (float)(double)e.NewValue);
- }
-
- ///
- /// Gets or sets the specular amount for the effect
- ///
- public double SpecularAmount
- {
- get => (double)GetValue(SpecularAmountProperty);
- set => SetValue(SpecularAmountProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty SpecularAmountProperty =
- DependencyProperty.Register(nameof(SpecularAmount), typeof(double), typeof(LightingBrush), new PropertyMetadata(1d, OnSpecularAmountChanged));
-
- private static void OnSpecularAmountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- d.To()?.CompositionBrush?.Properties.InsertScalar("Light.SpecularAmount", (float)(double)e.NewValue);
- }
-
- // The effect brush to use
- CompositionEffectBrush _Brush;
-
- // The factory to create the brush
- CompositionEffectFactory _Factory;
-
- // The color transformation effect to convert luminance to opacity
- ColorMatrixEffect _ColorMatrixEffect;
-
- ///
- protected override void OnConnected()
- {
- if (CompositionBrush == null)
- {
- // Effects setup
- SceneLightingEffect sceneLightingEffect = new SceneLightingEffect // Base lighting effect
- {
- Name = "Light",
- SpecularShine = (float)SpecularShine,
- SpecularAmount = (float)SpecularAmount,
- DiffuseAmount = (float)DiffuseAmount,
- AmbientAmount = 0
- };
-
- // Setup the color matrix effect to map the luminosity
- _ColorMatrixEffect = new ColorMatrixEffect
- {
- Source = sceneLightingEffect,
- ColorMatrix = new Matrix5x4
- {
- M11 = 0, M21 = 0, M31 = 0, M41 = 0, M51 = 1,
- M12 = 0, M22 = 0, M32 = 0, M42 = 0, M52 = 1,
- M13 = 0, M23 = 0, M33 = 0, M43 = 0, M53 = 1,
- M14 = 0.2125f, M24 = 0.7154f, M34 = 0.0721f, M44 = 0, M54 = 0
- }
- };
-
- // Initialize the factory
- _Factory = Window.Current.Compositor.CreateEffectFactory(_ColorMatrixEffect, new[] { "Light.DiffuseAmount", "Light.SpecularShine", "Light.SpecularAmount" });
-
- // Create and store the brush
- _Brush = _Factory.CreateBrush();
- CompositionBrush = _Brush;
- }
- base.OnConnected();
- }
-
- ///
- protected override void OnDisconnected()
- {
- if (CompositionBrush != null)
- {
- _Brush?.Dispose();
- _Factory?.Dispose();
- _ColorMatrixEffect?.Dispose();
- CompositionBrush = null;
- }
- base.OnDisconnected();
- }
- }
-}
diff --git a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs
index 254ac34..1b9e4e8 100644
--- a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs
+++ b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs
@@ -1,130 +1,21 @@
using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Windows.Graphics.Effects;
-using Windows.UI;
-using Microsoft.Graphics.Canvas.Effects;
-using Windows.UI.Composition;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Media;
using UICompositionAnimations.Behaviours;
-using UICompositionAnimations.Helpers;
-using Windows.ApplicationModel;
+using UICompositionAnimations.Brushes.Base;
namespace UICompositionAnimations.Brushes
{
///
- /// A custom XAML brush that overlays a noise texture over a tint color
+ /// A that displays a tiled noise texture
///
- public sealed class NoiseTextureBrush : XamlCompositionBrushBase
+ public sealed class NoiseTextureBrush : XamlCompositionEffectBrushBase
{
- // The name of the animatable color property of the color effect
- private const string ColorSourceParameterName = "ColorSource.Color";
-
- #region Properties
-
- ///
- /// Gets or sets the color for the tint effect
- ///
- public Color Tint
- {
- get => GetValue(TintProperty).To();
- set => SetValue(TintProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty TintProperty =
- DependencyProperty.Register(nameof(Tint), typeof(Color), typeof(NoiseTextureBrush), new PropertyMetadata(Colors.Transparent, OnTintPropertyChanged));
-
- private static async void OnTintPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- NoiseTextureBrush @this = d.To();
- await @this.ConnectedSemaphore.WaitAsync();
- if (@this.CompositionBrush != null)
- {
- @this._EffectBrush?.Properties.InsertColor(ColorSourceParameterName, e.NewValue.To());
- }
- @this.ConnectedSemaphore.Release();
- }
-
///
/// Gets or sets the to the texture to use
///
/// This property must be initialized before using the brush
public Uri TextureUri { get; set; }
- #endregion
-
- // Initialization semaphore (due to the Win2D image loading being asynchronous)
- private readonly SemaphoreSlim ConnectedSemaphore = new SemaphoreSlim(1);
-
- // The composition brush used to render the effect
- private CompositionEffectBrush _EffectBrush;
-
- ///
- protected override async void OnConnected()
- {
- if (CompositionBrush == null)
- {
- await ConnectedSemaphore.WaitAsync();
- if (CompositionBrush == null) // It could have been initialized while waiting on the semaphore
- {
- await SetupEffectAsync();
- }
- ConnectedSemaphore.Release();
- }
- base.OnConnected();
- }
-
///
- protected override async void OnDisconnected()
- {
- if (CompositionBrush != null)
- {
- await ConnectedSemaphore.WaitAsync();
- if (CompositionBrush != null)
- {
- CompositionBrush.Dispose();
- CompositionBrush = null;
- }
- ConnectedSemaphore.Release();
- }
- base.OnDisconnected();
- }
-
- ///
- /// Initializes the appropriate effect for the current instance
- ///
- private async Task SetupEffectAsync()
- {
- // Designer check
- if (DesignMode.DesignMode2Enabled) return;
-
- // Dictionary to track the reference and animatable parameters
- IDictionary sourceParameters = new Dictionary();
- List animatableParameters = new List { ColorSourceParameterName };
-
- // Get the noise brush using Win2D
- IGraphicsEffect source = await AcrylicEffectHelper.LoadTextureEffectWithTintAsync(Window.Current.Compositor, sourceParameters, Tint, TextureUri);
-
- // Extract and setup the tint and color effects
- ColorSourceEffect color = source as ColorSourceEffect ?? source.To().Background as ColorSourceEffect;
- if (color == null) throw new InvalidOperationException("Error while retrieving the color effect");
- color.Name = "ColorSource";
-
- // Make sure the Win2D brush was loaded correctly
- CompositionEffectFactory factory = Window.Current.Compositor.CreateEffectFactory(source, animatableParameters);
-
- // Create the effect factory and apply the final effect
- _EffectBrush = factory.CreateBrush();
- foreach (KeyValuePair pair in sourceParameters)
- {
- _EffectBrush.SetSourceParameter(pair.Key, pair.Value);
- }
- CompositionBrush = _EffectBrush;
- }
+ protected override PipelineBuilder OnBrushRequested() => PipelineBuilder.FromTiles(TextureUri);
}
}
diff --git a/UICompositionAnimations/Behaviours/Xaml/PipelineBrush.cs b/UICompositionAnimations/Brushes/PipelineBrush.cs
similarity index 72%
rename from UICompositionAnimations/Behaviours/Xaml/PipelineBrush.cs
rename to UICompositionAnimations/Brushes/PipelineBrush.cs
index 8bed0eb..0f33992 100644
--- a/UICompositionAnimations/Behaviours/Xaml/PipelineBrush.cs
+++ b/UICompositionAnimations/Brushes/PipelineBrush.cs
@@ -3,11 +3,13 @@
using System.Linq;
using Windows.UI.Xaml.Media;
using JetBrains.Annotations;
-using UICompositionAnimations.Behaviours.Xaml.Effects;
+using UICompositionAnimations.Behaviours;
using UICompositionAnimations.Brushes.Base;
+using UICompositionAnimations.Brushes.Effects;
+using UICompositionAnimations.Brushes.Effects.Interfaces;
using LuminanceToAlphaEffect = Microsoft.Graphics.Canvas.Effects.LuminanceToAlphaEffect;
-namespace UICompositionAnimations.Behaviours.Xaml
+namespace UICompositionAnimations.Brushes
{
///
/// A that renders a customizable Composition/Win2D effects pipeline
@@ -15,26 +17,26 @@ namespace UICompositionAnimations.Behaviours.Xaml
public sealed class PipelineBrush : XamlCompositionEffectBrushBase
{
///
- protected override CompositionBrushBuilder OnBrushRequested()
+ protected override PipelineBuilder OnBrushRequested()
{
// Starts a new composition pipeline from the given effect
- CompositionBrushBuilder Start(IPipelineEffect effect)
+ PipelineBuilder Start(IPipelineEffect effect)
{
switch (effect)
{
case BackdropEffect backdrop when backdrop.Source == AcrylicBackgroundSource.Backdrop:
- return CompositionBrushBuilder.FromBackdropBrush();
+ return PipelineBuilder.FromBackdropBrush();
case BackdropEffect backdrop when backdrop.Source == AcrylicBackgroundSource.HostBackdrop:
- return CompositionBrushBuilder.FromHostBackdropBrush();
- case SolidColorEffect color: return CompositionBrushBuilder.FromColor(color.Color);
- case ImageEffect image: return CompositionBrushBuilder.FromImage(image.Uri, image.DPIMode, image.CacheMode);
- case TileEffect tile: return CompositionBrushBuilder.FromTiles(tile.Uri, tile.DPIMode, tile.CacheMode);
+ return PipelineBuilder.FromHostBackdropBrush();
+ case SolidColorEffect color: return PipelineBuilder.FromColor(color.Color);
+ case ImageEffect image: return PipelineBuilder.FromImage(image.Uri, image.DPIMode, image.CacheMode);
+ case TileEffect tile: return PipelineBuilder.FromTiles(tile.Uri, tile.DPIMode, tile.CacheMode);
default: throw new ArgumentException($"Invalid initial pipeline effect: {effect.GetType()}");
}
}
// Appends an effect to an existing composition pipeline
- CompositionBrushBuilder Append(IPipelineEffect effect, CompositionBrushBuilder builder)
+ PipelineBuilder Append(IPipelineEffect effect, PipelineBuilder builder)
{
switch (effect)
{
@@ -49,7 +51,7 @@ CompositionBrushBuilder Append(IPipelineEffect effect, CompositionBrushBuilder b
}
// Builds a new effects pipeline from the input effects sequence
- CompositionBrushBuilder Build(IList effects)
+ PipelineBuilder Build(IList effects)
{
if (effects.Count == 0) throw new ArgumentException("An effects pipeline can't be empty");
return effects.Skip(1).Aggregate(Start(effects[0]), (b, e) => Append(e, b));
diff --git a/UICompositionAnimations/Brushes/XamlCompositionBrush.cs b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs
index 37a3173..4350496 100644
--- a/UICompositionAnimations/Brushes/XamlCompositionBrush.cs
+++ b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs
@@ -14,21 +14,21 @@ namespace UICompositionAnimations.Brushes
public delegate Task XamlEffectAnimation(float value, int ms);
///
- /// A simple that can be used to quickly create XAML brushes from arbitrary pipelines
+ /// A simple that can be used to quickly create XAML brushes from arbitrary pipelines
///
public sealed class XamlCompositionBrush : XamlCompositionEffectBrushBase
{
///
- /// Gets the pipeline for the current instance
+ /// Gets the pipeline for the current instance
///
[NotNull]
- public CompositionBrushBuilder Pipeline { get; }
+ public PipelineBuilder Pipeline { get; }
///
/// Creates a new XAML brush from the input effects pipeline
///
- /// The instance to create the effect
- public XamlCompositionBrush([NotNull] CompositionBrushBuilder pipeline) => Pipeline = pipeline;
+ /// The instance to create the effect
+ public XamlCompositionBrush([NotNull] PipelineBuilder pipeline) => Pipeline = pipeline;
///
/// Binds an to the composition brush in the current instance
@@ -43,7 +43,7 @@ public XamlCompositionBrush Bind([NotNull] EffectAnimation animation, out XamlEf
}
///
- protected override CompositionBrushBuilder OnBrushRequested() => Pipeline;
+ protected override PipelineBuilder OnBrushRequested() => Pipeline;
///
/// Clones the current instance by rebuilding the source . Use this method to reuse the same effects pipeline on a different
diff --git a/UICompositionAnimations/Composition/CompositionManager.cs b/UICompositionAnimations/Composition/CompositionManager.cs
deleted file mode 100644
index 4cba52e..0000000
--- a/UICompositionAnimations/Composition/CompositionManager.cs
+++ /dev/null
@@ -1,279 +0,0 @@
-using System;
-using System.Numerics;
-using Windows.UI.Composition;
-using Windows.UI.Xaml;
-using JetBrains.Annotations;
-
-namespace UICompositionAnimations.Composition
-{
- ///
- /// Create composition animations using this class
- ///
- [PublicAPI]
- public static class CompositionManager
- {
- #region Animations initialization
-
- ///
- /// Creates and starts a scalar animation on the target element
- ///
- /// The element to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginScalarAnimation([NotNull] UIElement element, [NotNull] string propertyPath,
- float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- element.GetVisual().BeginScalarAnimation(propertyPath, from, to, duration, delay, ease);
- }
-
- ///
- /// Creates and starts a animation on the target element
- ///
- /// The element to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginVector2Animation([NotNull] UIElement element, [NotNull] string propertyPath,
- Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- element.GetVisual().BeginVector2Animation(propertyPath, from, to, duration, delay, ease);
- }
-
- ///
- /// Creates and starts a animation on the target element
- ///
- /// The element to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginVector3Animation([NotNull] UIElement element, [NotNull] string propertyPath,
- Vector3? from, Vector3 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- element.GetVisual().BeginVector3Animation(propertyPath, from, to, duration, delay, ease);
- }
-
- ///
- /// Creates and starts a scalar animation on the current
- ///
- /// The target to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginScalarAnimation([NotNull] this CompositionObject compObj, [NotNull] string propertyPath,
- float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- compObj.StartAnimation(propertyPath, compObj.Compositor.CreateScalarKeyFrameAnimation(from, to, duration, delay, ease));
- }
-
- ///
- /// Creates and starts a animation on the current
- ///
- /// The target to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginVector2Animation([NotNull]this CompositionObject compObj, [NotNull] string propertyPath,
- Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- compObj.StartAnimation(propertyPath, compObj.Compositor.CreateVector2KeyFrameAnimation(from, to, duration, delay, ease));
- }
-
- ///
- /// Creates and starts a animation on the current
- ///
- /// The target to animate
- /// The path that identifies the property to animate
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- public static void BeginVector3Animation([NotNull] this CompositionObject compObj, [NotNull] string propertyPath,
- Vector3? from, Vector3 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- compObj.StartAnimation(propertyPath, compObj.Compositor.CreateVector3KeyFrameAnimation(from, to, duration, delay, ease));
- }
-
- #endregion
-
- #region KeyFrame animations
-
- ///
- /// Creates a instance with the given parameters to on a target element
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] this Compositor compositor,
- float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- ScalarKeyFrameAnimation ani = compositor.CreateScalarKeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element, using an expression animation
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// A string that indicates the final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] this Compositor compositor,
- float? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- ScalarKeyFrameAnimation ani = compositor.CreateScalarKeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] this Compositor compositor,
- Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- Vector2KeyFrameAnimation ani = compositor.CreateVector2KeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element, using an expression animation
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// A string that indicates the final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] this Compositor compositor,
- Vector2? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- Vector2KeyFrameAnimation ani = compositor.CreateVector2KeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] this Compositor compositor,
- Vector3? from, Vector3 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- Vector3KeyFrameAnimation ani = compositor.CreateVector3KeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element, using an expression animation
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// A string that indicates the final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] this Compositor compositor,
- Vector3? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- // Set duration and delay time
- Vector3KeyFrameAnimation ani = compositor.CreateVector3KeyFrameAnimation();
- ani.Duration = duration;
- if (delay.HasValue) ani.DelayTime = delay.Value;
-
- // Insert "to" and "from" keyframes
- ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
- if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
- return ani;
- }
-
- ///
- /// Creates a instance with the given parameters to on a target element, using an expression animation
- ///
- /// The current instance used to create the animation
- /// The optional starting value for the animation
- /// The final value for the animation
- /// The animation duration
- /// The optional initial delay for the animation
- /// The optional easing function for the animation
- [Pure, NotNull]
- public static CompositionAnimation CreateMatrix4x4KeyFrameAnimation([NotNull] this Compositor compositor,
- Matrix4x4? from, Matrix4x4 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null)
- {
- throw new NotImplementedException(); // TODO
- }
-
- #endregion
- }
-}
diff --git a/UICompositionAnimations/Composition/CubicBeizerEasingProvider.cs b/UICompositionAnimations/Composition/CubicBeizerEasingProvider.cs
deleted file mode 100644
index c0c1fce..0000000
--- a/UICompositionAnimations/Composition/CubicBeizerEasingProvider.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System;
-using System.Numerics;
-using Windows.UI.Composition;
-using JetBrains.Annotations;
-using UICompositionAnimations.Enums;
-
-namespace UICompositionAnimations.Composition
-{
- ///
- /// A static class that generates a CubicBeizer curve from an input easing function name
- ///
- internal static class CubicBeizerEasingProvider
- {
- ///
- /// Creates a from the input control points
- ///
- /// The source object used to create the easing function
- /// The X coordinate of the first control point
- /// The Y coordinate of the first control point
- /// The X coordinate of the second control point
- /// The Y coordinate of the second control point
- [Pure, NotNull]
- public static CubicBezierEasingFunction GetEasingFunction([NotNull] this CompositionObject compObject, float x1, float y1, float x2, float y2)
- {
- return compObject.Compositor.CreateCubicBezierEasingFunction(new Vector2 { X = x1, Y = y1 }, new Vector2 { X = x2, Y = y2 });
- }
-
- ///
- /// Creates the appropriate from the given easing function name
- ///
- /// The source object used to create the easing function
- /// The target easing function to create
- [Pure, NotNull]
- public static CubicBezierEasingFunction GetEasingFunction([NotNull] this CompositionObject compObject, EasingFunctionNames ease)
- {
- switch (ease)
- {
- case EasingFunctionNames.Linear: return compObject.GetEasingFunction(0, 0, 1, 1);
- case EasingFunctionNames.SineEaseIn: return compObject.GetEasingFunction(0.4f, 0, 1, 1);
- case EasingFunctionNames.SineEaseOut: return compObject.GetEasingFunction(0, 0, 0.6f, 1);
- case EasingFunctionNames.SineEaseInOut: return compObject.GetEasingFunction(0.4f, 0, 0.6f, 1);
- case EasingFunctionNames.QuadraticEaseIn: return compObject.GetEasingFunction(0.8f, 0, 1, 1);
- case EasingFunctionNames.QuadraticEaseOut: return compObject.GetEasingFunction(0, 0, 0.2f, 1);
- case EasingFunctionNames.QuadraticEaseInOut: return compObject.GetEasingFunction(0.8f, 0, 0.2f, 1);
- case EasingFunctionNames.CircleEaseIn: return compObject.GetEasingFunction(1, 0, 1, 0.8f);
- case EasingFunctionNames.CircleEaseOut: return compObject.GetEasingFunction(0, 0.3f, 0, 1);
- case EasingFunctionNames.CircleEaseInOut: return compObject.GetEasingFunction(0.9f, 0, 0.1f, 1);
- default: throw new ArgumentOutOfRangeException(nameof(ease), ease, "This shouldn't happen");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/UICompositionAnimations/Composition/Misc/CompositeRotationAnimationStartInfo.cs b/UICompositionAnimations/Composition/Misc/CompositeRotationAnimationStartInfo.cs
deleted file mode 100644
index 0d79c3a..0000000
--- a/UICompositionAnimations/Composition/Misc/CompositeRotationAnimationStartInfo.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-namespace UICompositionAnimations.Composition.Misc
-{
- ///
- /// A simple struct that keeps track of the initial values for a rotation animation
- ///
- internal struct CompositeRotationAnimationStartInfo
- {
- ///
- /// Gets the initial animation opacity
- ///
- public float Opacity { get; }
-
- ///
- /// Gets the initial value of the secondary animation parameter (either offset or scale)
- ///
- public float SecondaryProperty { get; }
-
- ///
- /// Gets the initial animation degrees property
- ///
- public float Degrees { get; }
-
- public CompositeRotationAnimationStartInfo(float opacity, float property, float degrees)
- {
- Opacity = opacity;
- SecondaryProperty = property;
- Degrees = degrees;
- }
- }
-}
\ No newline at end of file
diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs
deleted file mode 100644
index c23ffe5..0000000
--- a/UICompositionAnimations/CompositionExtensions.cs
+++ /dev/null
@@ -1,2089 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Hosting;
-using System.Numerics;
-using Windows.Foundation;
-using Windows.UI;
-using Windows.UI.Composition;
-using Windows.UI.Core;
-using Windows.UI.Xaml.Controls;
-using JetBrains.Annotations;
-using UICompositionAnimations.Composition;
-using UICompositionAnimations.Composition.Misc;
-using UICompositionAnimations.Enums;
-using UICompositionAnimations.Helpers;
-using Windows.UI.Xaml.Shapes;
-
-namespace UICompositionAnimations
-{
- ///
- /// A static class that wraps the animation methods in the Windows.UI.Composition namespace
- ///
- [PublicAPI]
- public static class CompositionExtensions
- {
- #region Internal tools
-
- ///
- /// Sets the property of a object to the center of a given
- ///
- /// The source element
- /// The Visual object for the source
- private static void SetFixedCenterPoint([NotNull] this FrameworkElement element, [NotNull] Visual visual)
- {
- if (double.IsNaN(element.Width) || double.IsNaN(element.Height))
- throw new InvalidOperationException("The target element must have a fixed size");
- visual.CenterPoint = new Vector3((float)element.Width / 2, (float)element.Height / 2, 0);
- }
-
- ///
- /// Sets the property of a object to the center of a given
- ///
- /// The source element
- /// The Visual object for the source
- private static async Task SetCenterPointAsync([NotNull] this FrameworkElement element, [NotNull] Visual visual)
- {
- // Check if the control hasn't already been loaded
- bool CheckLoadingPending() => element.ActualWidth + element.ActualHeight < 0.1;
- if (CheckLoadingPending())
- {
- // Wait for the loaded event and set the CenterPoint
- TaskCompletionSource
[PublicAPI]
- public enum ImplicitAnimationType
+ public enum AnimationType
{
///
/// The animation plays when the item becomes visible
diff --git a/UICompositionAnimations/Enums/TranslationAxis.cs b/UICompositionAnimations/Enums/Axis.cs
similarity index 92%
rename from UICompositionAnimations/Enums/TranslationAxis.cs
rename to UICompositionAnimations/Enums/Axis.cs
index 8c68a6d..0bdcb42 100644
--- a/UICompositionAnimations/Enums/TranslationAxis.cs
+++ b/UICompositionAnimations/Enums/Axis.cs
@@ -6,7 +6,7 @@ namespace UICompositionAnimations.Enums
/// Indicates the translation axis to use in an animation
///
[PublicAPI]
- public enum TranslationAxis
+ public enum Axis
{
///
/// Horizontal translation
diff --git a/UICompositionAnimations/Enums/BitmapCacheMode.cs b/UICompositionAnimations/Enums/CacheMode.cs
similarity index 94%
rename from UICompositionAnimations/Enums/BitmapCacheMode.cs
rename to UICompositionAnimations/Enums/CacheMode.cs
index 451a86e..7e7f65b 100644
--- a/UICompositionAnimations/Enums/BitmapCacheMode.cs
+++ b/UICompositionAnimations/Enums/CacheMode.cs
@@ -6,7 +6,7 @@ namespace UICompositionAnimations.Enums
/// Indicates the cache mode to use when loading a Win2D image
///
[PublicAPI]
- public enum BitmapCacheMode
+ public enum CacheMode
{
///
/// The default behavior, the cache is enabled
diff --git a/UICompositionAnimations/Enums/BitmapDPIMode.cs b/UICompositionAnimations/Enums/DpiMode.cs
similarity index 80%
rename from UICompositionAnimations/Enums/BitmapDPIMode.cs
rename to UICompositionAnimations/Enums/DpiMode.cs
index dac988d..94c44ec 100644
--- a/UICompositionAnimations/Enums/BitmapDPIMode.cs
+++ b/UICompositionAnimations/Enums/DpiMode.cs
@@ -3,26 +3,26 @@
///
/// Indicates the DPI mode to use to load an image
///
- public enum BitmapDPIMode
+ public enum DpiMode
{
///
/// Uses the original DPI settings of the loaded image
///
- UseSourceDPI,
+ UseSourceDpi,
///
/// Uses the default value of 96 DPI
///
- Default96DPI,
+ Default96Dpi,
///
/// Overrides the image DPI settings with the current screen DPI value
///
- CopyDisplayDPISetting,
+ DisplayDpi,
///
/// Overrides the image DPI settings with the current screen DPI value and ensures the resulting value is at least 96
///
- CopyDisplayDPISettingsWith96AsLowerBound
+ DisplayDpiWith96AsLowerBound
}
}
\ No newline at end of file
diff --git a/UICompositionAnimations/Enums/EasingFunctionNames.cs b/UICompositionAnimations/Enums/Easing.cs
similarity index 79%
rename from UICompositionAnimations/Enums/EasingFunctionNames.cs
rename to UICompositionAnimations/Enums/Easing.cs
index ce93c9e..8556b61 100644
--- a/UICompositionAnimations/Enums/EasingFunctionNames.cs
+++ b/UICompositionAnimations/Enums/Easing.cs
@@ -1,10 +1,11 @@
-#pragma warning disable 1591
+#pragma warning disable 1591 // Missing XML comments in the enum values
+
namespace UICompositionAnimations.Enums
{
///
/// Indicates an easing function for an animation
///
- public enum EasingFunctionNames
+ public enum Easing
{
Linear,
SineEaseIn,
diff --git a/UICompositionAnimations/Enums/FrameworkLayer.cs b/UICompositionAnimations/Enums/FrameworkLayer.cs
new file mode 100644
index 0000000..52e3952
--- /dev/null
+++ b/UICompositionAnimations/Enums/FrameworkLayer.cs
@@ -0,0 +1,21 @@
+using JetBrains.Annotations;
+
+namespace UICompositionAnimations.Enums
+{
+ ///
+ /// An that indicates the framework layer to target in a specific animation
+ ///
+ [PublicAPI]
+ public enum FrameworkLayer
+ {
+ ///
+ /// Indicates the APIs
+ ///
+ Composition,
+
+ ///
+ /// Indicates the APIs
+ ///
+ Xaml
+ }
+}
\ No newline at end of file
diff --git a/UICompositionAnimations/Enums/EffectPlacement.cs b/UICompositionAnimations/Enums/Placement.cs
similarity index 95%
rename from UICompositionAnimations/Enums/EffectPlacement.cs
rename to UICompositionAnimations/Enums/Placement.cs
index 5c61248..d74cbb3 100644
--- a/UICompositionAnimations/Enums/EffectPlacement.cs
+++ b/UICompositionAnimations/Enums/Placement.cs
@@ -7,7 +7,7 @@ namespace UICompositionAnimations.Enums
/// An used to modify the default placement of the input instance in a blend operation
///
[PublicAPI]
- public enum EffectPlacement
+ public enum Placement
{
///
/// The instance used to call the blend method is placed on top of the other
diff --git a/UICompositionAnimations/Enums/MarginSide.cs b/UICompositionAnimations/Enums/Side.cs
similarity index 95%
rename from UICompositionAnimations/Enums/MarginSide.cs
rename to UICompositionAnimations/Enums/Side.cs
index 90fc418..d1f4c89 100644
--- a/UICompositionAnimations/Enums/MarginSide.cs
+++ b/UICompositionAnimations/Enums/Side.cs
@@ -3,7 +3,7 @@
///
/// Indicates a side to animate in the margin of a given element
///
- public enum MarginSide
+ public enum Side
{
///
/// Maps the top side of the target margin
diff --git a/UICompositionAnimations/Extensions/EnumExtensions.cs b/UICompositionAnimations/Extensions/EnumExtensions.cs
new file mode 100644
index 0000000..5af47e9
--- /dev/null
+++ b/UICompositionAnimations/Extensions/EnumExtensions.cs
@@ -0,0 +1,34 @@
+using System;
+using Windows.UI.Xaml.Media.Animation;
+using UICompositionAnimations.Enums;
+
+namespace UICompositionAnimations.Extensions
+{
+ ///
+ /// An extension for the types in the library
+ ///
+ internal static class EnumExtensions
+ {
+ ///
+ /// Converts an easing value to the right easing function
+ ///
+ /// The desired easing function
+ public static EasingFunctionBase ToEasingFunction(this Easing ease)
+ {
+ switch (ease)
+ {
+ case Easing.Linear: return null;
+ case Easing.SineEaseIn: return new SineEase { EasingMode = EasingMode.EaseIn };
+ case Easing.SineEaseOut: return new SineEase { EasingMode = EasingMode.EaseOut };
+ case Easing.SineEaseInOut: return new SineEase { EasingMode = EasingMode.EaseInOut };
+ case Easing.QuadraticEaseIn: return new QuadraticEase { EasingMode = EasingMode.EaseIn };
+ case Easing.QuadraticEaseOut: return new QuadraticEase { EasingMode = EasingMode.EaseOut };
+ case Easing.QuadraticEaseInOut: return new QuadraticEase { EasingMode = EasingMode.EaseInOut };
+ case Easing.CircleEaseIn: return new CircleEase { EasingMode = EasingMode.EaseIn };
+ case Easing.CircleEaseOut: return new CircleEase { EasingMode = EasingMode.EaseOut };
+ case Easing.CircleEaseInOut: return new CircleEase { EasingMode = EasingMode.EaseInOut };
+ default: throw new ArgumentOutOfRangeException(nameof(ease), ease, "This shouldn't happen");
+ }
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/System/BaseExtensions.cs b/UICompositionAnimations/Extensions/System/BaseExtensions.cs
new file mode 100644
index 0000000..15b822d
--- /dev/null
+++ b/UICompositionAnimations/Extensions/System/BaseExtensions.cs
@@ -0,0 +1,52 @@
+using System.Runtime.CompilerServices;
+using JetBrains.Annotations;
+
+namespace System
+{
+ ///
+ /// An extension for the
+ ///
+ public static class BaseExtensions
+ {
+ ///
+ /// Performs a direct cast on the given
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Pure]
+ public static T To(this object o) => (T)o;
+
+ ///
+ /// Converts an angle in radians to degrees
+ ///
+ /// The value to convert
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Pure]
+ public static float ToDegrees(this float radians) => (float)(Math.PI * radians / 180.0);
+
+ ///
+ /// Converts an angle in degrees to radians
+ ///
+ /// The value to convert
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [Pure]
+ public static float ToRadians(this float degrees) => (float)(Math.PI / 180 * degrees);
+
+ ///
+ /// Returns an that starts with the ms-appx:// prefix
+ ///
+ /// The input to process
+ [Pure, NotNull]
+ internal static Uri ToAppxUri([NotNull] this Uri uri)
+ {
+ if (uri.Scheme.Equals("ms-resource"))
+ {
+ string path = uri.AbsolutePath.StartsWith("/Files")
+ ? uri.AbsolutePath.Replace("/Files", string.Empty)
+ : uri.AbsolutePath;
+ return new Uri($"ms-appx://{path}");
+ }
+
+ return uri;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/System/Collections.Generic/GenericExtensions.cs b/UICompositionAnimations/Extensions/System/Collections.Generic/GenericExtensions.cs
new file mode 100644
index 0000000..13c2c31
--- /dev/null
+++ b/UICompositionAnimations/Extensions/System/Collections.Generic/GenericExtensions.cs
@@ -0,0 +1,37 @@
+using System.Linq;
+using JetBrains.Annotations;
+
+namespace System.Collections.Generic
+{
+ ///
+ /// An extension for the
+ ///
+ internal static class GenericExtensions
+ {
+ ///
+ /// Merges the two input instances and makes sure no duplicate keys are present
+ ///
+ /// The first to merge
+ /// The second to merge
+ [Pure, NotNull]
+ public static IReadOnlyDictionary Merge(
+ [NotNull] this IReadOnlyDictionary a,
+ [NotNull] IReadOnlyDictionary b)
+ {
+ if (a.Keys.FirstOrDefault(b.ContainsKey) is TKey key) throw new InvalidOperationException($"The key {key} already exists in the current pipeline");
+ return new Dictionary(a.Concat(b));
+ }
+
+ ///
+ /// Merges the two input instances and makes sure no duplicate items are present
+ ///
+ /// The first to merge
+ /// The second to merge
+ [Pure, NotNull, ItemNotNull]
+ public static IReadOnlyCollection Merge([NotNull, ItemNotNull] this IReadOnlyCollection a, [NotNull, ItemNotNull] IReadOnlyCollection b)
+ {
+ if (a.Any(b.Contains)) throw new InvalidOperationException("The input collection has at least an item already present in the second collection");
+ return a.Concat(b).ToArray();
+ }
+ }
+}
diff --git a/UICompositionAnimations/Helpers/AsyncMutex.cs b/UICompositionAnimations/Extensions/System/Threading.Tasks/AsyncMutex.cs
similarity index 97%
rename from UICompositionAnimations/Helpers/AsyncMutex.cs
rename to UICompositionAnimations/Extensions/System/Threading.Tasks/AsyncMutex.cs
index 663a6c5..dd61c76 100644
--- a/UICompositionAnimations/Helpers/AsyncMutex.cs
+++ b/UICompositionAnimations/Extensions/System/Threading.Tasks/AsyncMutex.cs
@@ -6,7 +6,7 @@ namespace System.Threading.Tasks
///
/// An implementation that can be easily used inside a block
///
- internal sealed class AsyncMutex
+ public sealed class AsyncMutex
{
// The underlying semaphore used by this instance
[NotNull]
@@ -15,8 +15,8 @@ internal sealed class AsyncMutex
///
/// Acquires a lock for the current instance, that is automatically released outside the block
///
- [NotNull, ItemNotNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [NotNull, ItemNotNull]
public async Task LockAsync()
{
await Semaphore.WaitAsync().ConfigureAwait(false);
diff --git a/UICompositionAnimations/Extensions/System/Threading.Tasks/TaskCompletionSource.cs b/UICompositionAnimations/Extensions/System/Threading.Tasks/TaskCompletionSource.cs
new file mode 100644
index 0000000..afda6ac
--- /dev/null
+++ b/UICompositionAnimations/Extensions/System/Threading.Tasks/TaskCompletionSource.cs
@@ -0,0 +1,18 @@
+namespace System.Threading.Tasks
+{
+ ///
+ /// A implementation without the generic argument
+ ///
+ public sealed class TaskCompletionSource : TaskCompletionSource
+ {
+ ///
+ /// Transitions the underlying into the state
+ ///
+ public void SetResult() => SetResult(Unit.Value);
+
+ ///
+ /// Attempts to transition the underlying into the state
+ ///
+ public void TrySetResult() => TrySetResult(Unit.Value);
+ }
+}
diff --git a/UICompositionAnimations/Extensions/System/Unit.cs b/UICompositionAnimations/Extensions/System/Unit.cs
new file mode 100644
index 0000000..76071f1
--- /dev/null
+++ b/UICompositionAnimations/Extensions/System/Unit.cs
@@ -0,0 +1,25 @@
+namespace System
+{
+ ///
+ /// An empty that represents the F# unit type
+ ///
+ public sealed class Unit : IEquatable, IComparable
+ {
+ // Private constructor
+ private Unit() { }
+
+ ///
+ /// Gets the default value
+ ///
+ public static Unit Value { get; } = new Unit();
+
+ ///
+ public bool Equals(Unit other) => other != null;
+
+ ///
+ public override int GetHashCode() => 0;
+
+ ///
+ public int CompareTo(Unit other) => 0;
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositionExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositionExtensions.cs
new file mode 100644
index 0000000..34d2f11
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositionExtensions.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Numerics;
+using System.Threading.Tasks;
+using Windows.UI.Core;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Hosting;
+using JetBrains.Annotations;
+using UICompositionAnimations.Enums;
+
+namespace Windows.UI.Composition
+{
+ ///
+ /// An extension for some composition types
+ ///
+ [PublicAPI]
+ public static class CompositionExtensions
+ {
+ #region Easing functions
+
+ ///
+ /// Creates a from the input control points
+ ///
+ /// The source used to create the easing function
+ /// The X coordinate of the first control point
+ /// The Y coordinate of the first control point
+ /// The X coordinate of the second control point
+ /// The Y coordinate of the second control point
+ [Pure, NotNull]
+ public static CubicBezierEasingFunction GetEasingFunction([NotNull] this CompositionObject source, float x1, float y1, float x2, float y2)
+ {
+ return source.Compositor.CreateCubicBezierEasingFunction(new Vector2 { X = x1, Y = y1 }, new Vector2 { X = x2, Y = y2 });
+ }
+
+ ///
+ /// Creates the appropriate from the given easing function name
+ ///
+ /// The source used to create the easing function
+ /// The target easing function to create
+ [Pure, NotNull]
+ public static CubicBezierEasingFunction GetEasingFunction([NotNull] this CompositionObject source, Easing ease)
+ {
+ switch (ease)
+ {
+ case Easing.Linear: return source.GetEasingFunction(0, 0, 1, 1);
+ case Easing.SineEaseIn: return source.GetEasingFunction(0.4f, 0, 1, 1);
+ case Easing.SineEaseOut: return source.GetEasingFunction(0, 0, 0.6f, 1);
+ case Easing.SineEaseInOut: return source.GetEasingFunction(0.4f, 0, 0.6f, 1);
+ case Easing.QuadraticEaseIn: return source.GetEasingFunction(0.8f, 0, 1, 1);
+ case Easing.QuadraticEaseOut: return source.GetEasingFunction(0, 0, 0.2f, 1);
+ case Easing.QuadraticEaseInOut: return source.GetEasingFunction(0.8f, 0, 0.2f, 1);
+ case Easing.CircleEaseIn: return source.GetEasingFunction(1, 0, 1, 0.8f);
+ case Easing.CircleEaseOut: return source.GetEasingFunction(0, 0.3f, 0, 1);
+ case Easing.CircleEaseInOut: return source.GetEasingFunction(0.9f, 0, 0.1f, 1);
+ default: throw new ArgumentOutOfRangeException(nameof(ease), ease, "This shouldn't happen");
+ }
+ }
+
+ #endregion
+
+ #region Animations
+
+ ///
+ /// Creates and starts a scalar animation on the current
+ ///
+ /// The target to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginScalarAnimation(
+ [NotNull] this CompositionObject target,
+ [NotNull] string propertyPath,
+ float? from, float to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ target.StartAnimation(propertyPath, target.Compositor.CreateScalarKeyFrameAnimation(from, to, duration, delay, ease));
+ }
+
+ ///
+ /// Creates and starts a animation on the current
+ ///
+ /// The target to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginVector2Animation(
+ [NotNull]this CompositionObject target,
+ [NotNull] string propertyPath,
+ Vector2? from, Vector2 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ target.StartAnimation(propertyPath, target.Compositor.CreateVector2KeyFrameAnimation(from, to, duration, delay, ease));
+ }
+
+ ///
+ /// Creates and starts a animation on the current
+ ///
+ /// The target to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginVector3Animation(
+ [NotNull] this CompositionObject target,
+ [NotNull] string propertyPath,
+ Vector3? from, Vector3 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ target.StartAnimation(propertyPath, target.Compositor.CreateVector3KeyFrameAnimation(from, to, duration, delay, ease));
+ }
+
+ ///
+ /// Starts an animation on the given property of a
+ ///
+ /// The target
+ /// The name of the property to animate
+ /// The final value of the property
+ /// The animation duration
+ public static Task StartAnimationAsync([NotNull] this CompositionObject target, string property, float value, TimeSpan duration)
+ {
+ // Stop previous animations
+ target.StopAnimation(property);
+
+ // Setup the animation
+ ScalarKeyFrameAnimation animation = target.Compositor.CreateScalarKeyFrameAnimation();
+ animation.InsertKeyFrame(1f, value);
+ animation.Duration = duration;
+
+ // Get the batch and start the animations
+ CompositionScopedBatch batch = target.Compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ batch.Completed += (s, e) => tcs.SetResult(null);
+ target.StartAnimation(property, animation);
+ batch.End();
+ return tcs.Task;
+ }
+
+ #endregion
+
+ #region Misc
+
+ ///
+ /// Adds a instance on top of the target
+ ///
+ /// The instance to display
+ /// The target that will host the effect
+ public static void AttachToElement([NotNull] this CompositionBrush brush, [NotNull] FrameworkElement target)
+ {
+ // Add the brush to a sprite and attach it to the target element
+ SpriteVisual sprite = Window.Current.Compositor.CreateSpriteVisual();
+ sprite.Brush = brush;
+ sprite.Size = new Vector2((float)target.ActualWidth, (float)target.ActualHeight);
+ ElementCompositionPreview.SetElementChildVisual(target, sprite);
+ }
+
+ ///
+ /// Starts an to keep the size of the source in sync with the target
+ ///
+ /// The to start the animation on
+ /// The target to read the size updates from
+ public static void BindSize([NotNull] this CompositionObject source, [NotNull] UIElement target)
+ {
+ Visual visual = target.GetVisual();
+ ExpressionAnimation bindSizeAnimation = Window.Current.Compositor.CreateExpressionAnimation($"{nameof(visual)}.Size");
+ bindSizeAnimation.SetReferenceParameter(nameof(visual), visual);
+
+ // Start the animation
+ source.StartAnimation("Size", bindSizeAnimation);
+ }
+
+ ///
+ /// Tries to retrieve the instance of the input
+ ///
+ /// The source instance
+ /// The resulting , if existing
+ [MustUseReturnValue]
+ public static bool TryGetDispatcher([NotNull] this CompositionObject source, out CoreDispatcher dispatcher)
+ {
+ try
+ {
+ dispatcher = source.Dispatcher;
+ return true;
+ }
+ catch (ObjectDisposedException)
+ {
+ // I'm sorry Jack, I was too late! :'(
+ dispatcher = null;
+ return false;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositorExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositorExtensions.cs
new file mode 100644
index 0000000..625c7d1
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Composition/CompositorExtensions.cs
@@ -0,0 +1,175 @@
+using System;
+using System.Numerics;
+using JetBrains.Annotations;
+
+namespace Windows.UI.Composition
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class CompositorExtensions
+ {
+ ///
+ /// Creates a instance with the given parameters to on a target element
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ float? from, float to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ ScalarKeyFrameAnimation ani = compositor.CreateScalarKeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+
+ ///
+ /// Creates a instance with the given parameters to on a target element, using an expression animation
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// A string that indicates the final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ float? from, [NotNull] string to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ ScalarKeyFrameAnimation ani = compositor.CreateScalarKeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+
+ ///
+ /// Creates a instance with the given parameters to on a target element
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ Vector2? from, Vector2 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ Vector2KeyFrameAnimation ani = compositor.CreateVector2KeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+
+ ///
+ /// Creates a instance with the given parameters to on a target element, using an expression animation
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// A string that indicates the final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ Vector2? from, [NotNull] string to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ Vector2KeyFrameAnimation ani = compositor.CreateVector2KeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+
+ ///
+ /// Creates a instance with the given parameters to on a target element
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ Vector3? from, Vector3 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ Vector3KeyFrameAnimation ani = compositor.CreateVector3KeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+
+ ///
+ /// Creates a instance with the given parameters to on a target element, using an expression animation
+ ///
+ /// The current instance used to create the animation
+ /// The optional starting value for the animation
+ /// A string that indicates the final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ [Pure, NotNull]
+ public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation(
+ [NotNull] this Compositor compositor,
+ Vector3? from, [NotNull] string to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ // Set duration and delay time
+ Vector3KeyFrameAnimation ani = compositor.CreateVector3KeyFrameAnimation();
+ ani.Duration = duration;
+ if (delay.HasValue) ani.DelayTime = delay.Value;
+
+ // Insert "to" and "from" keyframes
+ ani.InsertExpressionKeyFrame(1, to, ease ?? compositor.CreateLinearEasingFunction());
+ if (from.HasValue) ani.InsertKeyFrame(0, from.Value);
+ return ani;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs b/UICompositionAnimations/Extensions/Windows.UI/Composition/ScalarExpressionAnimation.cs
similarity index 68%
rename from UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs
rename to UICompositionAnimations/Extensions/Windows.UI/Composition/ScalarExpressionAnimation.cs
index e0770d2..fc5bccb 100644
--- a/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs
+++ b/UICompositionAnimations/Extensions/Windows.UI/Composition/ScalarExpressionAnimation.cs
@@ -1,15 +1,13 @@
using System;
-using Windows.UI.Composition;
using Windows.UI.Xaml;
using JetBrains.Annotations;
-using UICompositionAnimations.Helpers;
-namespace UICompositionAnimations.Composition
+namespace Windows.UI.Composition
{
///
- /// A simple wrapper class around an instance with a custom parameter
+ /// A simple wrapper around an instance with a custom parameter
///
- public sealed class ExpressionAnimationWithScalarParameter : DependencyObject
+ public sealed class ScalarExpressionAnimation : DependencyObject
{
///
/// Gets the actual instance that's being played
@@ -24,13 +22,12 @@ public sealed class ExpressionAnimationWithScalarParameter : DependencyObject
private readonly CompositionPropertySet PropertySet;
///
- /// Internal constructor for a target animation
+ /// Internal constructor for a target
///
- /// The new animation that will be played
- /// The property set with the custom parameter
+ /// The that will be played
+ /// The with the custom parameter
/// The name of the custom parameter that will be updated when requested
- internal ExpressionAnimationWithScalarParameter([NotNull] ExpressionAnimation animation,
- [NotNull] CompositionPropertySet propertySet, [NotNull] string parameterName)
+ internal ScalarExpressionAnimation([NotNull] ExpressionAnimation animation, [NotNull] CompositionPropertySet propertySet, [NotNull] string parameterName)
{
Animation = animation;
PropertySet = propertySet;
@@ -50,19 +47,22 @@ public float Parameter
/// Gets the for the property
///
public static readonly DependencyProperty ParameterProperty = DependencyProperty.Register(
- nameof(Parameter), typeof(float), typeof(ExpressionAnimationWithScalarParameter),
+ nameof(Parameter),
+ typeof(float),
+ typeof(ScalarExpressionAnimation),
new PropertyMetadata(default(float), OnParameterPropertyChanged));
private static void OnParameterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Unpack
- ExpressionAnimationWithScalarParameter @this = d.To();
+ ScalarExpressionAnimation @this = d.To();
float value = e.NewValue.To();
// Update the parameter when needed
CompositionGetValueStatus status = @this.PropertySet.TryGetScalar(@this.ParameterName, out float old);
if (status != CompositionGetValueStatus.Succeeded) throw new InvalidOperationException("The target parameter has not been found");
- float delta = value - old,
+ float
+ delta = value - old,
abs = delta >= 0 ? delta : -delta;
if (abs > 0.1) @this.PropertySet.InsertScalar(@this.ParameterName, value);
}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Core/DispatcherExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Core/DispatcherExtensions.cs
new file mode 100644
index 0000000..668589b
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Core/DispatcherExtensions.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Windows.UI.Core
+{
+ ///
+ /// An extension for the
+ ///
+ public static class DispatcherExtensions
+ {
+ ///
+ /// Executes a given action on the UI thread without awaiting the operation
+ ///
+ /// The target dispatcher to use to schedule the callback execution
+ /// The action to execute on the UI thread
+ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
+ {
+ if (dispatcher.HasThreadAccess) callback();
+ else _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => callback());
+ }
+
+ ///
+ /// Executes a given async action on the UI thread without awaiting the operation
+ ///
+ /// The target dispatcher to use to schedule the callback execution
+ /// The action to execute on the UI thread
+ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback)
+ {
+ if (dispatcher.HasThreadAccess) asyncCallback();
+ else _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => asyncCallback());
+ }
+
+ ///
+ /// Executes a given action on the UI thread and waits for it to be completed
+ ///
+ /// The target dispatcher to use to schedule the callback execution
+ /// The action to execute on the UI thread
+ public static async Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
+ {
+ if (dispatcher.HasThreadAccess) callback();
+ else
+ {
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ callback();
+ tcs.SetResult(null);
+ });
+ await tcs.Task;
+ }
+ }
+
+ ///
+ /// Executes a given action on the UI thread and waits for it to be completed
+ ///
+ /// The target dispatcher to use to schedule the callback execution
+ /// The action to execute on the UI thread
+ public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback)
+ {
+ if (dispatcher.HasThreadAccess) return asyncCallback();
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => tcs.SetResult(asyncCallback()));
+ return tcs.Task.Unwrap();
+ }
+
+ ///
+ /// Executes a given function on the UI thread and returns its result
+ ///
+ /// The return type
+ /// The target dispatcher to use to schedule the callback execution
+ /// The function to execute on the UI thread
+ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func function)
+ {
+ if (dispatcher.HasThreadAccess) return Task.FromResult(function());
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ T result = function();
+ tcs.SetResult(result);
+ });
+ return tcs.Task;
+ }
+
+ ///
+ /// Executes a given async function on the UI thread and returns its result
+ ///
+ /// The return type
+ /// The target dispatcher to use to schedule the callback execution
+ /// The async function to execute on the UI thread
+ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func> function)
+ {
+ if (dispatcher.HasThreadAccess) return function();
+ TaskCompletionSource> tcs = new TaskCompletionSource>();
+ _ = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => tcs.SetResult(function()));
+ return tcs.Task.Unwrap();
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/DependencyObjectExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/DependencyObjectExtensions.cs
new file mode 100644
index 0000000..4ef8761
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/DependencyObjectExtensions.cs
@@ -0,0 +1,46 @@
+using System;
+using Windows.UI.Xaml.Media.Animation;
+using JetBrains.Annotations;
+using UICompositionAnimations.Enums;
+using UICompositionAnimations.Extensions;
+
+namespace Windows.UI.Xaml
+{
+ ///
+ /// An extension for the type
+ ///
+ [PublicAPI]
+ public static class DependencyObjectExtensions
+ {
+ ///
+ /// Prepares a with the given info
+ ///
+ /// The target to animate
+ /// The property to animate inside the target
+ /// The initial property value
+ /// The final property value
+ /// The duration of the
+ /// The easing function to use inside the
+ /// Indicates whether or not to apply this animation to elements that need the visual tree to be rearranged
+ [Pure, NotNull]
+ public static DoubleAnimation CreateDoubleAnimation(
+ [NotNull] this DependencyObject target, string property,
+ double? from, double? to,
+ TimeSpan duration,
+ Easing easing = Easing.Linear,
+ bool enableDependecyAnimations = false)
+ {
+ DoubleAnimation animation = new DoubleAnimation
+ {
+ From = from,
+ To = to,
+ Duration = duration,
+ EnableDependentAnimation = enableDependecyAnimations
+ };
+ if (easing != Easing.Linear) animation.EasingFunction = easing.ToEasingFunction();
+ Storyboard.SetTarget(animation, target);
+ Storyboard.SetTargetProperty(animation, property);
+ return animation;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/FrameworkElementExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/FrameworkElementExtensions.cs
new file mode 100644
index 0000000..2f4b865
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/FrameworkElementExtensions.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Numerics;
+using Windows.UI.Composition;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Hosting;
+using Windows.UI.Xaml.Shapes;
+using JetBrains.Annotations;
+
+namespace Windows.UI.Xaml
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class FrameworkElementExtensions
+ {
+ ///
+ /// Sets the property of the behind a given
+ ///
+ /// The source
+ /// The center to use on the X axis. If , the center of the will be used
+ /// The center to use on the Y axis. If , the center of the will be used
+ public static void SetVisualCenterPoint([NotNull] this FrameworkElement element, double? x, double? y)
+ {
+ if (double.IsNaN(element.Width) || double.IsNaN(element.Height))
+ throw new InvalidOperationException("The target element must have a fixed size");
+ element.GetVisual().CenterPoint = new Vector3((float?)x ?? (float)(element.Width / 2), (float?)y ?? (float)(element.Height / 2), 0);
+ }
+
+ ///
+ /// Creates a from the given
+ ///
+ /// The source for the
+ /// The optional target to apply the to (it can be the same as the source )
+ /// Indicates whether or not to immediately add the to the visual tree
+ /// The optional width of the (if null, the element width will be used)
+ /// The optional height of the (if null, the element height will be used)
+ /// The color (the default is
+ /// The opacity of the
+ /// The optional horizontal offset of the
+ /// The optional vertical offset of the
+ /// The optional margin of the clip area of the
+ /// The optional horizontal offset of the clip area of the
+ /// The optional vertical offset of the clip area of the
+ /// The optional explicit blur radius
+ /// The optional to use to create an alpha mask for the
+ /// The that hosts the
+ public static SpriteVisual AttachVisualShadow(
+ [NotNull] this FrameworkElement element, [NotNull] UIElement target,
+ bool apply,
+ float? width, float? height,
+ Color color, float opacity,
+ float offsetX = 0, float offsetY = 0,
+ Thickness? clipMargin = null, float clipOffsetX = 0, float clipOffsetY = 0,
+ float? blurRadius = null,
+ [CanBeNull] UIElement maskElement = null)
+ {
+ // Setup the shadow
+ Visual elementVisual = ElementCompositionPreview.GetElementVisual(element);
+ Compositor compositor = elementVisual.Compositor;
+ SpriteVisual sprite = compositor.CreateSpriteVisual();
+ DropShadow shadow = compositor.CreateDropShadow();
+ shadow.Color = color;
+ shadow.Opacity = opacity;
+ shadow.Offset = new Vector3(offsetX, offsetY, 0);
+ if (blurRadius != null) shadow.BlurRadius = blurRadius.Value;
+ sprite.Shadow = shadow;
+ sprite.Size = new Vector2(width ?? (float)element.Width, height ?? (float)element.Height);
+
+ // Clip it (if needed) and add it to the visual tree
+ if (clipMargin != null || clipOffsetX > 0 || clipOffsetY > 0)
+ {
+ InsetClip clip = compositor.CreateInsetClip(
+ (float)(clipMargin?.Left ?? 0), (float)(clipMargin?.Top ?? 0),
+ (float)(clipMargin?.Right ?? 0), (float)(clipMargin?.Bottom ?? 0));
+ clip.Offset = new Vector2(clipOffsetX, clipOffsetY);
+ sprite.Clip = clip;
+ }
+
+ // Alpha mask
+ switch (maskElement)
+ {
+ case null: break;
+ case Shape shape: shadow.Mask = shape.GetAlphaMask(); break;
+ case Image image: shadow.Mask = image.GetAlphaMask(); break;
+ case TextBlock textBlock: shadow.Mask = textBlock.GetAlphaMask(); break;
+ }
+ if (apply) ElementCompositionPreview.SetElementChildVisual(target, sprite);
+ return sprite;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/StoryboardExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/StoryboardExtensions.cs
new file mode 100644
index 0000000..7a87919
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/StoryboardExtensions.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Windows.UI.Xaml.Media.Animation
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class StoryboardExtensions
+ {
+ ///
+ /// Schedules a callback to run when the animation completes
+ ///
+ /// The to start
+ /// The callback to execute when the animation ends
+ public static void Then([NotNull] this Storyboard target, Action action)
+ {
+ // Set up the token
+ TaskCompletionSource tcs = new TaskCompletionSource();
+
+ // Prepare the handler
+ void Handler(object sender, object e)
+ {
+ action();
+ target.Completed -= Handler;
+ tcs.SetResult();
+ }
+
+ // Assign the handler
+ target.Completed += Handler;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/TimelineExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/TimelineExtensions.cs
new file mode 100644
index 0000000..669c08a
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/Animation/TimelineExtensions.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
+namespace Windows.UI.Xaml.Media.Animation
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class TimelineExtensions
+ {
+ ///
+ /// Creates a new with the given animation
+ ///
+ /// The single animation to insert into the returned
+ [NotNull]
+ public static Storyboard ToStoryboard([NotNull] this Timeline animation)
+ {
+ Storyboard storyboard = new Storyboard();
+ storyboard.Children.Add(animation);
+ return storyboard;
+ }
+
+ ///
+ /// Prepares a with the given animations
+ ///
+ /// The animations to run inside the
+ public static Storyboard ToStoryboard([NotNull, ItemNotNull] this IEnumerable animations)
+ {
+ Storyboard storyboard = new Storyboard();
+ foreach (Timeline animation in animations)
+ storyboard.Children.Add(animation);
+ return storyboard;
+ }
+
+ ///
+ /// Starts an animation and waits for it to be completed
+ ///
+ /// The target storyboard
+ public static Task BeginAsync(this Storyboard storyboard)
+ {
+ if (storyboard == null) throw new ArgumentNullException();
+ TaskCompletionSource tcs = new TaskCompletionSource();
+ storyboard.Completed += (s, e) => tcs.SetResult(null);
+ storyboard.Begin();
+ return tcs.Task;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/SolidColorBrushExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/SolidColorBrushExtensions.cs
new file mode 100644
index 0000000..0da50a0
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/Media/SolidColorBrushExtensions.cs
@@ -0,0 +1,37 @@
+using System;
+using Windows.UI.Xaml.Media.Animation;
+using JetBrains.Annotations;
+using UICompositionAnimations.Enums;
+using UICompositionAnimations.Extensions;
+
+namespace Windows.UI.Xaml.Media
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class SolidColorBrushExtensions
+ {
+ ///
+ /// Animates the target to a given color
+ ///
+ /// The to animate
+ /// The target value to set
+ /// The duration of the animation
+ /// The easing function to use
+ [Pure, NotNull]
+ public static ColorAnimation CreateColorAnimation([NotNull] this SolidColorBrush solidColorBrush, Color to, TimeSpan duration, Easing easing)
+ {
+ ColorAnimation animation = new ColorAnimation
+ {
+ From = solidColorBrush.Color,
+ To = to,
+ Duration = duration,
+ EasingFunction = easing.ToEasingFunction()
+ };
+ Storyboard.SetTarget(animation, solidColorBrush);
+ Storyboard.SetTargetProperty(animation, nameof(SolidColorBrush.Color));
+ return animation;
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/ScrollViewerExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/ScrollViewerExtensions.cs
new file mode 100644
index 0000000..be72cfb
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/ScrollViewerExtensions.cs
@@ -0,0 +1,67 @@
+using Windows.UI.Composition;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Hosting;
+using JetBrains.Annotations;
+using UICompositionAnimations.Enums;
+
+namespace Windows.UI.Xaml
+{
+ ///
+ /// An extension for the control
+ ///
+ [PublicAPI]
+ public static class ScrollViewerExtensions
+ {
+ ///
+ /// Creates and starts an animation on the target element that binds either the X or Y axis of the source
+ ///
+ /// The source control to use
+ /// The target that will be animated
+ /// The scrolling axis of the source
+ /// The optional scrolling axis of the target element, if the source axis will be used
+ /// Indicates whether or not to invert the animation from the source
+ [NotNull]
+ public static ExpressionAnimation StartExpressionAnimation(
+ [NotNull] this ScrollViewer scroller, [NotNull] UIElement target,
+ Axis sourceXY, Axis? targetXY = null, bool invertSourceAxis = false)
+ {
+ CompositionPropertySet scrollSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scroller);
+ string sign = invertSourceAxis ? "-" : string.Empty;
+ ExpressionAnimation animation = scrollSet.Compositor.CreateExpressionAnimation($"{sign}scroll.Translation.{sourceXY}");
+ animation.SetReferenceParameter("scroll", scrollSet);
+ target.GetVisual().StartAnimation($"Offset.{targetXY ?? sourceXY}", animation);
+ return animation;
+ }
+
+ ///
+ /// Creates and starts an animation on the target element, with the addition of a scalar parameter in the resulting
+ ///
+ /// The source control to use
+ /// The target that will be animated
+ /// The scrolling axis of the source
+ /// An additional parameter that will be included in the expression animation
+ /// The optional scrolling axis of the target element, if the source axis will be used
+ /// Indicates whether or not to invert the animation from the source
+ [NotNull]
+ public static ScalarExpressionAnimation StartExpressionAnimation(
+ [NotNull] this ScrollViewer scroller, [NotNull] UIElement target,
+ Axis sourceXY, float parameter,
+ Axis? targetXY = null, bool invertSourceAxis = false)
+ {
+ // Get the property set and setup the scroller offset sign
+ CompositionPropertySet scrollSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scroller);
+ string sign = invertSourceAxis ? "-" : "+";
+
+ // Prepare the second property set to insert the additional parameter
+ CompositionPropertySet properties = scroller.GetVisual().Compositor.CreatePropertySet();
+ properties.InsertScalar(nameof(parameter), parameter);
+
+ // Create and start the animation
+ ExpressionAnimation animation = scrollSet.Compositor.CreateExpressionAnimation($"{nameof(properties)}.{nameof(parameter)} {sign} scroll.Translation.{sourceXY}");
+ animation.SetReferenceParameter("scroll", scrollSet);
+ animation.SetReferenceParameter(nameof(properties), properties);
+ target.GetVisual().StartAnimation($"Offset.{targetXY ?? sourceXY}", animation);
+ return new ScalarExpressionAnimation(animation, properties, nameof(parameter));
+ }
+ }
+}
diff --git a/UICompositionAnimations/Extensions/Windows.UI/Xaml/UIElementExtensions.cs b/UICompositionAnimations/Extensions/Windows.UI/Xaml/UIElementExtensions.cs
new file mode 100644
index 0000000..18c60e2
--- /dev/null
+++ b/UICompositionAnimations/Extensions/Windows.UI/Xaml/UIElementExtensions.cs
@@ -0,0 +1,265 @@
+using System;
+using System.Numerics;
+using Windows.UI.Composition;
+using Windows.UI.Xaml.Hosting;
+using Windows.UI.Xaml.Media;
+using JetBrains.Annotations;
+using UICompositionAnimations.Animations;
+using UICompositionAnimations.Animations.Interfaces;
+using UICompositionAnimations.Enums;
+
+namespace Windows.UI.Xaml
+{
+ ///
+ /// An extension for the
+ ///
+ [PublicAPI]
+ public static class UIElementExtensions
+ {
+ ///
+ /// Returns the Visual object for a given UIElement
+ ///
+ /// The source UIElement
+ public static Visual GetVisual([NotNull] this UIElement element) => ElementCompositionPreview.GetElementVisual(element);
+
+ #region Animations
+
+ ///
+ /// Initializes an instance that targets the input
+ ///
+ /// The target to animate
+ /// The target layer to animate
+ [Pure, NotNull]
+ public static IAnimationBuilder Animation([NotNull] this UIElement target, FrameworkLayer layer = FrameworkLayer.Composition)
+ {
+ switch (layer)
+ {
+ case FrameworkLayer.Composition: return new CompositionAnimationBuilder(target);
+ case FrameworkLayer.Xaml: return new XamlAnimationBuilder(target);
+ default: throw new ArgumentOutOfRangeException(nameof(layer), layer, $"The {layer} value isn't valid");
+ }
+ }
+
+ ///
+ /// Creates and starts a scalar animation on the target
+ ///
+ /// The to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginScalarAnimation(
+ [NotNull] this UIElement element,
+ [NotNull] string propertyPath,
+ float? from, float to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ element.GetVisual().BeginScalarAnimation(propertyPath, from, to, duration, delay, ease);
+ }
+
+ ///
+ /// Creates and starts a animation on the target
+ ///
+ /// The to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginVector2Animation(
+ [NotNull] this UIElement element,
+ [NotNull] string propertyPath,
+ Vector2? from, Vector2 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ element.GetVisual().BeginVector2Animation(propertyPath, from, to, duration, delay, ease);
+ }
+
+ ///
+ /// Creates and starts a animation on the target
+ ///
+ /// The to animate
+ /// The path that identifies the property to animate
+ /// The optional starting value for the animation
+ /// The final value for the animation
+ /// The animation duration
+ /// The optional initial delay for the animation
+ /// The optional easing function for the animation
+ public static void BeginVector3Animation(
+ [NotNull] this UIElement element,
+ [NotNull] string propertyPath,
+ Vector3? from, Vector3 to,
+ TimeSpan duration, TimeSpan? delay,
+ [CanBeNull] CompositionEasingFunction ease = null)
+ {
+ element.GetVisual().BeginVector3Animation(propertyPath, from, to, duration, delay, ease);
+ }
+
+ #endregion
+
+ #region Property setters
+
+ ///
+ /// Returns the desired instance after assigning it to the property of the target
+ ///
+ /// The desired type
+ /// The target to modify
+ /// If , a new instance will always be created and assigned to the
+ ///
+ public static T GetTransform(this UIElement element, bool reset = true) where T : Transform, new()
+ {
+ // Return the existing transform object, if it exists
+ if (element.RenderTransform is T && !reset) return element.RenderTransform.To();
+
+ // Create a new transform
+ T transform = new T();
+ element.RenderTransform = transform;
+ return transform;
+ }
+
+ ///
+ /// Stops the animations with the target names on the given
+ ///
+ /// The target
+ /// The names of the animations to stop
+ public static void StopVisualAnimations([NotNull] this UIElement element, [NotNull, ItemNotNull] params string[] properties)
+ {
+ if (properties.Length == 0) return;
+ Visual visual = element.GetVisual();
+ foreach (string property in properties)
+ visual.StopAnimation(property);
+ }
+
+ ///
+ /// Sets the translation property of the instance for a given object
+ ///
+ /// The target
+ /// The axis to edit
+ /// The translation value to set for that axis
+ public static void SetVisualTranslation([NotNull] this UIElement element, Axis axis, float translation)
+ {
+ // Get the element visual and stop the animation
+ Visual visual = element.GetVisual();
+ ElementCompositionPreview.SetIsTranslationEnabled(element, true);
+
+ // Set the desired offset
+ Matrix4x4 transform = visual.TransformMatrix;
+ Vector3 translation3 = visual.TransformMatrix.Translation;
+ if (axis == Axis.X) translation3.X = translation;
+ else translation3.Y = translation;
+ transform.Translation = translation3;
+ visual.TransformMatrix = transform;
+ }
+
+ ///
+ /// Sets the translation property of the instance for a given object
+ ///
+ /// The target
+ /// The horizontal translation value
+ /// The vertical translation value
+ public static void SetVisualTranslation([NotNull] this UIElement element, float x, float y)
+ {
+ // Get the element visual and stop the animation
+ Visual visual = element.GetVisual();
+ ElementCompositionPreview.SetIsTranslationEnabled(element, true);
+
+ // Set the desired offset
+ Matrix4x4 transform = visual.TransformMatrix;
+ Vector3 translation3 = visual.TransformMatrix.Translation;
+ translation3.X = x;
+ translation3.Y = y;
+ transform.Translation = translation3;
+ visual.TransformMatrix = transform;
+ }
+
+ ///
+ /// Sets the property of the instance for a given object
+ ///
+ /// The target
+ /// The axis to edit
+ /// The value to set for that axis
+ public static void SetVisualOffset([NotNull] this UIElement element, Axis axis, float offset)
+ {
+ // Get the element visual and stop the animation
+ Visual visual = element.GetVisual();
+
+ // Set the desired offset
+ Vector3 offset3 = visual.Offset;
+ if (axis == Axis.X) offset3.X = offset;
+ else offset3.Y = offset;
+ visual.Offset = offset3;
+ }
+
+ ///
+ /// Sets the property of the instance for a given
+ ///
+ /// The target
+ /// The X value of the property
+ /// The Y value of the property
+ /// The Z value of the property
+ public static void SetVisualScale([NotNull] this UIElement element, float? x, float? y, float? z)
+ {
+ // Get the default values and set the CenterPoint
+ Visual visual = element.GetVisual();
+
+ // Set the scale property
+ if (x == null && y == null && z == null) return;
+ Vector3 targetScale = new Vector3
+ {
+ X = x ?? visual.Scale.X,
+ Y = y ?? visual.Scale.Y,
+ Z = z ?? visual.Scale.Z
+ };
+ visual.Scale = targetScale;
+ }
+
+ ///
+ /// Sets the clip property of the visual instance for a given
+ ///
+ /// The target
+ /// The desired clip margins to set
+ public static void SetVisualClip([NotNull] this UIElement element, Thickness clip)
+ {
+ // Get the element visual
+ Visual visual = element.GetVisual();
+
+ // Set the desired clip
+ InsetClip inset = visual.Clip as InsetClip ?? (visual.Clip = visual.Compositor.CreateInsetClip()).To();
+ inset.TopInset = (float)clip.Top;
+ inset.BottomInset = (float)clip.Bottom;
+ inset.LeftInset = (float)clip.Left;
+ inset.RightInset = (float)clip.Right;
+ visual.Clip = inset;
+ }
+
+ ///
+ /// Sets the clip property of the visual instance for a given
+ ///
+ /// The target
+ /// The target clip side to update
+ /// The desired clip value to set
+ public static void SetVisualClip([NotNull] this UIElement element, Side side, float clip)
+ {
+ // Get the element visual
+ Visual visual = element.GetVisual();
+
+ // Set the desired clip
+ InsetClip inset = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip());
+ switch (side)
+ {
+ case Side.Top: inset.TopInset = clip; break;
+ case Side.Bottom: inset.BottomInset = clip; break;
+ case Side.Right: inset.RightInset = clip; break;
+ case Side.Left: inset.LeftInset = clip; break;
+ default: throw new ArgumentException("Invalid side", nameof(side));
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs b/UICompositionAnimations/Helpers/Cache/HostBackdropInstanceWrapper.cs
similarity index 96%
rename from UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs
rename to UICompositionAnimations/Helpers/Cache/HostBackdropInstanceWrapper.cs
index b776414..aaae8d7 100644
--- a/UICompositionAnimations/Brushes/Cache/HostBackdropInstanceWrapper.cs
+++ b/UICompositionAnimations/Helpers/Cache/HostBackdropInstanceWrapper.cs
@@ -2,7 +2,7 @@
using Windows.UI.Composition;
using JetBrains.Annotations;
-namespace UICompositionAnimations.Brushes.Cache
+namespace UICompositionAnimations.Helpers.Cache
{
///
/// A simple class that holds information on a instance and its effects pipeline
diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs b/UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionCache.cs
similarity index 97%
rename from UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs
rename to UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionCache.cs
index 9c4f371..71d7a93 100644
--- a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs
+++ b/UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionCache.cs
@@ -5,7 +5,7 @@
using Windows.UI.Core;
using JetBrains.Annotations;
-namespace UICompositionAnimations.Brushes.Cache
+namespace UICompositionAnimations.Helpers.Cache
{
///
/// A used to cache reusable instances
diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs b/UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionMapCache.cs
similarity index 98%
rename from UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs
rename to UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionMapCache.cs
index ec6e7c5..acaf7cb 100644
--- a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs
+++ b/UICompositionAnimations/Helpers/Cache/ThreadSafeCompositionMapCache.cs
@@ -5,7 +5,7 @@
using Windows.UI.Core;
using JetBrains.Annotations;
-namespace UICompositionAnimations.Brushes.Cache
+namespace UICompositionAnimations.Helpers.Cache
{
///
/// A used to cache reusable instances with an associated key
diff --git a/UICompositionAnimations/Helpers/ColorConverter.cs b/UICompositionAnimations/Helpers/ColorConverter.cs
deleted file mode 100644
index e72ede3..0000000
--- a/UICompositionAnimations/Helpers/ColorConverter.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System.Linq;
-using Windows.UI;
-
-namespace UICompositionAnimations.Helpers
-{
- ///
- /// A simple class that contains some color managment methods
- ///
- internal static class ColorConverter
- {
- ///
- /// Returns the Color represented by the hex string
- ///
- /// If it contains just the RGB values {RRBBGG} the Alpha channel is automatically set to FF
- public static Color String2Color(string color)
- {
- //Cancels the # symbol from the string, if present
- if (color.Contains('#')) color = color.Substring(1);
- byte alpha;
- string RGB;
- if (color.Length == 6)
- {
- alpha = 255;
- RGB = color;
- }
- else
- {
- alpha = byte.Parse(color.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
- RGB = color.Substring(2);
- }
- return ColorHelper.FromArgb(alpha,
- byte.Parse(RGB.Substring(0, 2), System.Globalization.NumberStyles.HexNumber),
- byte.Parse(RGB.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
- byte.Parse(RGB.Substring(4, 2), System.Globalization.NumberStyles.HexNumber));
- }
- }
-}
diff --git a/UICompositionAnimations/Helpers/DispatcherHelper.cs b/UICompositionAnimations/Helpers/DispatcherHelper.cs
index 02dc43e..6745ee1 100644
--- a/UICompositionAnimations/Helpers/DispatcherHelper.cs
+++ b/UICompositionAnimations/Helpers/DispatcherHelper.cs
@@ -7,161 +7,58 @@
namespace UICompositionAnimations.Helpers
{
///
- /// A static class that manages the UI dispatching from background threads
+ /// A that manages the UI dispatching from background threads
///
[PublicAPI]
public static class DispatcherHelper
{
- #region Target dispatcher helpers
-
- ///
- /// Executes a given action on the UI thread without awaiting the operation
- ///
- /// The target dispatcher to use to schedule the callback execution
- /// The action to execute on the UI thread
- public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
- {
- if (dispatcher.HasThreadAccess) callback();
- else dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => callback()).Forget();
- }
-
- ///
- /// Executes a given async action on the UI thread without awaiting the operation
- ///
- /// The target dispatcher to use to schedule the callback execution
- /// The action to execute on the UI thread
- public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback)
- {
- if (dispatcher.HasThreadAccess) asyncCallback();
- else dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => asyncCallback()).Forget();
- }
-
- ///
- /// Executes a given action on the UI thread and waits for it to be completed
- ///
- /// The target dispatcher to use to schedule the callback execution
- /// The action to execute on the UI thread
- public static async Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback)
- {
- if (dispatcher.HasThreadAccess) callback();
- else
- {
- TaskCompletionSource tcs = new TaskCompletionSource();
- dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- callback();
- tcs.SetResult(null);
- }).Forget();
- await tcs.Task;
- }
- }
-
- ///
- /// Executes a given action on the UI thread and waits for it to be completed
- ///
- /// The target dispatcher to use to schedule the callback execution
- /// The action to execute on the UI thread
- public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback)
- {
- if (dispatcher.HasThreadAccess) return asyncCallback();
- TaskCompletionSource tcs = new TaskCompletionSource();
- dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- tcs.SetResult(asyncCallback());
- }).Forget();
- return tcs.Task.Unwrap();
- }
-
///
- /// Executes a given function on the UI thread and returns its result
- ///
- /// The return type
- /// The target dispatcher to use to schedule the callback execution
- /// The function to execute on the UI thread
- public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func function)
- {
- if (dispatcher.HasThreadAccess) return Task.FromResult(function());
- TaskCompletionSource tcs = new TaskCompletionSource();
- dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- T result = function();
- tcs.SetResult(result);
- }).Forget();
- return tcs.Task;
- }
-
- ///
- /// Executes a given async function on the UI thread and returns its result
- ///
- /// The return type
- /// The target dispatcher to use to schedule the callback execution
- /// The async function to execute on the UI thread
- public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func> function)
- {
- if (dispatcher.HasThreadAccess) return function();
- TaskCompletionSource> tcs = new TaskCompletionSource>();
- dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
- {
- tcs.SetResult(function());
- }).Forget();
- return tcs.Task.Unwrap();
- }
-
- #endregion
-
- #region Standalone helpers
-
- private static CoreDispatcher _CoreDispatcher;
-
- ///
- /// Gets the current CoreDispatcher instance
+ /// Gets the current instance
///
[NotNull]
- private static CoreDispatcher CoreDispatcher => _CoreDispatcher ?? (_CoreDispatcher = CoreApplication.MainView.CoreWindow.Dispatcher);
+ private static CoreDispatcher CoreDispatcher { get; } = CoreApplication.MainView.CoreWindow.Dispatcher;
///
/// Checks whether or not the current thread has access to the UI
///
- public static bool HasUIThreadAccess => CoreDispatcher.HasThreadAccess;
+ public static bool HasThreadAccess => CoreDispatcher.HasThreadAccess;
///
- /// Executes a given action on the UI thread without awaiting the operation
+ /// Executes a given on the UI thread without awaiting the operation
///
- /// The action to execute on the UI thread
- public static void RunOnUIThread([NotNull] Action callback) => CoreDispatcher.Run(callback);
+ /// The to execute on the UI thread
+ public static void Run([NotNull] Action callback) => CoreDispatcher.Run(callback);
///
- /// Executes a given async action on the UI thread without awaiting the operation
+ /// Executes a given that returns a on the UI thread without awaiting the operation
///
- /// The action to execute on the UI thread
- public static void RunOnUIThread([NotNull] Func asyncCallback) => CoreDispatcher.Run(asyncCallback);
+ /// The to execute on the UI thread
+ public static void Run([NotNull] Func asyncCallback) => CoreDispatcher.Run(asyncCallback);
///
- /// Executes a given action on the UI thread and waits for it to be completed
+ /// Executes a given on the UI thread and waits for it to be completed
///
- /// The action to execute on the UI thread
- public static Task RunOnUIThreadAsync([NotNull] Action callback) => CoreDispatcher.RunAsync(callback);
+ /// The to execute on the UI thread
+ public static Task RunAsync([NotNull] Action callback) => CoreDispatcher.RunAsync(callback);
///
- /// Executes a given action on the UI thread and waits for it to be completed
+ /// Executes a given that returns a on the UI thread and waits for it to be completed
///
- /// The action to execute on the UI thread
- public static Task RunOnUIThreadAsync([NotNull] Func asyncCallback) => CoreDispatcher.RunAsync(asyncCallback);
+ /// The to execute on the UI thread
+ public static Task RunAsync([NotNull] Func asyncCallback) => CoreDispatcher.RunAsync(asyncCallback);
///
- /// Executes a given function on the UI thread and returns its result
+ /// Executes a given on the UI thread and returns its result
///
/// The return type
- /// The function to execute on the UI thread
- public static Task GetFromUIThreadAsync([NotNull] Func function) => CoreDispatcher.GetAsync(function);
+ /// The to execute on the UI thread
+ public static Task GetAsync([NotNull] Func function) => CoreDispatcher.GetAsync(function);
///
- /// Executes a given async function on the UI thread and returns its result
+ /// Executes a given that returns a on the UI thread and returns its result
///
- /// The return type
- /// The async function to execute on the UI thread
- public static Task GetFromUIThreadAsync([NotNull] Func> function) => CoreDispatcher.GetAsync(function);
-
- #endregion
+ /// The return type for the
+ /// The to execute on the UI thread
+ public static Task GetAsync([NotNull] Func> function) => CoreDispatcher.GetAsync(function);
}
}
diff --git a/UICompositionAnimations/Helpers/Extensions.cs b/UICompositionAnimations/Helpers/Extensions.cs
deleted file mode 100644
index f20972f..0000000
--- a/UICompositionAnimations/Helpers/Extensions.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using Windows.Foundation;
-using JetBrains.Annotations;
-
-namespace UICompositionAnimations.Helpers
-{
- ///
- /// A misc extensions class
- ///
- internal static class Extensions
- {
- ///
- /// Safely calls the Equals method on a given object, returning false if the object is null
- ///
- /// The Type of the two object
- /// The first object to test
- /// The comparison value
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool SafeEquals(this T value, T test) => value?.Equals(test) == true;
-
- ///
- /// Performs a direct cast on the given object
- ///
- public static T To(this object o) => (T)o;
-
- ///
- /// Converts an angle in radians to degrees
- ///
- /// The value to convert
- public static float ToDegrees(this float radians) => (float)(Math.PI * radians / 180.0);
-
- ///
- /// Converts an angle in degrees to radians
- ///
- /// The value to convert
- public static float ToRadians(this float degrees) => (float)(Math.PI / 180 * degrees);
-
- ///
- /// Suppresses the warnings when calling an async method without awaiting it
- ///
- /// The IAsyncAction returned by the async call
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Forget(this IAsyncAction action) { }
-
- ///
- /// Merges the two input instances and makes sure no duplicate keys are present
- ///
- /// The first to merge
- /// The second to merge
- [Pure, NotNull]
- public static IReadOnlyDictionary Merge(
- [NotNull] this IReadOnlyDictionary a,
- [NotNull] IReadOnlyDictionary b)
- {
- if (a.Keys.FirstOrDefault(b.ContainsKey) is TKey key)
- throw new InvalidOperationException($"The key {key} already exists in the current pipeline");
- return new Dictionary(a.Concat(b));
- }
-
- ///
- /// Merges the two input instances and makes sure no duplicate items are present
- ///
- /// The first to merge
- /// The second to merge
- [Pure, NotNull, ItemNotNull]
- public static IReadOnlyCollection Merge([NotNull, ItemNotNull] this IReadOnlyCollection a, [NotNull, ItemNotNull] IReadOnlyCollection b)
- {
- if (a.FirstOrDefault(b.Contains) is T animation)
- throw new InvalidOperationException($"The animation {animation} already exists in the current pipeline");
- return a.Concat(b).ToArray();
- }
- }
-}
diff --git a/UICompositionAnimations/Helpers/Win2DImageHelper.cs b/UICompositionAnimations/Helpers/Win2DImageHelper.cs
index 1686632..b69fcef 100644
--- a/UICompositionAnimations/Helpers/Win2DImageHelper.cs
+++ b/UICompositionAnimations/Helpers/Win2DImageHelper.cs
@@ -15,14 +15,15 @@
using Microsoft.Graphics.Canvas.UI;
using Microsoft.Graphics.Canvas.UI.Composition;
using Microsoft.Graphics.Canvas.UI.Xaml;
-using UICompositionAnimations.Brushes.Cache;
using UICompositionAnimations.Enums;
+using UICompositionAnimations.Helpers.Cache;
namespace UICompositionAnimations.Helpers
{
///
- /// A helper classe that loads Win2D images and manages an internal cache of objects with the loaded images
+ /// A helper that loads Win2D images and manages an internal cache of instances with the loaded images
///
+ [PublicAPI]
public static class Win2DImageHelper
{
///
@@ -48,9 +49,8 @@ public static class Win2DImageHelper
/// The path to the image to load
/// Indicates the desired DPI mode to use when loading the image
/// Indicates the cache option to use to load the image
- [PublicAPI]
[Pure, ItemCanBeNull]
- public static Task LoadImageAsync([NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static Task LoadImageAsync([NotNull] Uri uri, DpiMode dpiMode, CacheMode cache = CacheMode.Default)
{
return LoadImageAsync(Window.Current.Compositor, uri, dpiMode, cache);
}
@@ -62,9 +62,8 @@ public static Task LoadImageAsync([NotNull] Uri uri, Bi
/// The path to the image to load
/// Indicates the desired DPI mode to use when loading the image
/// Indicates the cache option to use to load the image
- [PublicAPI]
[Pure, ItemCanBeNull]
- public static Task LoadImageAsync([NotNull] this CanvasControl canvas, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache = BitmapCacheMode.Default)
+ public static Task LoadImageAsync([NotNull] this CanvasControl canvas, [NotNull] Uri uri, DpiMode dpiMode, CacheMode cache = CacheMode.Default)
{
return LoadImageAsync(Window.Current.Compositor, canvas, uri, dpiMode, cache);
}
@@ -73,8 +72,7 @@ public static Task LoadImageAsync([NotNull] this Canvas
/// Clears the internal cache of Win2D images
///
/// A sequence of the objects that were present in the cache
- /// The returned items should be manually disposed once checked that they are no longer being used in other effect pipelines
- [PublicAPI]
+ /// The returned items should be manually disposed after checking that they are no longer in use in other effect pipelines
[MustUseReturnValue, ItemNotNull]
public static async Task> ClearCacheAsync()
{
@@ -95,8 +93,10 @@ public static async Task> ClearCacheAsync()
/// The path to the image to load
/// Indicates the desired DPI mode to use when loading the image
[ItemNotNull]
- private static async Task LoadSurfaceBrushAsync([NotNull] ICanvasResourceCreator creator,
- [NotNull] Compositor compositor, [NotNull] CanvasDevice canvasDevice, [NotNull] Uri uri, BitmapDPIMode dpiMode)
+ private static async Task LoadSurfaceBrushAsync(
+ [NotNull] ICanvasResourceCreator creator,
+ [NotNull] Compositor compositor, [NotNull] CanvasDevice canvasDevice,
+ [NotNull] Uri uri, DpiMode dpiMode)
{
CanvasBitmap bitmap = null;
try
@@ -106,16 +106,16 @@ private static async Task LoadSurfaceBrushAsync([NotNul
float dpi = display.LogicalDpi;
switch (dpiMode)
{
- case BitmapDPIMode.UseSourceDPI:
+ case DpiMode.UseSourceDpi:
bitmap = await CanvasBitmap.LoadAsync(creator, uri);
break;
- case BitmapDPIMode.Default96DPI:
+ case DpiMode.Default96Dpi:
bitmap = await CanvasBitmap.LoadAsync(creator, uri, 96);
break;
- case BitmapDPIMode.CopyDisplayDPISetting:
+ case DpiMode.DisplayDpi:
bitmap = await CanvasBitmap.LoadAsync(creator, uri, dpi);
break;
- case BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound:
+ case DpiMode.DisplayDpiWith96AsLowerBound:
bitmap = await CanvasBitmap.LoadAsync(creator, uri, dpi >= 96 ? dpi : 96);
break;
default:
@@ -169,7 +169,10 @@ private static async Task LoadSurfaceBrushAsync([NotNul
/// Indicates the desired DPI mode to use when loading the image
/// Indicates the cache option to use to load the image
[ItemCanBeNull]
- internal static async Task LoadImageAsync([NotNull] Compositor compositor, [NotNull] CanvasControl canvas, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache)
+ internal static async Task LoadImageAsync(
+ [NotNull] Compositor compositor,
+ [NotNull] CanvasControl canvas,
+ [NotNull] Uri uri, DpiMode dpiMode, CacheMode cache)
{
TaskCompletionSource tcs = new TaskCompletionSource();
@@ -214,7 +217,7 @@ async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEve
// Lock the semaphore and check the cache first
using (await Win2DMutex.LockAsync())
{
- if (cache == BitmapCacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached;
+ if (cache == CacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached;
// Load the image
canvas.CreateResources += Canvas_CreateResources;
@@ -239,8 +242,8 @@ async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEve
// Cache when needed and return the result
if (brush != null)
{
- if (cache == BitmapCacheMode.Default) Cache.Add(uri, brush);
- else if (cache == BitmapCacheMode.Overwrite) Cache.Overwrite(uri, brush);
+ if (cache == CacheMode.Default) Cache.Add(uri, brush);
+ else if (cache == CacheMode.Overwrite) Cache.Overwrite(uri, brush);
}
return brush;
}
@@ -254,21 +257,13 @@ async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEve
/// Indicates the desired DPI mode to use when loading the image
/// Indicates the cache option to use to load the image
[ItemCanBeNull]
- internal static async Task LoadImageAsync([NotNull] Compositor compositor, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache)
+ internal static async Task LoadImageAsync([NotNull] Compositor compositor, [NotNull] Uri uri, DpiMode dpiMode, CacheMode cache)
{
- // Fix the Uri if it has been generated by the XAML designer
- if (uri.Scheme.Equals("ms-resource"))
- {
- string path = uri.AbsolutePath.StartsWith("/Files")
- ? uri.AbsolutePath.Replace("/Files", string.Empty)
- : uri.AbsolutePath;
- uri = new Uri($"ms-appx://{path}");
- }
-
// Lock and check the cache first
using (await Win2DMutex.LockAsync())
{
- if (cache == BitmapCacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached;
+ uri = uri.ToAppxUri();
+ if (cache == CacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached;
// Load the image
CompositionSurfaceBrush brush;
@@ -287,8 +282,8 @@ internal static async Task LoadImageAsync([NotNull] Com
// Cache when needed and return the result
if (brush != null)
{
- if (cache == BitmapCacheMode.Default) Cache.Add(uri, brush);
- else if (cache == BitmapCacheMode.Overwrite) Cache.Overwrite(uri, brush);
+ if (cache == CacheMode.Default) Cache.Add(uri, brush);
+ else if (cache == CacheMode.Overwrite) Cache.Overwrite(uri, brush);
}
return brush;
}
diff --git a/UICompositionAnimations/Lights/LightsSourceHelper.cs b/UICompositionAnimations/Lights/LightsSourceHelper.cs
deleted file mode 100644
index 35af443..0000000
--- a/UICompositionAnimations/Lights/LightsSourceHelper.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Windows.ApplicationModel;
-using Windows.Devices.Input;
-using Windows.UI.Xaml;
-using JetBrains.Annotations;
-using UICompositionAnimations.Helpers;
-using UICompositionAnimations.Helpers.PointerEvents;
-
-namespace UICompositionAnimations.Lights
-{
- ///
- /// A class that contains an attached property to register a target as a lights container
- ///
- [PublicAPI]
- public static class LightsSourceHelper
- {
- // The list of light generators
- private static Func[] LightGenerators;
-
- ///
- /// Initializes the helper with a series of light generators used to create the lights in each target
- ///
- /// The sequence of light generators to store and use when needed.
- /// Each generator can be as simple as () => new PointerPositionSpotLight(), or it can assign custom
- /// properties to each light when needed, like () => new PointerPositionSpotLight { IdAppendage = "MyID", Shade = 0x10 }
- public static void Initialize([NotNull] params Func[] generators)
- {
- if (LightGenerators != null) throw new InvalidOperationException("The helper has already been initialized");
- LightGenerators = generators;
- }
-
- ///
- /// Gets the value for the target
- ///
- /// The target element to inspect
- public static bool GetIsLightsContainer(UIElement element)
- {
- return element.GetValue(IsLightsContainerProperty).To();
- }
-
- ///
- /// Sets the value for the target
- ///
- /// The target element to edit
- /// The new value for the property
- public static void SetIsLightsContainer(UIElement element, bool value)
- {
- element?.SetValue(IsLightsContainerProperty, value);
- }
-
- ///
- /// Identifies the attached to set a target as a lights container
- ///
- public static readonly DependencyProperty IsLightsContainerProperty =
- DependencyProperty.RegisterAttached("IsLightsContainer", typeof(bool), typeof(LightsSourceHelper),
- new PropertyMetadata(default(bool), OnIsLightsContainerPropertyChanged));
-
- // Private dictionary to keep track of the added pointer event handlers
- private static readonly IDictionary HandlersMap = new Dictionary();
-
- private static void OnIsLightsContainerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- // Designer test
- if (DesignMode.DesignMode2Enabled) return;
-
- // Unpack
- if (LightGenerators == null) throw new NullReferenceException($"The {nameof(LightsSourceHelper)} class hasn't been initialized");
- UIElement @this = d.To();
- bool value = e.NewValue.To();
-
- // Lights setup
- if (value)
- {
- // Lights setup
- PointerPositionSpotLight[] lights = LightGenerators.Select(factory =>
- {
- PointerPositionSpotLight light = factory();
- light.Active = false;
- @this.Lights.Add(light);
- return light;
- }).ToArray();
-
- // Animate the lights when the pointer exits and leaves the area
- bool lightsEnabled = false;
- ControlAttachedHandlersInfo info = @this.ManageHostPointerStates((type, state) =>
- {
- bool lightsVisible = type == PointerDeviceType.Mouse && state;
- if (lightsEnabled == lightsVisible) return;
- lightsEnabled = lightsVisible;
- foreach (PointerPositionSpotLight light in lights)
- light.Active = lightsEnabled;
- });
- HandlersMap.Add(@this, info);
- }
- else
- {
- // Remove the lights and the custom pointer handlers
- if (!HandlersMap.TryGetValue(@this, out ControlAttachedHandlersInfo info))
- throw new InvalidOperationException("Error retrieving the pointer handlers info on the current control");
- HandlersMap.Remove(@this);
- info.TryRemoveHandlers();
- @this.Lights.Clear();
- }
- }
- }
-}
diff --git a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs
deleted file mode 100644
index 82f3363..0000000
--- a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using UICompositionAnimations.Helpers;
-using Windows.UI;
-using Windows.UI.Composition;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Hosting;
-using Windows.UI.Xaml.Media;
-using JetBrains.Annotations;
-
-namespace UICompositionAnimations.Lights
-{
- ///
- /// An attached XAML property to enable the XAML brush
- ///
- [PublicAPI]
- public class PointerPositionSpotLight : XamlLight
- {
- #region Properties
-
- ///
- /// Gets or sets the intensity of the light. A higher value will result in a brighter light
- ///
- public byte Shade
- {
- get => GetValue(ShadeProperty).To();
- set => SetValue(ShadeProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty ShadeProperty =
- DependencyProperty.Register(nameof(Shade), typeof(byte), typeof(PointerPositionSpotLight), new PropertyMetadata(byte.MaxValue, OnShadePropertyChanged));
-
- private static void OnShadePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is PointerPositionSpotLight @this && @this._Light != null)
- {
- byte shade = e.NewValue.To();
- Color color = new Color
- {
- A = byte.MaxValue,
- R = shade,
- G = shade,
- B = shade
- };
- @this._Light.InnerConeColor = @this._Light.OuterConeColor = color;
- }
- }
-
- float _Z = 20;
-
- ///
- /// Gets or sets the Z axis of the light
- ///
- public float Z
- {
- get => _Z;
- set
- {
- _Z = value;
- _Properties?.InsertScalar("Z", value);
- }
- }
-
- ///
- /// Gets the object for the current instance
- ///
- public CompositionPropertySet Properties => _Properties;
-
- ///
- /// Gets or sets the cone angle of the light
- ///
- public float OuterConeAngle
- {
- get => (float)GetValue(OuterConeAngleProperty);
- set => SetValue(OuterConeAngleProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty OuterConeAngleProperty =
- DependencyProperty.Register(nameof(OuterConeAngle), typeof(float), typeof(PointerPositionSpotLight), new PropertyMetadata(90f, OuterConeAngleChanged));
-
- private static void OuterConeAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is PointerPositionSpotLight @this && @this._Light != null)
- {
- @this._Light.OuterConeAngleInDegrees = (float)e.NewValue;
- }
- }
-
- // The constant attenuation value to use when the light is inactive
- private const int InactiveAttenuationValue = 50;
-
- ///
- /// Gets or sets whether or not the light is active
- ///
- public bool Active
- {
- get => GetValue(ActiveProperty).To();
- set => SetValue(ActiveProperty, value);
- }
-
- ///
- /// Gets the for the property
- ///
- public static readonly DependencyProperty ActiveProperty =
- DependencyProperty.Register(nameof(Active), typeof(bool), typeof(PointerPositionSpotLight), new PropertyMetadata(true, OnActivePropertyChanged));
-
- private static void OnActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is PointerPositionSpotLight @this)
- @this._Light?.StartAnimationAsync("ConstantAttenuation", e.NewValue.To() ? 0 : InactiveAttenuationValue, TimeSpan.FromMilliseconds(250));
- }
-
- #endregion
-
- ///
- /// Gets the value for the target object
- ///
- /// The target onject to inspect
- public static bool GetIsTarget(DependencyObject obj) => (bool)obj.GetValue(IsTargetProperty);
-
- ///
- /// Sets the value for the target object
- ///
- /// The target object
- /// The value of the property to set
- public static void SetIsTarget(DependencyObject obj, bool value) => obj.SetValue(IsTargetProperty, value);
-
- ///
- /// Gets or sets the value of the attached property
- ///
- public static readonly DependencyProperty IsTargetProperty =
- DependencyProperty.RegisterAttached("IsTarget", typeof(bool), typeof(PointerPositionSpotLight), new PropertyMetadata(false, OnIsTargetChanged));
-
- private static void OnIsTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- // Apply the changes
- if ((bool)e.NewValue)
- {
- // Add
- if (d is Brush b)
- {
- AddTargetBrush(GetIdStatic(), b);
- }
- else if (d is UIElement el)
- {
- AddTargetElement(GetIdStatic(), el);
- }
- }
- else
- {
- // Remove
- if (d is Brush b)
- {
- RemoveTargetBrush(GetIdStatic(), b);
- }
- else if (d is UIElement el)
- {
- RemoveTargetElement(GetIdStatic(), el);
- }
- }
- }
-
- // The light to use
- private SpotLight _Light;
-
- // The source compositor
- private Compositor _Compositor;
-
- // The expression animation for the light position
- private ExpressionAnimation _Animation;
-
- // The properties for the animation
- private CompositionPropertySet _Properties;
-
- ///
- protected override void OnConnected(UIElement newElement)
- {
- if (CompositionLight == null)
- {
- // Initialize the fields
- _Compositor = Window.Current.Compositor;
- _Properties = _Compositor.CreatePropertySet();
- _Properties.InsertScalar("Z", Z);
- _Light = Window.Current.Compositor.CreateSpotLight();
-
- // Setup the light
- CompositionPropertySet pointer = ElementCompositionPreview.GetPointerPositionPropertySet(newElement);
- _Animation = _Compositor.CreateExpressionAnimation("Vector3(pointer.Position.X, pointer.Position.Y, props.Z)");
- _Animation.SetReferenceParameter("pointer", pointer);
- _Animation.SetReferenceParameter("props", _Properties);
- _Light.StartAnimation("Offset", _Animation);
- _Light.InnerConeColor = _Light.OuterConeColor = Color.FromArgb(byte.MaxValue, Shade, Shade, Shade);
- _Light.InnerConeAngleInDegrees = 0;
- _Light.OuterConeAngleInDegrees = OuterConeAngle;
- _Light.ConstantAttenuation = Active ? 0 : InactiveAttenuationValue;
- CompositionLight = _Light;
- }
- base.OnConnected(newElement);
- }
-
- ///
- /// Gets or sets a custom appendage for the method
- ///
- [NotNull]
- public string IdAppendage { get; set; } = string.Empty;
-
- ///
- protected override string GetId() => GetIdStatic() + IdAppendage;
-
- ///
- /// Gets a static Id for the class
- ///
- public static string GetIdStatic() => typeof(PointerPositionSpotLight).FullName;
- }
-}
diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs
index 9e38cf0..dabc2f0 100644
--- a/UICompositionAnimations/Properties/AssemblyInfo.cs
+++ b/UICompositionAnimations/Properties/AssemblyInfo.cs
@@ -23,8 +23,8 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("3.1.0.0")]
-[assembly: AssemblyFileVersion("3.1.0.0")]
-[assembly: AssemblyInformationalVersion("3.1.0")]
+[assembly: AssemblyVersion("4.0.0.0")]
+[assembly: AssemblyFileVersion("4.0.0.0")]
+[assembly: AssemblyInformationalVersion("4.0.0")]
[assembly: ComVisible(false)]
diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj
index 7b44c92..e5d4ffd 100644
--- a/UICompositionAnimations/UICompositionAnimations.csproj
+++ b/UICompositionAnimations/UICompositionAnimations.csproj
@@ -120,68 +120,74 @@
latest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
- 2018.3.0
+ 2019.1.1
- 6.1.9
+ 6.2.81.23.0
diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec
index 9c3aed5..cda4dc0 100644
--- a/UICompositionAnimations/UICompositionAnimations.nuspec
+++ b/UICompositionAnimations/UICompositionAnimations.nuspec
@@ -2,7 +2,7 @@