diff --git a/.gitignore b/.gitignore
index 5539efd0..b3f7e22f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -206,6 +206,9 @@ FakesAssemblies/
# Visual Studio 6 workspace options file
*.opt
+# Visual Studio editor config file
+.editorconfig
+
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
@@ -234,4 +237,6 @@ msbuild.binlog
LottieGenOutput-*/
# Visual Studio debugging configuration file.
-launchSettings.json
\ No newline at end of file
+launchSettings.json
+
+Generated Files/
\ No newline at end of file
diff --git a/Lottie-Windows-WinUI3-Controls/Lottie-Windows-WinUI3-Controls.csproj b/Lottie-Windows-WinUI3-Controls/Lottie-Windows-WinUI3-Controls.csproj
new file mode 100644
index 00000000..a1b006a4
--- /dev/null
+++ b/Lottie-Windows-WinUI3-Controls/Lottie-Windows-WinUI3-Controls.csproj
@@ -0,0 +1,24 @@
+
+
+ net7.0-windows10.0.19041.0
+ Library
+ true
+ CommunityToolkit.WinUI.Lottie.Controls
+ WinUI3 Toolkit Windows Animations Lottie XAML
+
+ enable
+ x64;x86;ARM64
+ Microsoft
+ True
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lottie-Windows-WinUI3-Controls/LottieVisualSourceWinUI.cs b/Lottie-Windows-WinUI3-Controls/LottieVisualSourceWinUI.cs
new file mode 100644
index 00000000..0e8b0d8a
--- /dev/null
+++ b/Lottie-Windows-WinUI3-Controls/LottieVisualSourceWinUI.cs
@@ -0,0 +1,219 @@
+// 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.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using System.Threading.Tasks;
+using CommunityToolkit.WinAppSDK.LottieIsland;
+using CommunityToolkit.WinUI.Lottie;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Windows.Foundation;
+using Windows.Foundation.Metadata;
+using Windows.Storage;
+using Windows.Storage.Streams;
+
+namespace CommunityToolkit.WinUI.Lottie.Controls
+{
+ ///
+ /// An for a Lottie composition. This allows
+ /// a Lottie to be specified as the source for a .
+ ///
+ public sealed class LottieVisualSourceWinUI : DependencyObject, IDynamicAnimatedVisualSource
+ {
+ LottieVisualSource _lottieVisualSource;
+
+ HashSet> _compositionInvalidatedEventTokenTable = new HashSet>();
+
+ ///
+ /// Gets the options for the .
+ ///
+ // Optimize Lotties by default. Optimization takes a little longer but usually produces much
+ // more efficient translations. The only reason someone would turn optimization off is if
+ // the time to translate is too high, but in that case the Lottie is probably going to perform
+ // so badly on the machine that it won't really be usable with our without optimization.
+ public static DependencyProperty OptionsProperty { get; } =
+ RegisterDp(nameof(Options), LottieVisualOptions.Optimize);
+
+ ///
+ /// Gets the URI from which to load a JSON Lottie file.
+ ///
+ public static DependencyProperty UriSourceProperty { get; } =
+ RegisterDp(nameof(UriSource), null,
+ (owner, oldValue, newValue) => owner._lottieVisualSource.UriSource = newValue);
+
+ static DependencyProperty RegisterDp(string propertyName, T defaultValue) =>
+ DependencyProperty.Register(propertyName, typeof(T), typeof(LottieVisualSourceWinUI), new PropertyMetadata(defaultValue));
+
+ static DependencyProperty RegisterDp(string propertyName, T? defaultValue, Action callback)
+ where T : class
+ =>
+ DependencyProperty.Register(propertyName, typeof(T), typeof(LottieVisualSourceWinUI),
+ new PropertyMetadata(defaultValue, (d, e) => callback((LottieVisualSourceWinUI)d, (T)e.OldValue, (T)e.NewValue)));
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LottieVisualSourceWinUI()
+ {
+ _lottieVisualSource = new LottieVisualSource();
+ _lottieVisualSource.AnimatedVisualInvalidated += OnAnimatedVisualInvalidated;
+ }
+
+ public LottieVisualSourceWinUI(LottieVisualSource lottieVisualSource)
+ {
+ _lottieVisualSource = lottieVisualSource;
+ _lottieVisualSource.AnimatedVisualInvalidated += OnAnimatedVisualInvalidated;
+ }
+
+ ///
+ /// Gets or sets options for how the Lottie is loaded.
+ ///
+ public LottieVisualOptions Options
+ {
+ get => (LottieVisualOptions)GetValue(OptionsProperty);
+ set => SetValue(OptionsProperty, value);
+ }
+
+ ///
+ /// Gets or sets the Uniform Resource Identifier (URI) of the JSON source file for this .
+ ///
+ public Uri UriSource
+ {
+ get => (Uri)GetValue(UriSourceProperty);
+ set => SetValue(UriSourceProperty, value);
+ }
+
+ ///
+ /// Called by XAML to convert a string to an .
+ ///
+ /// The for the given url.
+ public static LottieVisualSourceWinUI? CreateFromString(string uri)
+ {
+ LottieVisualSource? lottieVisualSource = LottieVisualSource.CreateFromString(uri);
+ if (lottieVisualSource != null)
+ {
+ return new LottieVisualSourceWinUI(lottieVisualSource);
+ }
+ else
+ {
+ return new LottieVisualSourceWinUI();
+ }
+ }
+
+ ///
+ /// Sets the source for the .
+ ///
+ /// A stream containing the text of a JSON Lottie file encoded as UTF-8.
+ /// An that completes when the load completes or fails.
+ [Overload("SetSourceStreamAsync")]
+ public IAsyncAction SetSourceAsync(IInputStream stream)
+ {
+ return _lottieVisualSource.SetSourceAsync(stream);
+ }
+
+ ///
+ /// Sets the source for the .
+ ///
+ /// A file that is a JSON Lottie file.
+ /// An that completes when the load completes or fails.
+ [Overload("SetSourceFileAsync")]
+ public IAsyncAction SetSourceAsync(StorageFile file)
+ {
+ return _lottieVisualSource.SetSourceAsync(file);
+ }
+
+ ///
+ /// Sets the source for the .
+ ///
+ /// A URI that refers to a JSON Lottie file.
+ /// An that completes when the load completes or fails.
+ [DefaultOverload]
+ [Overload("SetSourceUriAsync")]
+ public IAsyncAction SetSourceAsync(Uri sourceUri)
+ {
+ return _lottieVisualSource.SetSourceAsync(sourceUri);
+ }
+
+ ///
+ /// Implements .
+ ///
+ // TODO: currently explicitly implemented interfaces are causing a problem with .NET Native. Make them implicit for now.
+ public event TypedEventHandler AnimatedVisualInvalidated
+ {
+ add
+ {
+ _compositionInvalidatedEventTokenTable.Add(value);
+ }
+
+ remove
+ {
+ _compositionInvalidatedEventTokenTable.Remove(value);
+ }
+ }
+
+ ///
+ /// Sets a delegate that returns an for the given image uri.
+ /// If this is null, no images will be loaded from references to external images.
+ ///
+ /// Most Lottie files do not reference external images, but those that do
+ /// will refer to the files via a uri. It is up to the user of
+ /// to manage the loading of the image, and return an for
+ /// that image. Alternatively the delegate may return null, and the image will not be
+ /// displayed.
+ public void SetImageAssetHandler(ImageAssetHandler? imageAssetHandler)
+ {
+ _lottieVisualSource.SetImageAssetHandler(imageAssetHandler);
+ }
+
+ ///
+ /// Implements .
+ ///
+ /// The that can be used as a factory for the resulting .
+ /// An optional object that may provide extra information about the result.
+ /// An .
+ // TODO: currently explicitly implemented interfaces are causing a problem with .NET Native. Make them implicit for now.
+ //bool IAnimatedVisualSource.TryCreateAnimatedVisual(
+ public IAnimatedVisual? TryCreateAnimatedVisual(
+ Compositor compositor,
+ out object? diagnostics)
+ {
+ if (_lottieVisualSource is null)
+ {
+ // No content has been loaded yet.
+ // Return an IAnimatedVisual that produces nothing.
+ diagnostics = null;
+ return null;
+ }
+ else
+ {
+ // Some content was loaded. Ask the factory to produce an
+ // IAnimatedVisual. If it returns null, the player will treat it
+ // as an error.
+ IAnimatedVisualFrameworkless? animatedVisual = _lottieVisualSource.TryCreateAnimatedVisual(compositor, out diagnostics);
+ if (animatedVisual != null)
+ {
+ return new LottieVisualWinUI(animatedVisual);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ private void OnAnimatedVisualInvalidated(LottieVisualSource? sender, object? args)
+ {
+ foreach (var v in _compositionInvalidatedEventTokenTable)
+ {
+ v.Invoke(this, null);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Lottie-Windows-WinUI3-Controls/LottieVisualWinUI.cs b/Lottie-Windows-WinUI3-Controls/LottieVisualWinUI.cs
new file mode 100644
index 00000000..34094fa6
--- /dev/null
+++ b/Lottie-Windows-WinUI3-Controls/LottieVisualWinUI.cs
@@ -0,0 +1,41 @@
+// 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.
+
+#nullable enable
+
+using System;
+using System.Numerics;
+using CommunityToolkit.WinAppSDK.LottieIsland;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml.Controls;
+using WinRT;
+
+namespace CommunityToolkit.WinUI.Lottie.Controls
+{
+ ///
+ /// Simple wrapper to convert an to an
+ /// for a Lottie composition. This allows
+ /// a Lottie to be specified as the source for a .
+ ///
+ internal class LottieVisualWinUI : IAnimatedVisual
+ {
+ IAnimatedVisualFrameworkless _animatedVisual;
+
+ internal LottieVisualWinUI(IAnimatedVisualFrameworkless animatedVisual)
+ {
+ _animatedVisual = animatedVisual;
+ }
+
+ public TimeSpan Duration => _animatedVisual.Duration;
+
+ public Visual RootVisual => _animatedVisual.RootVisual;
+
+ public Vector2 Size => _animatedVisual.Size;
+
+ public void Dispose()
+ {
+ _animatedVisual.As().Dispose();
+ }
+ }
+}
diff --git a/Lottie-Windows.sln b/Lottie-Windows.sln
index 5bbb5a05..3916a544 100644
--- a/Lottie-Windows.sln
+++ b/Lottie-Windows.sln
@@ -18,11 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "source", "source", "{AB232F
source\README.md = source\README.md
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LottieViewer", "LottieViewer\LottieViewer.csproj", "{5120EFD7-A556-46BF-8D56-F65F1EF9A305}"
- ProjectSection(ProjectDependencies) = postProject
- {6AB50ED0-6273-4919-9ADE-50195664EF15} = {6AB50ED0-6273-4919-9ADE-50195664EF15}
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lottie-Windows-Uwp", "Lottie-Windows\Lottie-Windows-Uwp\Lottie-Windows-Uwp.csproj", "{E392BAD0-F936-4B64-A445-552597795CC7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dlls", "dlls", "{C75BD686-21A6-4EB3-8D4B-D5A01C019C52}"
@@ -40,6 +35,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LottieToWinComp.dll", "dlls
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{00D8BD2B-70A1-48EC-AC5E-CC40561F8972}"
ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
LICENSE.md = LICENSE.md
README.md = README.md
EndProjectSection
@@ -169,7 +165,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LottieGen.MsBuild", "Lottie
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lottie-Windows-WinUI3", "Lottie-Windows\Lottie-Windows-WinUI3\Lottie-Windows-WinUI3.csproj", "{C505CD2D-5D26-42EE-8FAA-41BB784821EF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LottieSamples", "LottieSamples\LottieSamples.csproj", "{6AB50ED0-6273-4919-9ADE-50195664EF15}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LottieWinRT", "LottieWinRT\LottieWinRT.csproj", "{DDA0D223-4B59-455D-A8B1-CFAE59523FFB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {9BA7A2F2-B723-458B-A575-3F726D271465} = {9BA7A2F2-B723-458B-A575-3F726D271465}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LottieIsland", "LottieIsland\LottieIsland.vcxproj", "{9BA7A2F2-B723-458B-A575-3F726D271465}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LottieIslandProjection", "LottieIslandProjection\LottieIslandProjection.csproj", "{D2DDC0A0-FD88-4053-9EA2-4233477D8477}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleLottieIslandApp", "SimpleLottieIslandApp\SimpleLottieIslandApp.vcxproj", "{350A5EC2-B156-4AAF-9D80-A864C76BA0C5}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lottie-Windows-WinUI3-Controls", "Lottie-Windows-WinUI3-Controls\Lottie-Windows-WinUI3-Controls.csproj", "{A896C58F-1A1D-49A5-9044-75312051C455}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -190,45 +197,6 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|Any CPU.ActiveCfg = BETA|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM.ActiveCfg = BETA|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM.Build.0 = BETA|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM.Deploy.0 = BETA|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM64.ActiveCfg = BETA|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM64.Build.0 = BETA|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|ARM64.Deploy.0 = BETA|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x64.ActiveCfg = BETA|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x64.Build.0 = BETA|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x64.Deploy.0 = BETA|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x86.ActiveCfg = BETA|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x86.Build.0 = BETA|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.BETA|x86.Deploy.0 = BETA|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|Any CPU.ActiveCfg = Debug|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM.ActiveCfg = Debug|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM.Build.0 = Debug|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM.Deploy.0 = Debug|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM64.Build.0 = Debug|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|ARM64.Deploy.0 = Debug|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x64.ActiveCfg = Debug|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x64.Build.0 = Debug|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x64.Deploy.0 = Debug|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x86.ActiveCfg = Debug|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x86.Build.0 = Debug|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Debug|x86.Deploy.0 = Debug|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|Any CPU.ActiveCfg = Release|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM.ActiveCfg = Release|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM.Build.0 = Release|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM.Deploy.0 = Release|ARM
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM64.ActiveCfg = Release|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM64.Build.0 = Release|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|ARM64.Deploy.0 = Release|ARM64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x64.ActiveCfg = Release|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x64.Build.0 = Release|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x64.Deploy.0 = Release|x64
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x86.ActiveCfg = Release|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x86.Build.0 = Release|x86
- {5120EFD7-A556-46BF-8D56-F65F1EF9A305}.Release|x86.Deploy.0 = Release|x86
{E392BAD0-F936-4B64-A445-552597795CC7}.BETA|Any CPU.ActiveCfg = Release|Any CPU
{E392BAD0-F936-4B64-A445-552597795CC7}.BETA|ARM.ActiveCfg = Release|Any CPU
{E392BAD0-F936-4B64-A445-552597795CC7}.BETA|ARM64.ActiveCfg = Release|Any CPU
@@ -540,79 +508,193 @@ Global
{192FBD28-8531-4607-A17C-44A1A51A1565}.Release|x86.ActiveCfg = Release|Any CPU
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|Any CPU.ActiveCfg = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|Any CPU.Build.0 = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM.ActiveCfg = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM.Build.0 = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM64.ActiveCfg = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM64.Build.0 = Debug|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM.ActiveCfg = Release|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM.Build.0 = Release|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM64.ActiveCfg = Debug|ARM64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|ARM64.Build.0 = Debug|ARM64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|x64.ActiveCfg = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|x64.Build.0 = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|x86.ActiveCfg = Debug|x86
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.BETA|x86.Build.0 = Debug|x86
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|Any CPU.Build.0 = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM.ActiveCfg = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM.Build.0 = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM64.ActiveCfg = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM64.Build.0 = Debug|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|ARM64.Build.0 = Debug|ARM64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x64.ActiveCfg = Debug|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x64.Build.0 = Debug|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x86.Build.0 = Debug|Any CPU
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x86.ActiveCfg = Debug|x86
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Debug|x86.Build.0 = Debug|x86
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|Any CPU.ActiveCfg = Release|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|Any CPU.Build.0 = Release|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM.ActiveCfg = Release|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM.Build.0 = Release|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM64.ActiveCfg = Release|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM64.Build.0 = Release|x64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM64.ActiveCfg = Release|ARM64
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|ARM64.Build.0 = Release|ARM64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x64.ActiveCfg = Release|x64
{C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x64.Build.0 = Release|x64
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x86.ActiveCfg = Release|Any CPU
- {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x86.Build.0 = Release|Any CPU
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|Any CPU.ActiveCfg = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|Any CPU.Build.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|Any CPU.Deploy.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM.ActiveCfg = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM.Build.0 = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM.Deploy.0 = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM64.ActiveCfg = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM64.Build.0 = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|ARM64.Deploy.0 = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x64.ActiveCfg = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x64.Build.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x64.Deploy.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x86.ActiveCfg = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x86.Build.0 = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.BETA|x86.Deploy.0 = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|Any CPU.ActiveCfg = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|Any CPU.Build.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|Any CPU.Deploy.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM.ActiveCfg = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM.Build.0 = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM.Deploy.0 = Debug|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM64.Build.0 = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|ARM64.Deploy.0 = Debug|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x64.ActiveCfg = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x64.Build.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x64.Deploy.0 = Debug|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x86.ActiveCfg = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x86.Build.0 = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Debug|x86.Deploy.0 = Debug|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|Any CPU.ActiveCfg = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|Any CPU.Build.0 = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|Any CPU.Deploy.0 = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM.ActiveCfg = Release|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM.Build.0 = Release|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM.Deploy.0 = Release|ARM
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM64.ActiveCfg = Release|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM64.Build.0 = Release|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|ARM64.Deploy.0 = Release|ARM64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x64.ActiveCfg = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x64.Build.0 = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x64.Deploy.0 = Release|x64
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x86.ActiveCfg = Release|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x86.Build.0 = Release|x86
- {6AB50ED0-6273-4919-9ADE-50195664EF15}.Release|x86.Deploy.0 = Release|x86
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x86.ActiveCfg = Release|x86
+ {C505CD2D-5D26-42EE-8FAA-41BB784821EF}.Release|x86.Build.0 = Release|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|Any CPU.ActiveCfg = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|Any CPU.Build.0 = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|ARM.ActiveCfg = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|ARM.Build.0 = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|ARM64.ActiveCfg = Debug|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|ARM64.Build.0 = Debug|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|x64.ActiveCfg = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|x64.Build.0 = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|x86.ActiveCfg = Debug|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.BETA|x86.Build.0 = Debug|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|Any CPU.Build.0 = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|ARM.ActiveCfg = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|ARM.Build.0 = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|ARM64.Build.0 = Debug|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|x64.ActiveCfg = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|x64.Build.0 = Debug|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|x86.ActiveCfg = Debug|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Debug|x86.Build.0 = Debug|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|Any CPU.ActiveCfg = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|Any CPU.Build.0 = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|ARM.ActiveCfg = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|ARM.Build.0 = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|ARM64.ActiveCfg = Release|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|ARM64.Build.0 = Release|ARM64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|x64.ActiveCfg = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|x64.Build.0 = Release|x64
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|x86.ActiveCfg = Release|x86
+ {DDA0D223-4B59-455D-A8B1-CFAE59523FFB}.Release|x86.Build.0 = Release|x86
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|Any CPU.ActiveCfg = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|Any CPU.Build.0 = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|ARM.ActiveCfg = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|ARM.Build.0 = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|ARM64.ActiveCfg = Debug|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|ARM64.Build.0 = Debug|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|x64.ActiveCfg = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|x64.Build.0 = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|x86.ActiveCfg = Debug|Win32
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.BETA|x86.Build.0 = Debug|Win32
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|Any CPU.Build.0 = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|ARM.ActiveCfg = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|ARM.Build.0 = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|ARM64.Build.0 = Debug|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|x64.ActiveCfg = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|x64.Build.0 = Debug|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|x86.ActiveCfg = Debug|Win32
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Debug|x86.Build.0 = Debug|Win32
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|Any CPU.ActiveCfg = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|Any CPU.Build.0 = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|ARM.ActiveCfg = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|ARM.Build.0 = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|ARM64.ActiveCfg = Release|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|ARM64.Build.0 = Release|ARM64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|x64.ActiveCfg = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|x64.Build.0 = Release|x64
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|x86.ActiveCfg = Release|Win32
+ {9BA7A2F2-B723-458B-A575-3F726D271465}.Release|x86.Build.0 = Release|Win32
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|Any CPU.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|Any CPU.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|ARM.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|ARM.Build.0 = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|ARM64.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|ARM64.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|x64.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|x64.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|x86.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.BETA|x86.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|ARM.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|x64.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Debug|x86.Build.0 = Debug|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|ARM.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|ARM.Build.0 = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|ARM64.Build.0 = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|x64.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|x64.Build.0 = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|x86.ActiveCfg = Release|Any CPU
+ {D2DDC0A0-FD88-4053-9EA2-4233477D8477}.Release|x86.Build.0 = Release|Any CPU
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|Any CPU.ActiveCfg = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM.ActiveCfg = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM.Build.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM.Deploy.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM64.ActiveCfg = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM64.Build.0 = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|ARM64.Deploy.0 = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x64.ActiveCfg = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x64.Build.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x64.Deploy.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x86.ActiveCfg = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x86.Build.0 = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.BETA|x86.Deploy.0 = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM.ActiveCfg = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM.Build.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM.Deploy.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM64.Build.0 = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x64.ActiveCfg = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x64.Build.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x64.Deploy.0 = Debug|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x86.ActiveCfg = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x86.Build.0 = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Debug|x86.Deploy.0 = Debug|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|Any CPU.ActiveCfg = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM.ActiveCfg = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM.Build.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM.Deploy.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM64.ActiveCfg = Release|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM64.Build.0 = Release|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|ARM64.Deploy.0 = Release|ARM64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x64.ActiveCfg = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x64.Build.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x64.Deploy.0 = Release|x64
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x86.ActiveCfg = Release|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x86.Build.0 = Release|Win32
+ {350A5EC2-B156-4AAF-9D80-A864C76BA0C5}.Release|x86.Deploy.0 = Release|Win32
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|Any CPU.ActiveCfg = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|Any CPU.Build.0 = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|ARM.ActiveCfg = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|ARM.Build.0 = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|ARM64.ActiveCfg = Debug|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|ARM64.Build.0 = Debug|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|x64.ActiveCfg = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|x64.Build.0 = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|x86.ActiveCfg = Debug|x86
+ {A896C58F-1A1D-49A5-9044-75312051C455}.BETA|x86.Build.0 = Debug|x86
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|Any CPU.Build.0 = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|ARM.ActiveCfg = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|ARM.Build.0 = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|ARM64.Build.0 = Debug|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|x64.ActiveCfg = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|x64.Build.0 = Debug|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|x86.ActiveCfg = Debug|x86
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Debug|x86.Build.0 = Debug|x86
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|Any CPU.ActiveCfg = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|Any CPU.Build.0 = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|ARM.ActiveCfg = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|ARM.Build.0 = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|ARM64.ActiveCfg = Release|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|ARM64.Build.0 = Release|ARM64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|x64.ActiveCfg = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|x64.Build.0 = Release|x64
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|x86.ActiveCfg = Release|x86
+ {A896C58F-1A1D-49A5-9044-75312051C455}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -665,21 +747,6 @@ Global
source\WinUIXamlMediaData\WinUIXamlMediaData.projitems*{30059ca7-0745-4eec-8d11-b14850a70c98}*SharedItemsImports = 13
source\YamlData\YamlData.projitems*{39c6b7f3-5e75-4019-82ab-00fd8a0a06e2}*SharedItemsImports = 13
source\LottieReader\LottieReader.projitems*{4e7d8957-3f5f-46e1-99a8-2012b806c9b0}*SharedItemsImports = 13
- source\Animatables\Animatables.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\CompMetadata\CompMetadata.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\DotLottie\DotLottie.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\GenericData\GenericData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\LottieData\LottieData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\LottieMetadata\LottieMetadata.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\LottieReader\LottieReader.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\LottieToWinComp\LottieToWinComp.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\Lottie\Lottie.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\NullablesAttributes\NullablesAttributes.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\UIData\UIData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\WinCompData\WinCompData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\WinStorageStreamsData\WinStorageStreamsData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\WinUIXamlMediaData\WinUIXamlMediaData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
- source\YamlData\YamlData.projitems*{5120efd7-a556-46bf-8d56-f65f1ef9a305}*SharedItemsImports = 4
source\LottieData\LottieData.projitems*{6221591a-e8f4-4a5e-8f0c-2651c24ad495}*SharedItemsImports = 5
source\WinCompData\WinCompData.projitems*{68317393-f5a5-4b2c-918a-688db2c10f54}*SharedItemsImports = 5
source\Animatables\Animatables.projitems*{6984af37-d580-4087-806b-480a04f2df77}*SharedItemsImports = 5
@@ -698,7 +765,6 @@ Global
source\WinStorageStreamsData\WinStorageStreamsData.projitems*{6984af37-d580-4087-806b-480a04f2df77}*SharedItemsImports = 5
source\WinUIXamlMediaData\WinUIXamlMediaData.projitems*{6984af37-d580-4087-806b-480a04f2df77}*SharedItemsImports = 5
source\YamlData\YamlData.projitems*{6984af37-d580-4087-806b-480a04f2df77}*SharedItemsImports = 5
- source\NullablesAttributes\NullablesAttributes.projitems*{6ab50ed0-6273-4919-9ade-50195664ef15}*SharedItemsImports = 4
source\DotLottie\DotLottie.projitems*{7012420d-624c-4bd4-a1d2-1c6c1655ed3a}*SharedItemsImports = 13
source\UIData\UIData.projitems*{74601e6c-2dfe-4842-b170-047941abff2c}*SharedItemsImports = 13
source\LottieGen\LottieGen.projitems*{7654a857-9a99-4185-9f8e-dd0ce662af23}*SharedItemsImports = 13
diff --git a/Lottie-Windows/Lottie-Windows-WinUI3/Lottie-Windows-WinUI3.csproj b/Lottie-Windows/Lottie-Windows-WinUI3/Lottie-Windows-WinUI3.csproj
index ec59b7c9..74b74ec8 100644
--- a/Lottie-Windows/Lottie-Windows-WinUI3/Lottie-Windows-WinUI3.csproj
+++ b/Lottie-Windows/Lottie-Windows-WinUI3/Lottie-Windows-WinUI3.csproj
@@ -1,7 +1,7 @@
- net7.0-windows10.0.18362.0
+ net7.0-windows10.0.19041.0
true
Library
@@ -9,6 +9,7 @@
WinUI3 Toolkit Windows Animations Lottie XAML
enable
+ x64;x86;ARM64
Microsoft
WINAPPSDK
True
@@ -16,7 +17,12 @@
-
+
+
+
+
+
+
diff --git a/LottieIsland/AutomationBase.cpp b/LottieIsland/AutomationBase.cpp
new file mode 100644
index 00000000..3e248dfe
--- /dev/null
+++ b/LottieIsland/AutomationBase.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "AutomationBase.h"
+
+namespace AutomationHelpers
+{
+
+void AutomationBase::RemoveHandler(
+ IAutomationCallbackHandler const* const handler)
+{
+ std::unique_lock lock{ m_mutex };
+
+ auto iterator = std::remove_if(
+ m_handlers.begin(), m_handlers.end(), [handler](auto const& handlerEntry)
+ {
+ return handlerEntry.Match(handler);
+ });
+
+ m_handlers.erase(iterator, m_handlers.end());
+}
+
+void AutomationBase::AddHandler(
+ AutomationCallbackHandlerType const& type,
+ IAutomationCallbackHandler* const handler)
+{
+ // Remove any existing handler of the same type.
+ auto iterator = std::remove_if(
+ m_handlers.begin(), m_handlers.end(), [type](auto const& handlerEntry)
+ {
+ return handlerEntry.Match(type);
+ });
+
+ m_handlers.erase(iterator, m_handlers.end());
+
+ if (nullptr != handler)
+ {
+ m_handlers.emplace_back(AutomationCallbackHandlerEntry{ type, handler });
+ }
+}
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationBase.h b/LottieIsland/AutomationBase.h
new file mode 100644
index 00000000..7571dce7
--- /dev/null
+++ b/LottieIsland/AutomationBase.h
@@ -0,0 +1,100 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include
+#include "AutomationCallbackHandler.h"
+
+namespace AutomationHelpers
+{
+
+struct AutomationBase : winrt::implements
+{
+ [[nodiscard]] winrt::weak_ref GetWeak() const noexcept
+ {
+ try
+ {
+ return const_cast(this)->get_weak();
+ }
+ catch (...) {}
+ return nullptr;
+ }
+
+ template
+ [[nodiscard]] inline static winrt::com_ptr LockWeak(
+ winrt::weak_ref const& weakRef) noexcept
+ {
+ static_assert(std::is_base_of_v);
+
+ try
+ {
+ if (nullptr != weakRef)
+ {
+ winrt::com_ptr weakRefGet = weakRef.get();
+ if (nullptr != weakRefGet)
+ {
+ winrt::com_ptr strongRef{ nullptr };
+ strongRef.copy_from(static_cast(weakRefGet.get()));
+ return strongRef;
+ }
+ }
+ }
+ catch (...) {}
+ return nullptr;
+ }
+
+ template
+ [[nodiscard]] winrt::com_ptr GetStrong() const noexcept
+ {
+ static_assert(std::is_base_of_v);
+
+ try
+ {
+ return static_cast(const_cast(this))->get_strong();
+ }
+ catch (...) {}
+ return nullptr;
+ }
+
+ template
+ [[nodiscard]] IUnknown* GetIUnknown() const noexcept
+ {
+ static_assert(std::is_base_of_v);
+
+ try
+ {
+ return GetStrong().as().get();
+ }
+ catch (...) {}
+ return nullptr;
+ }
+
+ void RemoveHandler(
+ IAutomationCallbackHandler const* const handler);
+
+protected:
+ void AddHandler(
+ AutomationCallbackHandlerType const& type,
+ IAutomationCallbackHandler* const handler);
+
+ template
+ [[nodiscard]] HandlerT* GetHandler(
+ AutomationCallbackHandlerType const& type) const
+ {
+ static_assert(std::is_base_of_v);
+
+ auto iterator = std::find_if(
+ m_handlers.cbegin(), m_handlers.cend(), [&type](auto const& handlerEntry)
+ {
+ return handlerEntry.Match(type);
+ });
+
+ return (m_handlers.cend() != iterator) ? iterator->Get() : nullptr;
+ }
+
+ mutable std::mutex m_mutex{};
+
+private:
+ std::vector m_handlers{};
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationCallbackHandler.h b/LottieIsland/AutomationCallbackHandler.h
new file mode 100644
index 00000000..0d720d0a
--- /dev/null
+++ b/LottieIsland/AutomationCallbackHandler.h
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include
+#include
+#include
+
+namespace AutomationHelpers
+{
+
+enum class AutomationCallbackHandlerType : unsigned char
+{
+ None,
+ Fragment,
+ FragmentRoot,
+ Invoke
+};
+
+struct IAutomationCallbackHandler
+{
+ virtual ~IAutomationCallbackHandler() noexcept = default;
+};
+
+struct IAutomationFragmentCallbackHandler : IAutomationCallbackHandler
+{
+ virtual winrt::Windows::Graphics::RectInt32 GetBoundingRectangleInScreenSpaceForAutomation(
+ ::IUnknown const* const sender) const = 0;
+
+ virtual void HandleSetFocusForAutomation(
+ ::IUnknown const* const sender) = 0;
+};
+
+struct IAutomationFragmentRootCallbackHandler : IAutomationCallbackHandler
+{
+ virtual winrt::com_ptr<::IRawElementProviderFragment> GetFragmentFromPointForAutomation(
+ double x,
+ double y,
+ ::IUnknown const* const sender) const = 0;
+
+ virtual winrt::com_ptr<::IRawElementProviderFragment> GetFragmentInFocusForAutomation(
+ ::IUnknown const* const sender) const = 0;
+};
+
+struct IAutomationInvokeCallbackHandler : IAutomationCallbackHandler
+{
+ virtual void HandleInvokeForAutomation(
+ ::IUnknown const* const sender) = 0;
+};
+
+struct AutomationCallbackHandlerEntry
+{
+ explicit AutomationCallbackHandlerEntry(
+ AutomationCallbackHandlerType const& type,
+ IAutomationCallbackHandler* const handler) :
+ _type{ type }, _handler{ handler } {}
+
+ bool Match(
+ AutomationCallbackHandlerType const& type) const { return type == _type; }
+
+ bool Match(
+ IAutomationCallbackHandler const* const handler) const { return handler == _handler; }
+
+ template
+ DerivedT* Get() const
+ {
+ static_assert(std::is_base_of_v);
+ return static_cast(_handler);
+ }
+
+private:
+ AutomationCallbackHandlerType _type{ AutomationCallbackHandlerType::None };
+ IAutomationCallbackHandler* _handler{ nullptr };
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationCallbackRevoker.h b/LottieIsland/AutomationCallbackRevoker.h
new file mode 100644
index 00000000..6ceb0799
--- /dev/null
+++ b/LottieIsland/AutomationCallbackRevoker.h
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "AutomationBase.h"
+
+namespace AutomationHelpers
+{
+
+struct AutomationCallbackRevoker
+{
+ [[nodiscard]] static std::unique_ptr Create(
+ winrt::weak_ref const& automationObject,
+ IAutomationCallbackHandler* const handler) noexcept
+ {
+ try
+ {
+ auto newRevoker = std::make_unique();
+ newRevoker->Initialize(automationObject, handler);
+ return newRevoker;
+ }
+ catch (...) {}
+ return nullptr;
+ }
+
+ explicit AutomationCallbackRevoker() noexcept = default;
+
+ ~AutomationCallbackRevoker() noexcept
+ {
+ if (auto strongAutomationObject = _automationObject.get())
+ {
+ if (nullptr != _handler)
+ {
+ strongAutomationObject->RemoveHandler(_handler);
+ }
+ }
+ }
+
+ // Disable move and copy.
+ explicit AutomationCallbackRevoker(AutomationCallbackRevoker const&) = delete;
+ explicit AutomationCallbackRevoker(AutomationCallbackRevoker&&) = delete;
+ AutomationCallbackRevoker& operator=(AutomationCallbackRevoker const&) = delete;
+ AutomationCallbackRevoker& operator=(AutomationCallbackRevoker&&) = delete;
+
+private:
+ void Initialize(
+ winrt::weak_ref const& automationObject,
+ IAutomationCallbackHandler* const handler)
+ {
+ _automationObject = automationObject;
+ _handler = handler;
+ }
+
+ winrt::weak_ref _automationObject{ nullptr };
+ IAutomationCallbackHandler* _handler{ nullptr };
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationElement.cpp b/LottieIsland/AutomationElement.cpp
new file mode 100644
index 00000000..6c117a16
--- /dev/null
+++ b/LottieIsland/AutomationElement.cpp
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "AutomationElement.h"
+#include
+
+namespace AutomationHelpers
+{
+
+HRESULT __stdcall AutomationElement::get_ProviderOptions(
+ _Out_ ::ProviderOptions* providerOptions)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != providerOptions)
+ {
+ *providerOptions = m_providerOptions;
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationElement::GetPatternProvider(
+ _In_ PATTERNID patternId,
+ _COM_Outptr_opt_result_maybenull_ ::IUnknown** patternProvider)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != patternProvider)
+ {
+ *patternProvider = nullptr;
+ switch (patternId)
+ {
+ case UIA_InvokePatternId:
+ {
+ if (auto invokeProvider = get_strong().try_as<::IInvokeProvider>())
+ {
+ invokeProvider.as<::IUnknown>().copy_to(patternProvider);
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationElement::GetPropertyValue(
+ _In_ PROPERTYID propertyId,
+ _Out_ VARIANT* propertyValue)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != propertyValue)
+ {
+ ::VariantInit(propertyValue);
+ switch (propertyId)
+ {
+ case UIA_NamePropertyId:
+ {
+ propertyValue->bstrVal = wil::make_bstr(m_name.c_str()).release();
+ propertyValue->vt = VT_BSTR;
+ break;
+ }
+
+ case UIA_IsContentElementPropertyId:
+ {
+ propertyValue->boolVal = m_isContent ? VARIANT_TRUE : VARIANT_FALSE;
+ propertyValue->vt = VT_BOOL;
+ break;
+ }
+
+ case UIA_IsControlElementPropertyId:
+ {
+ propertyValue->boolVal = m_isControl ? VARIANT_TRUE : VARIANT_FALSE;
+ propertyValue->vt = VT_BOOL;
+ break;
+ }
+
+ case UIA_ControlTypePropertyId:
+ {
+ if (m_isControl)
+ {
+ propertyValue->vt = VT_I4;
+ propertyValue->lVal = m_uiaControlTypeId;
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationElement::get_HostRawElementProvider(
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderSimple** hostRawElementProviderSimple)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != hostRawElementProviderSimple)
+ {
+ m_hostProvider.copy_to(hostRawElementProviderSimple);
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationElement.h b/LottieIsland/AutomationElement.h
new file mode 100644
index 00000000..bea39ce0
--- /dev/null
+++ b/LottieIsland/AutomationElement.h
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "AutomationBase.h"
+
+namespace AutomationHelpers
+{
+
+struct AutomationElement : winrt::implements
+{
+ // Settable properties.
+ void ProviderOptions(::ProviderOptions const& providerOptions) { std::unique_lock lock{ m_mutex }; m_providerOptions = providerOptions; }
+ void Name(std::wstring_view const& name) { std::unique_lock lock{ m_mutex }; m_name = name; }
+ void IsContent(bool const& isContent) { std::unique_lock lock{ m_mutex }; m_isContent = isContent; }
+ void IsControl(bool const& isControl) { std::unique_lock lock{ m_mutex }; m_isControl = isControl; }
+ void UiaControlTypeId(long const& uiaControlTypeId) { std::unique_lock lock{ m_mutex }; m_uiaControlTypeId = uiaControlTypeId; }
+ void HostProvider(winrt::com_ptr<::IRawElementProviderSimple> const& hostProvider) { std::unique_lock lock{ m_mutex }; m_hostProvider = hostProvider; }
+
+ // IRawElementProviderSimple implementation.
+ HRESULT __stdcall get_ProviderOptions(
+ _Out_ ::ProviderOptions* providerOptions) final override;
+
+ HRESULT __stdcall GetPatternProvider(
+ _In_ PATTERNID patternId,
+ _COM_Outptr_opt_result_maybenull_ ::IUnknown** patternProvider) final override;
+
+ HRESULT __stdcall GetPropertyValue(
+ _In_ PROPERTYID propertyId,
+ _Out_ VARIANT* propertyValue) final override;
+
+ HRESULT __stdcall get_HostRawElementProvider(
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderSimple** hostRawElementProviderSimple) final override;
+
+private:
+ ::ProviderOptions m_providerOptions{ ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading };
+ std::wstring m_name{ L"" };
+ bool m_isContent{ true };
+ bool m_isControl{ true };
+ long m_uiaControlTypeId{ UIA_CustomControlTypeId };
+ winrt::com_ptr<::IRawElementProviderSimple> m_hostProvider{ nullptr };
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationFragment.cpp b/LottieIsland/AutomationFragment.cpp
new file mode 100644
index 00000000..f98a5b2d
--- /dev/null
+++ b/LottieIsland/AutomationFragment.cpp
@@ -0,0 +1,294 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "AutomationFragment.h"
+#include
+
+using unique_safearray = wil::unique_any;
+
+namespace AutomationHelpers
+{
+
+std::unique_ptr AutomationFragment::SetFragmentCallbackHandler(
+ IAutomationFragmentCallbackHandler* const handler)
+{
+ AddHandler(AutomationCallbackHandlerType::Fragment, handler);
+ return AutomationCallbackRevoker::Create(GetWeak(), handler);
+}
+
+void AutomationFragment::AddChildToEnd(
+ winrt::com_ptr const& child)
+{
+ std::unique_lock lock{ m_mutex };
+
+ if (nullptr == child)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ // The child should not already have a parent.
+ winrt::check_bool(nullptr == child->Parent());
+
+ // Set us up as the parent for the new child.
+ child->Parent(GetWeak());
+
+ // Set up the sibling relationships.
+ if (!m_children.empty())
+ {
+ auto& previousSiblingForNewChild = m_children.back();
+ previousSiblingForNewChild->NextSibling(child);
+ child->PreviousSibling(previousSiblingForNewChild);
+ }
+
+ // Finally add the child.
+ m_children.push_back(child);
+}
+
+void AutomationFragment::RemoveChild(
+ winrt::com_ptr const& child)
+{
+ std::unique_lock lock{ m_mutex };
+
+ if (nullptr == child)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ auto iterator = std::find_if(
+ m_children.begin(), m_children.end(), [&child](auto const& childEntry)
+ {
+ // See if we find a matching child entry in our children.
+ return (childEntry.as<::IUnknown>().get() == child.as<::IUnknown>().get());
+ });
+
+ // We cannot remove a child that isn't ours.
+ winrt::check_bool(m_children.end() != iterator);
+
+ // Remove us from the parent relationship with the child.
+ child->Parent(nullptr);
+
+ // Reset the sibling relationships.
+ auto previousSibling = child->PreviousSibling();
+ auto nextSibling = child->NextSibling();
+ if (nullptr != previousSibling)
+ {
+ previousSibling->NextSibling(nextSibling);
+ }
+ if (nullptr != nextSibling)
+ {
+ nextSibling->PreviousSibling(previousSibling);
+ }
+ child->PreviousSibling(nullptr);
+ child->NextSibling(nullptr);
+
+ // Finally, remove the child.
+ m_children.erase(iterator);
+}
+
+void AutomationFragment::RemoveAllChildren()
+{
+ std::unique_lock lock{ m_mutex };
+
+ for (auto& child : m_children)
+ {
+ // Disconnect the relationships from all our children.
+ child->Parent(nullptr);
+ child->PreviousSibling(nullptr);
+ child->NextSibling(nullptr);
+ }
+
+ // Remove all the children.
+ m_children.clear();
+}
+
+HRESULT __stdcall AutomationFragment::Navigate(
+ _In_ NavigateDirection direction,
+ _COM_Outptr_opt_result_maybenull_ IRawElementProviderFragment** fragment)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != fragment)
+ {
+ *fragment = nullptr;
+ switch (direction)
+ {
+ case NavigateDirection_Parent:
+ {
+ if (auto strongParent = LockWeak(m_parent))
+ {
+ strongParent.as().copy_to(fragment);
+ }
+ break;
+ }
+ case NavigateDirection_NextSibling:
+ {
+ if (auto strongSibling = LockWeak(m_nextSibling))
+ {
+ strongSibling.as().copy_to(fragment);
+ }
+ break;
+ }
+ case NavigateDirection_PreviousSibling:
+ {
+ if (auto strongSibling = LockWeak(m_previousSibling))
+ {
+ strongSibling.as().copy_to(fragment);
+ }
+ break;
+ }
+ case NavigateDirection_FirstChild:
+ {
+ if (!m_children.empty())
+ {
+ m_children.front().as().copy_to(fragment);
+ }
+ break;
+ }
+ case NavigateDirection_LastChild:
+ {
+ if (!m_children.empty())
+ {
+ m_children.back().as().copy_to(fragment);
+ }
+ break;
+ }
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragment::GetRuntimeId(
+ _Outptr_opt_result_maybenull_ SAFEARRAY** runtimeId)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != runtimeId)
+ {
+ *runtimeId = nullptr;
+
+ unsigned long arraySizeAsUnsignedLong = static_cast(m_runtimeId.size());
+
+ unique_safearray runtimeIdArray{ ::SafeArrayCreateVector(VT_I4, 0, arraySizeAsUnsignedLong) };
+ SAFEARRAY* rawPointerToSafeArray = runtimeIdArray.get();
+ winrt::check_pointer(rawPointerToSafeArray);
+
+ for (long i = 0; i < static_cast(arraySizeAsUnsignedLong); ++i)
+ {
+ winrt::check_hresult(::SafeArrayPutElement(rawPointerToSafeArray, &i, &(m_runtimeId[i])));
+ }
+
+ *runtimeId = runtimeIdArray.release();
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragment::get_BoundingRectangle(
+ _Out_ UiaRect* boundingRectangle)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != boundingRectangle)
+ {
+ *boundingRectangle = { 0, 0, 0, 0 };
+ if (auto handler = GetHandler(AutomationCallbackHandlerType::Fragment))
+ {
+ auto screenRectangle =
+ handler->GetBoundingRectangleInScreenSpaceForAutomation(GetIUnknown());
+
+ boundingRectangle->left = screenRectangle.X;
+ boundingRectangle->top = screenRectangle.Y;
+ boundingRectangle->width = screenRectangle.Width;
+ boundingRectangle->height = screenRectangle.Height;
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragment::GetEmbeddedFragmentRoots(
+ _Outptr_opt_result_maybenull_ SAFEARRAY** embeddedFragmentRoots)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != embeddedFragmentRoots)
+ {
+ *embeddedFragmentRoots = nullptr;
+
+ if (!m_embeddedFragments.empty())
+ {
+ unsigned long vectorSizeAsUnsignedLong = static_cast(m_embeddedFragments.size());
+
+ unique_safearray embeddedFragmentRootsArray{ ::SafeArrayCreateVector(VT_UNKNOWN, 0, vectorSizeAsUnsignedLong) };
+ SAFEARRAY* rawPointerToSafeArray = embeddedFragmentRootsArray.get();
+ winrt::check_pointer(rawPointerToSafeArray);
+
+ for (long i = 0; i < static_cast(vectorSizeAsUnsignedLong); ++i)
+ {
+ winrt::check_hresult(::SafeArrayPutElement(rawPointerToSafeArray, &i, m_embeddedFragments.at(i).as<::IUnknown>().get()));
+ }
+
+ *embeddedFragmentRoots = embeddedFragmentRootsArray.release();
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragment::SetFocus()
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (auto handler = GetHandler(AutomationCallbackHandlerType::Fragment))
+ {
+ handler->HandleSetFocusForAutomation(GetIUnknown());
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragment::get_FragmentRoot(
+ _COM_Outptr_opt_result_maybenull_ IRawElementProviderFragmentRoot** fragmentRoot)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != fragmentRoot)
+ {
+ *fragmentRoot = nullptr;
+
+ // Walk up our fragment tree until we find our fragment root.
+ auto fragmentRootCandidate = GetStrong();
+ bool currentCandidateIsThisObject = true;
+ while (nullptr != fragmentRootCandidate && nullptr == fragmentRootCandidate.try_as())
+ {
+ // Haven't found the fragment root yet, keep walking up our tree.
+ fragmentRootCandidate = currentCandidateIsThisObject ? LockWeak(m_parent) : fragmentRootCandidate->Parent();
+ currentCandidateIsThisObject = false;
+ }
+
+ if (nullptr != fragmentRootCandidate)
+ {
+ // Found the fragment root, return it.
+ fragmentRootCandidate.as().copy_to(fragmentRoot);
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationFragment.h b/LottieIsland/AutomationFragment.h
new file mode 100644
index 00000000..817ebb5e
--- /dev/null
+++ b/LottieIsland/AutomationFragment.h
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "AutomationElement.h"
+#include "AutomationCallbackRevoker.h"
+
+namespace AutomationHelpers
+{
+
+struct AutomationFragment : winrt::implements
+{
+ // Automation callback handler.
+ [[nodiscard]] std::unique_ptr SetFragmentCallbackHandler(
+ IAutomationFragmentCallbackHandler* const handler);
+
+ // Methods.
+ void AddChildToEnd(
+ winrt::com_ptr const& child);
+
+ void RemoveChild(
+ winrt::com_ptr const& child);
+
+ void RemoveAllChildren();
+
+ // IRawElementProviderFragment implementation.
+ HRESULT __stdcall Navigate(
+ _In_ NavigateDirection direction,
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragment** fragment) final override;
+
+ HRESULT __stdcall GetRuntimeId(
+ _Outptr_opt_result_maybenull_ SAFEARRAY** runtimeId) final override;
+
+ HRESULT __stdcall get_BoundingRectangle(
+ _Out_ UiaRect* boundingRectangle) final override;
+
+ HRESULT __stdcall GetEmbeddedFragmentRoots(
+ _Outptr_opt_result_maybenull_ SAFEARRAY** embeddedFragmentRoots) final override;
+
+ HRESULT __stdcall SetFocus() final override;
+
+ HRESULT __stdcall get_FragmentRoot(
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragmentRoot** fragmentRoot) final override;
+
+private:
+ // Property setters.
+ void Parent(winrt::weak_ref const& parent) { std::unique_lock lock{ m_mutex }; m_parent = parent; }
+ void PreviousSibling(winrt::weak_ref const& previousSibling) { std::unique_lock lock{ m_mutex }; m_previousSibling = previousSibling; }
+ void NextSibling(winrt::weak_ref const& nextSibling) { std::unique_lock lock{ m_mutex }; m_nextSibling = nextSibling; }
+
+ // Property getters.
+ winrt::com_ptr Parent() const { std::unique_lock lock{ m_mutex }; return LockWeak(m_parent); }
+ winrt::com_ptr PreviousSibling() const { std::unique_lock lock{ m_mutex }; return LockWeak(m_previousSibling); }
+ winrt::com_ptr NextSibling() const { std::unique_lock lock{ m_mutex }; return LockWeak(m_nextSibling); }
+ int* RuntimeId() { std::unique_lock lock{ m_mutex }; return reinterpret_cast(&(m_runtimeId[0])); }
+ int RuntimeIdSize() const { std::unique_lock lock{ m_mutex }; return static_cast(m_runtimeId.size()); }
+
+ // Automatically generate unique runtime IDs per fragment.
+ inline static unsigned __int32 s_nextAvailableInternalRuntimeId{ 0 };
+ std::array m_runtimeId{ UiaAppendRuntimeId, ++s_nextAvailableInternalRuntimeId };
+
+ winrt::weak_ref m_parent{ nullptr };
+ winrt::weak_ref m_previousSibling{ nullptr };
+ winrt::weak_ref m_nextSibling{ nullptr };
+ std::vector> m_children{};
+ std::vector> m_embeddedFragments{};
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationFragmentRoot.cpp b/LottieIsland/AutomationFragmentRoot.cpp
new file mode 100644
index 00000000..13c9005d
--- /dev/null
+++ b/LottieIsland/AutomationFragmentRoot.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "AutomationFragmentRoot.h"
+
+namespace AutomationHelpers
+{
+
+std::unique_ptr AutomationFragmentRoot::SetFragmentRootCallbackHandler(
+ IAutomationFragmentRootCallbackHandler* const handler)
+{
+ AddHandler(AutomationCallbackHandlerType::FragmentRoot, handler);
+ return AutomationCallbackRevoker::Create(GetWeak(), handler);
+}
+
+HRESULT __stdcall AutomationFragmentRoot::ElementProviderFromPoint(
+ _In_ double x,
+ _In_ double y,
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragment** fragment)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != fragment)
+ {
+ *fragment = nullptr;
+ if (auto handler = GetHandler(AutomationCallbackHandlerType::FragmentRoot))
+ {
+ handler->GetFragmentFromPointForAutomation(x, y, GetIUnknown()).copy_to(fragment);
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+HRESULT __stdcall AutomationFragmentRoot::GetFocus(
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragment** fragmentInFocus)
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (nullptr != fragmentInFocus)
+ {
+ *fragmentInFocus = nullptr;
+ if (auto handler = GetHandler(AutomationCallbackHandlerType::FragmentRoot))
+ {
+ handler->GetFragmentInFocusForAutomation(GetIUnknown()).copy_to(fragmentInFocus);
+ }
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+}
\ No newline at end of file
diff --git a/LottieIsland/AutomationFragmentRoot.h b/LottieIsland/AutomationFragmentRoot.h
new file mode 100644
index 00000000..15ee6a1d
--- /dev/null
+++ b/LottieIsland/AutomationFragmentRoot.h
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "AutomationFragment.h"
+
+namespace AutomationHelpers
+{
+
+struct AutomationFragmentRoot : winrt::implements
+{
+ // Automation callback handler.
+ [[nodiscard]] std::unique_ptr SetFragmentRootCallbackHandler(
+ IAutomationFragmentRootCallbackHandler* const handler);
+
+ // IRawElementProviderFragmentRoot implementation.
+ HRESULT __stdcall ElementProviderFromPoint(
+ _In_ double x,
+ _In_ double y,
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragment** fragment) final override;
+
+ HRESULT __stdcall GetFocus(
+ _COM_Outptr_opt_result_maybenull_ ::IRawElementProviderFragment** fragmentInFocus) final override;
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.def b/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.def
new file mode 100644
index 00000000..24e7c123
--- /dev/null
+++ b/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.def
@@ -0,0 +1,3 @@
+EXPORTS
+DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
+DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
diff --git a/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.idl b/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.idl
new file mode 100644
index 00000000..495050df
--- /dev/null
+++ b/LottieIsland/CommunityToolkit.WinAppSDK.LottieIsland.idl
@@ -0,0 +1,45 @@
+namespace CommunityToolkit.WinAppSDK.LottieIsland
+{
+ interface IAnimatedVisualFrameworkless
+ requires Windows.Foundation.IClosable
+ {
+ Windows.Foundation.TimeSpan Duration{ get; };
+ Microsoft.UI.Composition.Visual RootVisual{ get; };
+ Windows.Foundation.Numerics.Vector2 Size{ get; };
+ };
+
+ runtimeclass LottieContentIsland
+ {
+ static LottieContentIsland Create(Microsoft.UI.Composition.Compositor compositor);
+
+ IAnimatedVisualFrameworkless AnimatedVisual;
+
+ Windows.Foundation.TimeSpan Duration{ get; };
+
+ Boolean IsAnimationLoaded{ get; };
+
+ Microsoft.UI.Content.ContentIsland Island{ get; };
+
+ Boolean IsPlaying{ get; };
+
+ Windows.Foundation.IAsyncAction PlayAsync(Single fromProgress, Single toProgress, Boolean looped);
+
+ Single PlaybackRate;
+
+ void Pause();
+
+ void Resume();
+
+ void Stop();
+
+ event Windows.Foundation.TypedEventHandler PointerEntered;
+
+ event Windows.Foundation.TypedEventHandler PointerExited;
+
+ event Windows.Foundation.TypedEventHandler PointerMoved;
+
+ event Windows.Foundation.TypedEventHandler PointerPressed;
+
+ event Windows.Foundation.TypedEventHandler PointerReleased;
+ }
+}
diff --git a/LottieIsland/Directory.Build.props b/LottieIsland/Directory.Build.props
new file mode 100644
index 00000000..22b90cca
--- /dev/null
+++ b/LottieIsland/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+ $([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))
+ $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))
+ $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))
+
+
\ No newline at end of file
diff --git a/LottieIsland/LottieContentIsland.cpp b/LottieIsland/LottieContentIsland.cpp
new file mode 100644
index 00000000..a0fc5082
--- /dev/null
+++ b/LottieIsland/LottieContentIsland.cpp
@@ -0,0 +1,347 @@
+#include "pch.h"
+#include "LottieContentIsland.h"
+#include "LottieContentIsland.g.cpp"
+
+namespace winrt::CommunityToolkit::WinAppSDK::LottieIsland::implementation
+{
+ winrt::LottieContentIsland LottieContentIsland::Create(const winrt::Compositor& compositor)
+ {
+ return winrt::make(compositor);
+ }
+
+ LottieContentIsland::LottieContentIsland(
+ const winrt::Compositor& compositor)
+ : m_compositor(compositor)
+ {
+ m_rootVisual = m_compositor.CreateContainerVisual();
+ m_island = winrt::ContentIsland::Create(m_rootVisual);
+ m_island.AppData(get_strong().as());
+
+ m_island.AutomationProviderRequested({ get_weak(), &LottieContentIsland::OnIslandAutomationProviderRequested });
+ m_island.StateChanged({ get_weak(), &LottieContentIsland::OnIslandStateChanged });
+
+ // Once it's not experimental, we should use InputPointerSource::GetForVisual on our root visual.
+ // This will give us automatic hittesting for whatever content and shape the Lottie animation has.
+ // Currently hittesting will just be a rectangle the size of the island, regardless of content.
+ m_inputPointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(m_island);
+
+ InitializeInputHandlers();
+ }
+
+ LottieContentIsland::~LottieContentIsland()
+ {
+ // Dispose (Close) our island. This will revoke any event handlers from it or sub-objects, which
+ // is why the LottieContentIsland doesn't need to manually revoke event handlers.
+ m_island.Close();
+ }
+
+ winrt::IAnimatedVisualFrameworkless LottieContentIsland::AnimatedVisual() const
+ {
+ // Return the AnimatedVisualSource
+ return m_animatedVisual;
+ }
+
+ void LottieContentIsland::AnimatedVisual(winrt::IAnimatedVisualFrameworkless const& value)
+ {
+ if (m_animatedVisual == value)
+ {
+ return;
+ }
+
+ if (m_animatedVisual != nullptr)
+ {
+ StopAnimation();
+ m_rootVisual.Children().RemoveAll();
+ m_animatedVisual = nullptr;
+ }
+
+ if (value != nullptr)
+ {
+ // Set the AnimatedVisualSource
+ m_animatedVisual = value;
+
+ // Set up lottie
+ winrt::Visual lottieVisual = m_animatedVisual.RootVisual();
+ m_rootVisual.Children().InsertAtTop(lottieVisual);
+
+ // Tell our hosting environment that our size changed, and ask for confirmation of our ActualSize.
+ // Any changes will come back through a StateChanged notification
+ m_island.RequestSize(m_animatedVisual.Size());
+
+ // While that request is propagating, resize ourselves to fill the island's current size
+ Resize(m_island.ActualSize());
+
+ StartAnimation(0.0, 1.0, true /*loop*/);
+ }
+ }
+
+ winrt::Windows::Foundation::TimeSpan LottieContentIsland::Duration() const
+ {
+ if (m_animatedVisual == nullptr)
+ {
+ return 0ms;
+ }
+
+ return m_animatedVisual.Duration();
+ }
+
+ bool LottieContentIsland::IsAnimationLoaded() const
+ {
+ // Revisit this when we get JSON loading to work.
+ return m_animatedVisual != nullptr;
+ }
+
+ bool LottieContentIsland::IsPlaying() const
+ {
+ return m_progressPropertySet != nullptr;
+ }
+
+ float LottieContentIsland::PlaybackRate() const
+ {
+ return m_playbackRate;
+ }
+
+ void LottieContentIsland::PlaybackRate(float rate)
+ {
+ m_playbackRate = rate;
+ if (m_animationController != nullptr)
+ {
+ m_animationController.PlaybackRate(m_playbackRate);
+ }
+ }
+
+ void LottieContentIsland::Pause()
+ {
+ if (m_animationController != nullptr)
+ {
+ m_animationController.Pause();
+ }
+ }
+
+ winrt::Windows::Foundation::IAsyncAction LottieContentIsland::PlayAsync(float fromProgress, float toProgress, bool looped)
+ {
+ if (m_animationCompletionEvent.get() == nullptr)
+ {
+ m_animationCompletionEvent = winrt::handle(CreateEvent(nullptr, false, false, nullptr));
+ }
+
+ // Stop any existing animation
+ StopAnimation();
+
+ auto batch = m_compositor.CreateScopedBatch(CompositionBatchTypes::Animation);
+
+ StartAnimation(fromProgress, toProgress, looped);
+
+ // Keep track of whether the animation is looped, since we will have to
+ // manually fire the event if Stop() is called in the non-looped case.
+ // We don't hook up the event here in the looped case, because ScopedBatches
+ // complete immediately if their animation is looped.
+ m_looped = looped;
+ if (!looped)
+ {
+ // Hook up an event handler to the Completed event of the batch
+ batch.Completed([&](auto&&, auto&&)
+ {
+ // Set the completion event when the batch completes
+ SetEvent(m_animationCompletionEvent.get());
+ });
+ }
+
+ // Commit the batch
+ batch.End();
+
+ // Wait for the completion event asynchronously
+ co_await winrt::resume_on_signal(m_animationCompletionEvent.get()); // Wait for the event to be signaled
+ }
+
+ void LottieContentIsland::Resume()
+ {
+ if (m_animationController != nullptr)
+ {
+ m_animationController.Resume();
+ }
+ }
+
+ void LottieContentIsland::Stop()
+ {
+ StopAnimation();
+ }
+
+ winrt::Windows::Graphics::RectInt32 LottieContentIsland::GetBoundingRectangleInScreenSpaceForAutomation(
+ ::IUnknown const* const /*sender*/) const
+ {
+ // Convert our local bounds to screen space and return it to UI Automation.
+ auto coordinateConverter = m_island.CoordinateConverter();
+ auto actualSize = m_island.ActualSize();
+ winrt::Windows::Foundation::Rect islandLocalBounds{ 0, 0, actualSize.x, actualSize.y };
+ auto islandScreenBounds = coordinateConverter.ConvertLocalToScreen(islandLocalBounds);
+ return islandScreenBounds;
+ }
+
+ void LottieContentIsland::HandleSetFocusForAutomation(
+ ::IUnknown const* const /*sender*/)
+ {
+ // No-op.
+ }
+
+ winrt::com_ptr<::IRawElementProviderFragment> LottieContentIsland::GetFragmentFromPointForAutomation(
+ double /*x*/,
+ double /*y*/,
+ ::IUnknown const* const /*sender*/) const
+ {
+ // No child automation fragments.
+ return nullptr;
+ }
+
+ winrt::com_ptr<::IRawElementProviderFragment> LottieContentIsland::GetFragmentInFocusForAutomation(
+ ::IUnknown const* const /*sender*/) const
+ {
+ // No child automation fragments.
+ return nullptr;
+ }
+
+ void LottieContentIsland::HandleInvokeForAutomation(
+ ::IUnknown const* const /*sender*/)
+ {
+ if (nullptr != m_animatedVisual)
+ {
+ IsPlaying() ? StopAnimation() : StartAnimation(0.0f, 1.0f, false);
+ }
+ }
+
+ void LottieContentIsland::StartAnimation(float fromProgress, float toProgress, bool loop)
+ {
+ if (m_animatedVisual == nullptr)
+ {
+ throw winrt::hresult_illegal_method_call{ L"Cannot start an animation before the animation is loaded." };
+ }
+
+ auto animation = m_compositor.CreateScalarKeyFrameAnimation();
+ animation.Duration(m_animatedVisual.Duration());
+ auto linearEasing = m_compositor.CreateLinearEasingFunction();
+ animation.InsertKeyFrame(0, fromProgress);
+ animation.InsertKeyFrame(1, toProgress, linearEasing);
+ if (loop)
+ {
+ animation.IterationBehavior(winrt::AnimationIterationBehavior::Forever);
+ }
+ else
+ {
+ animation.IterationBehavior(winrt::AnimationIterationBehavior::Count);
+ animation.IterationCount(1);
+ }
+
+ m_progressPropertySet = m_animatedVisual.RootVisual().Properties();
+ m_progressPropertySet.StartAnimation(L"Progress", animation);
+ m_animationController = m_progressPropertySet.TryGetAnimationController(L"Progress");
+ m_animationController.PlaybackRate(m_playbackRate);
+ m_previousFromProgress = fromProgress;
+ }
+
+ void LottieContentIsland::StopAnimation()
+ {
+ if (!IsPlaying())
+ {
+ // No-op
+ return;
+ }
+
+ // Stop and snap to the beginning of the animation
+ m_progressPropertySet.StopAnimation(L"Progress");
+ m_progressPropertySet.InsertScalar(L"Progress", m_previousFromProgress);
+
+ if (m_looped)
+ {
+ SetEvent(m_animationCompletionEvent.get());
+ }
+
+ // Cleanup
+ m_previousFromProgress = 0.0;
+ m_animationController = nullptr;
+ m_progressPropertySet = nullptr;
+ }
+
+ void LottieContentIsland::OnIslandAutomationProviderRequested(
+ const winrt::ContentIsland& island,
+ const winrt::ContentIslandAutomationProviderRequestedEventArgs& args)
+ {
+ if (nullptr == m_automationProvider)
+ {
+ // We need to create the automation provider.
+ m_automationProvider = winrt::make_self();
+ m_automationProvider->Name(L"Lottie");
+
+ // Register ourselves as the callback for our automation provider.
+ m_fragmentCallbackRevoker = m_automationProvider->SetFragmentCallbackHandler(this);
+ m_fragmentRootCallbackRevoker = m_automationProvider->SetFragmentRootCallbackHandler(this);
+ m_invokeCallbackRevoker = m_automationProvider->SetInvokeCallbackHandler(this);
+
+ // Set up the host provider.
+ auto hostProviderAsIInspectable = island.GetAutomationHostProvider();
+ m_automationProvider->HostProvider(hostProviderAsIInspectable.try_as<::IRawElementProviderSimple>());
+ }
+
+ args.AutomationProvider(m_automationProvider.as());
+ args.Handled(true);
+ }
+
+ void LottieContentIsland::OnIslandStateChanged(const winrt::ContentIsland& /*island*/, const winrt::ContentIslandStateChangedEventArgs& args)
+ {
+ if (args.DidActualSizeChange() && IsAnimationLoaded())
+ {
+ Resize(m_island.ActualSize());
+ }
+ }
+
+ void LottieContentIsland::Resize(const float2& newSize)
+ {
+ float2 desiredSize = m_animatedVisual.Size();
+ if (newSize.x == 0 || newSize.y == 0 || desiredSize.x == 0 || desiredSize.y == 0)
+ {
+ // Don't try to scale (and hit fun divide by 0) if we have no effective size
+ m_rootVisual.Size({ 0, 0 });
+ }
+ else
+ {
+ // We implement Uniform stretching here, where we don't overflow bounds,
+ // but keep aspect ratio.
+ float2 scale = newSize / m_animatedVisual.Size();
+
+ // Take the smaller scale and set both axes to that.
+ if (scale.x < scale.y)
+ {
+ scale.y = scale.x;
+ }
+ else
+ {
+ scale.x = scale.y;
+ }
+
+ m_rootVisual.Size(desiredSize);
+ m_rootVisual.Scale({ scale.x, scale.y, 1.f });
+ }
+ }
+
+ void LottieContentIsland::InitializeInputHandlers()
+ {
+ m_inputPointerSource.PointerEntered([this](auto& /*sender*/, auto& args) {
+ m_pointerEnteredEvent(*this, args);
+ });
+
+ m_inputPointerSource.PointerExited([this](auto& /*sender*/, auto& args) {
+ m_pointerExitedEvent(*this, args);
+ });
+
+ m_inputPointerSource.PointerMoved([this](auto& /*sender*/, auto& args) {
+ m_pointerMovedEvent(*this, args);
+ });
+
+ m_inputPointerSource.PointerPressed([this](auto& /*sender*/, auto& args) {
+ m_pointerPressedEvent(*this, args);
+ });
+
+ m_inputPointerSource.PointerReleased([this](auto& /*sender*/, auto& args) {
+ m_pointerReleasedEvent(*this, args);
+ });
+ }
+}
diff --git a/LottieIsland/LottieContentIsland.h b/LottieIsland/LottieContentIsland.h
new file mode 100644
index 00000000..0653c33c
--- /dev/null
+++ b/LottieIsland/LottieContentIsland.h
@@ -0,0 +1,126 @@
+#pragma once
+
+#include "LottieContentIsland.g.h"
+#include "winrt/CommunityToolkit.WinAppSDK.LottieIsland.h"
+#include "LottieIslandAutomationProvider.h"
+
+namespace winrt
+{
+ using namespace ::winrt::CommunityToolkit::WinAppSDK::LottieIsland;
+}
+
+namespace winrt::CommunityToolkit::WinAppSDK::LottieIsland::implementation
+{
+ struct LottieContentIsland : LottieContentIslandT,
+ AutomationHelpers::IAutomationFragmentCallbackHandler,
+ AutomationHelpers::IAutomationFragmentRootCallbackHandler,
+ AutomationHelpers::IAutomationInvokeCallbackHandler
+ {
+ using PointerEventHandler = Windows::Foundation::TypedEventHandler;
+
+ static winrt::LottieContentIsland Create(const winrt::Compositor& compositor);
+
+ LottieContentIsland(const winrt::Compositor& compositor);
+ ~LottieContentIsland();
+
+ winrt::ContentIsland Island() const
+ {
+ return m_island;
+ }
+
+ winrt::IAnimatedVisualFrameworkless AnimatedVisual() const;
+ void AnimatedVisual(const winrt::IAnimatedVisualFrameworkless& source);
+
+ winrt::Windows::Foundation::TimeSpan Duration() const;
+
+ bool IsAnimationLoaded() const;
+
+ bool IsPlaying() const;
+
+ float PlaybackRate() const;
+ void PlaybackRate(float rate);
+
+ winrt::event_token PointerEntered(const PointerEventHandler& handler) { return m_pointerEnteredEvent.add(handler); }
+ void PointerEntered(winrt::event_token const& token) noexcept { m_pointerEnteredEvent.remove(token); }
+
+ winrt::event_token PointerExited(const PointerEventHandler& handler) { return m_pointerExitedEvent.add(handler); }
+ void PointerExited(winrt::event_token const& token) noexcept { m_pointerExitedEvent.remove(token); }
+
+ winrt::event_token PointerMoved(const PointerEventHandler& handler) { return m_pointerMovedEvent.add(handler); }
+ void PointerMoved(winrt::event_token const& token) noexcept { m_pointerMovedEvent.remove(token); }
+
+ winrt::event_token PointerPressed(const PointerEventHandler& handler) { return m_pointerPressedEvent.add(handler); }
+ void PointerPressed(winrt::event_token const& token) noexcept { m_pointerPressedEvent.remove(token); }
+
+ winrt::event_token PointerReleased(const PointerEventHandler& handler) { return m_pointerReleasedEvent.add(handler); }
+ void PointerReleased(winrt::event_token const& token) noexcept { m_pointerReleasedEvent.remove(token); }
+
+ void Pause();
+
+ winrt::Windows::Foundation::IAsyncAction PlayAsync(float fromProgress, float toProgress, bool looped);
+
+ void Resume();
+
+ void Stop();
+
+ // UI Automation callback implementation.
+ winrt::Windows::Graphics::RectInt32 GetBoundingRectangleInScreenSpaceForAutomation(
+ ::IUnknown const* const sender) const final override;
+
+ void HandleSetFocusForAutomation(
+ ::IUnknown const* const sender) final override;
+
+ winrt::com_ptr<::IRawElementProviderFragment> GetFragmentFromPointForAutomation(
+ double x,
+ double y,
+ ::IUnknown const* const sender) const final override;
+
+ winrt::com_ptr<::IRawElementProviderFragment> GetFragmentInFocusForAutomation(
+ ::IUnknown const* const sender) const final override;
+
+ void HandleInvokeForAutomation(
+ ::IUnknown const* const sender) final override;
+
+ private:
+ void StartAnimation(float fromProgress, float toProgress, bool loop);
+ void StopAnimation();
+
+ void OnIslandAutomationProviderRequested(const winrt::ContentIsland& island, const winrt::ContentIslandAutomationProviderRequestedEventArgs& args);
+ void OnIslandStateChanged(const winrt::ContentIsland& island, const winrt::ContentIslandStateChangedEventArgs& args);
+
+ void Resize(const float2& size);
+
+ void InitializeInputHandlers();
+
+ winrt::event m_pointerEnteredEvent;
+ winrt::event m_pointerExitedEvent;
+ winrt::event m_pointerMovedEvent;
+ winrt::event m_pointerPressedEvent;
+ winrt::event m_pointerReleasedEvent;
+
+ // UI Automation.
+ winrt::com_ptr m_automationProvider{ nullptr };
+ std::unique_ptr m_fragmentCallbackRevoker{ nullptr };
+ std::unique_ptr m_fragmentRootCallbackRevoker{ nullptr };
+ std::unique_ptr m_invokeCallbackRevoker{ nullptr };
+
+ winrt::Compositor m_compositor{ nullptr };
+ winrt::ContainerVisual m_rootVisual{ nullptr };
+ winrt::ContentIsland m_island{ nullptr };
+ winrt::InputPointerSource m_inputPointerSource{ nullptr };
+ winrt::IAnimatedVisualFrameworkless m_animatedVisual{ nullptr };
+ winrt::CompositionPropertySet m_progressPropertySet{ nullptr };
+ winrt::AnimationController m_animationController{ nullptr };
+ float m_previousFromProgress = 0.0;
+ float m_playbackRate = 1.0f;
+ winrt::handle m_animationCompletionEvent{ nullptr };
+ bool m_looped;
+ };
+}
+
+namespace winrt::CommunityToolkit::WinAppSDK::LottieIsland::factory_implementation
+{
+ struct LottieContentIsland : LottieContentIslandT
+ {
+ };
+}
diff --git a/LottieIsland/LottieIsland.vcxproj b/LottieIsland/LottieIsland.vcxproj
new file mode 100644
index 00000000..bc881453
--- /dev/null
+++ b/LottieIsland/LottieIsland.vcxproj
@@ -0,0 +1,180 @@
+
+
+
+
+
+
+ true
+ true
+ true
+ {9ba7a2f2-b723-458b-a575-3f726d271465}
+ LottieIsland
+ CommunityToolkit.WinAppSDK.LottieIsland
+ CommunityToolkit.WinAppSDK.LottieIsland
+
+ 0.2.7-prerelease
+ en-US
+ 14.0
+ Windows Store
+ 10.0
+ 10.0.19041.0
+ 10.0.17134.0
+ None
+ Win32Proj
+
+
+
+
+ Debug
+ x64
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ DynamicLibrary
+ v143
+ Unicode
+ false
+
+
+ true
+ true
+
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Use
+ pch.h
+ $(IntDir)pch.pch
+ Level4
+ %(AdditionalOptions) /bigobj
+ _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)
+ $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
+
+
+ Console
+ false
+ CommunityToolkit.WinAppSDK.LottieIsland.def
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland.idl
+
+
+
+
+
+
+
+
+
+ Create
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland.idl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LottieIsland/LottieIsland.vcxproj.filters b/LottieIsland/LottieIsland.vcxproj.filters
new file mode 100644
index 00000000..fe286ca2
--- /dev/null
+++ b/LottieIsland/LottieIsland.vcxproj.filters
@@ -0,0 +1,74 @@
+
+
+
+
+ accd3aa8-1ba0-4223-9bbe-0c431709210b
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {926ab91d-31b4-48c3-b9a4-e681349f27f0}
+
+
+ {8de2e0c6-cc70-4b56-a68f-dd3ec76ce5fc}
+
+
+ {f244735f-9786-4673-9823-f1b890d98b19}
+
+
+
+
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+
+
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+ Automation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LottieIsland/LottieIslandAutomationProvider.cpp b/LottieIsland/LottieIslandAutomationProvider.cpp
new file mode 100644
index 00000000..7a05f90d
--- /dev/null
+++ b/LottieIsland/LottieIslandAutomationProvider.cpp
@@ -0,0 +1,33 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "LottieIslandAutomationProvider.h"
+
+namespace LottieIslandInternal
+{
+
+
+std::unique_ptr LottieIslandAutomationProvider::SetInvokeCallbackHandler(
+ AutomationHelpers::IAutomationInvokeCallbackHandler* const handler)
+{
+ AddHandler(AutomationHelpers::AutomationCallbackHandlerType::Invoke, handler);
+ return AutomationHelpers::AutomationCallbackRevoker::Create(GetWeak(), handler);
+}
+
+
+HRESULT __stdcall LottieIslandAutomationProvider::Invoke()
+{
+ try
+ {
+ std::unique_lock lock{ m_mutex };
+ if (auto handler = GetHandler(
+ AutomationHelpers::AutomationCallbackHandlerType::Invoke))
+ {
+ handler->HandleInvokeForAutomation(GetIUnknown());
+ }
+ }
+ catch (...) { return UIA_E_ELEMENTNOTAVAILABLE; }
+ return S_OK;
+}
+
+}
\ No newline at end of file
diff --git a/LottieIsland/LottieIslandAutomationProvider.h b/LottieIsland/LottieIslandAutomationProvider.h
new file mode 100644
index 00000000..5b50952c
--- /dev/null
+++ b/LottieIsland/LottieIslandAutomationProvider.h
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "AutomationFragmentRoot.h"
+
+namespace LottieIslandInternal
+{
+
+struct LottieIslandAutomationProvider : winrt::implements
+{
+ // Automation callback handler.
+ [[nodiscard]] std::unique_ptr SetInvokeCallbackHandler(
+ AutomationHelpers::IAutomationInvokeCallbackHandler* const handler);
+
+ // IInvokeProvider implementation.
+ HRESULT __stdcall Invoke() final override;
+};
+
+}
\ No newline at end of file
diff --git a/LottieIsland/PropertySheet.props b/LottieIsland/PropertySheet.props
new file mode 100644
index 00000000..e34141b0
--- /dev/null
+++ b/LottieIsland/PropertySheet.props
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LottieIsland/packages.config b/LottieIsland/packages.config
new file mode 100644
index 00000000..c4397c32
--- /dev/null
+++ b/LottieIsland/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LottieIsland/pch.cpp b/LottieIsland/pch.cpp
new file mode 100644
index 00000000..bcb5590b
--- /dev/null
+++ b/LottieIsland/pch.cpp
@@ -0,0 +1 @@
+#include "pch.h"
diff --git a/LottieIsland/pch.h b/LottieIsland/pch.h
new file mode 100644
index 00000000..8b532686
--- /dev/null
+++ b/LottieIsland/pch.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include
+
+#include
+
+// Xaml has a GetCurrentTime, and somewhere in the windows sdk there's a macro for it.
+// These conflict and cause issues.
+#undef GetCurrentTime
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace winrt
+{
+ using namespace ::winrt::Microsoft::UI::Composition;
+ using namespace ::winrt::Microsoft::UI::Content;
+ using namespace ::winrt::Microsoft::UI::Input;
+}
+
+// Opt into time literals (i.e. 200ms, 1min, 15s)
+using namespace std::chrono_literals;
+
+using float2 = winrt::Windows::Foundation::Numerics::float2;
diff --git a/LottieIslandProjection/Directory.Build.props b/LottieIslandProjection/Directory.Build.props
new file mode 100644
index 00000000..22b90cca
--- /dev/null
+++ b/LottieIslandProjection/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+ $([MSBuild]::NormalizeDirectory('$(SolutionDir)', '_build', '$(Platform)', '$(Configuration)'))
+ $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'bin'))
+ $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj'))
+
+
\ No newline at end of file
diff --git a/LottieIslandProjection/LottieIslandProjection.csproj b/LottieIslandProjection/LottieIslandProjection.csproj
new file mode 100644
index 00000000..14ddd1bb
--- /dev/null
+++ b/LottieIslandProjection/LottieIslandProjection.csproj
@@ -0,0 +1,31 @@
+
+
+
+ LottieIslandProjection
+ net7.0-windows10.0.19041.0
+ CommunityToolkit.WinAppSDK.LottieIsland
+
+ 0.2.7-prerelease
+
+ Any CPU
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland.Projection
+ $(MSBuildThisFileDirectory)..\bin\nupkg\
+ True
+ nuget\CommunityToolkit.WinAppSDK.LottieIsland.nuspec
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland
+ $(OutDir)
+ None
+
+
+
+
+
+
+
+
+
+
diff --git a/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-WinUI3.targets b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-WinUI3.targets
new file mode 100644
index 00000000..b3495602
--- /dev/null
+++ b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-WinUI3.targets
@@ -0,0 +1,27 @@
+
+
+
+
+ x64
+ x86
+
+
+
+ x86
+ $(Platform)
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-managed.targets b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-managed.targets
new file mode 100644
index 00000000..f80afb25
--- /dev/null
+++ b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-managed.targets
@@ -0,0 +1,31 @@
+
+
+
+
+ x64
+ x86
+
+
+
+ x86
+ $(Platform)
+
+
+
+
+ true
+
+
+
+
+ $(MSBuildThisFileDirectory)..\..\lib\uap10.0\CommunityToolkit.WinAppSDK.LottieIsland.winmd
+ CommunityToolkit.WinAppSDK.LottieIsland.dll
+
+
+
+
+
+
+
+
+
diff --git a/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-native.targets b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-native.targets
new file mode 100644
index 00000000..c26e7ef5
--- /dev/null
+++ b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-native.targets
@@ -0,0 +1,25 @@
+
+
+
+
+ x86
+ $(Platform)
+
+
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland.dll
+
+
+
+
+
+
+
+ $(MSBuildThisFileDirectory)..\..\Include;
+ $(MSBuildThisFileDirectory)..\..\Include\$(lottieisland-Platform);
+ %(AdditionalIncludeDirectories);
+
+
+
+
diff --git a/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-win10.targets b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-win10.targets
new file mode 100644
index 00000000..671e0291
--- /dev/null
+++ b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland-win10.targets
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland.nuspec b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland.nuspec
new file mode 100644
index 00000000..4bdb0a9d
--- /dev/null
+++ b/LottieIslandProjection/nuget/CommunityToolkit.WinAppSDK.LottieIsland.nuspec
@@ -0,0 +1,44 @@
+
+
+
+ CommunityToolkit.WinAppSDK.LottieIsland
+ Microsoft
+
+ 0.2.7-prerelease
+ A ContentIsland to host Lottie Animations
+ images\icon.png
+ docs\readme.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LottieIslandProjection/nuget/icon.png b/LottieIslandProjection/nuget/icon.png
new file mode 100644
index 00000000..5cae1f76
Binary files /dev/null and b/LottieIslandProjection/nuget/icon.png differ
diff --git a/LottieIslandProjection/nuget/readme.md b/LottieIslandProjection/nuget/readme.md
new file mode 100644
index 00000000..6ff4aa2b
--- /dev/null
+++ b/LottieIslandProjection/nuget/readme.md
@@ -0,0 +1,7 @@
+# LottieIsland
+
+This project shows a sample ContentIsland that hosts a Lottie animation.
+
+For more information on Lottie animations, check out [Lottie-Windows](https://github.com/CommunityToolkit/Lottie-Windows) on GitHub!
+
+To generate a nuget package, pack the LottieIslandProjection project using Visual Studio. Make sure you have built LottieIsland for x86, x64, and ARM64 Release, and built/pack LottieIslandProjection for Release|AnyCPU.
\ No newline at end of file
diff --git a/LottieWinRT/LottieVisualSourceWinRT.cs b/LottieWinRT/LottieVisualSourceWinRT.cs
new file mode 100644
index 00000000..c13e9a9a
--- /dev/null
+++ b/LottieWinRT/LottieVisualSourceWinRT.cs
@@ -0,0 +1,76 @@
+// 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;
+using CommunityToolkit.WinAppSDK.LottieIsland;
+using CommunityToolkit.WinUI.Lottie;
+using Microsoft.UI.Composition;
+using LottieIsland = CommunityToolkit.WinAppSDK.LottieIsland;
+
+namespace CommunityToolkit.WinAppSDK.LottieWinRT
+{
+ public sealed class LottieVisualSourceWinRT
+ {
+ public event EventHandler