Skip to content

Commit

Permalink
Ported source code for CommunityToolkit.WinUI.Media (#113)
Browse files Browse the repository at this point in the history
* Initial port of CommunityToolkit.WinUI.Media to 8.x

* Unport Geometry

* Add missing file header

* Updated tooling submodule, suppress CS0067

* Removed RadialGradientBrush

* Update submodule, testing hotfixes

* Update tooling to latest main

* Fixed null handling

* Fixed version number

* Use parameter validation for FontIconExtension

* Add ThrowIfNull polyfill to extensions

* Move internal ArgumentNullExceptionExtensions from Animations to Extensions

* Expose internals to CommunityToolkit.WinUI.Media
  • Loading branch information
Arlodotexe authored Jul 13, 2023
1 parent 34e547b commit 90e6909
Show file tree
Hide file tree
Showing 88 changed files with 6,022 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if WINAPPSDK
#if WINUI3
using Microsoft.UI.Composition;
#else
#elif WINUI2
using Windows.UI.Composition;
#endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="CommunityToolkit.WinUI.Behaviors.Animations" />
<InternalsVisibleTo Include="CommunityToolkit.WinUI.Media" />
<InternalsVisibleTo Include="CommunityToolkit.WinUI.Behaviors" />

<PackageReference Include="PolySharp" Version="1.13.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,13 @@ public IList<IKeyFrame<TKeyFrame>> KeyFrames
/// <summary>
/// Gets the explicit target for the animation. This is the primary target property that is animated.
/// </summary>
protected abstract string ExplicitTarget { get; }
protected abstract string? ExplicitTarget { get; }

/// <inheritdoc/>
public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint)
{
default(ArgumentNullException).ThrowIfNull(ExplicitTarget);

return builder.NormalizedKeyFrames<TKeyFrame, (Animation<TValue, TKeyFrame> This, EasingType? EasingTypeHint, EasingMode? EasingModeHint)>(
property: ExplicitTarget,
state: (this, easingTypeHint, easingModeHint),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ public abstract class CustomAnimation<TValue, TKeyFrame> : ImplicitAnimation<TVa
#endif

/// <inheritdoc/>
protected override string ExplicitTarget => Target!;
protected override string? ExplicitTarget => Target;

/// <inheritdoc/>
public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint)
{
default(ArgumentNullException).ThrowIfNull(ExplicitTarget);

return builder.NormalizedKeyFrames<TKeyFrame, (CustomAnimation<TValue, TKeyFrame> This, EasingType? EasingTypeHint, EasingMode? EasingModeHint)>(
property: ExplicitTarget,
state: (this, easingTypeHint, easingModeHint),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ protected ImplicitAnimation()
/// <inheritdoc/>
public CompositionAnimation GetAnimation(UIElement element, out string? target)
{
default(ArgumentNullException).ThrowIfNull(ExplicitTarget);

NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition builder = new(
ExplicitTarget,
Delay ?? DefaultDelay,
Expand Down
4 changes: 1 addition & 3 deletions components/Animations/src/Xaml/Default/ClipAnimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ namespace CommunityToolkit.WinUI.Animations;
public sealed class ClipAnimation : Animation<Thickness?, Thickness>
{
/// <inheritdoc/>
#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations
protected override string ExplicitTarget => throw new NotImplementedException();
#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations
protected override string? ExplicitTarget => null;

/// <inheritdoc/>
public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint)
Expand Down
65 changes: 65 additions & 0 deletions components/Extensions/src/ArgumentNullExceptionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace System;

/// <summary>
/// Throw helper extensions for <see cref="ArgumentNullException"/>.
/// </summary>
internal static class ArgumentNullExceptionExtensions
{
/// <summary>
/// Throws an <see cref="ArgumentNullException"/> for a given parameter name.
/// </summary>
/// <param name="_">Dummy value to invoke the extension upon (always pass <see langword="null"/>.</param>
/// <param name="parameterName">The name of the parameter to report in the exception.</param>
/// <exception cref="ArgumentNullException">Thrown with <paramref name="parameterName"/>.</exception>
[DoesNotReturn]
public static void Throw(this ArgumentNullException? _, string? parameterName)
{
throw new ArgumentNullException(parameterName);
}

/// <summary>
/// Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is <see langword="null"/>.
/// </summary>
/// <param name="_">Dummy value to invoke the extension upon (always pass <see langword="null"/>.</param>
/// <param name="argument">The reference type argument to validate as non-<see langword="null"/>.</param>
/// <param name="parameterName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="argument"/> is <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ThrowIfNull(this ArgumentNullException? _, [NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? parameterName = null)
{
if (argument is null)
{
Throw(parameterName);
}
}

/// <summary>
/// Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is <see langword="null"/>.
/// </summary>
/// <param name="_">Dummy value to invoke the extension upon (always pass <see langword="null"/>.</param>
/// <param name="argument">The pointer argument to validate as non-<see langword="null"/>.</param>
/// <param name="parameterName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="argument"/> is <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void ThrowIfNull(this ArgumentNullException? _, [NotNull] void* argument, [CallerArgumentExpression(nameof(argument))] string? parameterName = null)
{
if (argument is null)
{
Throw(parameterName);
}
}

/// <inheritdoc cref="Throw(ArgumentNullException?, string?)"/>
[DoesNotReturn]
private static void Throw(string? parameterName)
{
throw new ArgumentNullException(parameterName);
}
}
10 changes: 10 additions & 0 deletions components/Extensions/src/CommunityToolkit.WinUI.Extensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@

<!-- Rns suffix is required for namespaces shared across projects. See https://github.com/CommunityToolkit/Labs-Windows/issues/152 -->
<RootNamespace>CommunityToolkit.WinUI.ExtensionsRns</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<!-- Sets this up as a toolkit component's source project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SourceProject.props" />

<ItemGroup>
<PackageReference Include="CommunityToolkit.Common" Version="8.1.0" />

<PackageReference Include="PolySharp" Version="1.13.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

<InternalsVisibleTo Include="CommunityToolkit.WinUI.Animations" />
<InternalsVisibleTo Include="CommunityToolkit.WinUI.Media" />
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions components/Extensions/src/Markup/FontIconExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class FontIconExtension : TextIconExtension
/// <inheritdoc/>
protected override object ProvideValue()
{
default(ArgumentNullException).ThrowIfNull(Glyph);

var fontIcon = new FontIcon
{
Glyph = Glyph,
Expand Down
3 changes: 3 additions & 0 deletions components/Media/OpenSolution.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@ECHO OFF

powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %*
Binary file added components/Media/samples/Assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions components/Media/samples/Dependencies.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!--
WinUI 2 under UWP uses TargetFramework uap10.0.*
WinUI 3 under WinAppSdk uses TargetFramework net6.0-windows10.*
However, under Uno-powered platforms, both WinUI 2 and 3 can share the same TargetFramework.
MSBuild doesn't play nicely with this out of the box, so we've made it easy for you.
For .NET Standard packages, you can use the Nuget Package Manager in Visual Studio.
For UWP / WinAppSDK / Uno packages, place the package references here.
-->
<Project>
<!-- WinUI 2 / UWP -->
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>

<!-- WinUI 2 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
</ItemGroup>

<!-- WinUI 3 / WinAppSdk -->
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>

<!-- WinUI 3 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
</ItemGroup>
</Project>
8 changes: 8 additions & 0 deletions components/Media/samples/Media.Samples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23">
<PropertyGroup>
<ToolkitComponentName>Media</ToolkitComponentName>
</PropertyGroup>

<!-- Sets this up as a toolkit component's sample project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
</Project>
13 changes: 13 additions & 0 deletions components/Media/src/AdditionalAssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Runtime.CompilerServices;

// These `InternalsVisibleTo` calls are intended to make it easier for
// for any internal code to be testable in all the different test projects
// used with the Labs infrastructure.
[assembly: InternalsVisibleTo("Media.Tests.Uwp")]
[assembly: InternalsVisibleTo("Media.Tests.WinAppSdk")]
[assembly: InternalsVisibleTo("CommunityToolkit.Tests.Uwp")]
[assembly: InternalsVisibleTo("CommunityToolkit.Tests.WinAppSdk")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Media;
using static CommunityToolkit.WinUI.Animations.AnimationExtensions;

#if WINUI2
using Windows.UI.Composition;
#elif WINUI3
using Microsoft.UI.Composition;
#endif

namespace CommunityToolkit.WinUI.Animations;

/// <summary>
/// A custom animation targeting a property on an <see cref="IPipelineEffect"/> instance.
/// </summary>
/// <typeparam name="TEffect">The type of effect to animate.</typeparam>
/// <typeparam name="TValue">
/// The type to use for the public <see cref="Animation{TValue,TKeyFrame}.To"/> and <see cref="Animation{TValue,TKeyFrame}.From"/>
/// properties. This can differ from <typeparamref name="TKeyFrame"/> to facilitate XAML parsing.
/// </typeparam>
/// <typeparam name="TKeyFrame">The actual type of keyframe values in use.</typeparam>
public abstract class EffectAnimation<TEffect, TValue, TKeyFrame> : Animation<TValue, TKeyFrame>
where TEffect : class, IPipelineEffect
where TKeyFrame : unmanaged
{
/// <summary>
/// Gets or sets the linked <typeparamref name="TEffect"/> instance to animate.
/// </summary>
public TEffect? Target
{
get => (TEffect?)GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
}

/// <summary>
/// Identifies the <seealso cref="Target"/> dependency property.
/// </summary>
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
nameof(Target),
typeof(TEffect),
typeof(EffectAnimation<TEffect, TValue, TKeyFrame>),
new PropertyMetadata(null));

/// <inheritdoc/>
public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeSpan? delayHint, TimeSpan? durationHint, EasingType? easingTypeHint, EasingMode? easingModeHint)
{
if (Target is not TEffect target)
{
static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target effect is null, make sure to set the Target property");

return ThrowArgumentNullException();
}

if (ExplicitTarget is not string explicitTarget)
{
static AnimationBuilder ThrowArgumentNullException()
{
throw new ArgumentNullException(
"The target effect cannot be animated at this time. If you're targeting one of the " +
"built-in effects, make sure that the PipelineEffect.IsAnimatable property is set to true.");
}

return ThrowArgumentNullException();
}

NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition keyFrameBuilder = new(
explicitTarget,
Delay ?? delayHint ?? DefaultDelay,
Duration ?? durationHint ?? DefaultDuration,
Repeat,
DelayBehavior);

AppendToBuilder(keyFrameBuilder, easingTypeHint, easingModeHint);

CompositionAnimation animation = keyFrameBuilder.GetAnimation(target.Brush!, out _);

return builder.ExternalAnimation(target.Brush!, animation);
}
}
22 changes: 22 additions & 0 deletions components/Media/src/Animations/BlurEffectAnimation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Media;

namespace CommunityToolkit.WinUI.Animations;

/// <summary>
/// An effect animation that targets <see cref="BlurEffect.Amount"/>.
/// </summary>
public sealed class BlurEffectAnimation : EffectAnimation<BlurEffect, double?, double>
{
/// <inheritdoc/>
protected override string? ExplicitTarget => Target?.Id;

/// <inheritdoc/>
protected override (double?, double?) GetParsedValues()
{
return (To, From);
}
}
23 changes: 23 additions & 0 deletions components/Media/src/Animations/ColorEffectAnimation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Media;
using Windows.UI;

namespace CommunityToolkit.WinUI.Animations;

/// <summary>
/// An effect animation that targets <see cref="TintEffect.Color"/>.
/// </summary>
public sealed class ColorEffectAnimation : EffectAnimation<TintEffect, Color?, Color>
{
/// <inheritdoc/>
protected override string? ExplicitTarget => Target?.Id;

/// <inheritdoc/>
protected override (Color?, Color?) GetParsedValues()
{
return (To, From);
}
}
22 changes: 22 additions & 0 deletions components/Media/src/Animations/CrossFadeEffectAnimation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Media;

namespace CommunityToolkit.WinUI.Animations;

/// <summary>
/// An effect animation that targets <see cref="CrossFadeEffect.Factor"/>.
/// </summary>
public sealed class CrossFadeEffectAnimation : EffectAnimation<CrossFadeEffect, double?, double>
{
/// <inheritdoc/>
protected override string? ExplicitTarget => Target?.Id;

/// <inheritdoc/>
protected override (double?, double?) GetParsedValues()
{
return (To, From);
}
}
Loading

0 comments on commit 90e6909

Please sign in to comment.