From 7a8efb9b074ce41106ee48da981233e313010202 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 14:28:02 +0200 Subject: [PATCH 01/47] Switched SDK to target 17134 --- .../UICompositionAnimations.csproj | 23 ++++++++++++++----- UICompositionAnimations/project.json | 19 --------------- 2 files changed, 17 insertions(+), 25 deletions(-) delete mode 100644 UICompositionAnimations/project.json diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 671a449..81e9843 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -11,11 +11,12 @@ UICompositionAnimations it-IT UAP - 10.0.16299.0 - 10.0.10586.0 + 10.0.17134.0 + 10.0.17134.0 14 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot AnyCPU @@ -107,10 +108,6 @@ prompt bin\x64\Release\UICompositionAnimations.XML - - - - @@ -155,6 +152,20 @@ + + + 11.1.0 + + + 6.1.5 + + + 4.5.1 + + + 1.23.0 + + 14.0 diff --git a/UICompositionAnimations/project.json b/UICompositionAnimations/project.json deleted file mode 100644 index 991a94b..0000000 --- a/UICompositionAnimations/project.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "dependencies": { - "JetBrains.Annotations": "11.1.0", - "Microsoft.NETCore.UniversalWindowsPlatform": "6.0.7", - "System.Threading.Tasks.Extensions": "4.4.0", - "Win2D.uwp": "1.21.0" - }, - "frameworks": { - "uap10.0.10586": {} - }, - "runtimes": { - "win10-arm": {}, - "win10-arm-aot": {}, - "win10-x86": {}, - "win10-x86-aot": {}, - "win10-x64": {}, - "win10-x64-aot": {} - } -} \ No newline at end of file From 66ab32fa9e5ae2e89465324c6e532a41c87adeda Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 14:46:27 +0200 Subject: [PATCH 02/47] Removed ValueTask type (workaround) --- .../AttachedCompositionEffectFactory.cs | 18 +++++++++--------- .../Helpers/DispatcherHelper.cs | 19 +++++++------------ .../UICompositionAnimations.csproj | 3 --- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs index 548734c..4b9a9c0 100644 --- a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs +++ b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs @@ -21,7 +21,7 @@ namespace UICompositionAnimations.Behaviours /// /// A static class that manages the creation of attached composition effects /// - + [PublicAPI] public static class AttachedCompositionEffectsFactory { #region Static effects @@ -34,9 +34,9 @@ public static class AttachedCompositionEffectsFactory /// The amount of blur to apply to the element /// The duration of the initial blur animation, in milliseconds /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - /// This method returns a instance and runs synchronously if called on the UI thread + /// This method returns a instance and attempts to run synchronously if called on the UI thread [ItemNotNull] - public static async ValueTask> AttachCompositionBlurEffect( + public static async Task> AttachCompositionBlurEffect( [NotNull] this T element, float blur, int ms, bool disposeOnUnload = false) where T : FrameworkElement { // Get the visual and the compositor @@ -99,8 +99,8 @@ public static async Task> AttachCompositionIn where T : FrameworkElement { // Percentage check - if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException("The input saturation value must be in the [0,1] range"); - if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException("The mix factors must be in the [0,1] range"); + if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException(nameof(saturation), "The input saturation value must be in the [0,1] range"); + if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException(nameof(colorMix), "The mix factors must be in the [0,1] range"); // Setup the compositor Visual visual = ElementCompositionPreview.GetElementVisual(element); @@ -181,9 +181,9 @@ const String /// The type of element to use to host the effect /// The target element /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - /// This method returns a instance and runs synchronously if called on the UI thread + /// This method returns a instance and attempts to run synchronously if called on the UI thread [ItemNotNull] - public static async ValueTask> AttachCompositionHostBackdropBlurEffect( + public static async Task> AttachCompositionHostBackdropBlurEffect( [NotNull] this T element, bool disposeOnUnload = false) where T : FrameworkElement { // Setup the host backdrop effect @@ -217,7 +217,7 @@ public static async Task> AttachCompositionCu where T : FrameworkElement { // Percentage check - if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException("The mix factors must be in the [0,1] range"); + if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException(nameof(colorMix), "The mix factors must be in the [0,1] range"); // Setup the compositor Visual visual = ElementCompositionPreview.GetElementVisual(element); @@ -294,7 +294,7 @@ public static async Task> AttachCompositionCustom { // Percentage check if (hostColorMix <= 0 || hostColorMix >= 1 || - inAppColorMix <= 0 || inAppColorMix >= 1) throw new ArgumentOutOfRangeException("The mix factors must be in the [0,1] range"); + inAppColorMix <= 0 || inAppColorMix >= 1) throw new ArgumentOutOfRangeException(nameof(hostColorMix), "The mix factors must be in the [0,1] range"); // Setup the compositor Visual visual = ElementCompositionPreview.GetElementVisual(element); diff --git a/UICompositionAnimations/Helpers/DispatcherHelper.cs b/UICompositionAnimations/Helpers/DispatcherHelper.cs index d3fbe94..8705b23 100644 --- a/UICompositionAnimations/Helpers/DispatcherHelper.cs +++ b/UICompositionAnimations/Helpers/DispatcherHelper.cs @@ -24,10 +24,7 @@ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Actio if (dispatcher.HasThreadAccess) callback(); else { - dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - callback(); - }).Forget(); + dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => callback()).Forget(); } } @@ -43,10 +40,7 @@ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func< if (dispatcher.HasThreadAccess) asyncCallback(); // Schedule on the UI thread if necessary - dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => - { - asyncCallback(); - }).Forget(); + dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => asyncCallback()).Forget(); } /// @@ -97,16 +91,16 @@ public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] /// The target dispatcher to use to schedule the callback execution /// The function to execute on the UI thread [PublicAPI] - public static async ValueTask GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func function) + public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func function) { - if (dispatcher.HasThreadAccess) return function(); + if (dispatcher.HasThreadAccess) return Task.FromResult(function()); TaskCompletionSource tcs = new TaskCompletionSource(); dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { T result = function(); tcs.SetResult(result); }).Forget(); - return await tcs.Task; + return tcs.Task; } /// @@ -145,6 +139,7 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// /// Checks whether or not the current thread has access to the UI /// + [PublicAPI] public static bool HasUIThreadAccess => CoreDispatcher.HasThreadAccess; /// @@ -181,7 +176,7 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// The return type /// The function to execute on the UI thread [PublicAPI] - public static ValueTask GetFromUIThreadAsync([NotNull] Func function) => CoreDispatcher.GetAsync(function); + public static Task GetFromUIThreadAsync([NotNull] Func function) => CoreDispatcher.GetAsync(function); /// /// Executes a given async function on the UI thread and returns its result diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 81e9843..026b0cd 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -159,9 +159,6 @@ 6.1.5 - - 4.5.1 - 1.23.0 From 3be225e9dcc726eec9c0d0939053616f4e90e387 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 14:54:44 +0200 Subject: [PATCH 03/47] Removed legacy APIs (from before SDK 17134) --- .../Brushes/CustomAcrylicBrush.cs | 55 ++---- .../Helpers/ApiInformationHelper.cs | 165 +----------------- .../Helpers/PointerEvents/PointerHelper.cs | 14 +- .../Lights/LightsSourceHelper.cs | 4 +- .../Lights/PointerPositionSpotLight.cs | 24 ++- 5 files changed, 35 insertions(+), 227 deletions(-) diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index 9dd9cbc..f71a1c5 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -49,8 +49,8 @@ public sealed class CustomAcrylicBrush : XamlCompositionBrushBase /// public AcrylicEffectMode Mode { - get { return GetValue(ModeProperty).To(); } - set { SetValue(ModeProperty, value); } + get => GetValue(ModeProperty).To(); + set => SetValue(ModeProperty, value); } /// @@ -80,8 +80,8 @@ private static async void OnModePropertyChanged(DependencyObject d, DependencyPr /// This property is ignored when the active mode is public double BlurAmount { - get { return GetValue(BlurAmountProperty).To(); } - set { SetValue(BlurAmountProperty, value); } + get => GetValue(BlurAmountProperty).To(); + set => SetValue(BlurAmountProperty, value); } /// @@ -112,8 +112,8 @@ private static async void OnBlurAmountPropertyChanged(DependencyObject d, Depend /// This property is ignored when the active mode is public int BlurAnimationDuration { - get { return GetValue(BlurAnimationDurationProperty).To(); } - set { SetValue(BlurAnimationDurationProperty, value); } + get => GetValue(BlurAnimationDurationProperty).To(); + set => SetValue(BlurAnimationDurationProperty, value); } /// @@ -127,8 +127,8 @@ public int BlurAnimationDuration /// public Color Tint { - get { return GetValue(TintProperty).To(); } - set { SetValue(TintProperty, value); } + get => GetValue(TintProperty).To(); + set => SetValue(TintProperty, value); } /// @@ -153,8 +153,8 @@ private static async void OnTintPropertyChanged(DependencyObject d, DependencyPr /// public double TintMix { - get { return GetValue(TintMixProperty).To(); } - set { SetValue(TintMixProperty, value); } + get => GetValue(TintMixProperty).To(); + set => SetValue(TintMixProperty, value); } /// @@ -167,7 +167,7 @@ private static async void OnTintMixPropertyChanged(DependencyObject d, Dependenc { double value = e.NewValue.To(); float fvalue = (float)value; - if (value < 0 || value >= 1) throw new ArgumentOutOfRangeException("The tint mix must be in the [0..1) range"); + 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) @@ -183,8 +183,8 @@ private static async void OnTintMixPropertyChanged(DependencyObject d, Dependenc /// public Color UnsupportedEffectFallbackColor { - get { return GetValue(UnsupportedEffectFallbackColorProperty).To(); } - set { SetValue(UnsupportedEffectFallbackColorProperty, value); } + get => GetValue(UnsupportedEffectFallbackColorProperty).To(); + set => SetValue(UnsupportedEffectFallbackColorProperty, value); } /// @@ -217,13 +217,6 @@ private static async void OnUnsupportedEffectFallbackColorPropertyChanged(Depend @this._EffectBrush?.Properties.InsertColor(FallbackColorParameterName, value); } } - else if (@this._State == AcrylicBrushEffectState.Default && // The effect is currently disabled - @this.Mode == AcrylicEffectMode.HostBackdrop && // The current settings are not valid - ApiInformationHelper.IsMobileDevice && - !value.Equals(Colors.Transparent)) // The new value allows the fallback mode to be enabled - { - await @this.SetupEffectAsync(); - } @this.ConnectedSemaphore.Release(); } @@ -346,25 +339,6 @@ private async Task SetupEffectAsync() // Designer check if (DesignMode.DesignModeEnabled) return; - // Platform check - if (ApiInformationHelper.IsMobileDevice && Mode == AcrylicEffectMode.HostBackdrop) - { - // Create the fallback brush effect when needed - if (!UnsupportedEffectFallbackColor.Equals(Colors.Transparent)) - { - ColorSourceEffect fallback = new ColorSourceEffect - { - Name = "FallbackColor", - Color = UnsupportedEffectFallbackColor - }; - CompositionEffectFactory fallbackFactory = Window.Current.Compositor.CreateEffectFactory(fallback); - _EffectBrush = fallbackFactory.CreateBrush(); - CompositionBrush = _EffectBrush; - _State = AcrylicBrushEffectState.FallbackMode; - } - return; - } - // Dictionary to track the reference and animatable parameters IDictionary sourceParameters = new Dictionary(); List animatableParameters = new List @@ -450,8 +424,7 @@ private async Task SetupEffectAsync() 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"; - ColorSourceEffect color = tint.Source2 as ColorSourceEffect; - if (color == null) throw new InvalidOperationException("Error while retrieving the color effect"); + 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 diff --git a/UICompositionAnimations/Helpers/ApiInformationHelper.cs b/UICompositionAnimations/Helpers/ApiInformationHelper.cs index 4a3790a..5c7a52a 100644 --- a/UICompositionAnimations/Helpers/ApiInformationHelper.cs +++ b/UICompositionAnimations/Helpers/ApiInformationHelper.cs @@ -1,5 +1,4 @@ using System; -using Windows.ApplicationModel; using Windows.ApplicationModel.Resources.Core; using Windows.Foundation.Collections; using Windows.Foundation.Metadata; @@ -8,6 +7,7 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Input; +using JetBrains.Annotations; namespace UICompositionAnimations.Helpers { @@ -16,9 +16,7 @@ namespace UICompositionAnimations.Helpers /// public static class ApiInformationHelper { - #region OS build - - static Version _OSVersion; + private static Version _OSVersion; /// /// Gets the current OS version for the device in use @@ -27,6 +25,8 @@ public static class ApiInformationHelper /// instead of just checking the OS version and use it to make further decisions. /// In particular, users on a Windows Insider ring could already have a given set of APIs even though their OS /// build doesn't exactly match the official Windows 10 version that supports a requested API contract. + [PublicAPI] + [NotNull] public static Version OSVersion { get @@ -44,162 +44,5 @@ public static Version OSVersion return _OSVersion; } } - - /// - /// Gets whether or not the current OS version is at least the Anniversary Update (14393) - /// - /// - public static bool IsAnniversaryUpdateOrLater => OSVersion.Build > 14393; - - /// - /// Gets whether or not the current OS version is at least the Creator's Update (15063) - /// - /// - public static bool IsCreatorsUpdateOrLater => OSVersion.Build > 15063; - - /// - /// Gets whether or not the current OS version is at least the Fall Creator's Update (16xxx) - /// - /// - public static bool IsFallCreatorsUpdateOrLater => OSVersion.Build > 16299; - - #endregion - - #region Available APIs - - /// - /// Gets whether or not the connected animations APIs are available - /// - public static bool AreConnectedAnimationsAvailable => ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.Animation.ConnectedAnimationService"); - - /// - /// Gets whether or not the APIs to find the focusable elements are available - /// - public static bool IsFindFirstFocusableElementAvailable => ApiInformation.IsMethodPresent("Windows.UI.Xaml.Input.FocusManager", nameof(FocusManager.FindFirstFocusableElement)); - - /// - /// Gets or sets whether the Target property of the composition animation APIs is available - /// - public static bool SupportsCompositionAnimationTarget => ApiInformation.IsPropertyPresent("Windows.UI.Composition.CompositionAnimation", nameof(CompositionAnimation.Target)); - - /// - /// Gets whether or not the composition animation group type is present - /// - public static bool IsCompositionAnimationGroupAvailable => ApiInformation.IsTypePresent("Windows.UI.Composition.CompositionAnimationGroup"); - - /// - /// Gets whether or not the implicit animations APIs are available - /// - /// - public static bool AreImplicitAnimationsAvailable => ApiInformation.IsMethodPresent("Windows.UI.Composition.Compositor", nameof(Compositor.CreateImplicitAnimationCollection)); - - /// - /// Gets whether or not the implicit show/hide animation APIs are available - /// - public static bool AreImplicitShowHideAnimationsAvailable => ApiInformation.IsMethodPresent("Windows.UI.Xaml.Hosting.ElementCompositionPreview", nameof(ElementCompositionPreview.SetImplicitShowAnimation)); - - /// - /// Gets whether or not the XAML lights APIs are available - /// - public static bool AreXamlLightsSupported => ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.XamlLight"); - - /// - /// Gets whether or not the custom XAML brush APIs are available - /// - public static bool AreCustomBrushesSupported => ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.XamlCompositionBrushBase"); - - /// - /// Gets whether or not the XYFocusUp property is available/> - /// - /// - public static bool IsXYFocusAvailable => ApiInformation.IsPropertyPresent("Windows.UI.Xaml.Controls.Control", nameof(Control.XYFocusUp)); - - /// - /// Gets whether or not the host backdrop effects are available - /// - /// - public static bool AreHostBackdropEffectsAvailable => ApiInformation.IsTypePresent("Microsoft.Graphics.Canvas.Effects.GaussianBlurEffect") && - ApiInformation.IsMethodPresent("Windows.UI.Composition.Compositor", nameof(Compositor.CreateHostBackdropBrush)); - - /// - /// Gets whether or not the backdrop effects APIs are available - /// - /// - public static bool AreBackdropEffectsAvailable => ApiInformation.IsTypePresent("Microsoft.Graphics.Canvas.Effects.GaussianBlurEffect") && - ApiInformation.IsMethodPresent("Windows.UI.Composition.Compositor", nameof(Compositor.CreateBackdropBrush)); - - /// - /// Gets whether or not the drop shadows APIs are available - /// - public static bool AreDropShadowsAvailable => ApiInformation.IsMethodPresent("Windows.UI.Composition.Compositor", nameof(Compositor.CreateDropShadow)); - - /// - /// Gets whether or not the staggering method is available - /// - /// - public static bool IsRepositionStaggerringAvailable => ApiInformation.IsMethodPresent("Windows.UI.Xaml.Media.Animation.RepositionThemeTransition", "IsStaggeringEnabled"); - - /// - /// Gets whether or not the RequiresPointer property is available - /// - public static bool IsRequiresPointerAvailable => ApiInformation.IsPropertyPresent("Windows.UI.Xaml.Controls.Control", nameof(Control.RequiresPointer)); - - #endregion - - #region Device family - - private static bool? _IsMobileDevice; - - /// - /// Gets whether or not the device is a mobile phone - /// - public static bool IsMobileDevice - { - get - { - if (_IsMobileDevice == null) - { - try - { - IObservableMap qualifiers = ResourceContext.GetForCurrentView().QualifierValues; - _IsMobileDevice = qualifiers.ContainsKey("DeviceFamily") && qualifiers["DeviceFamily"] == "Mobile"; - } - catch (UnauthorizedAccessException) - { - // No idea why this should happen - return ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"); - } - } - return _IsMobileDevice.Value; - } - } - - private static bool? _IsDesktop; - - /// - /// Gets whether or not the device is running Windows 10 Desktop - /// - public static bool IsDesktop - { - get - { - if (_IsDesktop == null) - { - try - { - IObservableMap qualifiers = ResourceContext.GetForCurrentView().QualifierValues; - _IsDesktop = qualifiers.ContainsKey("DeviceFamily") && qualifiers["DeviceFamily"] == "Desktop"; - } - catch (UnauthorizedAccessException) - { - // Weird crash, but still... - return false; - } - } - return _IsDesktop.Value; - } - } - - #endregion } } diff --git a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs index a8caeab..956776c 100644 --- a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs +++ b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs @@ -24,15 +24,14 @@ public static ControlAttachedHandlersInfo ManageControlPointerStates(this UIElem // Nested functions that adds the actual handlers PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func predicate) { - PointerEventHandler handler = (_, e) => + void Handler(object _, PointerRoutedEventArgs e) { if (predicate == null || predicate(e.Pointer.PointerDeviceType)) - { action(e.Pointer.PointerDeviceType, state); - } - }; - control.AddHandler(@event, handler, true); - return new PointerHandlerInfo(@event, handler); + } + + control.AddHandler(@event, (PointerEventHandler) Handler, true); + return new PointerHandlerInfo(@event, Handler); } // Add handlers @@ -83,9 +82,6 @@ PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func action) { - // Platform check - if (ApiInformationHelper.IsMobileDevice) return null; - // Nested functions that adds the actual handlers return element.ManageHostPointerStates((pointer, value) => { diff --git a/UICompositionAnimations/Lights/LightsSourceHelper.cs b/UICompositionAnimations/Lights/LightsSourceHelper.cs index 212abe1..c08184b 100644 --- a/UICompositionAnimations/Lights/LightsSourceHelper.cs +++ b/UICompositionAnimations/Lights/LightsSourceHelper.cs @@ -13,6 +13,7 @@ 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 @@ -64,9 +65,6 @@ private static void OnIsLightsContainerPropertyChanged(DependencyObject d, Depen // Designer test if (DesignMode.DesignModeEnabled) return; - // Platform test - if (ApiInformationHelper.IsMobileDevice) return; - // Unpack if (LightGenerators == null) throw new NullReferenceException($"The {nameof(LightsSourceHelper)} class hasn't been initialized"); UIElement @this = d.To(); diff --git a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs index 426d9fe..01816bd 100644 --- a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs +++ b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs @@ -12,6 +12,7 @@ namespace UICompositionAnimations.Lights /// /// An attached XAML property to enable the XAML brush /// + [PublicAPI] public class PointerPositionSpotLight : XamlLight { #region Properties @@ -21,8 +22,8 @@ public class PointerPositionSpotLight : XamlLight /// public byte Shade { - get { return GetValue(ShadeProperty).To(); } - set { SetValue(ShadeProperty, value); } + get => GetValue(ShadeProperty).To(); + set => SetValue(ShadeProperty, value); } /// @@ -73,8 +74,8 @@ public float Z /// public float OuterConeAngle { - get { return (float)GetValue(OuterConeAngleProperty); } - set { SetValue(OuterConeAngleProperty, value); } + get => (float)GetValue(OuterConeAngleProperty); + set => SetValue(OuterConeAngleProperty, value); } /// @@ -100,8 +101,8 @@ private static void OuterConeAngleChanged(DependencyObject d, DependencyProperty /// public bool Active { - get { return GetValue(ActiveProperty).To(); } - set { SetValue(ActiveProperty, value); } + get => GetValue(ActiveProperty).To(); + set => SetValue(ActiveProperty, value); } /// @@ -139,9 +140,6 @@ private static void OnActivePropertyChanged(DependencyObject d, DependencyProper private static void OnIsTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - // API test - if (!ApiInformationHelper.AreXamlLightsSupported) return; - // Apply the changes if ((bool)e.NewValue) { @@ -170,16 +168,16 @@ private static void OnIsTargetChanged(DependencyObject d, DependencyPropertyChan } // The light to use - SpotLight _Light; + private SpotLight _Light; // The source compositor - Compositor _Compositor; + private Compositor _Compositor; // The expression animation for the light position - ExpressionAnimation _Animation; + private ExpressionAnimation _Animation; // The properties for the animation - CompositionPropertySet _Properties; + private CompositionPropertySet _Properties; /// protected override void OnConnected(UIElement newElement) From fc384d382e6f9ca11e80a2682fa9edbd28eab78d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 14:55:48 +0200 Subject: [PATCH 04/47] Minor tweaks --- UICompositionAnimations/Brushes/CustomAcrylicBrush.cs | 2 +- UICompositionAnimations/Brushes/NoiseTextureBrush.cs | 2 +- UICompositionAnimations/Lights/LightsSourceHelper.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index f71a1c5..fff150c 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -337,7 +337,7 @@ public static async Task ClearCacheAsync(AcrylicEffectMode targets) private async Task SetupEffectAsync() { // Designer check - if (DesignMode.DesignModeEnabled) return; + if (DesignMode.DesignMode2Enabled) return; // Dictionary to track the reference and animatable parameters IDictionary sourceParameters = new Dictionary(); diff --git a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs index 965edf1..e9427fe 100644 --- a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs +++ b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs @@ -107,7 +107,7 @@ protected override async void OnDisconnected() private async Task SetupEffectAsync() { // Designer check - if (DesignMode.DesignModeEnabled) return; + if (DesignMode.DesignMode2Enabled) return; // Dictionary to track the reference and animatable parameters IDictionary sourceParameters = new Dictionary(); diff --git a/UICompositionAnimations/Lights/LightsSourceHelper.cs b/UICompositionAnimations/Lights/LightsSourceHelper.cs index c08184b..35af443 100644 --- a/UICompositionAnimations/Lights/LightsSourceHelper.cs +++ b/UICompositionAnimations/Lights/LightsSourceHelper.cs @@ -63,7 +63,7 @@ public static void SetIsLightsContainer(UIElement element, bool value) private static void OnIsLightsContainerPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // Designer test - if (DesignMode.DesignModeEnabled) return; + if (DesignMode.DesignMode2Enabled) return; // Unpack if (LightGenerators == null) throw new NullReferenceException($"The {nameof(LightsSourceHelper)} class hasn't been initialized"); From ebe457df3e80ed1738faf4240ba1d9739d70397f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 14:59:48 +0200 Subject: [PATCH 05/47] Code annotations improved --- .../Brushes/CustomAcrylicBrush.cs | 2 +- .../Composition/CompositionManager.cs | 13 +------------ .../Helpers/ApiInformationHelper.cs | 9 +-------- .../Helpers/DispatcherHelper.cs | 14 +------------- .../Helpers/PointerEvents/PointerHelper.cs | 15 ++++++--------- 5 files changed, 10 insertions(+), 43 deletions(-) diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index fff150c..b4fa8bc 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -21,6 +21,7 @@ namespace UICompositionAnimations.Brushes /// /// A custom XAML brush that includes an acrylic effect that blurs the in-app content /// + [PublicAPI] public sealed class CustomAcrylicBrush : XamlCompositionBrushBase { #region Constants @@ -309,7 +310,6 @@ protected override async void OnDisconnected() /// /// Clears the internal cache of instances /// - [PublicAPI] public static async Task ClearCacheAsync(AcrylicEffectMode targets) { // In-app backdrop brush diff --git a/UICompositionAnimations/Composition/CompositionManager.cs b/UICompositionAnimations/Composition/CompositionManager.cs index 06c8fcc..773e8c6 100644 --- a/UICompositionAnimations/Composition/CompositionManager.cs +++ b/UICompositionAnimations/Composition/CompositionManager.cs @@ -9,6 +9,7 @@ namespace UICompositionAnimations.Composition /// /// Create composition animations using this class /// + [PublicAPI] public static class CompositionManager { #region Animations initialization @@ -23,7 +24,6 @@ public static class CompositionManager /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] public static void BeginScalarAnimation([NotNull] UIElement element, [NotNull] String propertyPath, float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { @@ -40,7 +40,6 @@ public static void BeginScalarAnimation([NotNull] UIElement element, [NotNull] S /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] public static void BeginVector2Animation([NotNull] UIElement element, [NotNull] String propertyPath, Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { @@ -73,7 +72,6 @@ public static void BeginVector3Animation([NotNull] UIElement element, [NotNull] /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] public static void BeginScalarAnimation([NotNull] this CompositionObject compObj, [NotNull] String propertyPath, float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { @@ -90,7 +88,6 @@ public static void BeginScalarAnimation([NotNull] this CompositionObject compObj /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] public static void BeginVector2Animation([NotNull]this CompositionObject compObj, [NotNull] String propertyPath, Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { @@ -107,7 +104,6 @@ public static void BeginVector2Animation([NotNull]this CompositionObject compObj /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] public static void BeginVector3Animation([NotNull] this CompositionObject compObj, [NotNull] String propertyPath, Vector3? from, Vector3 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { @@ -127,7 +123,6 @@ public static void BeginVector3Animation([NotNull] this CompositionObject compOb /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] this Compositor compositor, float? from, float to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -152,7 +147,6 @@ public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] th /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] this Compositor compositor, float? from, [NotNull] String to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -177,7 +171,6 @@ public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] th /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] this Compositor compositor, Vector2? from, Vector2 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -202,7 +195,6 @@ public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] this Compositor compositor, Vector2? from, [NotNull] String to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -227,7 +219,6 @@ public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] this Compositor compositor, Vector3? from, Vector3 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -252,7 +243,6 @@ public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] this Compositor compositor, Vector3? from, [NotNull] String to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) @@ -277,7 +267,6 @@ public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] /// The animation duration /// The optional initial delay for the animation /// The optional easing function for the animation - [PublicAPI] [Pure, NotNull] public static CompositionAnimation CreateMatrix4x4KeyFrameAnimation([NotNull] this Compositor compositor, Matrix4x4? from, Matrix4x4 to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) diff --git a/UICompositionAnimations/Helpers/ApiInformationHelper.cs b/UICompositionAnimations/Helpers/ApiInformationHelper.cs index 5c7a52a..df52963 100644 --- a/UICompositionAnimations/Helpers/ApiInformationHelper.cs +++ b/UICompositionAnimations/Helpers/ApiInformationHelper.cs @@ -1,12 +1,5 @@ using System; -using Windows.ApplicationModel.Resources.Core; -using Windows.Foundation.Collections; -using Windows.Foundation.Metadata; using Windows.System.Profile; -using Windows.UI.Composition; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Hosting; -using Windows.UI.Xaml.Input; using JetBrains.Annotations; namespace UICompositionAnimations.Helpers @@ -14,6 +7,7 @@ namespace UICompositionAnimations.Helpers /// /// A static class that provides useful information on the available APIs /// + [PublicAPI] public static class ApiInformationHelper { private static Version _OSVersion; @@ -25,7 +19,6 @@ public static class ApiInformationHelper /// instead of just checking the OS version and use it to make further decisions. /// In particular, users on a Windows Insider ring could already have a given set of APIs even though their OS /// build doesn't exactly match the official Windows 10 version that supports a requested API contract. - [PublicAPI] [NotNull] public static Version OSVersion { diff --git a/UICompositionAnimations/Helpers/DispatcherHelper.cs b/UICompositionAnimations/Helpers/DispatcherHelper.cs index 8705b23..cd9adf4 100644 --- a/UICompositionAnimations/Helpers/DispatcherHelper.cs +++ b/UICompositionAnimations/Helpers/DispatcherHelper.cs @@ -9,6 +9,7 @@ namespace UICompositionAnimations.Helpers /// /// A static class that manages the UI dispatching from background threads /// + [PublicAPI] public static class DispatcherHelper { #region Target dispatcher helpers @@ -18,7 +19,6 @@ public static class DispatcherHelper /// /// The target dispatcher to use to schedule the callback execution /// The action to execute on the UI thread - [PublicAPI] public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback) { if (dispatcher.HasThreadAccess) callback(); @@ -33,7 +33,6 @@ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Actio /// /// The target dispatcher to use to schedule the callback execution /// The action to execute on the UI thread - [PublicAPI] public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback) { // Check the current thread @@ -48,7 +47,6 @@ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func< /// /// The target dispatcher to use to schedule the callback execution /// The action to execute on the UI thread - [PublicAPI] public static async Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback) { if (dispatcher.HasThreadAccess) callback(); @@ -69,7 +67,6 @@ public static async Task RunAsync([NotNull] this CoreDispatcher dispatcher, [Not /// /// The target dispatcher to use to schedule the callback execution /// The action to execute on the UI thread - [PublicAPI] public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback) { // Check the current thread @@ -90,7 +87,6 @@ public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] /// The return type /// The target dispatcher to use to schedule the callback execution /// The function to execute on the UI thread - [PublicAPI] public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func function) { if (dispatcher.HasThreadAccess) return Task.FromResult(function()); @@ -109,7 +105,6 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// The return type /// The target dispatcher to use to schedule the callback execution /// The async function to execute on the UI thread - [PublicAPI] public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func> function) { // Check the current thread @@ -139,35 +134,30 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// /// Checks whether or not the current thread has access to the UI /// - [PublicAPI] public static bool HasUIThreadAccess => CoreDispatcher.HasThreadAccess; /// /// Executes a given action on the UI thread without awaiting the operation /// /// The action to execute on the UI thread - [PublicAPI] public static void RunOnUIThread([NotNull] Action callback) => CoreDispatcher.Run(callback); /// /// Executes a given async action on the UI thread without awaiting the operation /// /// The action to execute on the UI thread - [PublicAPI] public static void RunOnUIThread([NotNull] Func asyncCallback) => CoreDispatcher.Run(asyncCallback); /// /// Executes a given action on the UI thread and waits for it to be completed /// /// The action to execute on the UI thread - [PublicAPI] public static Task RunOnUIThreadAsync([NotNull] Action callback) => CoreDispatcher.RunAsync(callback); /// /// Executes a given action on the UI thread and waits for it to be completed /// /// The action to execute on the UI thread - [PublicAPI] public static Task RunOnUIThreadAsync([NotNull] Func asyncCallback) => CoreDispatcher.RunAsync(asyncCallback); /// @@ -175,7 +165,6 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// /// The return type /// The function to execute on the UI thread - [PublicAPI] public static Task GetFromUIThreadAsync([NotNull] Func function) => CoreDispatcher.GetAsync(function); /// @@ -183,7 +172,6 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// /// The return type /// The async function to execute on the UI thread - [PublicAPI] public static Task GetFromUIThreadAsync([NotNull] Func> function) => CoreDispatcher.GetAsync(function); #endregion diff --git a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs index 956776c..a576cc6 100644 --- a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs +++ b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs @@ -9,6 +9,7 @@ namespace UICompositionAnimations.Helpers.PointerEvents /// /// A static class with some extension methods to manage different pointer states /// + [PublicAPI] public static class PointerHelper { /// @@ -17,7 +18,6 @@ public static class PointerHelper /// The control to monitor /// An action to call every time a pointer event is raised. The bool parameter /// indicates whether the pointer is moving to or from the control - [PublicAPI] [NotNull] public static ControlAttachedHandlersInfo ManageControlPointerStates(this UIElement control, Action action) { @@ -49,22 +49,20 @@ void Handler(object _, PointerRoutedEventArgs e) /// The element to monitor /// An action to call every time a pointer event is raised. The bool parameter /// indicates whether the pointer is moving to or from the control - [PublicAPI] [NotNull] public static ControlAttachedHandlersInfo ManageHostPointerStates(this UIElement host, Action action) { // Nested functions that adds the actual handlers PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func predicate) { - PointerEventHandler handler = (_, e) => + void Handler(object _, PointerRoutedEventArgs e) { if (predicate == null || predicate(e.Pointer.PointerDeviceType)) - { action(e.Pointer.PointerDeviceType, state); - } - }; - host.AddHandler(@event, handler, true); - return new PointerHandlerInfo(@event, handler); + } + + host.AddHandler(@event, (PointerEventHandler) Handler, true); + return new PointerHandlerInfo(@event, Handler); } // Add handlers @@ -78,7 +76,6 @@ PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func /// The element to monitor /// An action to call every time the light effects state should be changed - [PublicAPI] [CanBeNull] public static ControlAttachedHandlersInfo ManageLightsPointerStates(this UIElement element, Action action) { From c42ad0538b64d9f3822a5e9229eb6ab6b0423ee4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 15:03:47 +0200 Subject: [PATCH 06/47] Some more code tweaks --- UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs | 1 + UICompositionAnimations/XAMLTransformExtensions.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs b/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs index e90241f..72d99a9 100644 --- a/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs +++ b/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs @@ -13,6 +13,7 @@ namespace UICompositionAnimations.XAMLTransform /// /// A toolkit with some extensions for XAML animations and some useful methods /// + [PublicAPI] public static class XAMLTransformToolkit { #region Extensions diff --git a/UICompositionAnimations/XAMLTransformExtensions.cs b/UICompositionAnimations/XAMLTransformExtensions.cs index c34d4f2..288e7ee 100644 --- a/UICompositionAnimations/XAMLTransformExtensions.cs +++ b/UICompositionAnimations/XAMLTransformExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Windows.UI; using Windows.UI.Xaml; @@ -14,6 +15,7 @@ namespace UICompositionAnimations /// /// A static class that wraps the animation methods in the Windows.UI.Xaml.Media.Animation namespace /// + [PublicAPI] public static class XAMLTransformExtensions { #region Fade @@ -423,6 +425,7 @@ public static Storyboard GetXAMLTransformSlideStoryboard(this UIElement element, /// The margin side to animate /// The duration of the animation to create /// The optional initial delay for the animation + [SuppressMessage("ReSharper", "AccessToModifiedClosure")] // Margin updates at each animation timestep public static async Task StartXAMLMarginAnimation([NotNull] this FrameworkElement element, double? start, double end, MarginSide side, int duration, int? delay = null) { // Delay if needed, and calculate the start offset @@ -460,6 +463,7 @@ public static async Task StartXAMLMarginAnimation([NotNull] this FrameworkElemen DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(25) }; // 40fps timer.Tick += TickHandler; Thickness margin = element.Margin; + void TickHandler(object sender, object e) { elapsed++; From 8953ae04df5d327fb6a94a0eb178ecf68f2c3bac Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 20 Jul 2018 15:31:00 +0200 Subject: [PATCH 07/47] Published as beta on NuGet --- UICompositionAnimations/Properties/AssemblyInfo.cs | 6 +++--- UICompositionAnimations/UICompositionAnimations.nuspec | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index 2bf9f88..e83d64e 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -23,7 +23,7 @@ // 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("2.15.0.0")] -[assembly: AssemblyInformationalVersion("2.15.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("3.0.0.0")] +[assembly: AssemblyFileVersion("3.0.0.0")] +[assembly: AssemblyInformationalVersion("3.0.0-beta1")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index 1c9004c..231f11a 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,14 +2,15 @@ UICompositionAnimations - $version$ + 3.0.0-beta1 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri Sergio Pedri https://github.com/Sergio0694/UICompositionAnimations + https://github.com/Sergio0694/UICompositionAnimations/blob/master/LICENSE.md false - Added new Composition Translation animations APIs, bug fixes + Retargeted to support SDK 17134, bug fixes and code improvements Copyright 2017 uwp composition animations xaml csharp windows winrt universal app ui win2d graphics From badc510f6861de7897adce8000d9abca9af274be Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 22 Jul 2018 01:03:46 +0200 Subject: [PATCH 08/47] Minor code improvements --- .../Helpers/PointerEvents/PointerHelper.cs | 96 +++++++++---------- .../Properties/AssemblyInfo.cs | 2 +- .../UICompositionAnimations.nuspec | 2 +- 3 files changed, 46 insertions(+), 54 deletions(-) diff --git a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs index a576cc6..57fbea2 100644 --- a/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs +++ b/UICompositionAnimations/Helpers/PointerEvents/PointerHelper.cs @@ -13,78 +13,70 @@ namespace UICompositionAnimations.Helpers.PointerEvents public static class PointerHelper { /// - /// Adds an event handler to all the pointer events of the target control + /// Adds an event handler to the following targets of the input : + /// UIElement.PointerExitedEvent, UIElement.PointerCaptureLostEvent, UIElement.PointerCanceledEvent, UIElement.PointerEnteredEvent, UIElement.PointerReleasedEvent. + /// Some additional handling of the input device is also performed automatically to filter out some useful input combinations (eg. disable touch input for the case). /// - /// The control to monitor - /// An action to call every time a pointer event is raised. The bool parameter - /// indicates whether the pointer is moving to or from the control + /// The to monitor + /// An to call every time a pointer event is raised. The parameter indicates whether the pointer is moving to or from the control [NotNull] - public static ControlAttachedHandlersInfo ManageControlPointerStates(this UIElement control, Action action) + public static ControlAttachedHandlersInfo ManageControlPointerStates([NotNull] this UIElement control, [NotNull] Action action) { - // Nested functions that adds the actual handlers - PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func predicate) - { - void Handler(object _, PointerRoutedEventArgs e) - { - if (predicate == null || predicate(e.Pointer.PointerDeviceType)) - action(e.Pointer.PointerDeviceType, state); - } - - control.AddHandler(@event, (PointerEventHandler) Handler, true); - return new PointerHandlerInfo(@event, Handler); - } - - // Add handlers return new ControlAttachedHandlersInfo(control, - AddHandler(UIElement.PointerExitedEvent, false, null), - AddHandler(UIElement.PointerCaptureLostEvent, false, null), - AddHandler(UIElement.PointerCanceledEvent, false, null), - AddHandler(UIElement.PointerEnteredEvent, true, p => p != PointerDeviceType.Touch), - AddHandler(UIElement.PointerReleasedEvent, false, p => p == PointerDeviceType.Touch)); + AddHandler(control, UIElement.PointerExitedEvent, false, null, action), + AddHandler(control, UIElement.PointerCaptureLostEvent, false, null, action), + AddHandler(control, UIElement.PointerCanceledEvent, false, null, action), + AddHandler(control, UIElement.PointerEnteredEvent, true, p => p != PointerDeviceType.Touch, action), + AddHandler(control, UIElement.PointerReleasedEvent, false, p => p == PointerDeviceType.Touch, action)); } /// - /// Adds an event handler to all the pointer events of the target element + /// Adds an event handler to the following targets of the input : UIElement.PointerExitedEvent, UIElement.PointerMovedEvent. + /// This is especially useful to check whether or not the user is currently moving the input cursor above a host control, as this method will correctly handle scenarios + /// where the cursor would be considered as exited from the target control, while still being inside its area. + /// For example, this could happen if the user clicked on a nested control inside the target host, without actually moving the cursor away. /// - /// The element to monitor - /// An action to call every time a pointer event is raised. The bool parameter - /// indicates whether the pointer is moving to or from the control + /// The to monitor + /// An to call every time a pointer event is raised. The parameter indicates whether the pointer is moving to or from the control [NotNull] - public static ControlAttachedHandlersInfo ManageHostPointerStates(this UIElement host, Action action) + public static ControlAttachedHandlersInfo ManageHostPointerStates([NotNull] this UIElement host, [NotNull] Action action) { - // Nested functions that adds the actual handlers - PointerHandlerInfo AddHandler(RoutedEvent @event, bool state, Func predicate) - { - void Handler(object _, PointerRoutedEventArgs e) - { - if (predicate == null || predicate(e.Pointer.PointerDeviceType)) - action(e.Pointer.PointerDeviceType, state); - } - - host.AddHandler(@event, (PointerEventHandler) Handler, true); - return new PointerHandlerInfo(@event, Handler); - } - - // Add handlers return new ControlAttachedHandlersInfo(host, - AddHandler(UIElement.PointerExitedEvent, false, null), - AddHandler(UIElement.PointerMovedEvent, true, p => p != PointerDeviceType.Touch)); + AddHandler(host, UIElement.PointerExitedEvent, false, null, action), + AddHandler(host, UIElement.PointerMovedEvent, true, p => p != PointerDeviceType.Touch, action)); } /// - /// Adds the appropriate handlers to a control to help setup the light effects (skipped when on a mobile phone) + /// Adds the appropriate handlers to a control to help setup the light effects (skipped when using a touch screen) /// - /// The element to monitor - /// An action to call every time the light effects state should be changed - [CanBeNull] - public static ControlAttachedHandlersInfo ManageLightsPointerStates(this UIElement element, Action action) + /// The to monitor + /// An to call every time the light effects state should be changed + [NotNull] + public static ControlAttachedHandlersInfo ManageLightsPointerStates([NotNull] this UIElement element, [NotNull] Action action) { - // Nested functions that adds the actual handlers + bool state = false; return element.ManageHostPointerStates((pointer, value) => { - if (pointer != PointerDeviceType.Mouse) return; + if (pointer != PointerDeviceType.Mouse || state == value) return; + state = value; action(value); }); } + + // Private method that adds a custom handler to the target event and control + [NotNull] + private static PointerHandlerInfo AddHandler([NotNull] UIElement element, [NotNull] RoutedEvent @event, bool state, [CanBeNull] Func predicate, [NotNull] Action action) + { + // Callback with additional pointer type check + void Handler(object _, PointerRoutedEventArgs e) + { + if (predicate == null || predicate(e.Pointer.PointerDeviceType)) + action(e.Pointer.PointerDeviceType, state); + } + + // Handler setup + element.AddHandler(@event, (PointerEventHandler)Handler, true); + return new PointerHandlerInfo(@event, Handler); + } } } diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index e83d64e..df6cea1 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -25,5 +25,5 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("3.0.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")] -[assembly: AssemblyInformationalVersion("3.0.0-beta1")] +[assembly: AssemblyInformationalVersion("3.0.0-beta2")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index 231f11a..b42c685 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,7 +2,7 @@ UICompositionAnimations - 3.0.0-beta1 + 3.0.0-beta2 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri From d01abe74e08aa19cd1911ba0839a753e4ee3db30 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 27 Jul 2018 21:29:44 +0200 Subject: [PATCH 09/47] Added base structure for the BrushBuilder class --- .../Behaviours/CompositionBrushBuilder.cs | 126 ++++++++++++++++++ .../UICompositionAnimations.csproj | 1 + 2 files changed, 127 insertions(+) create mode 100644 UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs new file mode 100644 index 0000000..ccb4a0c --- /dev/null +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using Windows.Graphics.Effects; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; +using JetBrains.Annotations; +using Microsoft.Graphics.Canvas.Effects; +using UICompositionAnimations.Helpers; + +namespace UICompositionAnimations.Behaviours +{ + public sealed class CompositionBrushBuilder + { + private readonly Func> SourceProducer; + + private readonly IReadOnlyDictionary> LazyParameters; + + private readonly IReadOnlyCollection AnimationParameters; + + #region Initialization + + private CompositionBrushBuilder(Func factory) + { + SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter("source").To()); + LazyParameters = new Dictionary> { { "source", factory } }; + AnimationParameters = new string[0]; + } + + private CompositionBrushBuilder(Func> factory) + { + SourceProducer = factory; + LazyParameters = new Dictionary>(); + AnimationParameters = new string[0]; + } + + private CompositionBrushBuilder(CompositionBrushBuilder source, Func> factory, IReadOnlyDictionary> lazy, IReadOnlyCollection animations) + { + SourceProducer = factory; + + // Merge the lazy parameters dictionaries + if (source.LazyParameters.Keys.FirstOrDefault(lazy.ContainsKey) is String key) + throw new InvalidOperationException($"The key {key} already exists in the current pipeline"); + LazyParameters = new Dictionary>(source.LazyParameters.Concat(lazy)); + + // Merge the animation parameters + if (source.AnimationParameters.FirstOrDefault(animations.Contains) is String animation) + throw new InvalidOperationException($"The animation {animation} already exists in the current pipeline"); + AnimationParameters = source.AnimationParameters.Concat(animations).ToArray(); + } + + [Pure, NotNull] + public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => Window.Current.Compositor.CreateBackdropBrush()); + + [Pure, NotNull] + public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => Window.Current.Compositor.CreateHostBackdropBrush()); + + [Pure, NotNull] + public static CompositionBrushBuilder FromEffect(Func factory) => new CompositionBrushBuilder(() => Task.FromResult(factory())); + + #endregion + + #region Effects + + public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBorderMode.Hard, EffectOptimization optimization = EffectOptimization.Balanced) + { + // Blur effect + async Task Factory() => new GaussianBlurEffect + { + Name = "Blur", + BlurAmount = blur, + BorderMode = mode, + Optimization = optimization, + Source = await SourceProducer() + }; + + // Info + String[] parameters = { "Blur.BlurAmount" }; + return new CompositionBrushBuilder(this, Factory, new Dictionary>(), parameters); + } + + #endregion + + public async Task BuildAsync() + { + // Validate the pipeline and build the effects factory + if (!(await SourceProducer() is IGraphicsEffect effect)) throw new InvalidOperationException("The pipeline doesn't contain a valid effects sequence"); + CompositionEffectFactory factory = Window.Current.Compositor.CreateEffectFactory(effect, AnimationParameters); + + // Create the effect factory and apply the final effect + CompositionEffectBrush effectBrush = factory.CreateBrush(); + foreach (KeyValuePair> pair in LazyParameters) + effectBrush.SetSourceParameter(pair.Key, pair.Value()); + return effectBrush; + } + } + + public static class CompositionBrushExtensions + { + /// + /// Adds a object on top of the target and binds the size of the two items with an expression animation + /// + /// The object that will host the effect + /// The target (bound to the given visual) that will host the effect + /// The source object to display + 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); + + // Keep the sprite size in sync + Visual visual = target.GetVisual(); + ExpressionAnimation bindSizeAnimation = Window.Current.Compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); + bindSizeAnimation.SetReferenceParameter(nameof(visual), visual); + + // Start the animation + sprite.StartAnimation("Size", bindSizeAnimation); + } + } +} diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 026b0cd..ed3f628 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -111,6 +111,7 @@ + From 57af7ad7ec758f6e0bf64e2e604de90fc9922ee2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 27 Jul 2018 22:06:03 +0200 Subject: [PATCH 10/47] Minor refactoring, added some XML comments --- .../Behaviours/CompositionBrushBuilder.cs | 115 ++++++++++++++++-- .../Helpers/Win2DImageHelper.cs | 2 +- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index ccb4a0c..5337205 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -4,47 +4,82 @@ using System.Numerics; using System.Threading.Tasks; using Windows.Graphics.Effects; +using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; using JetBrains.Annotations; using Microsoft.Graphics.Canvas.Effects; +using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; namespace UICompositionAnimations.Behaviours { + /// + /// A that allows to build custom effects pipelines and create instances from them + /// + [PublicAPI] public sealed class CompositionBrushBuilder { + /// + /// The instance used to produce the output for this pipeline + /// + [NotNull] private readonly Func> SourceProducer; - private readonly IReadOnlyDictionary> LazyParameters; + /// + /// The collection of info on the parameters that need to be initialized after creating the final + /// + [NotNull] + private readonly IReadOnlyDictionary>> LazyParameters; + /// + /// The collection of animation parameters present in the current pipeline + /// + [NotNull, ItemNotNull] private readonly IReadOnlyCollection AnimationParameters; - #region Initialization + #region Constructors - private CompositionBrushBuilder(Func factory) + /// + /// 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) { SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter("source").To()); - LazyParameters = new Dictionary> { { "source", factory } }; + LazyParameters = new Dictionary>> { { "source", factory } }; AnimationParameters = new string[0]; } - private CompositionBrushBuilder(Func> factory) + /// + /// Constructor used to initialize a pipeline from a custom instance + /// + /// A instance that will return the initial + private CompositionBrushBuilder([NotNull] Func> factory) { SourceProducer = factory; - LazyParameters = new Dictionary>(); + LazyParameters = new Dictionary>>(); AnimationParameters = new string[0]; } - private CompositionBrushBuilder(CompositionBrushBuilder source, Func> factory, IReadOnlyDictionary> lazy, IReadOnlyCollection animations) + /// + /// Constructor used to create a new instance obtained by concatenation between the current pipeline and the input effect info + /// + /// The source pipeline to attach the new effect to + /// A instance that will produce the new to add to the pipeline + /// The collection of instances that needs to be initialized for the new effect + /// The collection of animation properties for the new effect + private CompositionBrushBuilder( + [NotNull] CompositionBrushBuilder source, + [NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) { SourceProducer = factory; // Merge the lazy parameters dictionaries if (source.LazyParameters.Keys.FirstOrDefault(lazy.ContainsKey) is String key) throw new InvalidOperationException($"The key {key} already exists in the current pipeline"); - LazyParameters = new Dictionary>(source.LazyParameters.Concat(lazy)); + LazyParameters = new Dictionary>>(source.LazyParameters.Concat(lazy)); // Merge the animation parameters if (source.AnimationParameters.FirstOrDefault(animations.Contains) is String animation) @@ -52,15 +87,69 @@ private CompositionBrushBuilder(CompositionBrushBuilder source, Func + /// Starts a new pipeline from the returned by + /// [Pure, NotNull] - public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => Window.Current.Compositor.CreateBackdropBrush()); + public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => Task.FromResult(Window.Current.Compositor.CreateBackdropBrush())); + /// + /// Starts a new pipeline from the returned by + /// [Pure, NotNull] - public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => Window.Current.Compositor.CreateHostBackdropBrush()); + public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => Task.FromResult(Window.Current.Compositor.CreateHostBackdropBrush())); + /// + /// Starts a new pipeline from a solid with the specified color + /// + /// The desired for the initial + [Pure, NotNull] + public static CompositionBrushBuilder FromColor(Color color) => new CompositionBrushBuilder(() => Task.FromResult(new ColorSourceEffect { Color = color }.To())); + + /// + /// 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())); + + /// + /// 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); + + /// + /// 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())); + /// + /// 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); + + /// + /// Starts a new pipeline from a Win2D image + /// + /// The path for the image to load + /// Indicates whether or not to force the reload of the Win2D image + /// Indicates the desired DPI mode to use when loading the image + [Pure, NotNull] + public static CompositionBrushBuilder FromImage([NotNull] Uri uri, BitmapCacheMode options, BitmapDPIMode dpiMode) + { + return new CompositionBrushBuilder(() => Win2DImageHelper.LoadImageAsync(Window.Current.Compositor, uri, options, dpiMode).ContinueWith(t => t.Result as CompositionBrush)); + } + #endregion #region Effects @@ -79,7 +168,7 @@ public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBo // Info String[] parameters = { "Blur.BlurAmount" }; - return new CompositionBrushBuilder(this, Factory, new Dictionary>(), parameters); + return new CompositionBrushBuilder(this, Factory, new Dictionary>>(), parameters); } #endregion @@ -92,8 +181,8 @@ public async Task BuildAsync() // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair> pair in LazyParameters) - effectBrush.SetSourceParameter(pair.Key, pair.Value()); + foreach (KeyValuePair>> pair in LazyParameters) + effectBrush.SetSourceParameter(pair.Key, await pair.Value()); return effectBrush; } } diff --git a/UICompositionAnimations/Helpers/Win2DImageHelper.cs b/UICompositionAnimations/Helpers/Win2DImageHelper.cs index 7ba2568..aae1d00 100644 --- a/UICompositionAnimations/Helpers/Win2DImageHelper.cs +++ b/UICompositionAnimations/Helpers/Win2DImageHelper.cs @@ -120,7 +120,7 @@ private static async Task LoadSurfaceBrushAsync([NotNul bitmap = await CanvasBitmap.LoadAsync(creator, uri, dpi >= 96 ? dpi : 96); break; default: - throw new ArgumentOutOfRangeException("Unsupported DPI mode"); + throw new ArgumentOutOfRangeException(nameof(dpiMode), "Unsupported DPI mode"); } // Get the device and the target surface From 025daea4e20512bc9b6822456ae08b2d663fb6a9 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 00:43:49 +0200 Subject: [PATCH 11/47] More pipeline APIs added --- .../Behaviours/CompositionBrushBuilder.cs | 229 ++++++++++++++---- .../CompositionExtensions.cs | 24 +- UICompositionAnimations/Helpers/Extensions.cs | 31 +++ 3 files changed, 230 insertions(+), 54 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 5337205..5f7ce71 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Numerics; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Windows.Graphics.Effects; using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; -using Windows.UI.Xaml.Hosting; using JetBrains.Annotations; +using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Effects; using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; @@ -47,22 +47,45 @@ public sealed class CompositionBrushBuilder /// A instance that will return the initial private CompositionBrushBuilder([NotNull] Func> factory) { - SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter("source").To()); - LazyParameters = new Dictionary>> { { "source", factory } }; + String + guid = Guid.NewGuid().ToString("N"), + replaced = Regex.Replace(guid, "[0-9]", "_"), + id = new String(replaced.ToCharArray().Select((c, i) => c == '_' ? char.ToUpper((char)('a' + i % 26)) : c).ToArray()); + SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter(id).To()); + LazyParameters = new Dictionary>> { { id, factory } }; AnimationParameters = new string[0]; } /// - /// Constructor used to initialize a pipeline from a custom instance + /// Base constructor used to create a new instance from scratch /// - /// A instance that will return the initial - private CompositionBrushBuilder([NotNull] Func> factory) + /// A instance that will produce the new to add to the pipeline + /// The collection of instances that needs to be initialized for the new effect + /// The collection of animation properties for the new effect + private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) { SourceProducer = factory; - LazyParameters = new Dictionary>>(); - AnimationParameters = new string[0]; + LazyParameters = lazy; + AnimationParameters = animations; } + /// + /// Constructor used to initialize a pipeline from a custom instance + /// + /// A instance that will return the initial + private CompositionBrushBuilder([NotNull] Func> factory) + : this(factory, new Dictionary>>(), new string[0]) + { } + + /// + /// Constructor used to create a new instance obtained by concatenation between the current pipeline and the input effect info + /// + /// The source pipeline to attach the new effect to + /// A instance that will produce the new to add to the pipeline + private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source, [NotNull] Func> factory) + : this(factory, source.LazyParameters, source.AnimationParameters) + { } + /// /// Constructor used to create a new instance obtained by concatenation between the current pipeline and the input effect info /// @@ -73,19 +96,20 @@ private CompositionBrushBuilder([NotNull] Func> fact private CompositionBrushBuilder( [NotNull] CompositionBrushBuilder source, [NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) - { - SourceProducer = factory; - - // Merge the lazy parameters dictionaries - if (source.LazyParameters.Keys.FirstOrDefault(lazy.ContainsKey) is String key) - throw new InvalidOperationException($"The key {key} already exists in the current pipeline"); - LazyParameters = new Dictionary>>(source.LazyParameters.Concat(lazy)); + : this(factory, source.LazyParameters.Merge(lazy), source.AnimationParameters.Merge(animations)) + { } - // Merge the animation parameters - if (source.AnimationParameters.FirstOrDefault(animations.Contains) is String animation) - throw new InvalidOperationException($"The animation {animation} already exists in the current pipeline"); - AnimationParameters = source.AnimationParameters.Concat(animations).ToArray(); - } + /// + /// Constructor used to create a new instance obtained by merging two separate pipelines + /// + /// A instance that will produce the new to add to the pipeline + /// The first pipeline to merge + /// The second pipeline to merge + private CompositionBrushBuilder( + [NotNull] Func> factory, + [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b) + : this(factory, a.LazyParameters.Merge(b.LazyParameters), a.AnimationParameters.Merge(b.AnimationParameters)) + { } #endregion @@ -145,39 +169,164 @@ private CompositionBrushBuilder( /// Indicates whether or not to force the reload of the Win2D image /// Indicates the desired DPI mode to use when loading the image [Pure, NotNull] - public static CompositionBrushBuilder FromImage([NotNull] Uri uri, BitmapCacheMode options, BitmapDPIMode dpiMode) + public static CompositionBrushBuilder FromImage([NotNull] Uri uri, BitmapCacheMode options = BitmapCacheMode.EnableCaching, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound) { return new CompositionBrushBuilder(() => Win2DImageHelper.LoadImageAsync(Window.Current.Compositor, uri, options, dpiMode).ContinueWith(t => t.Result as CompositionBrush)); } + /// + /// Starts a new pipeline from a Win2D image tiled to cover the available space + /// + /// The path for the image to load + /// Indicates whether or not to force the reload of the Win2D image + /// Indicates the desired DPI mode to use when loading the image + [Pure, NotNull] + public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapCacheMode options = BitmapCacheMode.EnableCaching, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound) + { + CompositionBrushBuilder image = FromImage(uri, options, dpiMode); + + async Task Factory() => new BorderEffect + { + ExtendX = CanvasEdgeBehavior.Wrap, + ExtendY = CanvasEdgeBehavior.Wrap, + Source = await image.SourceProducer() + }; + + return new CompositionBrushBuilder(image, Factory); + } + + #endregion + + #region Blends + + public static CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background, BlendEffectMode mode) + { + async Task Factory() => new BlendEffect + { + Foreground = await foreground.SourceProducer(), + Background = await background.SourceProducer(), + Mode = mode + }; + + return new CompositionBrushBuilder(Factory, foreground, background); + } + + public static CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background, float mix) + { + if (mix <= 0 || mix >= 1) throw new ArgumentOutOfRangeException(nameof(mix), "The mix value must be in the (0,1) range"); + async Task Factory() => new ArithmeticCompositeEffect + { + MultiplyAmount = 0, + Source1Amount = 1 - mix, + Source2Amount = mix, + Source1 = await background.SourceProducer(), + Source2 = await foreground.SourceProducer() + }; + + return new CompositionBrushBuilder(Factory, foreground, background); + } + + public static CompositionBrushBuilder Merge( + [NotNull] Func factory, + [NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background) + { + async Task Factory() => factory(await foreground.SourceProducer(), await background.SourceProducer()); + + return new CompositionBrushBuilder(Factory, foreground, background); + } + + public static CompositionBrushBuilder Merge( + [NotNull] Func> factory, + [NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background) + { + async Task Factory() => await factory(await foreground.SourceProducer(), await background.SourceProducer()); + + return new CompositionBrushBuilder(Factory, foreground, background); + } + #endregion #region Effects + /// + /// Adds a new to the current pipeline + /// + /// The blur amount to apply + /// 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) { // Blur effect async Task Factory() => new GaussianBlurEffect { - Name = "Blur", BlurAmount = blur, BorderMode = mode, Optimization = optimization, Source = await SourceProducer() }; - // Info - String[] parameters = { "Blur.BlurAmount" }; - return new CompositionBrushBuilder(this, Factory, new Dictionary>>(), parameters); + return new CompositionBrushBuilder(this, Factory); + } + + /// + /// Adds a new to the current pipeline + /// + /// The saturation amount for the new effect + [Pure, NotNull] + public CompositionBrushBuilder 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 + { + Saturation = saturation, + Source = await SourceProducer() + }; + + return new CompositionBrushBuilder(this, Factory); + } + + public CompositionBrushBuilder 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 + { + Opacity = opacity, + Source = await SourceProducer() + }; + + return new CompositionBrushBuilder(this, Factory); + } + + public CompositionBrushBuilder Tint(Color color, float mix) => Mix(FromColor(color), this, mix); + + public CompositionBrushBuilder Effect([NotNull] Func factory) + { + async Task Factory() => factory(await SourceProducer()); + + return new CompositionBrushBuilder(this, Factory); + } + + public CompositionBrushBuilder Effect([NotNull] Func> factory) + { + async Task Factory() => await factory(await SourceProducer()); + + return new CompositionBrushBuilder(this, Factory); } #endregion + /// + /// Builds a instance from the current effects pipeline + /// + [Pure, NotNull, ItemNotNull] public async Task BuildAsync() { // Validate the pipeline and build the effects factory if (!(await SourceProducer() is IGraphicsEffect effect)) throw new InvalidOperationException("The pipeline doesn't contain a valid effects sequence"); - CompositionEffectFactory factory = Window.Current.Compositor.CreateEffectFactory(effect, AnimationParameters); + CompositionEffectFactory factory = AnimationParameters.Count > 0 + ? Window.Current.Compositor.CreateEffectFactory(effect, AnimationParameters) + : Window.Current.Compositor.CreateEffectFactory(effect); // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); @@ -186,30 +335,4 @@ public async Task BuildAsync() return effectBrush; } } - - public static class CompositionBrushExtensions - { - /// - /// Adds a object on top of the target and binds the size of the two items with an expression animation - /// - /// The object that will host the effect - /// The target (bound to the given visual) that will host the effect - /// The source object to display - 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); - - // Keep the sprite size in sync - Visual visual = target.GetVisual(); - ExpressionAnimation bindSizeAnimation = Window.Current.Compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); - bindSizeAnimation.SetReferenceParameter(nameof(visual), visual); - - // Start the animation - sprite.StartAnimation("Size", bindSizeAnimation); - } - } } diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index 0ae33ed..ab14b51 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -19,6 +19,7 @@ namespace UICompositionAnimations /// /// A static class that wraps the animation methods in the Windows.UI.Composition namespace /// + [PublicAPI] public static class CompositionExtensions { #region Internal tools @@ -1719,7 +1720,6 @@ public static SpriteVisual AttachVisualShadow( case Shape shape: shadow.Mask = shape.GetAlphaMask(); break; case Image image: shadow.Mask = image.GetAlphaMask(); break; case TextBlock textBlock: shadow.Mask = textBlock.GetAlphaMask(); break; - default: break; } if (apply) ElementCompositionPreview.SetElementChildVisual(target, sprite); return sprite; @@ -2029,6 +2029,28 @@ public static async Task ResetCompositionVisualPropertiesAsync(this FrameworkEle /// The source UIElement public static Visual GetVisual(this UIElement element) => ElementCompositionPreview.GetElementVisual(element); + /// + /// Adds a instance on top of the target and binds the size of the two items with an expression animation + /// + /// 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); + + // Keep the sprite size in sync + Visual visual = target.GetVisual(); + ExpressionAnimation bindSizeAnimation = Window.Current.Compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); + bindSizeAnimation.SetReferenceParameter(nameof(visual), visual); + + // Start the animation + sprite.StartAnimation("Size", bindSizeAnimation); + } + #endregion } } diff --git a/UICompositionAnimations/Helpers/Extensions.cs b/UICompositionAnimations/Helpers/Extensions.cs index e98734c..7db5bde 100644 --- a/UICompositionAnimations/Helpers/Extensions.cs +++ b/UICompositionAnimations/Helpers/Extensions.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Windows.Foundation; +using JetBrains.Annotations; namespace UICompositionAnimations.Helpers { @@ -49,5 +52,33 @@ public static void Forget(this IAsyncAction action) { } /// The task returned by the async call [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Forget(this Task task) { } + + /// + /// 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(); + } } } From 56c2468813cbbb60a61c3a3232f41907ea0d1feb Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 01:20:41 +0200 Subject: [PATCH 12/47] Minor refactoring, more XML comments added --- .../Behaviours/CompositionBrushBuilder.cs | 90 ++++++++++++++++--- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 5f7ce71..70b0909 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -199,8 +199,34 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapCacheMo #region Blends - public static CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background, BlendEffectMode mode) + /// + /// An used to modify the default sorting of the input instances in a blend operation + /// + [PublicAPI] + public enum InputsSorting { + /// + /// The instance used to call the blend method is placed on top of the other + /// + ForegroundToBackground, + + /// + /// The instance used to call the blend method is placed behind the other + /// + BackgroundToForeground + } + + /// + /// Blends two pipelines using a instance with the specified mode + /// + /// 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, InputsSorting sorting = InputsSorting.ForegroundToBackground) + { + (var foreground, var background) = sorting == InputsSorting.ForegroundToBackground ? (this, pipeline) : (pipeline, this); + async Task Factory() => new BlendEffect { Foreground = await foreground.SourceProducer(), @@ -211,9 +237,18 @@ public static CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder fo return new CompositionBrushBuilder(Factory, foreground, background); } - public static CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background, float mix) + /// + /// Blends two pipelines using an instance + /// + /// The second instance to blend + /// The intensity of the foreground effect in the final pipeline + /// The sorting mode to use with the two input pipelines + [Pure, NotNull] + public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float mix, InputsSorting sorting = InputsSorting.ForegroundToBackground) { if (mix <= 0 || mix >= 1) throw new ArgumentOutOfRangeException(nameof(mix), "The mix value must be in the (0,1) range"); + (var foreground, var background) = sorting == InputsSorting.ForegroundToBackground ? (this, pipeline) : (pipeline, this); + async Task Factory() => new ArithmeticCompositeEffect { MultiplyAmount = 0, @@ -226,22 +261,34 @@ public static CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder fore return new CompositionBrushBuilder(Factory, foreground, background); } - public static CompositionBrushBuilder Merge( + /// + /// Blends two pipelines using the provided to do so + /// + /// The blend function to use + /// The background pipeline to blend with the current instance + [Pure, NotNull] + public CompositionBrushBuilder Merge( [NotNull] Func factory, - [NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background) + [NotNull] CompositionBrushBuilder background) { - async Task Factory() => factory(await foreground.SourceProducer(), await background.SourceProducer()); + async Task Factory() => factory(await SourceProducer(), await background.SourceProducer()); - return new CompositionBrushBuilder(Factory, foreground, background); + return new CompositionBrushBuilder(Factory, this, background); } - public static CompositionBrushBuilder Merge( + /// + /// Blends two pipelines using the provided asynchronous to do so + /// + /// The asynchronous blend function to use + /// The background pipeline to blend with the current instance + [Pure, NotNull] + public CompositionBrushBuilder Merge( [NotNull] Func> factory, - [NotNull] CompositionBrushBuilder foreground, [NotNull] CompositionBrushBuilder background) + [NotNull] CompositionBrushBuilder background) { - async Task Factory() => await factory(await foreground.SourceProducer(), await background.SourceProducer()); + async Task Factory() => await factory(await SourceProducer(), await background.SourceProducer()); - return new CompositionBrushBuilder(Factory, foreground, background); + return new CompositionBrushBuilder(Factory, this, background); } #endregion @@ -286,6 +333,11 @@ public CompositionBrushBuilder Saturation(float saturation) return new CompositionBrushBuilder(this, Factory); } + /// + /// Adds a new to the current pipeline + /// + /// The opacity value to apply to the pipeline + [Pure, NotNull] public CompositionBrushBuilder Opacity(float opacity) { if (opacity < 0 || opacity > 1) throw new ArgumentOutOfRangeException(nameof(opacity), "The opacity must be in the [0,1] range"); @@ -298,8 +350,19 @@ public CompositionBrushBuilder Opacity(float opacity) return new CompositionBrushBuilder(this, Factory); } - public CompositionBrushBuilder Tint(Color color, float mix) => Mix(FromColor(color), this, mix); + /// + /// Applies a tint color on the current pipeline + /// + /// 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); + /// + /// Applies a custom effect to the current pipeline + /// + /// A that takes the current instance and produces a new effect to display + [Pure, NotNull] public CompositionBrushBuilder Effect([NotNull] Func factory) { async Task Factory() => factory(await SourceProducer()); @@ -307,6 +370,11 @@ public CompositionBrushBuilder Effect([NotNull] Func + /// Applies a custom effect to the current pipeline + /// + /// An asynchronous that takes the current instance and produces a new effect to display + [Pure, NotNull] public CompositionBrushBuilder Effect([NotNull] Func> factory) { async Task Factory() => await factory(await SourceProducer()); From b0d70603b204447c2253e736584e014c71cefb86 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 01:32:02 +0200 Subject: [PATCH 13/47] Minor code refactoring --- .../Behaviours/AcrylicEffectHelper.cs | 4 +- .../AttachedCompositionEffectFactory.cs | 44 +++++++++---------- .../Behaviours/CompositionBrushBuilder.cs | 18 ++++---- ...hedCompositeAnimatableCompositionEffect.cs | 6 +-- .../Misc/CompositionAnimationParameters.cs | 7 ++- .../Brushes/CustomAcrylicBrush.cs | 20 ++++----- .../Brushes/NoiseTextureBrush.cs | 8 ++-- .../Composition/CompositionManager.cs | 18 ++++---- .../ExpressionAnimationWithScalarParameter.cs | 4 +- .../CompositionExtensions.cs | 14 +++--- .../Helpers/ColorConverter.cs | 11 +++-- .../Helpers/Win2DImageHelper.cs | 4 +- .../Lights/PointerPositionSpotLight.cs | 6 +-- .../XAMLTransform/XAMLTransformToolkit.cs | 6 +-- .../XAMLTransformExtensions.cs | 6 +-- 15 files changed, 87 insertions(+), 89 deletions(-) diff --git a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs b/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs index b007dce..f15dc5f 100644 --- a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs +++ b/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs @@ -34,7 +34,7 @@ internal static class AcrylicEffectHelper [MustUseReturnValue, ItemNotNull] public static async Task ConcatenateEffectWithTintAndBorderAsync( [NotNull] Compositor compositor, - [NotNull] IGraphicsEffectSource source, [NotNull] IDictionary parameters, + [NotNull] IGraphicsEffectSource source, [NotNull] IDictionary parameters, Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, BitmapCacheMode options) { @@ -87,7 +87,7 @@ public static async Task ConcatenateEffectWithTintAndBorderAsyn /// The method does side effect on the variable [MustUseReturnValue, ItemNotNull] public static async Task LoadTextureEffectWithTintAsync( - [NotNull] Compositor compositor, [NotNull] IDictionary parameters, + [NotNull] Compositor compositor, [NotNull] IDictionary parameters, Color color, [NotNull] Uri uri, BitmapCacheMode options) { // Initial setup diff --git a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs index 4b9a9c0..ea97629 100644 --- a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs +++ b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs @@ -108,7 +108,7 @@ public static async Task> AttachCompositionIn // Prepare a luminosity to alpha effect to adjust the background contrast CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - const String + const string blurName = "Blur", blurParameterName = "Blur.BlurAmount"; GaussianBlurEffect blurEffect = new GaussianBlurEffect @@ -121,7 +121,7 @@ const String }; // Background with blur and tint overlay - IDictionary sourceParameters = new Dictionary + IDictionary sourceParameters = new Dictionary { { nameof(backdropBrush), backdropBrush } }; @@ -146,7 +146,7 @@ const String // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { effectBrush.SetSourceParameter(pair.Key, pair.Value); } @@ -240,7 +240,7 @@ public static async Task> AttachCompositionCu Foreground = opacityEffect, Mode = BlendEffectMode.Overlay }; - IDictionary sourceParameters = new Dictionary + IDictionary sourceParameters = new Dictionary { { nameof(hostBackdropBrush), hostBackdropBrush } }; @@ -254,7 +254,7 @@ public static async Task> AttachCompositionCu // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { effectBrush.SetSourceParameter(pair.Key, pair.Value); } @@ -320,7 +320,7 @@ public static async Task> AttachCompositionCustom // In-app backdrop effect CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - const String + const string blurName = "Blur", blurParameterName = "Blur.BlurAmount"; GaussianBlurEffect blurEffect = new GaussianBlurEffect @@ -344,7 +344,7 @@ const String }; // Get the tint and noise brushes using Win2D - IDictionary sourceParameters = new Dictionary + IDictionary sourceParameters = new Dictionary { { nameof(hostBackdropBrush), hostBackdropBrush }, { nameof(backdropBrush), backdropBrush } @@ -356,12 +356,12 @@ const String 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"; - const String + const string tint1Name = "Tint.Source1Amount", tint2Name = "Tint.Source2Amount"; // Make sure the Win2D brush was loaded correctly - const String + const string source1Name = "Switch.Source1Amount", source2Name = "Switch.Source2Amount"; CompositionEffectFactory factory = compositor.CreateEffectFactory(source, new[] @@ -375,7 +375,7 @@ const String // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { effectBrush.SetSourceParameter(pair.Key, pair.Value); } @@ -434,7 +434,7 @@ public static async Task> AttachCompositi Saturation = initiallyVisible ? off : on, Source = new CompositionEffectSourceParameter("source") }; - const String animationPropertyName = "SEffect.Saturation"; + const string animationPropertyName = "SEffect.Saturation"; CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] { animationPropertyName }); // Setup the rest of the effect @@ -475,7 +475,7 @@ public static async Task> AttachCompositi Optimization = EffectOptimization.Balanced, Source = new CompositionEffectSourceParameter("source") }; - const String animationPropertyName = "Blur.BlurAmount"; + const string animationPropertyName = "Blur.BlurAmount"; CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(blurEffect, new[] { animationPropertyName }); // Setup the rest of the effect @@ -529,10 +529,10 @@ public static async Task> AttachCompositi Optimization = EffectOptimization.Balanced, Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) }; - const String animationPropertyName = "Blur.BlurAmount"; + const string animationPropertyName = "Blur.BlurAmount"; // Prepare the dictionary with the parameters to add - IDictionary sourceParameters = new Dictionary + IDictionary sourceParameters = new Dictionary { { nameof(backdropBrush), backdropBrush } }; @@ -546,7 +546,7 @@ public static async Task> AttachCompositi // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { effectBrush.SetSourceParameter(pair.Key, pair.Value); } @@ -602,10 +602,10 @@ public static async Task> Attach Optimization = EffectOptimization.Balanced, Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) }; - const String animationPropertyName = "Blur.BlurAmount"; + const string animationPropertyName = "Blur.BlurAmount"; // Prepare the dictionary with the parameters to add - IDictionary sourceParameters = new Dictionary + IDictionary sourceParameters = new Dictionary { { nameof(backdropBrush), backdropBrush } }; @@ -621,7 +621,7 @@ public static async Task> Attach Saturation = initiallyVisible ? offSaturation : onSaturation, Source = source }; - const String saturationParameter = "SEffect.Saturation"; + const string saturationParameter = "SEffect.Saturation"; // Make sure the Win2D brush was loaded correctly CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] @@ -632,7 +632,7 @@ public static async Task> Attach // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { effectBrush.SetSourceParameter(pair.Key, pair.Value); } @@ -643,7 +643,7 @@ public static async Task> Attach await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite); if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); return new AttachedCompositeAnimatableCompositionEffect(target, sprite, - new Dictionary + new Dictionary { { animationPropertyName, new CompositionAnimationValueParameters(onBlur, offBlur) }, { saturationParameter, new CompositionAnimationValueParameters(onSaturation, offSaturation) } @@ -686,7 +686,7 @@ public static async Task> Attach Saturation = initiallyVisible ? offSaturation : onSaturation, Source = blurEffect }; - const String blurParameter = "Blur.BlurAmount", saturationParameter = "SEffect.Saturation"; + const string blurParameter = "Blur.BlurAmount", saturationParameter = "SEffect.Saturation"; CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] { blurParameter, @@ -705,7 +705,7 @@ public static async Task> Attach // Prepare and return the wrapped effect return new AttachedCompositeAnimatableCompositionEffect(element, sprite, - new Dictionary + new Dictionary { { blurParameter, new CompositionAnimationValueParameters(onBlur, offBlur) }, { saturationParameter, new CompositionAnimationValueParameters(onSaturation, offSaturation) } diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 70b0909..50d3b68 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -31,13 +31,13 @@ public sealed class CompositionBrushBuilder /// The collection of info on the parameters that need to be initialized after creating the final /// [NotNull] - private readonly IReadOnlyDictionary>> LazyParameters; + private readonly IReadOnlyDictionary>> LazyParameters; /// /// The collection of animation parameters present in the current pipeline /// [NotNull, ItemNotNull] - private readonly IReadOnlyCollection AnimationParameters; + private readonly IReadOnlyCollection AnimationParameters; #region Constructors @@ -47,12 +47,12 @@ public sealed class CompositionBrushBuilder /// A instance that will return the initial private CompositionBrushBuilder([NotNull] Func> factory) { - String + string guid = Guid.NewGuid().ToString("N"), replaced = Regex.Replace(guid, "[0-9]", "_"), - id = new String(replaced.ToCharArray().Select((c, i) => c == '_' ? char.ToUpper((char)('a' + i % 26)) : c).ToArray()); + id = new string(replaced.ToCharArray().Select((c, i) => c == '_' ? char.ToUpper((char)('a' + i % 26)) : c).ToArray()); SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter(id).To()); - LazyParameters = new Dictionary>> { { id, factory } }; + LazyParameters = new Dictionary>> { { id, factory } }; AnimationParameters = new string[0]; } @@ -62,7 +62,7 @@ private CompositionBrushBuilder([NotNull] Func> factory) /// A instance that will produce the new to add to the pipeline /// The collection of instances that needs to be initialized for the new effect /// The collection of animation properties for the new effect - private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) + private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) { SourceProducer = factory; LazyParameters = lazy; @@ -74,7 +74,7 @@ private CompositionBrushBuilder([NotNull] Func> fact /// /// A instance that will return the initial private CompositionBrushBuilder([NotNull] Func> factory) - : this(factory, new Dictionary>>(), new string[0]) + : this(factory, new Dictionary>>(), new string[0]) { } /// @@ -95,7 +95,7 @@ private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source, [NotNu /// The collection of animation properties for the new effect private CompositionBrushBuilder( [NotNull] CompositionBrushBuilder source, - [NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) + [NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) : this(factory, source.LazyParameters.Merge(lazy), source.AnimationParameters.Merge(animations)) { } @@ -398,7 +398,7 @@ public async Task BuildAsync() // Create the effect factory and apply the final effect CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair>> pair in LazyParameters) + foreach (KeyValuePair>> pair in LazyParameters) effectBrush.SetSourceParameter(pair.Key, await pair.Value()); return effectBrush; } diff --git a/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs b/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs index 052f7f5..737d246 100644 --- a/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs +++ b/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs @@ -18,12 +18,12 @@ public sealed class AttachedCompositeAnimatableCompositionEffect : AttachedAn { // Private animations parameters [NotNull] - private readonly IDictionary PropertiesAnimationValues; + private readonly IDictionary PropertiesAnimationValues; // Internal constructor internal AttachedCompositeAnimatableCompositionEffect( [NotNull] T element, [NotNull] SpriteVisual sprite, - [NotNull] IDictionary propertyValues, bool disposeOnUnload) : base(element, sprite, disposeOnUnload) + [NotNull] IDictionary propertyValues, bool disposeOnUnload) : base(element, sprite, disposeOnUnload) { PropertiesAnimationValues = propertyValues; } @@ -31,7 +31,7 @@ internal AttachedCompositeAnimatableCompositionEffect( /// protected override void DisposeCore() { - foreach (String key in PropertiesAnimationValues.Keys) EffectBrush.StopAnimation(key); + foreach (string key in PropertiesAnimationValues.Keys) EffectBrush.StopAnimation(key); base.DisposeCore(); } diff --git a/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs b/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs index dc1f7bc..584e3b5 100644 --- a/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs +++ b/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs @@ -1,5 +1,4 @@ -using System; -using JetBrains.Annotations; +using JetBrains.Annotations; namespace UICompositionAnimations.Behaviours.Misc { @@ -39,7 +38,7 @@ internal sealed class CompositionAnimationParameters : CompositionAnimationValue /// Gets the property to animate /// [NotNull] - public String Property { get; } + public string Property { get; } /// /// Creates a new instance for the target property and states @@ -47,7 +46,7 @@ internal sealed class CompositionAnimationParameters : CompositionAnimationValue /// The target property to animate /// The on parameter /// The off parameter - public CompositionAnimationParameters([NotNull] String property, float on, float off) : base(on, off) + public CompositionAnimationParameters([NotNull] string property, float on, float off) : base(on, off) { Property = property; } diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index b4fa8bc..f4ac4c7 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -27,19 +27,19 @@ public sealed class CustomAcrylicBrush : XamlCompositionBrushBase #region Constants // The name of the animatable blur amount property - private const String BlurAmountParameterName = "Blur.BlurAmount"; + 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"; + 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"; + private const string TintColor2ParameterName = "Tint.Source2Amount"; // The name of the animatable color property of the color effect - private const String ColorSourceParameterName = "ColorSource.Color"; + private const string ColorSourceParameterName = "ColorSource.Color"; // The name of the animatable color property of the fallback color effect - private const String FallbackColorParameterName = "FallbackColor.Color"; + private const string FallbackColorParameterName = "FallbackColor.Color"; #endregion @@ -296,7 +296,7 @@ protected override async void OnDisconnected() private static CompositionBackdropBrush _BackdropInstance; // The name to use for the in-app backdrop reference parameter - private const String BackdropReferenceParameterName = "BackdropBrush"; + private const string BackdropReferenceParameterName = "BackdropBrush"; // The synchronization semaphore for the host backdrop brush private static readonly SemaphoreSlim HostBackdropSemaphore = new SemaphoreSlim(1); @@ -305,7 +305,7 @@ protected override async void OnDisconnected() private static HostBackdropInstanceWrapper _HostBackdropCache; // The name to use for the host backdrop reference parameter - private const String HostBackdropReferenceParameterName = "HostBackdropBrush"; + private const string HostBackdropReferenceParameterName = "HostBackdropBrush"; /// /// Clears the internal cache of instances @@ -340,8 +340,8 @@ private async Task SetupEffectAsync() if (DesignMode.DesignMode2Enabled) return; // Dictionary to track the reference and animatable parameters - IDictionary sourceParameters = new Dictionary(); - List animatableParameters = new List + IDictionary sourceParameters = new Dictionary(); + List animatableParameters = new List { TintColor1ParameterName, TintColor2ParameterName, @@ -432,7 +432,7 @@ private async Task SetupEffectAsync() // Create the effect factory and apply the final effect _EffectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { _EffectBrush.SetSourceParameter(pair.Key, pair.Value); } diff --git a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs index e9427fe..ca92187 100644 --- a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs +++ b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs @@ -21,7 +21,7 @@ namespace UICompositionAnimations.Brushes public sealed class NoiseTextureBrush : XamlCompositionBrushBase { // The name of the animatable color property of the color effect - private const String ColorSourceParameterName = "ColorSource.Color"; + private const string ColorSourceParameterName = "ColorSource.Color"; #region Properties @@ -110,8 +110,8 @@ private async Task SetupEffectAsync() if (DesignMode.DesignMode2Enabled) return; // Dictionary to track the reference and animatable parameters - IDictionary sourceParameters = new Dictionary(); - List animatableParameters = new List { ColorSourceParameterName }; + 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, CacheMode); @@ -126,7 +126,7 @@ private async Task SetupEffectAsync() // Create the effect factory and apply the final effect _EffectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) + foreach (KeyValuePair pair in sourceParameters) { _EffectBrush.SetSourceParameter(pair.Key, pair.Value); } diff --git a/UICompositionAnimations/Composition/CompositionManager.cs b/UICompositionAnimations/Composition/CompositionManager.cs index 773e8c6..4cba52e 100644 --- a/UICompositionAnimations/Composition/CompositionManager.cs +++ b/UICompositionAnimations/Composition/CompositionManager.cs @@ -24,7 +24,7 @@ public static class CompositionManager /// 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, + 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); @@ -40,7 +40,7 @@ public static void BeginScalarAnimation([NotNull] UIElement element, [NotNull] S /// 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, + 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); @@ -56,7 +56,7 @@ public static void BeginVector2Animation([NotNull] UIElement element, [NotNull] /// 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, + 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); @@ -72,7 +72,7 @@ public static void BeginVector3Animation([NotNull] UIElement element, [NotNull] /// 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, + 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)); @@ -88,7 +88,7 @@ public static void BeginScalarAnimation([NotNull] this CompositionObject compObj /// 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, + 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)); @@ -104,7 +104,7 @@ public static void BeginVector2Animation([NotNull]this CompositionObject compObj /// 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, + 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)); @@ -149,7 +149,7 @@ public static ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation([NotNull] th /// 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) + float? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { // Set duration and delay time ScalarKeyFrameAnimation ani = compositor.CreateScalarKeyFrameAnimation(); @@ -197,7 +197,7 @@ public static Vector2KeyFrameAnimation CreateVector2KeyFrameAnimation([NotNull] /// 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) + Vector2? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { // Set duration and delay time Vector2KeyFrameAnimation ani = compositor.CreateVector2KeyFrameAnimation(); @@ -245,7 +245,7 @@ public static Vector3KeyFrameAnimation CreateVector3KeyFrameAnimation([NotNull] /// 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) + Vector3? from, [NotNull] string to, TimeSpan duration, TimeSpan? delay, [CanBeNull] CompositionEasingFunction ease = null) { // Set duration and delay time Vector3KeyFrameAnimation ani = compositor.CreateVector3KeyFrameAnimation(); diff --git a/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs b/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs index bd77d37..e0770d2 100644 --- a/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs +++ b/UICompositionAnimations/Composition/ExpressionAnimationWithScalarParameter.cs @@ -18,7 +18,7 @@ public sealed class ExpressionAnimationWithScalarParameter : DependencyObject public ExpressionAnimation Animation { get; } // Variable parameter name - private readonly String ParameterName; + private readonly string ParameterName; // Target property set with the variable parameter private readonly CompositionPropertySet PropertySet; @@ -30,7 +30,7 @@ public sealed class ExpressionAnimationWithScalarParameter : DependencyObject /// The property set 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) + [NotNull] CompositionPropertySet propertySet, [NotNull] string parameterName) { Animation = animation; PropertySet = propertySet; diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index ab14b51..649cfc9 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -1524,7 +1524,7 @@ private static Task ManageCompositionClipAnimationAsync([NotNull] Visual visual, { // Setup InsetClip clip = visual.Clip as InsetClip ?? ((InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip())); - String property; + string property; switch (side) { case MarginSide.Top: @@ -1623,7 +1623,7 @@ public static ExpressionAnimation StartExpressionAnimation( TranslationAxis sourceXY, TranslationAxis? targetXY = null, bool invertSourceAxis = false) { CompositionPropertySet scrollSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scroller); - String sign = invertSourceAxis ? "-" : String.Empty; + string sign = invertSourceAxis ? "-" : string.Empty; ExpressionAnimation animation = scrollSet.Compositor.CreateExpressionAnimation($"{sign}scroll.Translation.{sourceXY}"); animation.SetReferenceParameter("scroll", scrollSet); element.GetVisual().StartAnimation($"Offset.{targetXY ?? sourceXY}", animation); @@ -1647,7 +1647,7 @@ public static ExpressionAnimationWithScalarParameter StartExpressionAnimation( { // Get the property set and setup the scroller offset sign CompositionPropertySet scrollSet = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scroller); - String sign = invertSourceAxis ? "-" : "+"; + string sign = invertSourceAxis ? "-" : "+"; // Prepare the second property set to insert the additional parameter CompositionPropertySet properties = scroller.GetVisual().Compositor.CreatePropertySet(); @@ -1735,7 +1735,7 @@ public static SpriteVisual AttachVisualShadow( /// The target object /// The name of the property to animate /// The final value of the property - public static void SetInstantValue(this CompositionObject compObject, String property, float value) + public static void SetInstantValue(this CompositionObject compObject, string property, float value) { // Stop previous animations compObject.StopAnimation(property); @@ -1754,7 +1754,7 @@ public static void SetInstantValue(this CompositionObject compObject, String pro /// The name of the property to animate /// The final value of the property /// The animation duration - public static Task StartAnimationAsync(this CompositionObject compObject, String property, float value, TimeSpan duration) + public static Task StartAnimationAsync(this CompositionObject compObject, string property, float value, TimeSpan duration) { // Stop previous animations compObject.StopAnimation(property); @@ -1778,11 +1778,11 @@ public static Task StartAnimationAsync(this CompositionObject compObject, String /// /// The target element /// The names of the animations to stop - public static void StopAnimations(this UIElement element, params String[] properties) + public static void StopAnimations(this UIElement element, params string[] properties) { if (properties == null || properties.Length == 0) return; Visual visual = element.GetVisual(); - foreach (String property in properties) visual.StopAnimation(property); + foreach (string property in properties) visual.StopAnimation(property); } /// diff --git a/UICompositionAnimations/Helpers/ColorConverter.cs b/UICompositionAnimations/Helpers/ColorConverter.cs index 221c8b8..e72ede3 100644 --- a/UICompositionAnimations/Helpers/ColorConverter.cs +++ b/UICompositionAnimations/Helpers/ColorConverter.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using Windows.UI; namespace UICompositionAnimations.Helpers @@ -10,15 +9,15 @@ namespace UICompositionAnimations.Helpers internal static class ColorConverter { /// - /// Returns the Color represented by the hex String + /// 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) + public static Color String2Color(string color) { - //Cancels the # symbol from the String, if present + //Cancels the # symbol from the string, if present if (color.Contains('#')) color = color.Substring(1); byte alpha; - String RGB; + string RGB; if (color.Length == 6) { alpha = 255; diff --git a/UICompositionAnimations/Helpers/Win2DImageHelper.cs b/UICompositionAnimations/Helpers/Win2DImageHelper.cs index aae1d00..3fb5c1d 100644 --- a/UICompositionAnimations/Helpers/Win2DImageHelper.cs +++ b/UICompositionAnimations/Helpers/Win2DImageHelper.cs @@ -259,8 +259,8 @@ internal static async Task LoadImageAsync( // 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) + string path = uri.AbsolutePath.StartsWith("/Files") + ? uri.AbsolutePath.Replace("/Files", string.Empty) : uri.AbsolutePath; uri = new Uri($"ms-appx://{path}"); } diff --git a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs index 01816bd..e3cef78 100644 --- a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs +++ b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs @@ -209,14 +209,14 @@ protected override void OnConnected(UIElement newElement) /// Gets or sets a custom appendage for the method /// [NotNull] - public String IdAppendage { get; set; } = String.Empty; + public string IdAppendage { get; set; } = string.Empty; /// - protected override String GetId() => GetIdStatic() + IdAppendage; + protected override string GetId() => GetIdStatic() + IdAppendage; /// /// Gets a static Id for the class /// - public static String GetIdStatic() => typeof(PointerPositionSpotLight).FullName; + public static string GetIdStatic() => typeof(PointerPositionSpotLight).FullName; } } diff --git a/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs b/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs index 72d99a9..3ef0868 100644 --- a/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs +++ b/UICompositionAnimations/XAMLTransform/XAMLTransformToolkit.cs @@ -110,7 +110,7 @@ public static bool CompareTargetValue(this Storyboard storyboard, Storyboard tes /// The duration of the animation /// The easing function to use inside the animation /// Indicates whether or not to apply this animation to elements that need the visual tree to be rearranged - public static DoubleAnimation CreateDoubleAnimation(DependencyObject target, String property, + public static DoubleAnimation CreateDoubleAnimation(DependencyObject target, string property, double? from, double? to, int ms, EasingFunctionNames easing = EasingFunctionNames.Linear, bool enableDependecyAnimations = false) { DoubleAnimation animation = new DoubleAnimation @@ -141,10 +141,10 @@ public static Storyboard PrepareStoryboard(params Timeline[] animations) } /// - /// Converts the given TranslationAxis enum into its String representation + /// Converts the given TranslationAxis enum into its string representation /// /// The enum to convert - public static String ToPropertyString(this TranslationAxis axis) => axis == TranslationAxis.X ? "X" : "Y"; + public static string ToPropertyString(this TranslationAxis axis) => axis == TranslationAxis.X ? "X" : "Y"; #endregion } diff --git a/UICompositionAnimations/XAMLTransformExtensions.cs b/UICompositionAnimations/XAMLTransformExtensions.cs index 288e7ee..b2e95ca 100644 --- a/UICompositionAnimations/XAMLTransformExtensions.cs +++ b/UICompositionAnimations/XAMLTransformExtensions.cs @@ -152,7 +152,7 @@ public static Task StartXAMLTransformFadeSlideAnimationAsync(this UIElement elem /// The element to animate /// The initial opacity /// The end opacity - /// A String that indicates which axis to use with the TranslateTransform animation + /// A string that indicates which axis to use with the TranslateTransform animation /// The initial axis value /// The final axis value /// The duration of the animation in milliseconds @@ -387,7 +387,7 @@ public static Task StartXAMLTransformSlideAnimationAsync(this UIElement element, /// Slides a target element over a given axis /// /// The element to animate - /// A String that indicates which axis to use with the TranslateTransform animation + /// A string that indicates which axis to use with the TranslateTransform animation /// The initial axis value /// The final axis value /// The duration of the animation in milliseconds @@ -525,7 +525,7 @@ void TickHandler(object sender, object e) /// The target color to set /// The duration of the animation /// The easing function to use - public static void AnimateColor(this SolidColorBrush solidColorBrush, String toColor, int ms, EasingFunctionNames easing) + public static void AnimateColor(this SolidColorBrush solidColorBrush, string toColor, int ms, EasingFunctionNames easing) { // Get the target color Color targetColor = ColorConverter.String2Color(toColor); From aef907b0ede270288147642a741f6b0242ccecbc Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 01:33:27 +0200 Subject: [PATCH 14/47] Removed VS warning (?) --- UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 50d3b68..e67b4d2 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -130,7 +130,7 @@ private CompositionBrushBuilder( /// /// Starts a new pipeline from a solid with the specified color /// - /// The desired for the initial + /// The desired color for the initial [Pure, NotNull] public static CompositionBrushBuilder FromColor(Color color) => new CompositionBrushBuilder(() => Task.FromResult(new ColorSourceEffect { Color = color }.To())); From 727011774365025982fc6bc81bc1022d05497659 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 16:14:18 +0200 Subject: [PATCH 15/47] Minor refactoring --- .../Behaviours/CompositionBrushBuilder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index e67b4d2..19f1865 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -200,20 +200,20 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapCacheMo #region Blends /// - /// An used to modify the default sorting of the input instances in a blend operation + /// An used to modify the default placement of the input instance in a blend operation /// [PublicAPI] - public enum InputsSorting + public enum EffectPlacement { /// /// The instance used to call the blend method is placed on top of the other /// - ForegroundToBackground, + Foreground, /// /// The instance used to call the blend method is placed behind the other /// - BackgroundToForeground + Background } /// @@ -223,9 +223,9 @@ public enum InputsSorting /// 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, InputsSorting sorting = InputsSorting.ForegroundToBackground) + public CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder pipeline, BlendEffectMode mode, EffectPlacement sorting = EffectPlacement.Foreground) { - (var foreground, var background) = sorting == InputsSorting.ForegroundToBackground ? (this, pipeline) : (pipeline, this); + (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this); async Task Factory() => new BlendEffect { @@ -244,10 +244,10 @@ public CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder pipeline, /// The intensity of the foreground effect in the final pipeline /// The sorting mode to use with the two input pipelines [Pure, NotNull] - public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float mix, InputsSorting sorting = InputsSorting.ForegroundToBackground) + public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float mix, EffectPlacement sorting = EffectPlacement.Foreground) { if (mix <= 0 || mix >= 1) throw new ArgumentOutOfRangeException(nameof(mix), "The mix value must be in the (0,1) range"); - (var foreground, var background) = sorting == InputsSorting.ForegroundToBackground ? (this, pipeline) : (pipeline, this); + (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this); async Task Factory() => new ArithmeticCompositeEffect { From ffe269dc77fac8f1da04935795b32cf78d22fc8c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 16:34:33 +0200 Subject: [PATCH 16/47] Added CrossFade blend effect --- .../Behaviours/CompositionBrushBuilder.cs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 19f1865..3ae9771 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -252,10 +252,32 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f async Task Factory() => new ArithmeticCompositeEffect { MultiplyAmount = 0, - Source1Amount = 1 - mix, - Source2Amount = mix, - Source1 = await background.SourceProducer(), - Source2 = await foreground.SourceProducer() + Source1Amount = mix, + Source2Amount = 1 - mix, + Source1 = await foreground.SourceProducer(), + Source2 = await background.SourceProducer() + }; + + return new CompositionBrushBuilder(Factory, foreground, background); + } + + /// + /// Blends two pipelines using an instance + /// + /// 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 CrossFade([NotNull] CompositionBrushBuilder pipeline, float factor = 0.5f, EffectPlacement sorting = EffectPlacement.Foreground) + { + if (factor <= 0 || factor >= 1) throw new ArgumentOutOfRangeException(nameof(factor), "The mix value must be in the (0,1) range"); + (var foreground, var background) = sorting == EffectPlacement.Foreground ? (this, pipeline) : (pipeline, this); + + async Task Factory() => new CrossFadeEffect + { + CrossFade = factor, + Source1 = await foreground.SourceProducer(), + Source2 = await background.SourceProducer() }; return new CompositionBrushBuilder(Factory, foreground, background); From 6b1180a94abbd4a1a567293d0060e5547635104d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 17:05:19 +0200 Subject: [PATCH 17/47] Added backdrop brush cache system --- .../Behaviours/CompositionBrushBuilder.cs | 45 ++++++++++++++++++- .../Cache/ThreadSafeCompositionCache.cs | 24 ++++++++++ .../UICompositionAnimations.csproj | 1 + 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 3ae9771..32cfeb6 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -10,6 +10,7 @@ using JetBrains.Annotations; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Effects; +using UICompositionAnimations.Brushes.Cache; using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; @@ -115,17 +116,53 @@ private CompositionBrushBuilder( #region Initialization + // The cache manager for backdrop brushes + [NotNull] + private static readonly ThreadSafeCompositionCache BackdropBrushCache = new ThreadSafeCompositionCache(); + /// /// Starts a new pipeline from the returned by /// [Pure, NotNull] - public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => Task.FromResult(Window.Current.Compositor.CreateBackdropBrush())); + public static CompositionBrushBuilder FromBackdropBrush() + { + CompositionBrush Factory() + { + CompositionBrush brush = BackdropBrushCache.TryGetInstance(); + if (brush == null) + { + brush = Window.Current.Compositor.CreateBackdropBrush(); + BackdropBrushCache.Add(brush); + } + return brush; + } + + return new CompositionBrushBuilder(() => Task.FromResult(Factory())); + } + + // The cache manager for host backdrop brushes + [NotNull] + private static readonly ThreadSafeCompositionCache HostBackdropBrushCache = new ThreadSafeCompositionCache(); /// /// Starts a new pipeline from the returned by /// [Pure, NotNull] - public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => Task.FromResult(Window.Current.Compositor.CreateHostBackdropBrush())); + public static CompositionBrushBuilder FromHostBackdropBrush() + { + CompositionBrush Factory() + { + CompositionBrush brush = HostBackdropBrushCache.TryGetInstance(); + if (brush == null) + { + brush = Window.Current.Compositor.CreateHostBackdropBrush(); + HostBackdropBrushCache.Add(brush); + } + return brush; + } + + return new CompositionBrushBuilder(() => Task.FromResult(Factory())); + } /// /// Starts a new pipeline from a solid with the specified color @@ -422,6 +459,10 @@ public async Task BuildAsync() CompositionEffectBrush effectBrush = factory.CreateBrush(); foreach (KeyValuePair>> pair in LazyParameters) effectBrush.SetSourceParameter(pair.Key, await pair.Value()); + + // Cleanup + BackdropBrushCache.Cleanup(); + HostBackdropBrushCache.Cleanup(); return effectBrush; } } diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs new file mode 100644 index 0000000..5e80e53 --- /dev/null +++ b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using Windows.UI.Composition; +using JetBrains.Annotations; + +namespace UICompositionAnimations.Brushes.Cache +{ + internal sealed class ThreadSafeCompositionCache where T : CompositionObject + { + private readonly List> Cache = new List>(); + + public T TryGetInstance() + { + foreach (WeakReference value in Cache) + if (value.TryGetTarget(out T instance) && instance.Dispatcher.HasThreadAccess) + return instance; + return null; + } + + public void Add([NotNull] T value) => Cache.Add(new WeakReference(value)); + + public void Cleanup() => Cache.RemoveAll(reference => !reference.TryGetTarget(out _)); + } +} diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index ed3f628..3e6f489 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -121,6 +121,7 @@ + From e95888ee8a7cbd5cfe42bf059e1d6decbfe78e76 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 17:15:24 +0200 Subject: [PATCH 18/47] Code improvements, more XML comments added --- .../Behaviours/CompositionBrushBuilder.cs | 32 +----------- .../Cache/ThreadSafeCompositionCache.cs | 49 ++++++++++++++++--- UICompositionAnimations/Helpers/AsyncMutex.cs | 42 ++++++++++++++++ .../UICompositionAnimations.csproj | 1 + 4 files changed, 86 insertions(+), 38 deletions(-) create mode 100644 UICompositionAnimations/Helpers/AsyncMutex.cs diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 32cfeb6..b27ad21 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -124,21 +124,7 @@ private CompositionBrushBuilder( /// Starts a new pipeline from the returned by /// [Pure, NotNull] - public static CompositionBrushBuilder FromBackdropBrush() - { - CompositionBrush Factory() - { - CompositionBrush brush = BackdropBrushCache.TryGetInstance(); - if (brush == null) - { - brush = Window.Current.Compositor.CreateBackdropBrush(); - BackdropBrushCache.Add(brush); - } - return brush; - } - - return new CompositionBrushBuilder(() => Task.FromResult(Factory())); - } + public static CompositionBrushBuilder FromBackdropBrush() => new CompositionBrushBuilder(() => BackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateBackdropBrush)); // The cache manager for host backdrop brushes [NotNull] @@ -148,21 +134,7 @@ CompositionBrush Factory() /// Starts a new pipeline from the returned by /// [Pure, NotNull] - public static CompositionBrushBuilder FromHostBackdropBrush() - { - CompositionBrush Factory() - { - CompositionBrush brush = HostBackdropBrushCache.TryGetInstance(); - if (brush == null) - { - brush = Window.Current.Compositor.CreateHostBackdropBrush(); - HostBackdropBrushCache.Add(brush); - } - return brush; - } - - return new CompositionBrushBuilder(() => Task.FromResult(Factory())); - } + public static CompositionBrushBuilder FromHostBackdropBrush() => new CompositionBrushBuilder(() => HostBackdropBrushCache.TryGetInstanceAsync(Window.Current.Compositor.CreateHostBackdropBrush)); /// /// Starts a new pipeline from a solid with the specified color diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs index 5e80e53..cd54952 100644 --- a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs +++ b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs @@ -1,24 +1,57 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Windows.UI.Composition; using JetBrains.Annotations; namespace UICompositionAnimations.Brushes.Cache { + /// + /// A used to cache reusable instances + /// + /// The type of instances to cache internal sealed class ThreadSafeCompositionCache where T : CompositionObject { + /// + /// The cache of weak references, to avoid memory leaks + /// + [NotNull, ItemNotNull] private readonly List> Cache = new List>(); - public T TryGetInstance() + /// + /// The instance used to synchronize concurrent operations on the cache + /// + [NotNull] + private readonly AsyncMutex Mutex = new AsyncMutex(); + + /// + /// Tries to retrieve a valid instance from the cache, and uses the provided factory if an existing item is not found + /// + /// A used when the requested value is not present in the cache + [MustUseReturnValue, NotNull, ItemNotNull] + public async Task TryGetInstanceAsync([NotNull] Func factory) { - foreach (WeakReference value in Cache) - if (value.TryGetTarget(out T instance) && instance.Dispatcher.HasThreadAccess) - return instance; - return null; - } + using (await Mutex.LockAsync()) + { + // Try to retrieve an valid instance from the cache + foreach (WeakReference value in Cache) + if (value.TryGetTarget(out T instance) && instance.Dispatcher.HasThreadAccess) + return instance; - public void Add([NotNull] T value) => Cache.Add(new WeakReference(value)); + // Create a new instance when needed + T fallback = factory(); + Cache.Add(new WeakReference(fallback)); + return fallback; + } + } - public void Cleanup() => Cache.RemoveAll(reference => !reference.TryGetTarget(out _)); + /// + /// Performs a cleanup of the cache by removing invalid references + /// + public async void Cleanup() + { + using (await Mutex.LockAsync()) + Cache.RemoveAll(reference => !reference.TryGetTarget(out _)); + } } } diff --git a/UICompositionAnimations/Helpers/AsyncMutex.cs b/UICompositionAnimations/Helpers/AsyncMutex.cs new file mode 100644 index 0000000..663a6c5 --- /dev/null +++ b/UICompositionAnimations/Helpers/AsyncMutex.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace System.Threading.Tasks +{ + /// + /// An implementation that can be easily used inside a block + /// + internal sealed class AsyncMutex + { + // The underlying semaphore used by this instance + [NotNull] + private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); + + /// + /// Acquires a lock for the current instance, that is automatically released outside the block + /// + [NotNull, ItemNotNull] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public async Task LockAsync() + { + await Semaphore.WaitAsync().ConfigureAwait(false); + return new _Lock(Semaphore); + } + + /// + /// Private class that implements the automatic release of the semaphore + /// + private sealed class _Lock : IDisposable + { + // Reference to the semaphore instance of the parent class + [NotNull] + private readonly SemaphoreSlim Semaphore; + + public _Lock([NotNull] SemaphoreSlim semaphore) => Semaphore = semaphore; + + // Releases the lock when the instance is disposed + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IDisposable.Dispose() => Semaphore.Release(); + } + } +} \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 3e6f489..5a80823 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -137,6 +137,7 @@ + From c83beff1a64a7e772650a30e5661a8897fb107d6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 19:56:36 +0200 Subject: [PATCH 19/47] Added animation support for built-in effects --- .../Behaviours/CompositionBrushBuilder.cs | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index b27ad21..cf97809 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -16,6 +16,14 @@ namespace UICompositionAnimations.Behaviours { + /// + /// A that represents a custom effect animation that can be applied to a + /// + /// The target instance to use to start the animation + /// The animation target value + /// The animation duration, in milliseconds + public delegate Task EffectAnimation([NotNull] CompositionBrush brush, float value, int ms); + /// /// A that allows to build custom effects pipelines and create instances from them /// @@ -324,7 +332,7 @@ public CompositionBrushBuilder Merge( #endregion - #region Effects + #region Built-in effects /// /// Adds a new to the current pipeline @@ -347,6 +355,32 @@ public CompositionBrushBuilder Blur(float blur, EffectBorderMode mode = EffectBo return new CompositionBrushBuilder(this, Factory); } + /// + /// Adds a new to the current pipeline + /// + /// The initial blur amount + /// The optional blur animation for the effect + /// The parameter for the effect, defaults to + /// 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) + { + // Blur effect + async Task Factory() => new GaussianBlurEffect + { + BlurAmount = blur, + BorderMode = mode, + Optimization = optimization, + Source = await SourceProducer(), + Name = "Blur" + }; + + animation = (brush, value, ms) => brush.StartAnimationAsync("Blur.BlurAmount", value, TimeSpan.FromMilliseconds(ms)); + + return new CompositionBrushBuilder(this, Factory); + } + /// /// Adds a new to the current pipeline /// @@ -364,6 +398,28 @@ public CompositionBrushBuilder Saturation(float saturation) return new CompositionBrushBuilder(this, Factory); } + /// + /// Adds a new to the current pipeline + /// + /// The initial saturation amount for the new effect + /// 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) + { + if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException(nameof(saturation), "The saturation must be in the [0,1] range"); + async Task Factory() => new SaturationEffect + { + Saturation = saturation, + Source = await SourceProducer(), + Name = "Saturation" + }; + + animation = (brush, value, ms) => brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms)); + + return new CompositionBrushBuilder(this, Factory); + } + /// /// Adds a new to the current pipeline /// @@ -381,6 +437,28 @@ public CompositionBrushBuilder Opacity(float opacity) return new CompositionBrushBuilder(this, Factory); } + /// + /// Adds a new to the current pipeline + /// + /// The opacity value to apply to the pipeline + /// 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) + { + if (opacity < 0 || opacity > 1) throw new ArgumentOutOfRangeException(nameof(opacity), "The opacity must be in the [0,1] range"); + async Task Factory() => new OpacityEffect + { + Opacity = opacity, + Source = await SourceProducer(), + Name = "Opacity" + }; + + animation = (brush, value, ms) => brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms)); + + return new CompositionBrushBuilder(this, Factory); + } + /// /// Applies a tint color on the current pipeline /// @@ -389,6 +467,10 @@ public CompositionBrushBuilder Opacity(float opacity) [Pure, NotNull] public CompositionBrushBuilder Tint(Color color, float mix) => FromColor(color).Mix(this, mix); + #endregion + + #region Custom effects + /// /// Applies a custom effect to the current pipeline /// From 343ec11090b1fd1c14ade5e0bf4e4eb4940cf406 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 20:03:48 +0200 Subject: [PATCH 20/47] Minor code tweaks, added mix with animation --- .../Behaviours/CompositionBrushBuilder.cs | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index cf97809..85531a6 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -255,22 +255,20 @@ public CompositionBrushBuilder Blend([NotNull] CompositionBrushBuilder pipeline, } /// - /// Blends two pipelines using an instance + /// Blends two pipelines using an instance /// /// The second instance to blend - /// The intensity of the foreground effect in the final pipeline + /// 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 mix, EffectPlacement sorting = EffectPlacement.Foreground) + public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float factor = 0.5f, EffectPlacement sorting = EffectPlacement.Foreground) { - if (mix <= 0 || mix >= 1) throw new ArgumentOutOfRangeException(nameof(mix), "The mix value must be in the (0,1) range"); + 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); - async Task Factory() => new ArithmeticCompositeEffect + async Task Factory() => new CrossFadeEffect { - MultiplyAmount = 0, - Source1Amount = mix, - Source2Amount = 1 - mix, + CrossFade = factor, Source1 = await foreground.SourceProducer(), Source2 = await background.SourceProducer() }; @@ -283,18 +281,27 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f /// /// 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 CrossFade([NotNull] CompositionBrushBuilder pipeline, float factor = 0.5f, EffectPlacement sorting = EffectPlacement.Foreground) + public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, float factor, out EffectAnimation animation, EffectPlacement sorting = EffectPlacement.Foreground) { - if (factor <= 0 || factor >= 1) throw new ArgumentOutOfRangeException(nameof(factor), "The mix value must be in the (0,1) range"); + 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); async Task Factory() => new CrossFadeEffect { CrossFade = factor, Source1 = await foreground.SourceProducer(), - Source2 = await background.SourceProducer() + Source2 = await background.SourceProducer(), + Name = "Fade" + }; + + animation = (brush, value, ms) => + { + if (factor < 0 || factor > 1) throw new ArgumentOutOfRangeException(nameof(value), "The factor must be in the [0,1] range"); + return brush.StartAnimationAsync("Fade.CrossFade", value, TimeSpan.FromMilliseconds(ms)); }; return new CompositionBrushBuilder(Factory, foreground, background); From 0ad68b7d7cd19b95382921f7e2e6c6f3f65d0ec0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 28 Jul 2018 20:11:12 +0200 Subject: [PATCH 21/47] Fixed animation properties pass through --- .../Behaviours/CompositionBrushBuilder.cs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 85531a6..c5184c0 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -86,15 +86,6 @@ private CompositionBrushBuilder([NotNull] Func> fact : this(factory, new Dictionary>>(), new string[0]) { } - /// - /// Constructor used to create a new instance obtained by concatenation between the current pipeline and the input effect info - /// - /// The source pipeline to attach the new effect to - /// A instance that will produce the new to add to the pipeline - private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source, [NotNull] Func> factory) - : this(factory, source.LazyParameters, source.AnimationParameters) - { } - /// /// Constructor used to create a new instance obtained by concatenation between the current pipeline and the input effect info /// @@ -104,8 +95,8 @@ private CompositionBrushBuilder([NotNull] CompositionBrushBuilder source, [NotNu /// The collection of animation properties for the new effect private CompositionBrushBuilder( [NotNull] CompositionBrushBuilder source, - [NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) - : this(factory, source.LazyParameters.Merge(lazy), source.AnimationParameters.Merge(animations)) + [NotNull] Func> factory, [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) + : this(factory, lazy?.Merge(source.LazyParameters) ?? source.LazyParameters, animations?.Merge(source.AnimationParameters) ?? source.AnimationParameters) { } /// @@ -114,10 +105,13 @@ private CompositionBrushBuilder( /// A instance that will produce the new to add to the pipeline /// The first pipeline to merge /// The second pipeline to merge + /// The collection of instances that needs to be initialized for the new effect + /// The collection of animation properties for the new effect private CompositionBrushBuilder( [NotNull] Func> factory, - [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b) - : this(factory, a.LazyParameters.Merge(b.LazyParameters), a.AnimationParameters.Merge(b.AnimationParameters)) + [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b, + [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) + : this(factory, lazy?.Merge(a.LazyParameters.Merge(b.LazyParameters)) ?? a.LazyParameters.Merge(b.LazyParameters), animations?.Merge(a.AnimationParameters.Merge(b.AnimationParameters)) ?? a.AnimationParameters.Merge(b.AnimationParameters)) { } #endregion @@ -304,7 +298,7 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f return brush.StartAnimationAsync("Fade.CrossFade", value, TimeSpan.FromMilliseconds(ms)); }; - return new CompositionBrushBuilder(Factory, foreground, background); + return new CompositionBrushBuilder(Factory, foreground, background, null, new[] { "Fade.CrossFade" }); } /// @@ -385,7 +379,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); + return new CompositionBrushBuilder(this, Factory, null, new[] { "Blur.BlurAmount" }); } /// @@ -424,7 +418,7 @@ public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation animation = (brush, value, ms) => brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms)); - return new CompositionBrushBuilder(this, Factory); + return new CompositionBrushBuilder(this, Factory, null, new[] { "Saturation.Saturation" }); } /// @@ -463,7 +457,7 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat animation = (brush, value, ms) => brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms)); - return new CompositionBrushBuilder(this, Factory); + return new CompositionBrushBuilder(this, Factory, null, new[] { "Opacity.Opacity" }); } /// From f329b170b51624c7d2b44d1b07fc49ab35bbe365 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 14:19:11 +0200 Subject: [PATCH 22/47] Minor refactoring --- .../Behaviours/CompositionBrushBuilder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index c5184c0..f3772ae 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -43,10 +43,10 @@ public sealed class CompositionBrushBuilder private readonly IReadOnlyDictionary>> LazyParameters; /// - /// The collection of animation parameters present in the current pipeline + /// The collection of animation properties present in the current pipeline /// [NotNull, ItemNotNull] - private readonly IReadOnlyCollection AnimationParameters; + private readonly IReadOnlyCollection AnimationProperties; #region Constructors @@ -62,7 +62,7 @@ private CompositionBrushBuilder([NotNull] Func> factory) id = new string(replaced.ToCharArray().Select((c, i) => c == '_' ? char.ToUpper((char)('a' + i % 26)) : c).ToArray()); SourceProducer = () => Task.FromResult(new CompositionEffectSourceParameter(id).To()); LazyParameters = new Dictionary>> { { id, factory } }; - AnimationParameters = new string[0]; + AnimationProperties = new string[0]; } /// @@ -75,7 +75,7 @@ private CompositionBrushBuilder([NotNull] Func> fact { SourceProducer = factory; LazyParameters = lazy; - AnimationParameters = animations; + AnimationProperties = animations; } /// @@ -96,7 +96,7 @@ private CompositionBrushBuilder([NotNull] Func> fact private CompositionBrushBuilder( [NotNull] CompositionBrushBuilder source, [NotNull] Func> factory, [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) - : this(factory, lazy?.Merge(source.LazyParameters) ?? source.LazyParameters, animations?.Merge(source.AnimationParameters) ?? source.AnimationParameters) + : this(factory, lazy?.Merge(source.LazyParameters) ?? source.LazyParameters, animations?.Merge(source.AnimationProperties) ?? source.AnimationProperties) { } /// @@ -111,7 +111,7 @@ private CompositionBrushBuilder( [NotNull] Func> factory, [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b, [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) - : this(factory, lazy?.Merge(a.LazyParameters.Merge(b.LazyParameters)) ?? a.LazyParameters.Merge(b.LazyParameters), animations?.Merge(a.AnimationParameters.Merge(b.AnimationParameters)) ?? a.AnimationParameters.Merge(b.AnimationParameters)) + : this(factory, lazy?.Merge(a.LazyParameters.Merge(b.LazyParameters)) ?? a.LazyParameters.Merge(b.LazyParameters), animations?.Merge(a.AnimationProperties.Merge(b.AnimationProperties)) ?? a.AnimationProperties.Merge(b.AnimationProperties)) { } #endregion @@ -506,8 +506,8 @@ public async Task BuildAsync() { // Validate the pipeline and build the effects factory if (!(await SourceProducer() is IGraphicsEffect effect)) throw new InvalidOperationException("The pipeline doesn't contain a valid effects sequence"); - CompositionEffectFactory factory = AnimationParameters.Count > 0 - ? Window.Current.Compositor.CreateEffectFactory(effect, AnimationParameters) + CompositionEffectFactory factory = AnimationProperties.Count > 0 + ? Window.Current.Compositor.CreateEffectFactory(effect, AnimationProperties) : Window.Current.Compositor.CreateEffectFactory(effect); // Create the effect factory and apply the final effect From 6a2efe21924082d403374e69a669702b649e1bac Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 14:21:25 +0200 Subject: [PATCH 23/47] More refactoring --- .../Behaviours/CompositionBrushBuilder.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index f3772ae..e74648c 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -36,18 +36,18 @@ public sealed class CompositionBrushBuilder [NotNull] private readonly Func> SourceProducer; - /// - /// The collection of info on the parameters that need to be initialized after creating the final - /// - [NotNull] - private readonly IReadOnlyDictionary>> LazyParameters; - /// /// The collection of animation properties present in the current pipeline /// [NotNull, ItemNotNull] private readonly IReadOnlyCollection AnimationProperties; + /// + /// The collection of info on the parameters that need to be initialized after creating the final + /// + [NotNull] + private readonly IReadOnlyDictionary>> LazyParameters; + #region Constructors /// @@ -69,13 +69,13 @@ private CompositionBrushBuilder([NotNull] Func> factory) /// Base constructor used to create a new instance from scratch /// /// A instance that will produce the new to add to the pipeline - /// The collection of instances that needs to be initialized for the new effect /// The collection of animation properties for the new effect - private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] IReadOnlyDictionary>> lazy, [NotNull, ItemNotNull] IReadOnlyCollection animations) + /// 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) { SourceProducer = factory; - LazyParameters = lazy; AnimationProperties = animations; + LazyParameters = lazy; } /// @@ -83,7 +83,7 @@ private CompositionBrushBuilder([NotNull] Func> fact /// /// A instance that will return the initial private CompositionBrushBuilder([NotNull] Func> factory) - : this(factory, new Dictionary>>(), new string[0]) + : this(factory, new string[0], new Dictionary>>()) { } /// @@ -91,12 +91,12 @@ private CompositionBrushBuilder([NotNull] Func> fact /// /// The source pipeline to attach the new effect to /// A instance that will produce the new to add to the pipeline - /// The collection of instances that needs to be initialized for the new effect /// The collection of animation properties for the new effect - private CompositionBrushBuilder( - [NotNull] CompositionBrushBuilder source, - [NotNull] Func> factory, [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) - : this(factory, lazy?.Merge(source.LazyParameters) ?? source.LazyParameters, animations?.Merge(source.AnimationProperties) ?? source.AnimationProperties) + /// The collection of instances that needs to be initialized for the new effect + private CompositionBrushBuilder([NotNull] CompositionBrushBuilder 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) { } /// @@ -105,13 +105,12 @@ private CompositionBrushBuilder( /// A instance that will produce the new to add to the pipeline /// The first pipeline to merge /// The second pipeline to merge - /// The collection of instances that needs to be initialized for the new effect /// The collection of animation properties for the new effect - private CompositionBrushBuilder( - [NotNull] Func> factory, + /// The collection of instances that needs to be initialized for the new effect + private CompositionBrushBuilder([NotNull] Func> factory, [NotNull] CompositionBrushBuilder a, [NotNull] CompositionBrushBuilder b, - [CanBeNull] IReadOnlyDictionary>> lazy = null, [CanBeNull, ItemNotNull] IReadOnlyCollection animations = null) - : this(factory, lazy?.Merge(a.LazyParameters.Merge(b.LazyParameters)) ?? a.LazyParameters.Merge(b.LazyParameters), animations?.Merge(a.AnimationProperties.Merge(b.AnimationProperties)) ?? a.AnimationProperties.Merge(b.AnimationProperties)) + [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)) { } #endregion @@ -298,7 +297,7 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f return brush.StartAnimationAsync("Fade.CrossFade", value, TimeSpan.FromMilliseconds(ms)); }; - return new CompositionBrushBuilder(Factory, foreground, background, null, new[] { "Fade.CrossFade" }); + return new CompositionBrushBuilder(Factory, foreground, background, new[] { "Fade.CrossFade" }, null); } /// @@ -379,7 +378,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, null, new[] { "Blur.BlurAmount" }); + return new CompositionBrushBuilder(this, Factory, new[] { "Blur.BlurAmount" }); } /// @@ -418,7 +417,7 @@ public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation animation = (brush, value, ms) => brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms)); - return new CompositionBrushBuilder(this, Factory, null, new[] { "Saturation.Saturation" }); + return new CompositionBrushBuilder(this, Factory, new[] { "Saturation.Saturation" }); } /// @@ -457,7 +456,7 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat animation = (brush, value, ms) => brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms)); - return new CompositionBrushBuilder(this, Factory, null, new[] { "Opacity.Opacity" }); + return new CompositionBrushBuilder(this, Factory, new[] { "Opacity.Opacity" }); } /// From e87409d252eeeb438dbf03820aa98200e75807c3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 14:37:31 +0200 Subject: [PATCH 24/47] Finished implementation for custom effects --- .../Behaviours/CompositionBrushBuilder.cs | 12 +++-- .../CompositionSourceInitializer.cs | 48 +++++++++++++++++++ .../UICompositionAnimations.csproj | 1 + 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index e74648c..d7651c5 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -475,24 +475,28 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat /// Applies a custom effect to the current pipeline /// /// A that takes the current instance and produces a new effect to display + /// 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) + public CompositionBrushBuilder Effect([NotNull] Func factory, IEnumerable animations = null, IEnumerable initializers = null) { async Task Factory() => factory(await SourceProducer()); - return new CompositionBrushBuilder(this, Factory); + return new CompositionBrushBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer)); } /// /// Applies a custom effect to the current pipeline /// /// An asynchronous that takes the current instance and produces a new effect to display + /// 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) + public CompositionBrushBuilder Effect([NotNull] Func> factory, IEnumerable animations = null, IEnumerable initializers = null) { async Task Factory() => await factory(await SourceProducer()); - return new CompositionBrushBuilder(this, Factory); + return new CompositionBrushBuilder(this, Factory, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer)); } #endregion diff --git a/UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs b/UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs new file mode 100644 index 0000000..7ab97a6 --- /dev/null +++ b/UICompositionAnimations/Behaviours/CompositionSourceInitializer.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Windows.UI.Composition; +using JetBrains.Annotations; + +namespace UICompositionAnimations.Behaviours +{ + /// + /// A simple container used to store info on a custom composition effect to create + /// + [PublicAPI] + public sealed class CompositionSourceInitializer + { + /// + /// Gets the stored effect initializer + /// + [NotNull] + internal Func> Initializer { get; } + + /// + /// Gets the name of the target + /// + [NotNull] + internal string Name { get; } + + private CompositionSourceInitializer([NotNull] string name, [NotNull] Func> initializer) + { + Name = name; + Initializer = initializer; + } + + /// + /// Creates a new instance with the info on a given to initialize + /// + /// The 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())); + + /// + /// Creates a new instance with the info on a given to initialize + /// + /// The 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); + } +} diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 5a80823..b2f0256 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -112,6 +112,7 @@ + From 8ec9051f2d22b8419a0ad7a2f4a1bd9f0eecfa74 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:00:45 +0200 Subject: [PATCH 25/47] Minor code improvements --- .../Behaviours/CompositionBrushBuilder.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index d7651c5..0241b08 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -293,11 +293,11 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f animation = (brush, value, ms) => { - if (factor < 0 || factor > 1) throw new ArgumentOutOfRangeException(nameof(value), "The factor must be in the [0,1] range"); + if (value < 0 || value > 1) throw new ArgumentOutOfRangeException(nameof(value), "The factor must be in the [0,1] range"); return brush.StartAnimationAsync("Fade.CrossFade", value, TimeSpan.FromMilliseconds(ms)); }; - return new CompositionBrushBuilder(Factory, foreground, background, new[] { "Fade.CrossFade" }, null); + return new CompositionBrushBuilder(Factory, foreground, background, new[] { "Fade.CrossFade" }); } /// @@ -415,7 +415,11 @@ public CompositionBrushBuilder Saturation(float saturation, out EffectAnimation Name = "Saturation" }; - animation = (brush, value, ms) => brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms)); + animation = (brush, value, ms) => + { + if (value < 0 || value > 1) throw new ArgumentOutOfRangeException(nameof(value), "The saturation must be in the [0,1] range"); + return brush.StartAnimationAsync("Saturation.Saturation", value, TimeSpan.FromMilliseconds(ms)); + }; return new CompositionBrushBuilder(this, Factory, new[] { "Saturation.Saturation" }); } @@ -454,7 +458,11 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat Name = "Opacity" }; - animation = (brush, value, ms) => brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms)); + animation = (brush, value, ms) => + { + if (value < 0 || value > 1) throw new ArgumentOutOfRangeException(nameof(value), "The opacity must be in the [0,1] range"); + return brush.StartAnimationAsync("Opacity.Opacity", value, TimeSpan.FromMilliseconds(ms)); + }; return new CompositionBrushBuilder(this, Factory, new[] { "Opacity.Opacity" }); } From ae3ccefce7d6f4e4a80502af6b8eb3d273e7d5ec Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:13:53 +0200 Subject: [PATCH 26/47] Added new XamlCompositionEffectBrushBase class --- .../Base/XamlCompositionEffectBrushBase.cs | 48 +++++++++++++++++++ .../UICompositionAnimations.csproj | 1 + 2 files changed, 49 insertions(+) create mode 100644 UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs diff --git a/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs b/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs new file mode 100644 index 0000000..685bd9a --- /dev/null +++ b/UICompositionAnimations/Brushes/Base/XamlCompositionEffectBrushBase.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Windows.UI.Xaml.Media; +using JetBrains.Annotations; +using UICompositionAnimations.Behaviours; + +namespace UICompositionAnimations.Brushes.Base +{ + /// + /// A custom that's ready to be used with a custom pipeline. + /// + [PublicAPI] + public abstract class XamlCompositionEffectBrushBase : XamlCompositionBrushBase + { + // Initialization mutex + [NotNull] + private readonly AsyncMutex ConnectedMutex = new AsyncMutex(); + + /// + /// 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 override async void OnConnected() + { + using (await ConnectedMutex.LockAsync()) + if (CompositionBrush == null) + CompositionBrush = await OnBrushRequested().BuildAsync(); + base.OnConnected(); + } + + /// + protected override async void OnDisconnected() + { + using (await ConnectedMutex.LockAsync()) + { + if (CompositionBrush != null) + { + CompositionBrush.Dispose(); + CompositionBrush = null; + } + } + base.OnDisconnected(); + } + } +} diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index b2f0256..148f4a8 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -121,6 +121,7 @@ + From 8fa5e565021010a716ca46e7290f3e7407563317 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:14:33 +0200 Subject: [PATCH 27/47] Removed unnecessary ApiInformationHelper class --- .../Helpers/ApiInformationHelper.cs | 41 ------------------- .../UICompositionAnimations.csproj | 1 - 2 files changed, 42 deletions(-) delete mode 100644 UICompositionAnimations/Helpers/ApiInformationHelper.cs diff --git a/UICompositionAnimations/Helpers/ApiInformationHelper.cs b/UICompositionAnimations/Helpers/ApiInformationHelper.cs deleted file mode 100644 index df52963..0000000 --- a/UICompositionAnimations/Helpers/ApiInformationHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Windows.System.Profile; -using JetBrains.Annotations; - -namespace UICompositionAnimations.Helpers -{ - /// - /// A static class that provides useful information on the available APIs - /// - [PublicAPI] - public static class ApiInformationHelper - { - private static Version _OSVersion; - - /// - /// Gets the current OS version for the device in use - /// - /// It is better to check the available APIs directly with the other properties exposed by the class - /// instead of just checking the OS version and use it to make further decisions. - /// In particular, users on a Windows Insider ring could already have a given set of APIs even though their OS - /// build doesn't exactly match the official Windows 10 version that supports a requested API contract. - [NotNull] - public static Version OSVersion - { - get - { - if (_OSVersion == null) - { - string deviceFamilyVersion = AnalyticsInfo.VersionInfo.DeviceFamilyVersion; - ulong version = ulong.Parse(deviceFamilyVersion); - ulong major = (version & 0xFFFF000000000000L) >> 48; - ulong minor = (version & 0x0000FFFF00000000L) >> 32; - ulong build = (version & 0x00000000FFFF0000L) >> 16; - ulong revision = version & 0x000000000000FFFFL; - _OSVersion = new Version((int)major, (int)minor, (int)build, (int)revision); - } - return _OSVersion; - } - } - } -} diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 148f4a8..a265f64 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -138,7 +138,6 @@ - From c19ec955e472565d08d54f9ce9880c92cf468766 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:18:05 +0200 Subject: [PATCH 28/47] Minor project tweaks --- UICompositionAnimations/Properties/AssemblyInfo.cs | 2 +- UICompositionAnimations/UICompositionAnimations.csproj | 2 +- UICompositionAnimations/UICompositionAnimations.nuspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index df6cea1..b02c744 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -25,5 +25,5 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("3.0.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")] -[assembly: AssemblyInformationalVersion("3.0.0-beta2")] +[assembly: AssemblyInformationalVersion("3.0.0-beta4")] [assembly: ComVisible(false)] \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index a265f64..a23ad2b 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -158,7 +158,7 @@ - 11.1.0 + 2018.2.1 6.1.5 diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index b42c685..d62ca9a 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,7 +2,7 @@ UICompositionAnimations - 3.0.0-beta2 + 3.0.0-beta4 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri From 16481c6fd030de1febd00a12048ebda6b45a21eb Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:55:36 +0200 Subject: [PATCH 29/47] Update README.md --- README.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c89e269..d47c5db 100644 --- a/README.md +++ b/README.md @@ -67,17 +67,9 @@ await MyControl.StartCompositionFadeScaleAnimationAsync( EasingFunctionNames.Linear); // Easing function ``` -#### ColorBrush animation -```C# -MyBrush.AnimateColor( - #FFFF2B1C, // Target color - 250, // Duration in ms - EasingFunctionNames.Linear); // Easing function -``` - ## `UI.Composition` effects -The library provides several ways to use `UI.Composition` effects. There's a custom acrylic brush that can be used when running Windows 10 build 15063.x or greater, and other "attached" effects. An attached effect (created using the `AttachedCompositionEffectsFactory` class) is an effect that is loaded and then applied to the underlying `Visual` object behing a target `UIElement`. The main advantage of brushes is that they can be initialized and used in XAML and don't need any code-behind. Here are some examples: +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. #### Declare a shared acrylic brush in XAML @@ -89,7 +81,7 @@ The library provides several ways to use `UI.Composition` effects. There's a cus 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, CompositionBrushBuilder.EffectPlacement.Background) + .BuildAsync(); +``` + +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 acrylic brush effect: ```C# AttachedStaticCompositionEffect attached = await BlurBorder.AttachCompositionInAppCustomAcrylicEffectAsync( @@ -180,8 +185,7 @@ 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 -- `ApiInformationHelper`: provides useful methods to check the capabilities of the current device - `PointerHelper`: exposes APIs to quickly setup pointer event handlers for `UIElement`s # Requirements -At least Windows 10 November Update (10586.x) +At least Windows 10 April Update (17134.x) From 4e723a71927ed3e961a2c02bec001e2f842c4ca2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 15:56:01 +0200 Subject: [PATCH 30/47] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d47c5db..0e3ebf5 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ The library provides several ways to use `UI.Composition` effects. There are rea #### Build an acrylic effect pipeline from scratch: ```C# - CompositionBrush brush = await CompositionBrushBuilder.FromHostBackdropBrush() +CompositionBrush brush = await CompositionBrushBuilder.FromHostBackdropBrush() .Effect(source => new LuminanceToAlphaEffect { Source = source }) .Opacity(0.4f) .Blend(CompositionBrushBuilder.FromHostBackdropBrush(), BlendEffectMode.Multiply) From 55b2c6d10f70322a039e48be6668dd144f9ace95 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 16:11:16 +0200 Subject: [PATCH 31/47] Added tint effect animation option --- .../Behaviours/CompositionBrushBuilder.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 0241b08..d1e2a93 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -475,6 +475,16 @@ public CompositionBrushBuilder Opacity(float opacity, out EffectAnimation animat [Pure, NotNull] public CompositionBrushBuilder Tint(Color color, float mix) => FromColor(color).Mix(this, mix); + /// + /// Applies a tint color on the current pipeline + /// + /// The tint color to use + /// The initial amount of tint to apply over the current effect + /// 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); + #endregion #region Custom effects From 29e62094dd77f4ac72b3d0d715a50df4a815cf3c Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 29 Jul 2018 16:13:13 +0200 Subject: [PATCH 32/47] Added custom properties support to custom blend effects --- .../Behaviours/CompositionBrushBuilder.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index d1e2a93..76ef438 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -305,14 +305,17 @@ public CompositionBrushBuilder Mix([NotNull] CompositionBrushBuilder pipeline, f /// /// The blend function to use /// The background pipeline to blend with the current instance + /// 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( [NotNull] Func factory, - [NotNull] CompositionBrushBuilder background) + [NotNull] CompositionBrushBuilder background, + IEnumerable animations = null, IEnumerable initializers = null) { async Task Factory() => factory(await SourceProducer(), await background.SourceProducer()); - return new CompositionBrushBuilder(Factory, this, background); + return new CompositionBrushBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer)); } /// @@ -320,14 +323,17 @@ public CompositionBrushBuilder Merge( /// /// The asynchronous blend function to use /// The background pipeline to blend with the current instance + /// 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( [NotNull] Func> factory, - [NotNull] CompositionBrushBuilder background) + [NotNull] CompositionBrushBuilder background, + IEnumerable animations = null, IEnumerable initializers = null) { async Task Factory() => await factory(await SourceProducer(), await background.SourceProducer()); - return new CompositionBrushBuilder(Factory, this, background); + return new CompositionBrushBuilder(Factory, this, background, animations?.ToArray(), initializers?.ToDictionary(item => item.Name, item => item.Initializer)); } #endregion From c551fb67b68459471cfffc37d4fe9aa17410d12a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Aug 2018 23:37:38 +0200 Subject: [PATCH 33/47] Minor code refactoring (backported missing code) --- .../Behaviours/AcrylicEffectHelper.cs | 14 +- .../AttachedCompositionEffectFactory.cs | 34 ++-- .../Behaviours/CompositionBrushBuilder.cs | 25 ++- .../Cache/ThreadSafeCompositionMapCache.cs | 91 ++++++++++ .../Brushes/CustomAcrylicBrush.cs | 7 +- .../Brushes/LightingBrush.cs | 12 +- .../Brushes/NoiseTextureBrush.cs | 12 +- .../Brushes/XamlCompositionBrush.cs | 44 +++++ .../Enums/BitmapCacheMode.cs | 14 +- .../Helpers/Win2DImageHelper.cs | 156 +++++++++--------- .../UICompositionAnimations.csproj | 10 ++ 11 files changed, 277 insertions(+), 142 deletions(-) create mode 100644 UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs create mode 100644 UICompositionAnimations/Brushes/XamlCompositionBrush.cs diff --git a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs b/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs index f15dc5f..f0f9ccb 100644 --- a/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs +++ b/UICompositionAnimations/Behaviours/AcrylicEffectHelper.cs @@ -28,7 +28,6 @@ internal static class AcrylicEffectHelper /// 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 - /// Indicates whether or not to force the reload of the Win2D image /// The resulting effect through the pipeline /// The method does side effect on the variable [MustUseReturnValue, ItemNotNull] @@ -36,7 +35,7 @@ public static async Task ConcatenateEffectWithTintAndBorderAsyn [NotNull] Compositor compositor, [NotNull] IGraphicsEffectSource source, [NotNull] IDictionary parameters, Color color, float colorMix, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, BitmapCacheMode options) + [CanBeNull] CanvasControl canvas, [NotNull] Uri uri) { // Setup the tint effect ArithmeticCompositeEffect tint = new ArithmeticCompositeEffect @@ -50,8 +49,8 @@ public static async Task ConcatenateEffectWithTintAndBorderAsyn // Get the noise brush using Win2D CompositionSurfaceBrush noiseBitmap = canvas == null - ? await Win2DImageHelper.LoadImageAsync(compositor, uri, options, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound) - : await Win2DImageHelper.LoadImageAsync(compositor, canvas, uri, options, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound); + ? 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) @@ -82,17 +81,16 @@ public static async Task ConcatenateEffectWithTintAndBorderAsyn /// 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 - /// Indicates whether or not to force the reload of the Win2D image /// 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, BitmapCacheMode options) + Color color, [NotNull] Uri uri) { // Initial setup ColorSourceEffect colorEffect = new ColorSourceEffect { Color = color }; - CompositionSurfaceBrush noiseBitmap = await Win2DImageHelper.LoadImageAsync(compositor, uri, options, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound); + CompositionSurfaceBrush noiseBitmap = await Win2DImageHelper.LoadImageAsync(compositor, uri, BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode.Default); if (noiseBitmap == null) return colorEffect; // Blend the effects @@ -112,4 +110,4 @@ public static async Task LoadTextureEffectWithTintAsync( return blendEffect; } } -} +} \ No newline at end of file diff --git a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs index ea97629..ae1f69c 100644 --- a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs +++ b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs @@ -87,14 +87,12 @@ public static async Task> AttachCompositionBl /// An optional parameter to set the overall saturation of the effect (if null, it will default to 1) /// The optional source to generate the noise image using Win2D /// The path of the noise image to use - /// Indicates whether or not to force the reload of the Win2D image /// Indicates whether or not to fade the effect in /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionInAppCustomAcrylicEffectAsync( [NotNull] this TSource element, [NotNull] T target, float blur, int ms, Color color, float colorMix, float? saturation, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, - BitmapCacheMode options = BitmapCacheMode.EnableCaching, bool fadeIn = false, bool disposeOnUnload = false) + [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool fadeIn = false, bool disposeOnUnload = false) where TSource : FrameworkElement where T : FrameworkElement { @@ -128,7 +126,7 @@ const string // Get the noise brush using Win2D IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri, options); + blurEffect, sourceParameters, color, colorMix, canvas, uri); // Add the final saturation effect if needed if (saturation != null) @@ -208,12 +206,11 @@ public static async Task> AttachCompositionHo /// The opacity of the color over the blurred background /// The optional source to generate the noise image using Win2D /// The path of the noise image to use - /// Indicates whether or not to force the reload of the Win2D image /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionCustomAcrylicEffectAsync( [NotNull] this T element, Color color, float colorMix, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, BitmapCacheMode options = BitmapCacheMode.EnableCaching, bool disposeOnUnload = false) + [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) where T : FrameworkElement { // Percentage check @@ -247,7 +244,7 @@ public static async Task> AttachCompositionCu // Get the noise brush using Win2D IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - alphaBlend, sourceParameters, color, colorMix, canvas, uri, options); + alphaBlend, sourceParameters, color, colorMix, canvas, uri); // Make sure the Win2D brush was loaded correctly CompositionEffectFactory factory = compositor.CreateEffectFactory(source); @@ -283,13 +280,12 @@ public static async Task> AttachCompositionCu /// The duration of the initial blur animation, in milliseconds /// The optional source to generate the noise image using Win2D /// The path of the noise image to use - /// Indicates whether or not to force the reload of the Win2D image /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionCustomAcrylicToggleEffectAsync( [NotNull] this T element, Color color, float inAppColorMix, float hostColorMix, AcrylicEffectMode mode, float blur, int ms, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, BitmapCacheMode options = BitmapCacheMode.EnableCaching, bool disposeOnUnload = false) + [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) where T : FrameworkElement { // Percentage check @@ -350,7 +346,7 @@ const string { nameof(backdropBrush), backdropBrush } }; IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - switchEffect, sourceParameters, color, mode == AcrylicEffectMode.InAppBlur ? inAppColorMix : hostColorMix, canvas, uri, options); + switchEffect, sourceParameters, color, mode == AcrylicEffectMode.InAppBlur ? inAppColorMix : hostColorMix, canvas, uri); // Setup the tint effect ArithmeticCompositeEffect tint = source as ArithmeticCompositeEffect ?? source.To().Background as ArithmeticCompositeEffect; @@ -384,7 +380,7 @@ const string void Toggle(AcrylicEffectMode m) { effectBrush.SetInstantValue(source1Name, m == AcrylicEffectMode.InAppBlur ? 1 : 0); - effectBrush.SetInstantValue(source2Name, m == AcrylicEffectMode.InAppBlur ? 0 :1); + effectBrush.SetInstantValue(source2Name, m == AcrylicEffectMode.InAppBlur ? 0 : 1); float mix = m == AcrylicEffectMode.InAppBlur ? inAppColorMix : hostColorMix, source1 = 1 - mix, @@ -420,7 +416,7 @@ void Toggle(AcrylicEffectMode m) /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionAnimatableSaturationEffectAsync( - [NotNull] this T element, float on, float off, bool initiallyVisible, bool disposeOnUnload = false) + [NotNull] this T element, float on, float off, bool initiallyVisible, bool disposeOnUnload = false) where T : FrameworkElement { // Get the compositor @@ -504,14 +500,12 @@ public static async Task> AttachCompositi /// The opacity of the color over the blurred background /// The optional source to generate the noise image using Win2D /// The path of the noise image to use - /// Indicates whether or not to force the reload of the Win2D image /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionAnimatableInAppCustomAcrylicEffectAsync( [NotNull] this TSource element, [NotNull] T target, float on, float off, bool initiallyVisible, - Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, - BitmapCacheMode options = BitmapCacheMode.EnableCaching, bool disposeOnUnload = false) + Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) where TSource : FrameworkElement where T : FrameworkElement { @@ -539,7 +533,7 @@ public static async Task> AttachCompositi // Get the noise brush using Win2D IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri, options); + blurEffect, sourceParameters, color, colorMix, canvas, uri); // Make sure the Win2D brush was loaded correctly CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(source, new[] { animationPropertyName }); @@ -575,7 +569,6 @@ public static async Task> AttachCompositi /// The opacity of the color over the blurred background /// The optional source to generate the noise image using Win2D /// The path of the noise image to use - /// Indicates whether or not to force the reload of the Win2D image /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionAnimatableInAppCustomAcrylicAndSaturationEffectAsync( @@ -583,8 +576,7 @@ public static async Task> Attach float onBlur, float offBlur, float onSaturation, float offSaturation, bool initiallyVisible, - Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, - BitmapCacheMode options = BitmapCacheMode.EnableCaching, bool disposeOnUnload = false) + Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) where TSource : FrameworkElement where T : FrameworkElement { @@ -612,7 +604,7 @@ public static async Task> Attach // Get the noise brush using Win2D IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri, options); + blurEffect, sourceParameters, color, colorMix, canvas, uri); // Add the final saturation effect SaturationEffect saturationEffect = new SaturationEffect @@ -664,7 +656,7 @@ public static async Task> Attach /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded [ItemNotNull] public static async Task> AttachCompositionAnimatableBlurAndSaturationEffectAsync( - [NotNull] this T element, float onBlur, float offBlur, float onSaturation, float offSaturation, bool initiallyVisible, bool disposeOnUnload = false) + [NotNull] this T element, float onBlur, float offBlur, float onSaturation, float offSaturation, bool initiallyVisible, bool disposeOnUnload = false) where T : FrameworkElement { // Get the compositor diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 76ef438..d698e13 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -10,6 +10,7 @@ using JetBrains.Annotations; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Effects; +using UICompositionAnimations.Brushes; using UICompositionAnimations.Brushes.Cache; using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; @@ -176,24 +177,24 @@ private CompositionBrushBuilder([NotNull] Func> fact /// Starts a new pipeline from a Win2D image /// /// The path for the image to load - /// Indicates whether or not to force the reload of the Win2D image /// 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, BitmapCacheMode options = BitmapCacheMode.EnableCaching, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound) + public static CompositionBrushBuilder FromImage([NotNull] Uri uri, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode cache = BitmapCacheMode.Default) { - return new CompositionBrushBuilder(() => Win2DImageHelper.LoadImageAsync(Window.Current.Compositor, uri, options, dpiMode).ContinueWith(t => t.Result as CompositionBrush)); + return new CompositionBrushBuilder(() => 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 /// /// The path for the image to load - /// Indicates whether or not to force the reload of the Win2D image /// 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, BitmapCacheMode options = BitmapCacheMode.EnableCaching, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound) + public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode dpiMode = BitmapDPIMode.CopyDisplayDPISettingsWith96AsLowerBound, BitmapCacheMode cache = BitmapCacheMode.Default) { - CompositionBrushBuilder image = FromImage(uri, options, dpiMode); + CompositionBrushBuilder image = FromImage(uri, dpiMode, cache); async Task Factory() => new BorderEffect { @@ -525,6 +526,8 @@ public CompositionBrushBuilder Effect([NotNull] Func /// Builds a instance from the current effects pipeline /// @@ -547,5 +550,13 @@ public async Task BuildAsync() HostBackdropBrushCache.Cleanup(); return effectBrush; } + + /// + /// Creates a new from the current effects pipeline + /// + [Pure, NotNull] + public XamlCompositionBrush AsBrush() => new XamlCompositionBrush(this); + + #endregion } -} +} \ No newline at end of file diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs new file mode 100644 index 0000000..b2be7c8 --- /dev/null +++ b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Windows.UI.Composition; +using JetBrains.Annotations; + +namespace UICompositionAnimations.Brushes.Cache +{ + /// + /// A used to cache reusable instances with an associated key + /// + /// The type of key to classify the items in the cache + /// The type of items stored in the cache + internal sealed class ThreadSafeCompositionMapCache where TValue : CompositionObject + { + /// + /// The cache of weak references, to avoid memory leaks + /// + [NotNull] + private readonly Dictionary>> Cache = new Dictionary>>(); + + /// + /// Tries to retrieve a valid instance from the cache, and uses the provided factory if an existing item is not found + /// + /// The key to look for + /// The resulting value, if existing + [MustUseReturnValue] + public bool TryGetInstance(TKey key, out TValue result) + { + // Try to retrieve an valid instance from the cache + if (Cache.TryGetValue(key, out List> values)) + foreach (WeakReference value in values) + if (value.TryGetTarget(out TValue instance) && instance.Dispatcher.HasThreadAccess) + { + result = instance; + return true; + } + + // Not found + result = null; + return false; + } + + /// + /// Adds a new value with the specified key to the cache + /// + /// The key of the item to add + /// The value to add + public void Add(TKey key, TValue value) + { + if (Cache.TryGetValue(key, out List> list)) list.Add(new WeakReference(value)); + else Cache.Add(key, new List> { new WeakReference(value) }); + } + + /// + /// Adds a new value and removes previous values with the same key, if any + /// + /// The key of the item to add + /// The value to add + public void Overwrite(TKey key, TValue value) + { + Cache.Remove(key); + Cache.Add(key, new List> { new WeakReference(value) }); + } + + /// + /// Performs a cleanup of the cache by removing invalid references + /// + public void Cleanup() + { + foreach (List> list in Cache.Values) + list.RemoveAll(reference => !reference.TryGetTarget(out _)); + foreach (TKey key in Cache.Keys.ToArray()) + if (Cache[key].Count == 0) + Cache.Remove(key); + } + + /// + /// Clears the cache by removing all the stored items + /// + public IReadOnlyList Clear() + { + List values = new List(); + foreach (WeakReference reference in Cache.Values.SelectMany(list => list)) + if (reference.TryGetTarget(out TValue value)) + values.Add(value); + Cache.Clear(); + return values; + } + } +} \ No newline at end of file diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index f4ac4c7..3e3ff44 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -227,11 +227,6 @@ private static async void OnUnsupportedEffectFallbackColorPropertyChanged(Depend /// This property must be initialized before using the brush public Uri NoiseTextureUri { get; set; } - /// - /// Gets or sets the caching setting for the acrylic brush - /// - public BitmapCacheMode CacheMode { get; set; } = BitmapCacheMode.EnableCaching; - /// /// 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 @@ -418,7 +413,7 @@ private async Task SetupEffectAsync() // Get the noise brush using Win2D IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(Window.Current.Compositor, - baseEffect, sourceParameters, Tint, (float)TintMix, null, NoiseTextureUri, CacheMode); + 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; diff --git a/UICompositionAnimations/Brushes/LightingBrush.cs b/UICompositionAnimations/Brushes/LightingBrush.cs index 3a7d273..0ce927b 100644 --- a/UICompositionAnimations/Brushes/LightingBrush.cs +++ b/UICompositionAnimations/Brushes/LightingBrush.cs @@ -17,8 +17,8 @@ public sealed class LightingBrush : XamlCompositionBrushBase /// public double DiffuseAmount { - get { return (double)GetValue(DiffuseAmountProperty); } - set { SetValue(DiffuseAmountProperty, value); } + get => (double)GetValue(DiffuseAmountProperty); + set => SetValue(DiffuseAmountProperty, value); } /// @@ -38,8 +38,8 @@ private static void OnDiffuseAmountChanged(DependencyObject d, DependencyPropert /// public double SpecularShine { - get { return (double)GetValue(SpecularShineProperty); } - set { SetValue(SpecularShineProperty, value); } + get => (double)GetValue(SpecularShineProperty); + set => SetValue(SpecularShineProperty, value); } /// @@ -58,8 +58,8 @@ private static void OnSpecularShineChanged(DependencyObject d, DependencyPropert /// public double SpecularAmount { - get { return (double)GetValue(SpecularAmountProperty); } - set { SetValue(SpecularAmountProperty, value); } + get => (double)GetValue(SpecularAmountProperty); + set => SetValue(SpecularAmountProperty, value); } /// diff --git a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs index ca92187..254ac34 100644 --- a/UICompositionAnimations/Brushes/NoiseTextureBrush.cs +++ b/UICompositionAnimations/Brushes/NoiseTextureBrush.cs @@ -9,7 +9,6 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Media; using UICompositionAnimations.Behaviours; -using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; using Windows.ApplicationModel; @@ -30,8 +29,8 @@ public sealed class NoiseTextureBrush : XamlCompositionBrushBase /// public Color Tint { - get { return GetValue(TintProperty).To(); } - set { SetValue(TintProperty, value); } + get => GetValue(TintProperty).To(); + set => SetValue(TintProperty, value); } /// @@ -57,11 +56,6 @@ private static async void OnTintPropertyChanged(DependencyObject d, DependencyPr /// This property must be initialized before using the brush public Uri TextureUri { get; set; } - /// - /// Gets or sets the caching setting for the acrylic brush - /// - public BitmapCacheMode CacheMode { get; set; } = BitmapCacheMode.EnableCaching; - #endregion // Initialization semaphore (due to the Win2D image loading being asynchronous) @@ -114,7 +108,7 @@ private async Task SetupEffectAsync() List animatableParameters = new List { ColorSourceParameterName }; // Get the noise brush using Win2D - IGraphicsEffect source = await AcrylicEffectHelper.LoadTextureEffectWithTintAsync(Window.Current.Compositor, sourceParameters, Tint, TextureUri, CacheMode); + 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; diff --git a/UICompositionAnimations/Brushes/XamlCompositionBrush.cs b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs new file mode 100644 index 0000000..18a4802 --- /dev/null +++ b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs @@ -0,0 +1,44 @@ +using System; +using JetBrains.Annotations; +using UICompositionAnimations.Behaviours; +using UICompositionAnimations.Brushes.Base; + +namespace UICompositionAnimations.Brushes +{ + /// + /// 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 + /// + [NotNull] + public CompositionBrushBuilder 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; + + /// + protected override CompositionBrushBuilder OnBrushRequested() => Pipeline; + + /// + /// Clones the current instance by rebuilding the source . Use this method to reuse the same effects pipeline on a different + /// + [PublicAPI, Pure, NotNull] + public XamlCompositionBrush Clone() + { + if (Dispatcher.HasThreadAccess) + { + throw new InvalidOperationException("The current thread already has access to the brush dispatcher, so a clone operation is not necessary. " + + "You can just assign this brush to an arbitrary number of controls and it will still work correctly. " + + "This method is only meant to be used to create a new instance of this brush using the same pipeline, " + + "on threads that can't access the current instance, for example in secondary app windows."); + } + return new XamlCompositionBrush(Pipeline); + } + } +} \ No newline at end of file diff --git a/UICompositionAnimations/Enums/BitmapCacheMode.cs b/UICompositionAnimations/Enums/BitmapCacheMode.cs index a50b52b..8b7f3c7 100644 --- a/UICompositionAnimations/Enums/BitmapCacheMode.cs +++ b/UICompositionAnimations/Enums/BitmapCacheMode.cs @@ -1,23 +1,23 @@ namespace UICompositionAnimations.Enums { /// - /// Indicates the cache mode to use when loading an item + /// Indicates the cache mode to use when loading a Win2D image /// public enum BitmapCacheMode { /// - /// The new item will be either loaded from the cache when possible, or saved in the cache for future use + /// The default behavior, the cache is enabled /// - EnableCaching, + Default, /// - /// The item will not be loaded from the cache, but it will be stored in the cache for future use if possible + /// Reload the target image and overwrite the cached entry, if it exists /// - DisableCachingOnRead, + Overwrite, /// - /// The new item will not be loaded from the cache and it will not be saved into the cache either + /// The cache is disabled and new images are always reloaded /// - DisableCaching + Disabled } } \ No newline at end of file diff --git a/UICompositionAnimations/Helpers/Win2DImageHelper.cs b/UICompositionAnimations/Helpers/Win2DImageHelper.cs index 3fb5c1d..9b0bcb1 100644 --- a/UICompositionAnimations/Helpers/Win2DImageHelper.cs +++ b/UICompositionAnimations/Helpers/Win2DImageHelper.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.Graphics.DirectX; @@ -16,6 +15,7 @@ using Microsoft.Graphics.Canvas.UI; using Microsoft.Graphics.Canvas.UI.Composition; using Microsoft.Graphics.Canvas.UI.Xaml; +using UICompositionAnimations.Brushes.Cache; using UICompositionAnimations.Enums; namespace UICompositionAnimations.Helpers @@ -30,15 +30,15 @@ public static class Win2DImageHelper /// private const int DeviceLostRecoveryThreshold = 1000; - /// - /// Gets a shared semaphore to avoid loading multiple Win2D resources at the same time - /// - private static readonly SemaphoreSlim Win2DSemaphore = new SemaphoreSlim(1); + // Synchronization mutex to access the cache and load Win2D images concurrently + [NotNull] + private static readonly AsyncMutex Win2DMutex = new AsyncMutex(); /// /// Gets the local cache mapping for previously loaded Win2D images /// - private static readonly IDictionary SurfacesCache = new Dictionary(); + [NotNull] + private static readonly ThreadSafeCompositionMapCache Cache = new ThreadSafeCompositionMapCache(); #region Public APIs @@ -47,11 +47,12 @@ 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) + public static Task LoadImageAsync([NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache = BitmapCacheMode.Default) { - return LoadImageAsync(Window.Current.Compositor, uri, BitmapCacheMode.DisableCaching, dpiMode); + return LoadImageAsync(Window.Current.Compositor, uri, dpiMode, cache); } /// @@ -60,11 +61,12 @@ public static Task LoadImageAsync([NotNull] Uri uri, Bi /// The to use to load the target image /// 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) + public static Task LoadImageAsync([NotNull] this CanvasControl canvas, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache = BitmapCacheMode.Default) { - return LoadImageAsync(Window.Current.Compositor, canvas, uri, BitmapCacheMode.DisableCaching, dpiMode); + return LoadImageAsync(Window.Current.Compositor, canvas, uri, dpiMode, cache); } /// @@ -74,13 +76,10 @@ public static Task LoadImageAsync([NotNull] this Canvas /// The returned items should be manually disposed once checked that they are no longer being used in other effect pipelines [PublicAPI] [MustUseReturnValue, ItemNotNull] - public static async Task> ClearCacheAsync() + public static async Task> ClearCacheAsync() { - await Win2DSemaphore.WaitAsync(); - IEnumerable surfaces = SurfacesCache.Values; - SurfacesCache.Clear(); - Win2DSemaphore.Release(); - return surfaces; + using (await Win2DMutex.LockAsync()) + return Cache.Clear(); } #endregion @@ -158,6 +157,7 @@ private static async Task LoadSurfaceBrushAsync([NotNul { // Manual using block to allow the initial switch statement bitmap?.Dispose(); + Cache.Cleanup(); } } @@ -167,13 +167,14 @@ private static async Task LoadSurfaceBrushAsync([NotNul /// The compositor to use to render the Win2D image /// The to use to load the target image /// The path to the image to load - /// Indicates whether or not to force the reload of the Win2D image /// 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, BitmapCacheMode options, BitmapDPIMode dpiMode) + internal static async Task LoadImageAsync([NotNull] Compositor compositor, [NotNull] CanvasControl canvas, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache) { TaskCompletionSource tcs = new TaskCompletionSource(); + + // Loads an image using the input CanvasDevice instance async Task LoadImageAsync(bool shouldThrow) { // Load the image - this will only succeed when there's an available Win2D device @@ -187,6 +188,8 @@ async Task LoadImageAsync(bool shouldThrow) return null; } } + + // Handler to create the Win2D image from the input CanvasControl async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEventArgs args) { // Cancel previous actions @@ -210,39 +213,38 @@ async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEve } // Lock the semaphore and check the cache first - await Win2DSemaphore.WaitAsync(); - if (options == BitmapCacheMode.EnableCaching && SurfacesCache.TryGetValue(uri, out CompositionSurfaceBrush cached)) + using (await Win2DMutex.LockAsync()) { - Win2DSemaphore.Release(); - return cached; - } + if (cache == BitmapCacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached; - // Load the image - canvas.CreateResources += Canvas_CreateResources; - try - { - // This will throw and the canvas will re-initialize the Win2D device if needed - await LoadImageAsync(true); - } - catch (ArgumentException) - { - // Just ignore here - } - catch - { - // Win2D messed up big time - tcs.TrySetResult(null); - } - await Task.WhenAny(tcs.Task, Task.Delay(DeviceLostRecoveryThreshold).ContinueWith(t => tcs.TrySetResult(null))); - canvas.CreateResources -= Canvas_CreateResources; - CompositionSurfaceBrush instance = tcs.Task.Result; + // Load the image + canvas.CreateResources += Canvas_CreateResources; + try + { + // This will throw and the canvas will re-initialize the Win2D device if needed + await LoadImageAsync(true); + } + catch (ArgumentException) + { + // Just ignore here + } + catch + { + // Win2D messed up big time + tcs.TrySetResult(null); + } + await Task.WhenAny(tcs.Task, Task.Delay(DeviceLostRecoveryThreshold).ContinueWith(t => tcs.TrySetResult(null))); + canvas.CreateResources -= Canvas_CreateResources; + CompositionSurfaceBrush brush = tcs.Task.Result; - // Cache when needed and return the result - if (instance != null && - options != BitmapCacheMode.DisableCaching && - !SurfacesCache.ContainsKey(uri)) SurfacesCache.Add(uri, instance); - Win2DSemaphore.Release(); - return instance; + // 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); + } + return brush; + } } /// @@ -250,11 +252,10 @@ async void Canvas_CreateResources(CanvasControl sender, CanvasCreateResourcesEve /// /// The compositor to use to render the Win2D image /// The path to the image to load - /// Indicates whether or not to force the reload of the Win2D image /// 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, BitmapCacheMode options, BitmapDPIMode dpiMode) + internal static async Task LoadImageAsync([NotNull] Compositor compositor, [NotNull] Uri uri, BitmapDPIMode dpiMode, BitmapCacheMode cache) { // Fix the Uri if it has been generated by the XAML designer if (uri.Scheme.Equals("ms-resource")) @@ -265,36 +266,35 @@ internal static async Task LoadImageAsync( uri = new Uri($"ms-appx://{path}"); } - // Lock the semaphore and check the cache first - await Win2DSemaphore.WaitAsync(); - if (options == BitmapCacheMode.EnableCaching && SurfacesCache.TryGetValue(uri, out CompositionSurfaceBrush cached)) + // Lock and check the cache first + using (await Win2DMutex.LockAsync()) { - Win2DSemaphore.Release(); - return cached; - } + if (cache == BitmapCacheMode.Default && Cache.TryGetInstance(uri, out CompositionSurfaceBrush cached)) return cached; - // Load the image - CompositionSurfaceBrush brush; - try - { - // This will throw and the canvas will re-initialize the Win2D device if needed - CanvasDevice sharedDevice = CanvasDevice.GetSharedDevice(); - brush = await LoadSurfaceBrushAsync(sharedDevice, compositor, sharedDevice, uri, dpiMode); - } - catch - { - // Device error - brush = null; - } + // Load the image + CompositionSurfaceBrush brush; + try + { + // This will throw and the canvas will re-initialize the Win2D device if needed + CanvasDevice sharedDevice = CanvasDevice.GetSharedDevice(); + brush = await LoadSurfaceBrushAsync(sharedDevice, compositor, sharedDevice, uri, dpiMode); + } + catch + { + // Device error + brush = null; + } - // Cache when needed and return the result - if (brush != null && - options != BitmapCacheMode.DisableCaching && - !SurfacesCache.ContainsKey(uri)) SurfacesCache.Add(uri, brush); - Win2DSemaphore.Release(); - return brush; + // 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); + } + return brush; + } } #endregion } -} +} \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index a23ad2b..9b915a3 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -28,6 +28,7 @@ prompt 4 bin\Debug\UICompositionAnimations.XML + latest AnyCPU @@ -38,6 +39,7 @@ prompt 4 bin\Release\UICompositionAnimations.XML + latest x86 @@ -49,6 +51,7 @@ x86 false prompt + latest x86 @@ -61,6 +64,7 @@ false prompt bin\x86\Release\UICompositionAnimations.XML + latest ARM @@ -72,6 +76,7 @@ ARM false prompt + latest ARM @@ -84,6 +89,7 @@ false prompt bin\ARM\Release\UICompositionAnimations.XML + latest x64 @@ -95,6 +101,7 @@ x64 false prompt + latest x64 @@ -107,6 +114,7 @@ false prompt bin\x64\Release\UICompositionAnimations.XML + latest @@ -124,9 +132,11 @@ + + From be92343ec6481a30b1041d90adddb66d2511e352 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Aug 2018 23:39:36 +0200 Subject: [PATCH 34/47] Minor code refactoring --- .../Behaviours/CompositionBrushBuilder.cs | 17 -------------- .../Enums/EffectPlacement.cs | 22 +++++++++++++++++++ .../UICompositionAnimations.csproj | 1 + 3 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 UICompositionAnimations/Enums/EffectPlacement.cs diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index d698e13..cbf9742 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -210,23 +210,6 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode #region Blends - /// - /// An used to modify the default placement of the input instance in a blend operation - /// - [PublicAPI] - public enum EffectPlacement - { - /// - /// The instance used to call the blend method is placed on top of the other - /// - Foreground, - - /// - /// The instance used to call the blend method is placed behind the other - /// - Background - } - /// /// Blends two pipelines using a instance with the specified mode /// diff --git a/UICompositionAnimations/Enums/EffectPlacement.cs b/UICompositionAnimations/Enums/EffectPlacement.cs new file mode 100644 index 0000000..5c61248 --- /dev/null +++ b/UICompositionAnimations/Enums/EffectPlacement.cs @@ -0,0 +1,22 @@ +using Windows.Graphics.Effects; +using JetBrains.Annotations; + +namespace UICompositionAnimations.Enums +{ + /// + /// An used to modify the default placement of the input instance in a blend operation + /// + [PublicAPI] + public enum EffectPlacement + { + /// + /// The instance used to call the blend method is placed on top of the other + /// + Foreground, + + /// + /// The instance used to call the blend method is placed behind the other + /// + Background + } +} \ No newline at end of file diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 9b915a3..5497fe8 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -146,6 +146,7 @@ + From 6f8df334b454225d76b65586612c4d2e19d45b90 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Aug 2018 23:57:02 +0200 Subject: [PATCH 35/47] Added ready to use acrylic effects --- .../Behaviours/CompositionBrushBuilder.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index cbf9742..9d3f47d 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -208,6 +208,73 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode #endregion + #region Prebuilt pipelines + + /// + /// 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] + private static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float mix, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.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); + } + + /// + /// 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 + /// The amount of blur to apply to the acrylic brush + /// 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) + { + return FromBackdropBrush() + .Effect(source => new LuminanceToAlphaEffect { Source = source }) + .Opacity(0.4f) + .Blend(FromBackdropBrush(), BlendEffectMode.Multiply) + .Tint(tint, mix) + .Blur(blur) + .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); + } + + /// + /// 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 + /// The animation to apply on the tint color of the effect + /// The amount of blur to apply to the acrylic brush + /// The animation to apply on the blur effect in the pipeline + /// 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, out EffectAnimation tintAnimation, + float blur, out EffectAnimation blurAnimation, + [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) + { + return FromBackdropBrush() + .Effect(source => new LuminanceToAlphaEffect { Source = source }) + .Opacity(0.4f) + .Blend(FromBackdropBrush(), BlendEffectMode.Multiply) + .Tint(tint, mix, out tintAnimation) + .Blur(blur, out blurAnimation) + .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); + } + + #endregion + #region Blends /// From 316ef06b1a74834f0d24808b1f06838ce8532741 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 27 Aug 2018 01:04:14 +0200 Subject: [PATCH 36/47] Added experimental Bind method for animations --- .../Brushes/XamlCompositionBrush.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/UICompositionAnimations/Brushes/XamlCompositionBrush.cs b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs index 18a4802..37a3173 100644 --- a/UICompositionAnimations/Brushes/XamlCompositionBrush.cs +++ b/UICompositionAnimations/Brushes/XamlCompositionBrush.cs @@ -1,10 +1,18 @@ using System; +using System.Threading.Tasks; using JetBrains.Annotations; using UICompositionAnimations.Behaviours; using UICompositionAnimations.Brushes.Base; namespace UICompositionAnimations.Brushes { + /// + /// A that represents a custom effect animation that can be applied to a instance + /// + /// The animation target value + /// The animation duration, in milliseconds + public delegate Task XamlEffectAnimation(float value, int ms); + /// /// A simple that can be used to quickly create XAML brushes from arbitrary pipelines /// @@ -22,6 +30,18 @@ public sealed class XamlCompositionBrush : XamlCompositionEffectBrushBase /// The instance to create the effect public XamlCompositionBrush([NotNull] CompositionBrushBuilder pipeline) => Pipeline = pipeline; + /// + /// Binds an to the composition brush in the current instance + /// + /// The input animation + /// The resulting animation + [Pure, NotNull] + public XamlCompositionBrush Bind([NotNull] EffectAnimation animation, out XamlEffectAnimation bound) + { + bound = (value, ms) => animation(CompositionBrush, value, ms); + return this; + } + /// protected override CompositionBrushBuilder OnBrushRequested() => Pipeline; From 0c49a980a48fd769b4db64dd6345fdc347386956 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 27 Aug 2018 02:02:18 +0200 Subject: [PATCH 37/47] Minor fixes --- .../Behaviours/CompositionBrushBuilder.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 9d3f47d..7ae82f0 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -218,7 +218,7 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode /// The for the noise texture to load for the acrylic effect /// The cache mode to use to load the image [Pure, NotNull] - private static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float mix, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) + public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float mix, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) { return FromHostBackdropBrush() .Effect(source => new LuminanceToAlphaEffect { Source = source }) @@ -240,9 +240,6 @@ private static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float public static CompositionBrushBuilder FromBackdropAcrylic(Color tint, float mix, float blur, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) { return FromBackdropBrush() - .Effect(source => new LuminanceToAlphaEffect { Source = source }) - .Opacity(0.4f) - .Blend(FromBackdropBrush(), BlendEffectMode.Multiply) .Tint(tint, mix) .Blur(blur) .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); @@ -265,9 +262,6 @@ public static CompositionBrushBuilder FromBackdropAcrylic( [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) { return FromBackdropBrush() - .Effect(source => new LuminanceToAlphaEffect { Source = source }) - .Opacity(0.4f) - .Blend(FromBackdropBrush(), BlendEffectMode.Multiply) .Tint(tint, mix, out tintAnimation) .Blur(blur, out blurAnimation) .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); From f9183f8de45a1b1c5154ddec31c628562b153ff4 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 31 Aug 2018 02:53:12 +0200 Subject: [PATCH 38/47] Added BindSize extension method --- UICompositionAnimations/CompositionExtensions.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index 649cfc9..42cfcd9 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -2043,12 +2043,22 @@ public static void AttachToElement([NotNull] this CompositionBrush brush, [NotNu ElementCompositionPreview.SetElementChildVisual(target, sprite); // Keep the sprite size in sync + sprite.BindSize(target); + } + + /// + /// Starts an expression animation to keep the size of the source in sync with the target + /// + /// The composition object to start the animation on + /// The target to read the size updates from + public static void BindSize([NotNull] this CompositionObject source, [NotNull] FrameworkElement target) + { Visual visual = target.GetVisual(); ExpressionAnimation bindSizeAnimation = Window.Current.Compositor.CreateExpressionAnimation($"{nameof(visual)}.Size"); bindSizeAnimation.SetReferenceParameter(nameof(visual), visual); // Start the animation - sprite.StartAnimation("Size", bindSizeAnimation); + source.StartAnimation("Size", bindSizeAnimation); } #endregion From 93cc80603c8d5e7e38992261fda63473de23c60f Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 4 Sep 2018 02:52:14 +0200 Subject: [PATCH 39/47] More API overloads added --- .../Behaviours/CompositionBrushBuilder.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 7ae82f0..95912f5 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -228,6 +228,25 @@ public static CompositionBrushBuilder FromHostBackdropAcrylic(Color tint, float .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); } + /// + /// 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 + /// 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, out EffectAnimation tintAnimation, [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.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); + } + /// /// Returns a new instance that implements the in-app backdrop acrylic effect /// @@ -245,6 +264,48 @@ public static CompositionBrushBuilder FromBackdropAcrylic(Color tint, float mix, .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); } + /// + /// 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 + /// The animation to apply on the tint color of the effect + /// The amount of blur to apply to the acrylic brush + /// 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, out EffectAnimation tintAnimation, + float blur, + [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) + { + return FromBackdropBrush() + .Tint(tint, mix, out tintAnimation) + .Blur(blur) + .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); + } + + /// + /// 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 + /// The amount of blur to apply to the acrylic brush + /// The animation to apply on the blur effect in the pipeline + /// 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, out EffectAnimation blurAnimation, + [NotNull] Uri noiseUri, BitmapCacheMode cache = BitmapCacheMode.Default) + { + return FromBackdropBrush() + .Tint(tint, mix) + .Blur(blur, out blurAnimation) + .Blend(FromTiles(noiseUri, cache: cache), BlendEffectMode.Overlay, EffectPlacement.Background); + } + /// /// Returns a new instance that implements the in-app backdrop acrylic effect /// From ad30e33483eae2d57eae85f67b88fde587a57a9e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 4 Sep 2018 03:03:48 +0200 Subject: [PATCH 40/47] Added new pipeline builder from UIElement --- .../Behaviours/CompositionBrushBuilder.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 95912f5..83af509 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -206,6 +206,16 @@ public static CompositionBrushBuilder FromTiles([NotNull] Uri uri, BitmapDPIMode return new CompositionBrushBuilder(image, Factory); } + /// + /// 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) + { + return new CompositionBrushBuilder(() => Task.FromResult(element.GetVisual().Compositor.CreateBackdropBrush().To())); + } + #endregion #region Prebuilt pipelines From 81eac145b624b9547ca429920e92f4d998742f4a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 4 Sep 2018 03:29:45 +0200 Subject: [PATCH 41/47] Removed unnecessary code --- .../AttachedCompositionEffectFactory.cs | 732 ------------------ .../Behaviours/CompositionBrushBuilder.cs | 16 + .../CompositionExtensions.cs | 44 +- .../UICompositionAnimations.csproj | 1 - 4 files changed, 38 insertions(+), 755 deletions(-) delete mode 100644 UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs diff --git a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs b/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs deleted file mode 100644 index ae1f69c..0000000 --- a/UICompositionAnimations/Behaviours/AttachedCompositionEffectFactory.cs +++ /dev/null @@ -1,732 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using System.Threading.Tasks; -using Windows.Graphics.Effects; -using Windows.UI; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Hosting; -using JetBrains.Annotations; -using Microsoft.Graphics.Canvas.Effects; -using Microsoft.Graphics.Canvas.UI.Xaml; -using UICompositionAnimations.Behaviours.Effects; -using UICompositionAnimations.Behaviours.Misc; -using UICompositionAnimations.Composition; -using UICompositionAnimations.Enums; -using UICompositionAnimations.Helpers; - -namespace UICompositionAnimations.Behaviours -{ - /// - /// A static class that manages the creation of attached composition effects - /// - [PublicAPI] - public static class AttachedCompositionEffectsFactory - { - #region Static effects - - /// - /// Creates a new instance for the target element - /// - /// The type of element to blur - /// The target element - /// The amount of blur to apply to the element - /// The duration of the initial blur animation, in milliseconds - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - /// This method returns a instance and attempts to run synchronously if called on the UI thread - [ItemNotNull] - public static async Task> AttachCompositionBlurEffect( - [NotNull] this T element, float blur, int ms, bool disposeOnUnload = false) where T : FrameworkElement - { - // Get the visual and the compositor - Visual visual = element.GetVisual(); - Compositor compositor = visual.Compositor; - - // Create the blur effect and the effect factory - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = "Blur", - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter("source") - }; - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(blurEffect, new[] { "Blur.BlurAmount" }); - - // Setup the rest of the effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush()); - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - sprite.Size = new Vector2((float)element.ActualWidth, (float)element.ActualHeight); - await AddToTreeAndBindSizeAsync(visual, element, sprite); - - // Animate the blur amount - effectBrush.StartAnimationAsync("Blur.BlurAmount", blur, TimeSpan.FromMilliseconds(ms)).Forget(); - - // Prepare and return the manager - return new AttachedStaticCompositionEffect(element, sprite, disposeOnUnload); - } - - /// - /// Creates an effect brush that's similar to the official Acrylic brush in the Fall Creator's Update. - /// The pipeline uses the following effects: BackdropBrush > > - /// > with customizable blend factors for each couple of layers - /// - /// The type of the element that will be the source for the composition effect - /// The type of the target element that will host the resulting - /// The that will be the source of the effect - /// The target host for the resulting effect - /// The amount of blur to apply to the element - /// The duration of the initial blur animation, in milliseconds - /// The tint color for the effect - /// The opacity of the color over the blurred background - /// An optional parameter to set the overall saturation of the effect (if null, it will default to 1) - /// The optional source to generate the noise image using Win2D - /// The path of the noise image to use - /// Indicates whether or not to fade the effect in - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionInAppCustomAcrylicEffectAsync( - [NotNull] this TSource element, [NotNull] T target, float blur, int ms, Color color, float colorMix, float? saturation, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool fadeIn = false, bool disposeOnUnload = false) - where TSource : FrameworkElement - where T : FrameworkElement - { - // Percentage check - if (saturation < 0 || saturation > 1) throw new ArgumentOutOfRangeException(nameof(saturation), "The input saturation value must be in the [0,1] range"); - if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException(nameof(colorMix), "The mix factors must be in the [0,1] range"); - - // Setup the compositor - Visual visual = ElementCompositionPreview.GetElementVisual(element); - Compositor compositor = visual.Compositor; - - // Prepare a luminosity to alpha effect to adjust the background contrast - CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - const string - blurName = "Blur", - blurParameterName = "Blur.BlurAmount"; - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = blurName, - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) - }; - - // Background with blur and tint overlay - IDictionary sourceParameters = new Dictionary - { - { nameof(backdropBrush), backdropBrush } - }; - - // Get the noise brush using Win2D - IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri); - - // Add the final saturation effect if needed - if (saturation != null) - { - SaturationEffect saturationEffect = new SaturationEffect - { - Saturation = saturation.Value, - Source = source - }; - source = saturationEffect; - } - - // Make sure the Win2D brush was loaded correctly - CompositionEffectFactory factory = compositor.CreateEffectFactory(source, new[] { blurParameterName }); - - // Create the effect factory and apply the final effect - CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) - { - effectBrush.SetSourceParameter(pair.Key, pair.Value); - } - - // Create the sprite to display and add it to the visual tree - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - - // Assign the visual - if (fadeIn) - { - sprite.StopAnimation("Opacity"); - sprite.Opacity = 0; - } - await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite); - if (fadeIn) - { - // Fade the effect in - ScalarKeyFrameAnimation opacityAnimation = sprite.Compositor.CreateScalarKeyFrameAnimation(0, - 1, TimeSpan.FromMilliseconds(ms), null, sprite.GetEasingFunction(EasingFunctionNames.Linear)); - sprite.StartAnimation("Opacity", opacityAnimation); - } - - // Animate the blur and return the result - effectBrush.StartAnimationAsync(blurParameterName, blur, TimeSpan.FromMilliseconds(ms)).Forget(); - return new AttachedStaticCompositionEffect(target, sprite, disposeOnUnload); - } - - /// - /// Creates a new instance for the target element - /// - /// The type of element to use to host the effect - /// The target element - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - /// This method returns a instance and attempts to run synchronously if called on the UI thread - [ItemNotNull] - public static async Task> AttachCompositionHostBackdropBlurEffect( - [NotNull] this T element, bool disposeOnUnload = false) where T : FrameworkElement - { - // Setup the host backdrop effect - Visual visual = ElementCompositionPreview.GetElementVisual(element); - Compositor compositor = visual.Compositor; - CompositionBackdropBrush brush = compositor.CreateHostBackdropBrush(); - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = brush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - return new AttachedStaticCompositionEffect(element, sprite, disposeOnUnload); - } - - /// - /// Creates an effect brush that's similar to the official Acrylic brush in the Fall Creator's Update. - /// The pipeline uses the following effects: HostBackdropBrush > > - /// > > > - /// > with customizable blend factors for each couple of layers - /// - /// The type of the target element that will host the resulting - /// The target element that will host the effect - /// The tint color for the effect - /// The opacity of the color over the blurred background - /// The optional source to generate the noise image using Win2D - /// The path of the noise image to use - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionCustomAcrylicEffectAsync( - [NotNull] this T element, Color color, float colorMix, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) - where T : FrameworkElement - { - // Percentage check - if (colorMix <= 0 || colorMix >= 1) throw new ArgumentOutOfRangeException(nameof(colorMix), "The mix factors must be in the [0,1] range"); - - // Setup the compositor - Visual visual = ElementCompositionPreview.GetElementVisual(element); - Compositor compositor = visual.Compositor; - - // Prepare a luminosity to alpha effect to adjust the background contrast - CompositionBackdropBrush hostBackdropBrush = 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 tint overlay - BlendEffect alphaBlend = new BlendEffect - { - Background = backgroundParameter, - Foreground = opacityEffect, - Mode = BlendEffectMode.Overlay - }; - IDictionary sourceParameters = new Dictionary - { - { nameof(hostBackdropBrush), hostBackdropBrush } - }; - - // Get the noise brush using Win2D - IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - alphaBlend, sourceParameters, color, colorMix, canvas, uri); - - // Make sure the Win2D brush was loaded correctly - CompositionEffectFactory factory = compositor.CreateEffectFactory(source); - - // Create the effect factory and apply the final effect - CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) - { - effectBrush.SetSourceParameter(pair.Key, pair.Value); - } - - // Create the sprite to display and add it to the visual tree - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - return new AttachedStaticCompositionEffect(element, sprite, disposeOnUnload); - } - - /// - /// Creates an effect brush that's similar to the official Acrylic brush in the Fall Creator's Update and can be toggled - /// between the host backdrop blur effect and the in-app acrylic brush effect. - /// The pipeline uses the following effects: HostBackdropBrush > > - /// > > > - /// > with customizable blend factors for each couple of layers - /// - /// The type of the target element that will host the resulting - /// The target element that will host the effect - /// The tint color for the effect - /// The opacity of the color over the in-app blurred contents - /// The opacity of the color over the blurred background - /// Indicates the initial mode for the custom effect - /// The amount of blur to apply to the element - /// The duration of the initial blur animation, in milliseconds - /// The optional source to generate the noise image using Win2D - /// The path of the noise image to use - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionCustomAcrylicToggleEffectAsync( - [NotNull] this T element, Color color, float inAppColorMix, float hostColorMix, - AcrylicEffectMode mode, float blur, int ms, - [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) - where T : FrameworkElement - { - // Percentage check - if (hostColorMix <= 0 || hostColorMix >= 1 || - inAppColorMix <= 0 || inAppColorMix >= 1) throw new ArgumentOutOfRangeException(nameof(hostColorMix), "The mix factors must be in the [0,1] range"); - - // Setup the compositor - Visual visual = ElementCompositionPreview.GetElementVisual(element); - Compositor compositor = visual.Compositor; - - // Prepare a luminosity to alpha effect to adjust the background contrast - CompositionBackdropBrush hostBackdropBrush = 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 tint overlay - BlendEffect alphaBlend = new BlendEffect - { - Background = backgroundParameter, - Foreground = opacityEffect, - Mode = BlendEffectMode.Overlay - }; - - // In-app backdrop effect - CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - const string - blurName = "Blur", - blurParameterName = "Blur.BlurAmount"; - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = blurName, - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) - }; - - // Setup the switch effect - ArithmeticCompositeEffect switchEffect = new ArithmeticCompositeEffect - { - Name = "Switch", - MultiplyAmount = 0, - Source1Amount = mode == AcrylicEffectMode.InAppBlur ? 1 : 0, - Source2Amount = mode == AcrylicEffectMode.InAppBlur ? 0 : 1, - Source1 = blurEffect, - Source2 = alphaBlend - }; - - // Get the tint and noise brushes using Win2D - IDictionary sourceParameters = new Dictionary - { - { nameof(hostBackdropBrush), hostBackdropBrush }, - { nameof(backdropBrush), backdropBrush } - }; - IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - switchEffect, sourceParameters, color, mode == AcrylicEffectMode.InAppBlur ? inAppColorMix : hostColorMix, canvas, uri); - - // Setup the tint effect - 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"; - const string - tint1Name = "Tint.Source1Amount", - tint2Name = "Tint.Source2Amount"; - - // Make sure the Win2D brush was loaded correctly - const string - source1Name = "Switch.Source1Amount", - source2Name = "Switch.Source2Amount"; - CompositionEffectFactory factory = compositor.CreateEffectFactory(source, new[] - { - blurParameterName, - tint1Name, - tint2Name, - source1Name, - source2Name - }); - - // Create the effect factory and apply the final effect - CompositionEffectBrush effectBrush = factory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) - { - effectBrush.SetSourceParameter(pair.Key, pair.Value); - } - - // Setup the toggle function - void Toggle(AcrylicEffectMode m) - { - effectBrush.SetInstantValue(source1Name, m == AcrylicEffectMode.InAppBlur ? 1 : 0); - effectBrush.SetInstantValue(source2Name, m == AcrylicEffectMode.InAppBlur ? 0 : 1); - float - mix = m == AcrylicEffectMode.InAppBlur ? inAppColorMix : hostColorMix, - source1 = 1 - mix, - source2 = mix; - effectBrush.SetInstantValue(tint1Name, source1); - effectBrush.SetInstantValue(tint2Name, source2); - } - - // Create the sprite to display and add it to the visual tree - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - if (mode == AcrylicEffectMode.InAppBlur) effectBrush.StartAnimationAsync(blurParameterName, blur, TimeSpan.FromMilliseconds(ms)).Forget(); - return new AttachedToggleAcrylicEffect(element, mode, - mode == AcrylicEffectMode.InAppBlur - ? (Action)null - : () => effectBrush.StartAnimationAsync(blurParameterName, blur, TimeSpan.FromMilliseconds(ms)).Forget(), - Toggle, sprite, disposeOnUnload); - } - - #endregion - - #region Animated effects - - /// - /// Creates a new instance for the target element - /// - /// The type of element to blur - /// The target element - /// The amount of saturation effect to apply - /// The default amount of saturation effect to apply - /// Indicates whether or not to apply the effect right away - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionAnimatableSaturationEffectAsync( - [NotNull] this T element, float on, float off, bool initiallyVisible, bool disposeOnUnload = false) - where T : FrameworkElement - { - // Get the compositor - Visual visual = await element.Dispatcher.GetAsync(element.GetVisual); - Compositor compositor = visual.Compositor; - - // Create the saturation effect and the effect factory - SaturationEffect saturationEffect = new SaturationEffect - { - Name = "SEffect", - Saturation = initiallyVisible ? off : on, - Source = new CompositionEffectSourceParameter("source") - }; - const string animationPropertyName = "SEffect.Saturation"; - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] { animationPropertyName }); - - // Setup the rest of the effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush()); - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); - return new AttachedAnimatableCompositionEffect(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload); - } - - /// - /// Creates a new instance for the target element - /// - /// The type of element to blur - /// The target element - /// The amount of blur effect to apply - /// The default amount of blur effect to apply - /// Indicates whether or not to apply the effect right away - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionAnimatableBlurEffectAsync( - [NotNull] this T element, float on, float off, bool initiallyVisible, bool disposeOnUnload = false) where T : FrameworkElement - { - // Get the compositor - Visual visual = await element.Dispatcher.GetAsync(element.GetVisual); - Compositor compositor = visual.Compositor; - - // Create the blur effect and the effect factory - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = "Blur", - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter("source") - }; - const string animationPropertyName = "Blur.BlurAmount"; - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(blurEffect, new[] { animationPropertyName }); - - // Setup the rest of the effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush()); - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); - return new AttachedAnimatableCompositionEffect(element, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload); - } - - /// - /// Creates a new instance with blur, tint and noise effects - /// - /// The type of the element that will be the source for the composition effect - /// The type of the target element that will host the resulting - /// The that will be the source of the effect - /// The target host for the resulting effect - /// The amount of blur effect to apply - /// The default amount of blur effect to apply - /// Indicates whether or not to apply the effect right away - /// The tint color for the effect - /// The opacity of the color over the blurred background - /// The optional source to generate the noise image using Win2D - /// The path of the noise image to use - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionAnimatableInAppCustomAcrylicEffectAsync( - [NotNull] this TSource element, [NotNull] T target, - float on, float off, bool initiallyVisible, - Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) - where TSource : FrameworkElement - where T : FrameworkElement - { - // Get the compositor - Visual visual = await element.Dispatcher.GetAsync(element.GetVisual); - Compositor compositor = visual.Compositor; - - // Create the blur effect and the effect factory - CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = "Blur", - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) - }; - const string animationPropertyName = "Blur.BlurAmount"; - - // Prepare the dictionary with the parameters to add - IDictionary sourceParameters = new Dictionary - { - { nameof(backdropBrush), backdropBrush } - }; - - // Get the noise brush using Win2D - IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri); - - // Make sure the Win2D brush was loaded correctly - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(source, new[] { animationPropertyName }); - - // Create the effect factory and apply the final effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) - { - effectBrush.SetSourceParameter(pair.Key, pair.Value); - } - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite); - if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); - return new AttachedAnimatableCompositionEffect(target, sprite, new CompositionAnimationParameters(animationPropertyName, on, off), disposeOnUnload); - } - - /// - /// Creates a new instance with blur, tint and noise effects - /// - /// The type of the element that will be the source for the composition effect - /// The type of the target element that will host the resulting - /// The that will be the source of the effect - /// The target host for the resulting effect - /// The amount of blur effect to apply - /// The default amount of blur effect to apply - /// The amount of saturation effect to apply - /// The default amount of saturation effect to apply - /// Indicates whether or not to apply the effect right away - /// The tint color for the effect - /// The opacity of the color over the blurred background - /// The optional source to generate the noise image using Win2D - /// The path of the noise image to use - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionAnimatableInAppCustomAcrylicAndSaturationEffectAsync( - [NotNull] this TSource element, [NotNull] T target, - float onBlur, float offBlur, - float onSaturation, float offSaturation, - bool initiallyVisible, - Color color, float colorMix, [CanBeNull] CanvasControl canvas, [NotNull] Uri uri, bool disposeOnUnload = false) - where TSource : FrameworkElement - where T : FrameworkElement - { - // Get the compositor - Visual visual = await element.Dispatcher.GetAsync(element.GetVisual); - Compositor compositor = visual.Compositor; - - // Create the blur effect and the effect factory - CompositionBackdropBrush backdropBrush = compositor.CreateBackdropBrush(); - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = "Blur", - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter(nameof(backdropBrush)) - }; - const string animationPropertyName = "Blur.BlurAmount"; - - // Prepare the dictionary with the parameters to add - IDictionary sourceParameters = new Dictionary - { - { nameof(backdropBrush), backdropBrush } - }; - - // Get the noise brush using Win2D - IGraphicsEffect source = await AcrylicEffectHelper.ConcatenateEffectWithTintAndBorderAsync(compositor, - blurEffect, sourceParameters, color, colorMix, canvas, uri); - - // Add the final saturation effect - SaturationEffect saturationEffect = new SaturationEffect - { - Name = "SEffect", - Saturation = initiallyVisible ? offSaturation : onSaturation, - Source = source - }; - const string saturationParameter = "SEffect.Saturation"; - - // Make sure the Win2D brush was loaded correctly - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] - { - animationPropertyName, - saturationParameter - }); - - // Create the effect factory and apply the final effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - foreach (KeyValuePair pair in sourceParameters) - { - effectBrush.SetSourceParameter(pair.Key, pair.Value); - } - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(target.GetVisual(), target, sprite); - if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); - return new AttachedCompositeAnimatableCompositionEffect(target, sprite, - new Dictionary - { - { animationPropertyName, new CompositionAnimationValueParameters(onBlur, offBlur) }, - { saturationParameter, new CompositionAnimationValueParameters(onSaturation, offSaturation) } - }, disposeOnUnload); - } - - /// - /// Creates a new instance for the target element that - /// applies both a blur and a saturation effect to the visual item - /// - /// The type of element to blur - /// The target element - /// The amount of blur effect to apply - /// The default amount of blur effect to apply - /// The amount of saturation effect to apply - /// The default amount of saturation effect to apply - /// Indicates whether or not to apply the effect right away - /// Indicates whether or not to automatically dispose and remove the effect when the target element is unloaded - [ItemNotNull] - public static async Task> AttachCompositionAnimatableBlurAndSaturationEffectAsync( - [NotNull] this T element, float onBlur, float offBlur, float onSaturation, float offSaturation, bool initiallyVisible, bool disposeOnUnload = false) - where T : FrameworkElement - { - // Get the compositor - Visual visual = await element.Dispatcher.GetAsync(element.GetVisual); - Compositor compositor = visual.Compositor; - - // Create the blur effect, the saturation effect and the effect factory - GaussianBlurEffect blurEffect = new GaussianBlurEffect - { - Name = "Blur", - BlurAmount = 0f, - BorderMode = EffectBorderMode.Hard, - Optimization = EffectOptimization.Balanced, - Source = new CompositionEffectSourceParameter("source") - }; - SaturationEffect saturationEffect = new SaturationEffect - { - Name = "SEffect", - Saturation = initiallyVisible ? offSaturation : onSaturation, - Source = blurEffect - }; - const string blurParameter = "Blur.BlurAmount", saturationParameter = "SEffect.Saturation"; - CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(saturationEffect, new[] - { - blurParameter, - saturationParameter - }); - - // Setup the rest of the effect - CompositionEffectBrush effectBrush = effectFactory.CreateBrush(); - effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush()); - - // Assign the effect to a brush and display it - SpriteVisual sprite = compositor.CreateSpriteVisual(); - sprite.Brush = effectBrush; - await AddToTreeAndBindSizeAsync(visual, element, sprite); - if (initiallyVisible) await element.Dispatcher.RunAsync(() => element.Opacity = 1); - - // Prepare and return the wrapped effect - return new AttachedCompositeAnimatableCompositionEffect(element, sprite, - new Dictionary - { - { blurParameter, new CompositionAnimationValueParameters(onBlur, offBlur) }, - { saturationParameter, new CompositionAnimationValueParameters(onSaturation, offSaturation) } - }, disposeOnUnload); - } - - #endregion - - #region Tools - - /// - /// Adds a object on top of the target and binds the size of the two items with an expression animation - /// - /// The object that will host the effect - /// The target (bound to the given visual) that will host the effect - /// The source object to display - private static async Task AddToTreeAndBindSizeAsync([NotNull] Visual host, [NotNull] UIElement element, [NotNull] Visual visual) - { - // Add the shadow as a child of the host in the visual tree - await element.Dispatcher.RunAsync(() => ElementCompositionPreview.SetElementChildVisual(element, visual)); - - // Make sure size of shadow host and shadow visual always stay in sync - ExpressionAnimation bindSizeAnimation = host.Compositor.CreateExpressionAnimation($"{nameof(host)}.Size"); - bindSizeAnimation.SetReferenceParameter(nameof(host), host); - - // Start the animation - visual.StartAnimation("Size", bindSizeAnimation); - } - - #endregion - } -} \ No newline at end of file diff --git a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs index 83af509..87f0069 100644 --- a/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs +++ b/UICompositionAnimations/Behaviours/CompositionBrushBuilder.cs @@ -7,6 +7,7 @@ using Windows.UI; using Windows.UI.Composition; using Windows.UI.Xaml; +using Windows.UI.Xaml.Hosting; using JetBrains.Annotations; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Effects; @@ -666,6 +667,21 @@ public async Task BuildAsync() return effectBrush; } + /// + /// Builds the current pipeline and creates a that is applied to the input + /// + /// The target to apply the brush to + /// An optional to use to bind the size of the created brush + [ItemNotNull] + public async Task AttachAsync([NotNull] UIElement target, [CanBeNull] UIElement reference = null) + { + SpriteVisual visual = Window.Current.Compositor.CreateSpriteVisual(); + visual.Brush = await BuildAsync(); + ElementCompositionPreview.SetElementChildVisual(target, visual); + if (reference != null) visual.BindSize(reference); + return visual; + } + /// /// Creates a new from the current effects pipeline /// diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index 42cfcd9..bd0d3eb 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -1735,7 +1735,7 @@ public static SpriteVisual AttachVisualShadow( /// The target object /// The name of the property to animate /// The final value of the property - public static void SetInstantValue(this CompositionObject compObject, string property, float value) + public static void SetInstantValue([NotNull] this CompositionObject compObject, string property, float value) { // Stop previous animations compObject.StopAnimation(property); @@ -1754,7 +1754,7 @@ public static void SetInstantValue(this CompositionObject compObject, string pro /// The name of the property to animate /// The final value of the property /// The animation duration - public static Task StartAnimationAsync(this CompositionObject compObject, string property, float value, TimeSpan duration) + public static Task StartAnimationAsync([NotNull] this CompositionObject compObject, string property, float value, TimeSpan duration) { // Stop previous animations compObject.StopAnimation(property); @@ -1778,9 +1778,9 @@ public static Task StartAnimationAsync(this CompositionObject compObject, string /// /// The target element /// The names of the animations to stop - public static void StopAnimations(this UIElement element, params string[] properties) + public static void StopAnimations([NotNull] this UIElement element, [NotNull, ItemNotNull] params string[] properties) { - if (properties == null || properties.Length == 0) return; + if (properties.Length == 0) return; Visual visual = element.GetVisual(); foreach (string property in properties) visual.StopAnimation(property); } @@ -1792,7 +1792,7 @@ public static void StopAnimations(this UIElement element, params string[] proper /// The X value of the scale property /// The Y value of the scale property /// The Z value of the scale property - public static void SetVisualScale(this UIElement element, float? x, float? y, float? z) + 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(); @@ -1815,7 +1815,7 @@ public static void SetVisualScale(this UIElement element, float? x, float? y, fl /// The X value of the scale property /// The Y value of the scale property /// The Z value of the scale property - public static async Task SetVisualScaleAsync(this FrameworkElement element, float? x, float? y, float? z) + public static async Task SetVisualScaleAsync([NotNull] this FrameworkElement element, float? x, float? y, float? z) { // Get the default values and set the CenterPoint Visual visual = element.GetVisual(); @@ -1838,7 +1838,7 @@ public static async Task SetVisualScaleAsync(this FrameworkElement element, floa /// The target element /// The offset axis to edit /// The final offset value to set for that axis - public static void SetVisualOffset(this UIElement element, TranslationAxis axis, float offset) + public static void SetVisualOffset([NotNull] this UIElement element, TranslationAxis axis, float offset) { // Get the element visual and stop the animation Visual visual = element.GetVisual(); @@ -1856,7 +1856,7 @@ public static void SetVisualOffset(this UIElement element, TranslationAxis axis, /// The target element /// The x offset /// The y offset - public static void SetVisualOffset(this UIElement element, float x, float y) + public static void SetVisualOffset([NotNull] this UIElement element, float x, float y) { // Get the element visual and stop the animation Visual visual = element.GetVisual(); @@ -1872,7 +1872,7 @@ public static void SetVisualOffset(this UIElement element, float x, float y) /// The to edit /// The offset axis to set /// The new value for the axis to set - public static Task SetVisualOffsetAsync(this UIElement element, TranslationAxis axis, float value) + public static Task SetVisualOffsetAsync([NotNull] this UIElement element, TranslationAxis axis, float value) { Visual visual = element.GetVisual(); return Task.Run(() => @@ -1890,7 +1890,7 @@ public static Task SetVisualOffsetAsync(this UIElement element, TranslationAxis /// The to edit /// The x offset /// The y offset - public static Task SetVisualOffsetAsync(this UIElement element, float x, float y) + public static Task SetVisualOffsetAsync([NotNull] this UIElement element, float x, float y) { Visual visual = element.GetVisual(); return Task.Run(() => @@ -1904,7 +1904,7 @@ public static Task SetVisualOffsetAsync(this UIElement element, float x, float y /// Returns the visual offset for a target /// /// The input element - public static Vector3 GetVisualOffset(this UIElement element) => element.GetVisual().Offset; + public static Vector3 GetVisualOffset([NotNull] this UIElement element) => element.GetVisual().Offset; /// /// Sets the translation property of the visual object for a given object @@ -1912,7 +1912,7 @@ public static Task SetVisualOffsetAsync(this UIElement element, float x, float y /// The target element /// The translation axis to edit /// The final translation value to set for that axis - public static void SetVisualTranslation(this UIElement element, TranslationAxis axis, float translation) + public static void SetVisualTranslation([NotNull] this UIElement element, TranslationAxis axis, float translation) { // Get the element visual and stop the animation Visual visual = element.GetVisual(); @@ -1940,7 +1940,7 @@ public static void SetVisualTranslation(this UIElement element, TranslationAxis /// The target element /// The x translation /// The y translation - public static void SetVisualTranslation(this UIElement element, float x, float y) + public static void SetVisualTranslation([NotNull] this UIElement element, float x, float y) { // Get the element visual and stop the animation Visual visual = element.GetVisual(); @@ -1954,7 +1954,7 @@ public static void SetVisualTranslation(this UIElement element, float x, float y /// /// The target element /// The desired clip margins to set - public static void SetVisualClip(this UIElement element, Thickness clip) + public static void SetVisualClip([NotNull] this UIElement element, Thickness clip) { // Get the element visual Visual visual = element.GetVisual(); @@ -1974,7 +1974,7 @@ public static void SetVisualClip(this UIElement element, Thickness clip) /// The target element /// The desired clip value to set /// The target clip side to update - public static void SetVisualClip(this UIElement element, float clip, MarginSide side) + public static void SetVisualClip([NotNull] this UIElement element, float clip, MarginSide side) { // Get the element visual Visual visual = element.GetVisual(); @@ -1995,7 +1995,7 @@ public static void SetVisualClip(this UIElement element, float clip, MarginSide /// Resets the scale, offset and opacity properties for a framework element /// /// The element to edit - public static async Task ResetCompositionVisualPropertiesAsync(this FrameworkElement element) + public static async Task ResetCompositionVisualPropertiesAsync([NotNull] this FrameworkElement element) { // Get the default values and set the CenterPoint Visual visual = element.GetVisual(); @@ -2014,20 +2014,20 @@ public static async Task ResetCompositionVisualPropertiesAsync(this FrameworkEle /// Gets the opacity for the Visual object behind a given UIElement /// /// The source UIElement - public static float GetVisualOpacity(this UIElement element) => element.GetVisual().Opacity; + public static float GetVisualOpacity([NotNull] this UIElement element) => element.GetVisual().Opacity; /// /// Sets the opacity for the Visual object behind a given UIElement /// /// The source UIElement /// The new opacity value - public static void SetVisualOpacity(this UIElement element, float value) => element.GetVisual().Opacity = value; + public static void SetVisualOpacity([NotNull] this UIElement element, float value) => element.GetVisual().Opacity = value; /// /// Returns the Visual object for a given UIElement /// /// The source UIElement - public static Visual GetVisual(this UIElement element) => ElementCompositionPreview.GetElementVisual(element); + public static Visual GetVisual([NotNull] this UIElement element) => ElementCompositionPreview.GetElementVisual(element); /// /// Adds a instance on top of the target and binds the size of the two items with an expression animation @@ -2047,11 +2047,11 @@ public static void AttachToElement([NotNull] this CompositionBrush brush, [NotNu } /// - /// Starts an expression animation to keep the size of the source in sync with the target + /// Starts an expression animation to keep the size of the source in sync with the target /// /// The composition object to start the animation on - /// The target to read the size updates from - public static void BindSize([NotNull] this CompositionObject source, [NotNull] FrameworkElement target) + /// 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"); diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 5497fe8..5133515 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -117,7 +117,6 @@ latest - From 8a711fa30851f96a464b77b967b05d69da710864 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 4 Sep 2018 15:41:33 +0200 Subject: [PATCH 42/47] Info updated --- UICompositionAnimations/Properties/AssemblyInfo.cs | 5 +++-- UICompositionAnimations/UICompositionAnimations.csproj | 3 +++ UICompositionAnimations/UICompositionAnimations.nuspec | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index b02c744..1c5e5d6 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Sergio Pedri")] [assembly: AssemblyProduct("UICompositionAnimations")] -[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -26,4 +26,5 @@ [assembly: AssemblyVersion("3.0.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")] [assembly: AssemblyInformationalVersion("3.0.0-beta4")] -[assembly: ComVisible(false)] \ No newline at end of file +[assembly: ComVisible(false)] + diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 5133515..7b4f10a 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -52,6 +52,7 @@ false prompt latest + bin\x86\Debug\UICompositionAnimations.XML x86 @@ -77,6 +78,7 @@ false prompt latest + bin\ARM\Debug\UICompositionAnimations.XML ARM @@ -102,6 +104,7 @@ false prompt latest + bin\x64\Debug\UICompositionAnimations.XML x64 diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index d62ca9a..5b78920 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,7 +2,7 @@ UICompositionAnimations - 3.0.0-beta4 + 3.0.0-beta7 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri @@ -11,7 +11,7 @@ https://github.com/Sergio0694/UICompositionAnimations/blob/master/LICENSE.md false Retargeted to support SDK 17134, bug fixes and code improvements - Copyright 2017 + Copyright © 2018 uwp composition animations xaml csharp windows winrt universal app ui win2d graphics \ No newline at end of file From 853d7d9dd39b0e863fb82b17bdd7df59323834eb Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 4 Sep 2018 18:24:33 +0200 Subject: [PATCH 43/47] Fixed NuGet package references (workaround) --- UICompositionAnimations/Properties/AssemblyInfo.cs | 2 +- UICompositionAnimations/UICompositionAnimations.nuspec | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index 1c5e5d6..fd8f970 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("3.0.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")] -[assembly: AssemblyInformationalVersion("3.0.0-beta4")] +[assembly: AssemblyInformationalVersion("3.0.0-beta8")] [assembly: ComVisible(false)] diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index 5b78920..b0a918b 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,7 +2,7 @@ UICompositionAnimations - 3.0.0-beta7 + 3.0.0-beta8 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri @@ -13,5 +13,10 @@ Retargeted to support SDK 17134, bug fixes and code improvements Copyright © 2018 uwp composition animations xaml csharp windows winrt universal app ui win2d graphics + + + + + \ No newline at end of file From c4369fa3b5bbba72e91afc8529faa867ec04f5f0 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 11 Sep 2018 00:28:13 +0200 Subject: [PATCH 44/47] Minor bug fixes --- .../Cache/ThreadSafeCompositionCache.cs | 5 ++-- .../Cache/ThreadSafeCompositionMapCache.cs | 5 ++-- .../CompositionExtensions.cs | 23 +++++++++++++++++++ .../Helpers/Win2DImageHelper.cs | 3 +-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs index cd54952..9c4f371 100644 --- a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs +++ b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionCache.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Windows.UI.Composition; +using Windows.UI.Core; using JetBrains.Annotations; namespace UICompositionAnimations.Brushes.Cache @@ -35,7 +36,7 @@ public async Task TryGetInstanceAsync([NotNull] Func factory) { // Try to retrieve an valid instance from the cache foreach (WeakReference value in Cache) - if (value.TryGetTarget(out T instance) && instance.Dispatcher.HasThreadAccess) + if (value.TryGetTarget(out T instance) && instance.TryGetDispatcher(out CoreDispatcher dispatcher) && dispatcher.HasThreadAccess) return instance; // Create a new instance when needed @@ -51,7 +52,7 @@ public async Task TryGetInstanceAsync([NotNull] Func factory) public async void Cleanup() { using (await Mutex.LockAsync()) - Cache.RemoveAll(reference => !reference.TryGetTarget(out _)); + Cache.RemoveAll(reference => !reference.TryGetTarget(out T target) || !target.TryGetDispatcher(out _)); } } } diff --git a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs index b2be7c8..ec6e7c5 100644 --- a/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs +++ b/UICompositionAnimations/Brushes/Cache/ThreadSafeCompositionMapCache.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Windows.UI.Composition; +using Windows.UI.Core; using JetBrains.Annotations; namespace UICompositionAnimations.Brushes.Cache @@ -30,7 +31,7 @@ public bool TryGetInstance(TKey key, out TValue result) // Try to retrieve an valid instance from the cache if (Cache.TryGetValue(key, out List> values)) foreach (WeakReference value in values) - if (value.TryGetTarget(out TValue instance) && instance.Dispatcher.HasThreadAccess) + if (value.TryGetTarget(out TValue instance) && instance.TryGetDispatcher(out CoreDispatcher dispatcher) && dispatcher.HasThreadAccess) { result = instance; return true; @@ -69,7 +70,7 @@ public void Overwrite(TKey key, TValue value) public void Cleanup() { foreach (List> list in Cache.Values) - list.RemoveAll(reference => !reference.TryGetTarget(out _)); + list.RemoveAll(reference => !reference.TryGetTarget(out TValue value) || !value.TryGetDispatcher(out _)); foreach (TKey key in Cache.Keys.ToArray()) if (Cache[key].Count == 0) Cache.Remove(key); diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index bd0d3eb..b5197a5 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -3,9 +3,11 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; using System.Numerics; +using System.Runtime.CompilerServices; 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; @@ -2061,6 +2063,27 @@ public static void BindSize([NotNull] this CompositionObject source, [NotNull] U 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/Helpers/Win2DImageHelper.cs b/UICompositionAnimations/Helpers/Win2DImageHelper.cs index 9b0bcb1..1686632 100644 --- a/UICompositionAnimations/Helpers/Win2DImageHelper.cs +++ b/UICompositionAnimations/Helpers/Win2DImageHelper.cs @@ -124,8 +124,7 @@ private static async Task LoadSurfaceBrushAsync([NotNul // Get the device and the target surface CompositionGraphicsDevice device = CanvasComposition.CreateCompositionGraphicsDevice(compositor, canvasDevice); - CompositionDrawingSurface surface = device.CreateDrawingSurface(default(Size), - DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); + CompositionDrawingSurface surface = device.CreateDrawingSurface(default, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); // Calculate the surface size Size From cee39578e0cd8e14e19dac76292c87f790110d2e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Tue, 11 Sep 2018 00:45:38 +0200 Subject: [PATCH 45/47] Code refactoring, removed unused APIs --- .../AttachedAnimatableCompositionEffect.cs | 42 ----------- ...hedCompositeAnimatableCompositionEffect.cs | 48 ------------ .../AttachedStaticCompositionEffect.cs | 74 ------------------- .../Effects/AttachedToggleAcrylicEffect.cs | 61 --------------- ...AttachedAnimatableCompositionEffectBase.cs | 27 ------- .../Misc/CompositionAnimationParameters.cs | 54 -------------- .../Behaviours/Misc/FixedAnimationType.cs | 18 ----- .../Brushes/CustomAcrylicBrush.cs | 1 - .../CompositionExtensions.cs | 3 +- .../Effects => Enums}/AcrylicEffectMode.cs | 2 +- .../Enums/BitmapCacheMode.cs | 5 +- .../Enums/ImplicitAnimationType.cs | 11 ++- .../Enums/TranslationAxis.cs | 11 ++- .../Helpers/DispatcherHelper.cs | 16 +--- UICompositionAnimations/Helpers/Extensions.cs | 8 -- .../Lights/PointerPositionSpotLight.cs | 14 ++-- .../UICompositionAnimations.csproj | 10 +-- 17 files changed, 36 insertions(+), 369 deletions(-) delete mode 100644 UICompositionAnimations/Behaviours/Effects/AttachedAnimatableCompositionEffect.cs delete mode 100644 UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs delete mode 100644 UICompositionAnimations/Behaviours/Effects/AttachedStaticCompositionEffect.cs delete mode 100644 UICompositionAnimations/Behaviours/Effects/AttachedToggleAcrylicEffect.cs delete mode 100644 UICompositionAnimations/Behaviours/Effects/Base/AttachedAnimatableCompositionEffectBase.cs delete mode 100644 UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs delete mode 100644 UICompositionAnimations/Behaviours/Misc/FixedAnimationType.cs rename UICompositionAnimations/{Behaviours/Effects => Enums}/AcrylicEffectMode.cs (89%) diff --git a/UICompositionAnimations/Behaviours/Effects/AttachedAnimatableCompositionEffect.cs b/UICompositionAnimations/Behaviours/Effects/AttachedAnimatableCompositionEffect.cs deleted file mode 100644 index 1c9bac6..0000000 --- a/UICompositionAnimations/Behaviours/Effects/AttachedAnimatableCompositionEffect.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Threading.Tasks; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using JetBrains.Annotations; -using UICompositionAnimations.Behaviours.Effects.Base; -using UICompositionAnimations.Behaviours.Misc; - -namespace UICompositionAnimations.Behaviours.Effects -{ - /// - /// An attached composition effect that supports a single in/out animation - /// - /// The type of the visual element the effect will be applied to - public sealed class AttachedAnimatableCompositionEffect : AttachedAnimatableCompositionEffectBase where T : FrameworkElement - { - // The animation parameters - private readonly CompositionAnimationParameters Parameters; - - // Internal constructor - internal AttachedAnimatableCompositionEffect( - [NotNull] T element, [NotNull] SpriteVisual sprite, - [NotNull] CompositionAnimationParameters parameters, bool disposeOnUnload) : base(element, sprite, disposeOnUnload) - { - Parameters = parameters; - } - - /// - protected override void DisposeCore() - { - EffectBrush.StopAnimation(Parameters.Property); - base.DisposeCore(); - } - - /// - public override Task AnimateAsync(FixedAnimationType animationType, TimeSpan duration) - { - return EffectBrush.StartAnimationAsync(Parameters.Property, - animationType == FixedAnimationType.In ? Parameters.On : Parameters.Off, duration); - } - } -} diff --git a/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs b/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs deleted file mode 100644 index 737d246..0000000 --- a/UICompositionAnimations/Behaviours/Effects/AttachedCompositeAnimatableCompositionEffect.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using JetBrains.Annotations; -using UICompositionAnimations.Behaviours.Effects.Base; -using UICompositionAnimations.Behaviours.Misc; - -namespace UICompositionAnimations.Behaviours.Effects -{ - /// - /// An attached composition effect that supports multiple in/out animations - /// - /// The type of the visual element the effects will be applied to - public sealed class AttachedCompositeAnimatableCompositionEffect : AttachedAnimatableCompositionEffectBase where T : FrameworkElement - { - // Private animations parameters - [NotNull] - private readonly IDictionary PropertiesAnimationValues; - - // Internal constructor - internal AttachedCompositeAnimatableCompositionEffect( - [NotNull] T element, [NotNull] SpriteVisual sprite, - [NotNull] IDictionary propertyValues, bool disposeOnUnload) : base(element, sprite, disposeOnUnload) - { - PropertiesAnimationValues = propertyValues; - } - - /// - protected override void DisposeCore() - { - foreach (string key in PropertiesAnimationValues.Keys) EffectBrush.StopAnimation(key); - base.DisposeCore(); - } - - /// - public override Task AnimateAsync(FixedAnimationType animationType, TimeSpan duration) - { - // Apply all the animations in parallel and wait for their completion - return Task.WhenAll( - from pair in PropertiesAnimationValues - let target = animationType == FixedAnimationType.In ? pair.Value.On : pair.Value.Off - select EffectBrush.StartAnimationAsync(pair.Key, target, duration)); - } - } -} \ No newline at end of file diff --git a/UICompositionAnimations/Behaviours/Effects/AttachedStaticCompositionEffect.cs b/UICompositionAnimations/Behaviours/Effects/AttachedStaticCompositionEffect.cs deleted file mode 100644 index 49a2f48..0000000 --- a/UICompositionAnimations/Behaviours/Effects/AttachedStaticCompositionEffect.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Hosting; -using JetBrains.Annotations; -using UICompositionAnimations.Helpers; - -namespace UICompositionAnimations.Behaviours.Effects -{ - /// - /// An base class for an attached composition effect - /// - /// Tye type of the target element - public class AttachedStaticCompositionEffect : IDisposable where T : FrameworkElement - { - /// - /// Gets the element used to apply the blur effect - /// - [NotNull] - public T Element { get; } - - /// - /// Gets the actual blur sprite shown above the visual element - /// - [NotNull] - public SpriteVisual Sprite { get; } - - /// - /// Gets the composition effect brush applied to the visual element - /// - [NotNull] - public CompositionEffectBrush EffectBrush => Sprite.Brush.To(); - - // Internal constructor - internal AttachedStaticCompositionEffect([NotNull] T element, [NotNull] SpriteVisual sprite, bool disposeOnUnload) - { - // Store the parameters - Element = element; - Sprite = sprite; - if (disposeOnUnload) element.Unloaded += (s, e) => Dispose(); - } - - /// - /// Disposes the resources in the current instance - /// - protected virtual void DisposeCore() - { - Sprite.StopAnimation("Size"); - ElementCompositionPreview.SetElementChildVisual(Element, null); - EffectBrush.Dispose(); - Sprite.Dispose(); - } - - // Indicates whether or not the wrapped effect has already been disposed - private bool _Disposed; - - /// - /// Stops the size animation, removes the effect from the visual tree and disposes it - /// - public void Dispose() - { - if (_Disposed) return; - _Disposed = true; - try - { - DisposeCore(); - } - catch - { - // Never trust the framework - } - } - } -} diff --git a/UICompositionAnimations/Behaviours/Effects/AttachedToggleAcrylicEffect.cs b/UICompositionAnimations/Behaviours/Effects/AttachedToggleAcrylicEffect.cs deleted file mode 100644 index b5b8523..0000000 --- a/UICompositionAnimations/Behaviours/Effects/AttachedToggleAcrylicEffect.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using JetBrains.Annotations; - -namespace UICompositionAnimations.Behaviours.Effects -{ - /// - /// A custom acrylic brush effect that can quickly toggle between different sources (in-app blur or host backdrop effect) - /// - /// The the host that will display the effect visual - public sealed class AttachedToggleAcrylicEffect : AttachedStaticCompositionEffect where T : FrameworkElement - { - /// - /// Gets the action that edits the effects pipeline to apply the requested change - /// - [NotNull] - private readonly Action Toggle; - - // An optional fade in animation for the blurred in-app acrylic effect - [CanBeNull] - private readonly Action InitialInAppEffectFadeIn; - - // Internal constructor - internal AttachedToggleAcrylicEffect([NotNull] T element, AcrylicEffectMode mode, [CanBeNull] Action fadeIn, - [NotNull] Action toggle, [NotNull] SpriteVisual sprite, bool disposeOnUnload) - : base(element, sprite, disposeOnUnload) - { - _AcrylicMode = mode; - _FadeInPending = mode == AcrylicEffectMode.HostBackdrop; - InitialInAppEffectFadeIn = fadeIn; - Toggle = toggle; - } - - // Indicates whether or not the fade in animation should be run - private bool _FadeInPending; - - private AcrylicEffectMode _AcrylicMode; - - /// - /// Gets or sets the current effect mode in use - /// - public AcrylicEffectMode AcrylicMode - { - get => _AcrylicMode; - set - { - if (_AcrylicMode != value) - { - Toggle(value); - if (_FadeInPending && InitialInAppEffectFadeIn != null) - { - _FadeInPending = false; - InitialInAppEffectFadeIn(); - } - _AcrylicMode = value; - } - } - } - } -} diff --git a/UICompositionAnimations/Behaviours/Effects/Base/AttachedAnimatableCompositionEffectBase.cs b/UICompositionAnimations/Behaviours/Effects/Base/AttachedAnimatableCompositionEffectBase.cs deleted file mode 100644 index 6ebafe4..0000000 --- a/UICompositionAnimations/Behaviours/Effects/Base/AttachedAnimatableCompositionEffectBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading.Tasks; -using Windows.UI.Composition; -using Windows.UI.Xaml; -using JetBrains.Annotations; -using UICompositionAnimations.Behaviours.Misc; - -namespace UICompositionAnimations.Behaviours.Effects.Base -{ - /// - /// An base class for an attached composition effect that supports a ready to use animation with fixed states - /// - /// Tye type of the target element the animation will be applied to - public abstract class AttachedAnimatableCompositionEffectBase : AttachedStaticCompositionEffect where T : FrameworkElement - { - // Protected constructor for the implementations - internal AttachedAnimatableCompositionEffectBase([NotNull] T element, [NotNull] SpriteVisual sprite, bool disposeOnUnload) - : base(element, sprite, disposeOnUnload) { } - - /// - /// Executes the animation to the desired destination status and returns a task that completes when the animation ends - /// - /// The target animation status - /// The animation duration - public abstract Task AnimateAsync(FixedAnimationType animationType, TimeSpan duration); - } -} \ No newline at end of file diff --git a/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs b/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs deleted file mode 100644 index 584e3b5..0000000 --- a/UICompositionAnimations/Behaviours/Misc/CompositionAnimationParameters.cs +++ /dev/null @@ -1,54 +0,0 @@ -using JetBrains.Annotations; - -namespace UICompositionAnimations.Behaviours.Misc -{ - /// - /// A class that stores the parameters for two different states for a pre-defined composition animation - /// - internal class CompositionAnimationValueParameters - { - /// - /// Gets the target value when the effect is enabled - /// - public float On { get; } - - /// - /// Gets the target value when the effect is disabled - /// - public float Off { get; } - - /// - /// Creates a new instance for the given states - /// - /// The on parameter - /// The off parameter - public CompositionAnimationValueParameters(float on, float off) - { - On = on; - Off = off; - } - } - - /// - /// A class that stores the parameters for two different states for a pre-defined composition animation, along with the target property to animate - /// - internal sealed class CompositionAnimationParameters : CompositionAnimationValueParameters - { - /// - /// Gets the property to animate - /// - [NotNull] - public string Property { get; } - - /// - /// Creates a new instance for the target property and states - /// - /// The target property to animate - /// The on parameter - /// The off parameter - public CompositionAnimationParameters([NotNull] string property, float on, float off) : base(on, off) - { - Property = property; - } - } -} diff --git a/UICompositionAnimations/Behaviours/Misc/FixedAnimationType.cs b/UICompositionAnimations/Behaviours/Misc/FixedAnimationType.cs deleted file mode 100644 index 5d585eb..0000000 --- a/UICompositionAnimations/Behaviours/Misc/FixedAnimationType.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace UICompositionAnimations.Behaviours.Misc -{ - /// - /// Indicates the direction of a ready to use composition animation - /// - public enum FixedAnimationType - { - /// - /// Applies the effect - /// - In, - - /// - /// Reverts the applied effect - /// - Out - } -} \ No newline at end of file diff --git a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs index 3e3ff44..4589b85 100644 --- a/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs +++ b/UICompositionAnimations/Brushes/CustomAcrylicBrush.cs @@ -10,7 +10,6 @@ using Windows.UI.Xaml.Media; using JetBrains.Annotations; using UICompositionAnimations.Behaviours; -using UICompositionAnimations.Behaviours.Effects; using UICompositionAnimations.Brushes.Cache; using UICompositionAnimations.Enums; using UICompositionAnimations.Helpers; diff --git a/UICompositionAnimations/CompositionExtensions.cs b/UICompositionAnimations/CompositionExtensions.cs index b5197a5..c23ffe5 100644 --- a/UICompositionAnimations/CompositionExtensions.cs +++ b/UICompositionAnimations/CompositionExtensions.cs @@ -3,7 +3,6 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; using System.Numerics; -using System.Runtime.CompilerServices; using Windows.Foundation; using Windows.UI; using Windows.UI.Composition; @@ -1525,7 +1524,7 @@ private static Task ManageCompositionClipAnimationAsync([NotNull] Visual visual, int ms, int? msDelay, [NotNull] CompositionEasingFunction easingFunction) { // Setup - InsetClip clip = visual.Clip as InsetClip ?? ((InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip())); + InsetClip clip = visual.Clip as InsetClip ?? (InsetClip)(visual.Clip = visual.Compositor.CreateInsetClip()); string property; switch (side) { diff --git a/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs b/UICompositionAnimations/Enums/AcrylicEffectMode.cs similarity index 89% rename from UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs rename to UICompositionAnimations/Enums/AcrylicEffectMode.cs index d7f3b7d..11b02f1 100644 --- a/UICompositionAnimations/Behaviours/Effects/AcrylicEffectMode.cs +++ b/UICompositionAnimations/Enums/AcrylicEffectMode.cs @@ -1,6 +1,6 @@ using System; -namespace UICompositionAnimations.Behaviours.Effects +namespace UICompositionAnimations.Enums { /// /// Indicates the UI mode for an acrylic effect brush diff --git a/UICompositionAnimations/Enums/BitmapCacheMode.cs b/UICompositionAnimations/Enums/BitmapCacheMode.cs index 8b7f3c7..451a86e 100644 --- a/UICompositionAnimations/Enums/BitmapCacheMode.cs +++ b/UICompositionAnimations/Enums/BitmapCacheMode.cs @@ -1,8 +1,11 @@ -namespace UICompositionAnimations.Enums +using JetBrains.Annotations; + +namespace UICompositionAnimations.Enums { /// /// Indicates the cache mode to use when loading a Win2D image /// + [PublicAPI] public enum BitmapCacheMode { /// diff --git a/UICompositionAnimations/Enums/ImplicitAnimationType.cs b/UICompositionAnimations/Enums/ImplicitAnimationType.cs index 932493a..191a086 100644 --- a/UICompositionAnimations/Enums/ImplicitAnimationType.cs +++ b/UICompositionAnimations/Enums/ImplicitAnimationType.cs @@ -1,12 +1,21 @@ -#pragma warning disable 1591 +using JetBrains.Annotations; + namespace UICompositionAnimations.Enums { /// /// Indicates the type of an implicit composition animation /// + [PublicAPI] public enum ImplicitAnimationType { + /// + /// The animation plays when the item becomes visible + /// Show, + + /// + /// The animation plays when the item is collapsed + /// Hide } } \ No newline at end of file diff --git a/UICompositionAnimations/Enums/TranslationAxis.cs b/UICompositionAnimations/Enums/TranslationAxis.cs index 71d4e94..8c68a6d 100644 --- a/UICompositionAnimations/Enums/TranslationAxis.cs +++ b/UICompositionAnimations/Enums/TranslationAxis.cs @@ -1,12 +1,21 @@ -#pragma warning disable 1591 +using JetBrains.Annotations; + namespace UICompositionAnimations.Enums { /// /// Indicates the translation axis to use in an animation /// + [PublicAPI] public enum TranslationAxis { + /// + /// Horizontal translation + /// X, + + /// + /// Vertical translation + /// Y } } \ No newline at end of file diff --git a/UICompositionAnimations/Helpers/DispatcherHelper.cs b/UICompositionAnimations/Helpers/DispatcherHelper.cs index cd9adf4..02dc43e 100644 --- a/UICompositionAnimations/Helpers/DispatcherHelper.cs +++ b/UICompositionAnimations/Helpers/DispatcherHelper.cs @@ -22,10 +22,7 @@ public static class DispatcherHelper public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Action callback) { if (dispatcher.HasThreadAccess) callback(); - else - { - dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => callback()).Forget(); - } + else dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => callback()).Forget(); } /// @@ -35,11 +32,8 @@ public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Actio /// The action to execute on the UI thread public static void Run([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback) { - // Check the current thread if (dispatcher.HasThreadAccess) asyncCallback(); - - // Schedule on the UI thread if necessary - dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => asyncCallback()).Forget(); + else dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => asyncCallback()).Forget(); } /// @@ -69,10 +63,7 @@ public static async Task RunAsync([NotNull] this CoreDispatcher dispatcher, [Not /// The action to execute on the UI thread public static Task RunAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func asyncCallback) { - // Check the current thread if (dispatcher.HasThreadAccess) return asyncCallback(); - - // Schedule on the UI thread if necessary TaskCompletionSource tcs = new TaskCompletionSource(); dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { @@ -107,10 +98,7 @@ public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [Not /// The async function to execute on the UI thread public static Task GetAsync([NotNull] this CoreDispatcher dispatcher, [NotNull] Func> function) { - // Check the current thread if (dispatcher.HasThreadAccess) return function(); - - // Schedule on the UI thread if necessary TaskCompletionSource> tcs = new TaskCompletionSource>(); dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { diff --git a/UICompositionAnimations/Helpers/Extensions.cs b/UICompositionAnimations/Helpers/Extensions.cs index 7db5bde..f20972f 100644 --- a/UICompositionAnimations/Helpers/Extensions.cs +++ b/UICompositionAnimations/Helpers/Extensions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using Windows.Foundation; using JetBrains.Annotations; @@ -46,13 +45,6 @@ internal static class Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Forget(this IAsyncAction action) { } - /// - /// Suppresses the warnings when calling an async method without awaiting it - /// - /// The task returned by the async call - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Forget(this Task task) { } - /// /// Merges the two input instances and makes sure no duplicate keys are present /// diff --git a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs index e3cef78..82f3363 100644 --- a/UICompositionAnimations/Lights/PointerPositionSpotLight.cs +++ b/UICompositionAnimations/Lights/PointerPositionSpotLight.cs @@ -34,8 +34,7 @@ public byte Shade private static void OnShadePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - PointerPositionSpotLight l = d as PointerPositionSpotLight; - if (l?._Light != null) + if (d is PointerPositionSpotLight @this && @this._Light != null) { byte shade = e.NewValue.To(); Color color = new Color @@ -45,7 +44,7 @@ private static void OnShadePropertyChanged(DependencyObject d, DependencyPropert G = shade, B = shade }; - l._Light.InnerConeColor = l._Light.OuterConeColor = color; + @this._Light.InnerConeColor = @this._Light.OuterConeColor = color; } } @@ -86,10 +85,9 @@ public float OuterConeAngle private static void OuterConeAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - PointerPositionSpotLight l = d as PointerPositionSpotLight; - if (l?._Light != null) + if (d is PointerPositionSpotLight @this && @this._Light != null) { - l._Light.OuterConeAngleInDegrees = (float)e.NewValue; + @this._Light.OuterConeAngleInDegrees = (float)e.NewValue; } } @@ -113,8 +111,8 @@ public bool Active private static void OnActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - PointerPositionSpotLight l = d as PointerPositionSpotLight; - l?._Light?.StartAnimationAsync("ConstantAttenuation", e.NewValue.To() ? 0 : InactiveAttenuationValue, TimeSpan.FromMilliseconds(250)); + if (d is PointerPositionSpotLight @this) + @this._Light?.StartAnimationAsync("ConstantAttenuation", e.NewValue.To() ? 0 : InactiveAttenuationValue, TimeSpan.FromMilliseconds(250)); } #endregion diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index 7b4f10a..d6cff2f 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -123,14 +123,7 @@ - - - - - - - - + @@ -180,6 +173,7 @@ 1.23.0 + 14.0 From 170e7742e628da49d1128fb32ffdeb89ef7d3216 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Nov 2018 20:16:39 +0100 Subject: [PATCH 46/47] NuGet info updated --- UICompositionAnimations/Properties/AssemblyInfo.cs | 2 +- UICompositionAnimations/UICompositionAnimations.csproj | 2 +- UICompositionAnimations/UICompositionAnimations.nuspec | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/UICompositionAnimations/Properties/AssemblyInfo.cs b/UICompositionAnimations/Properties/AssemblyInfo.cs index fd8f970..3eabee3 100644 --- a/UICompositionAnimations/Properties/AssemblyInfo.cs +++ b/UICompositionAnimations/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("3.0.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")] -[assembly: AssemblyInformationalVersion("3.0.0-beta8")] +[assembly: AssemblyInformationalVersion("3.0.0")] [assembly: ComVisible(false)] diff --git a/UICompositionAnimations/UICompositionAnimations.csproj b/UICompositionAnimations/UICompositionAnimations.csproj index d6cff2f..ae2433a 100644 --- a/UICompositionAnimations/UICompositionAnimations.csproj +++ b/UICompositionAnimations/UICompositionAnimations.csproj @@ -167,7 +167,7 @@ 2018.2.1 - 6.1.5 + 6.1.9 1.23.0 diff --git a/UICompositionAnimations/UICompositionAnimations.nuspec b/UICompositionAnimations/UICompositionAnimations.nuspec index b0a918b..d9f3389 100644 --- a/UICompositionAnimations/UICompositionAnimations.nuspec +++ b/UICompositionAnimations/UICompositionAnimations.nuspec @@ -2,7 +2,7 @@ UICompositionAnimations - 3.0.0-beta8 + 3.0.0 UICompositionAnimations A wrapper UWP PCL to work with Windows.UI.Composition and XAML animations, and Win2D effects Sergio Pedri @@ -10,12 +10,12 @@ https://github.com/Sergio0694/UICompositionAnimations https://github.com/Sergio0694/UICompositionAnimations/blob/master/LICENSE.md false - Retargeted to support SDK 17134, bug fixes and code improvements + Retargeted to support SDK 17134, new APIs and code refactoring Copyright © 2018 uwp composition animations xaml csharp windows winrt universal app ui win2d graphics - + From 29cec802ccf39e5c270cb2d69f9e6da497909e09 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Thu, 1 Nov 2018 20:35:40 +0100 Subject: [PATCH 47/47] Update README.md --- README.md | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 0e3ebf5..2401e6d 100644 --- a/README.md +++ b/README.md @@ -90,50 +90,36 @@ The library provides several ways to use `UI.Composition` effects. There are rea ``` -**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 a custom noise texture 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/). +**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# -CompositionBrush brush = await CompositionBrushBuilder.FromHostBackdropBrush() +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, CompositionBrushBuilder.EffectPlacement.Background) - .BuildAsync(); + .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 acrylic brush effect: -```C# -AttachedStaticCompositionEffect attached = await BlurBorder.AttachCompositionInAppCustomAcrylicEffectAsync( - BlurBorder, // The target host control for the effect visual (can be the same as the source) - 8, // The amount of blur to apply - 800, // The milliseconds to initially apply the blur effect with an automatic animation - Color.FromArgb(byte.MaxValue, 0x1B, 0x1B, 0x1B), // The tint overlay color - 0.8f, // The ratio of tint overlay over the source effect (the strength of the tint effect) - null, // Use the default saturation value for the effect (1) - Win2DCanvas, // A CanvasControl in the current visual tree, used to render parts of the acrylic brush - new Uri("ms-appx:///Assets/Misc/noise.png"), // A Uri to a custom noise texture to use to create the effect - BitmapCacheMode.EnableCaching, // The cache mode for the Win2D image to load - false, // Indicates whether to fade the effect it or to display it as soon as possible - true); // Indicates whether or not to automatically dispose the effect when the target `UIElement` is unloaded -``` - -**Note**: in order to remove the effect from the target `UIElement`, it is possible to call the `Dispose` method on the returned `AttachedStaticCompositionEffect` object - calling that method will remove the effect from the object `Visual`. - -#### Get an attached blur effect that can be animated (using composition and Win2D effects): +#### Get a custom effect that can be animated: ```C# -AttachedAnimatableCompositionEffect attached = await MyBorder.AttachCompositionAnimatableBlurEffectAsync( - 14f, // The amount of blur to apply when the effect is enabled - 0f, // The default amount of blur - false); // Indicates whether or not to immediately apply the effect to the target amount +// 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 -await attached.AnimateAsync( - FixedAnimationType.In, // Indicates whether to fade the blur effect in or out - TimeSpan.FromMilliseconds(500)); // The animation duration +saturation(0.2f, 250); // Animate the opacity to 0.2 in 250ms ``` ## Reveal highlight effect