diff --git a/Microsoft.Toolkit/Attributes/DoesNotReturnAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnAttribute.cs
similarity index 100%
rename from Microsoft.Toolkit/Attributes/DoesNotReturnAttribute.cs
rename to Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnAttribute.cs
diff --git a/Microsoft.Toolkit/Attributes/DoesNotReturnIfAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnIfAttribute.cs
similarity index 100%
rename from Microsoft.Toolkit/Attributes/DoesNotReturnIfAttribute.cs
rename to Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnIfAttribute.cs
diff --git a/Microsoft.Toolkit/Attributes/NotNullAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/NotNullAttribute.cs
similarity index 100%
rename from Microsoft.Toolkit/Attributes/NotNullAttribute.cs
rename to Microsoft.Toolkit.Diagnostics/Attributes/NotNullAttribute.cs
diff --git a/Microsoft.Toolkit/Attributes/SkipLocalsInitAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/SkipLocalsInitAttribute.cs
similarity index 100%
rename from Microsoft.Toolkit/Attributes/SkipLocalsInitAttribute.cs
rename to Microsoft.Toolkit.Diagnostics/Attributes/SkipLocalsInitAttribute.cs
diff --git a/Microsoft.Toolkit/Extensions/TypeExtensions.cs b/Microsoft.Toolkit.Diagnostics/Extensions/TypeExtensions.cs
similarity index 100%
rename from Microsoft.Toolkit/Extensions/TypeExtensions.cs
rename to Microsoft.Toolkit.Diagnostics/Extensions/TypeExtensions.cs
diff --git a/Microsoft.Toolkit/Extensions/ValueTypeExtensions.cs b/Microsoft.Toolkit.Diagnostics/Extensions/ValueTypeExtensions.cs
similarity index 100%
rename from Microsoft.Toolkit/Extensions/ValueTypeExtensions.cs
rename to Microsoft.Toolkit.Diagnostics/Extensions/ValueTypeExtensions.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/Guard.Collection.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.g.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/Guard.Collection.g.cs
rename to Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.g.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/Guard.Collection.tt b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.tt
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/Guard.Collection.tt
rename to Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.tt
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.g.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs
rename to Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.g.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/Guard.Comparable.Numeric.tt b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.tt
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/Guard.Comparable.Numeric.tt
rename to Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.tt
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/Guard.md b/Microsoft.Toolkit.Diagnostics/Generated/Guard.md
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/Guard.md
rename to Microsoft.Toolkit.Diagnostics/Generated/Guard.md
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/ThrowHelper.Collection.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.g.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/ThrowHelper.Collection.g.cs
rename to Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.g.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/ThrowHelper.Collection.tt b/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.tt
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/ThrowHelper.Collection.tt
rename to Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.tt
diff --git a/Microsoft.Toolkit/Diagnostics/Generated/TypeInfo.ttinclude b/Microsoft.Toolkit.Diagnostics/Generated/TypeInfo.ttinclude
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Generated/TypeInfo.ttinclude
rename to Microsoft.Toolkit.Diagnostics/Generated/TypeInfo.ttinclude
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.Comparable.Generic.cs b/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Generic.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.Comparable.Generic.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.Comparable.Generic.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.Comparable.Numeric.cs b/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Numeric.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.Comparable.Numeric.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.Comparable.Numeric.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.IO.cs b/Microsoft.Toolkit.Diagnostics/Guard.IO.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.IO.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.IO.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.String.cs b/Microsoft.Toolkit.Diagnostics/Guard.String.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.String.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.String.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.Tasks.cs b/Microsoft.Toolkit.Diagnostics/Guard.Tasks.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.Tasks.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.Tasks.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Guard.cs b/Microsoft.Toolkit.Diagnostics/Guard.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Guard.cs
rename to Microsoft.Toolkit.Diagnostics/Guard.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.IO.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.IO.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.IO.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.IO.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.String.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.String.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs
diff --git a/Microsoft.Toolkit/Diagnostics/Internals/Guard.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/Internals/Guard.ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/Internals/Guard.ThrowHelper.cs
diff --git a/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj b/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj
new file mode 100644
index 00000000000..4d541b559e3
--- /dev/null
+++ b/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj
@@ -0,0 +1,97 @@
+
+
+
+ netstandard1.4;netstandard2.0;netstandard2.1;net5.0
+ 9.0
+ true
+ enable
+ Windows Community Toolkit Diagnostics .NET Standard
+
+ This package includes .NET Standard code only helpers such as:
+ - Guard: Helper methods to verify conditions when running code.
+ - ThrowHelper: Helper methods to efficiently throw exceptions.
+
+ UWP Toolkit Windows IncrementalLoadingCollection String Array extensions helpers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NETSTANDARD2_1_OR_GREATER
+
+
+
+
+
+
+
+
+
+ NETSTANDARD2_1_OR_GREATER
+
+
+
+
+
+
+ TextTemplatingFileGenerator
+ Guard.Comparable.Numeric.g.cs
+
+
+ TextTemplatingFileGenerator
+ Guard.Collection.g.cs
+
+
+ TextTemplatingFileGenerator
+ ThrowHelper.Collection.g.cs
+
+
+ TextTemplatingFileGenerator
+ TypeInfo.g.cs
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Guard.Comparable.Numeric.tt
+
+
+ True
+ True
+ Guard.Collection.tt
+
+
+ True
+ True
+ ThrowHelper.Collection.tt
+
+
+ True
+ True
+ TypeInfo.ttinclude
+
+
+
+
diff --git a/Microsoft.Toolkit/Diagnostics/ThrowHelper.Generic.cs b/Microsoft.Toolkit.Diagnostics/ThrowHelper.Generic.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/ThrowHelper.Generic.cs
rename to Microsoft.Toolkit.Diagnostics/ThrowHelper.Generic.cs
diff --git a/Microsoft.Toolkit/Diagnostics/ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/ThrowHelper.cs
similarity index 100%
rename from Microsoft.Toolkit/Diagnostics/ThrowHelper.cs
rename to Microsoft.Toolkit.Diagnostics/ThrowHelper.cs
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs
index 5eb18772cb8..89781377df8 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs
@@ -11,7 +11,6 @@
#endif
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
-using Microsoft.Toolkit.HighPerformance.Memory;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
namespace Microsoft.Toolkit.HighPerformance.Extensions
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs
index 5c683047593..77ad9983a6e 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs
@@ -10,7 +10,6 @@
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
#endif
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
-using Microsoft.Toolkit.HighPerformance.Memory;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
namespace Microsoft.Toolkit.HighPerformance.Extensions
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs
index 4fd99b2303b..e4fa0a6df73 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs
@@ -23,7 +23,14 @@ public static class BoolExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe byte ToByte(this bool flag)
{
- return *(byte*)&flag;
+ // Whenever we need to take the address of an argument, we make a local copy first.
+ // This will be removed by the JIT anyway, but it can help produce better codegen and
+ // remove unwanted stack spills if the caller is using constant arguments. This is
+ // because taking the address of an argument can interfere with some of the flow
+ // analysis executed by the JIT, which can in some cases block constant propagation.
+ bool copy = flag;
+
+ return *(byte*)©
}
///
@@ -58,7 +65,8 @@ public static unsafe int ToInt(this bool flag)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int ToBitwiseMask32(this bool flag)
{
- byte rangeFlag = *(byte*)&flag;
+ bool copy = flag;
+ byte rangeFlag = *(byte*)©
int
negativeFlag = rangeFlag - 1,
mask = ~negativeFlag;
@@ -77,7 +85,8 @@ public static unsafe int ToBitwiseMask32(this bool flag)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long ToBitwiseMask64(this bool flag)
{
- byte rangeFlag = *(byte*)&flag;
+ bool copy = flag;
+ byte rangeFlag = *(byte*)©
long
negativeFlag = (long)rangeFlag - 1,
mask = ~negativeFlag;
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs
index a175a02b577..411fb5f390b 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs
@@ -7,9 +7,6 @@
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-#if SPAN_RUNTIME_SUPPORT
-using Microsoft.Toolkit.HighPerformance.Memory;
-#endif
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
namespace Microsoft.Toolkit.HighPerformance.Extensions
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs
index d9d996717b0..5e4a07df9ea 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs
@@ -10,9 +10,6 @@
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces;
-#if SPAN_RUNTIME_SUPPORT
-using Microsoft.Toolkit.HighPerformance.Memory;
-#endif
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
namespace Microsoft.Toolkit.HighPerformance.Extensions
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
index bbfb67c4721..2f81a359ff4 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs
@@ -8,9 +8,6 @@
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
-#if SPAN_RUNTIME_SUPPORT
-using Microsoft.Toolkit.HighPerformance.Memory;
-#endif
namespace Microsoft.Toolkit.HighPerformance.Extensions
{
diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs
index d70c743f56b..8ad885e0168 100644
--- a/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs
+++ b/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs
@@ -8,9 +8,6 @@
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
-#if SPAN_RUNTIME_SUPPORT
-using Microsoft.Toolkit.HighPerformance.Memory;
-#endif
namespace Microsoft.Toolkit.HighPerformance.Extensions
{
diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs b/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs
index e0411b2d8a9..3e1f5ea876e 100644
--- a/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs
+++ b/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs
@@ -209,8 +209,9 @@ public static unsafe uint SetFlag(uint value, int n, bool flag)
// and perform an OR with the resulting value of the previous
// operation. This will always guaranteed to work, thanks to the
// initial code clearing that bit before setting it again.
+ bool copy = flag;
uint
- flag32 = *(byte*)&flag,
+ flag32 = *(byte*)©,
shift = flag32 << n,
or = and | shift;
@@ -378,8 +379,9 @@ public static unsafe ulong SetFlag(ulong value, int n, bool flag)
ulong
bit = 1ul << n,
not = ~bit,
- and = value & not,
- flag64 = *(byte*)&flag,
+ and = value & not;
+ bool copy = flag;
+ ulong flag64 = *(byte*)©,
shift = flag64 << n,
or = and | shift;
diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
index 99ff3bbd5a1..2aed6d2e214 100644
--- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
+++ b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs
@@ -5,7 +5,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
-using Microsoft.Toolkit.HighPerformance.Memory;
namespace Microsoft.Toolkit.HighPerformance.Helpers
{
diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
index d25d9dfee47..31f3ea45fa0 100644
--- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
+++ b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs
@@ -5,7 +5,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
-using Microsoft.Toolkit.HighPerformance.Memory;
namespace Microsoft.Toolkit.HighPerformance.Helpers
{
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs
index 4d1a3c69634..bdd425a99e9 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs
@@ -19,7 +19,7 @@
#pragma warning disable CA2231
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
/// represents a 2D region of arbitrary memory. It is to
@@ -894,7 +894,7 @@ public override int GetHashCode()
///
public override string ToString()
{
- return $"Microsoft.Toolkit.HighPerformance.Memory.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
+ return $"Microsoft.Toolkit.HighPerformance.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
}
///
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs
index 4c5fe4dfff4..9f3ea970b86 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs
@@ -19,7 +19,7 @@
#pragma warning disable CA2231
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
/// A readonly version of .
@@ -907,7 +907,7 @@ public override int GetHashCode()
///
public override string ToString()
{
- return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
+ return $"Microsoft.Toolkit.HighPerformance.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
}
///
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs
index 30dfcbc6406..24f6e504462 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs
@@ -13,7 +13,7 @@
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#endif
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
public readonly ref partial struct ReadOnlySpan2D
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs
index 11b762ebaa3..46564207512 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs
@@ -17,7 +17,7 @@
#pragma warning disable CS0809, CA1065
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
/// A readonly version of .
@@ -974,7 +974,7 @@ public override int GetHashCode()
///
public override string ToString()
{
- return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
+ return $"Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
}
///
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs
index fc5baf9b930..99e796d7a79 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs
@@ -13,7 +13,7 @@
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#endif
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
public readonly ref partial struct Span2D
diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs
index 9d57eb334e5..7f9009ad947 100644
--- a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs
+++ b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs
@@ -17,7 +17,7 @@
#pragma warning disable CS0809, CA1065
-namespace Microsoft.Toolkit.HighPerformance.Memory
+namespace Microsoft.Toolkit.HighPerformance
{
///
/// represents a 2D region of arbitrary memory. Like the type,
@@ -1130,7 +1130,7 @@ public override int GetHashCode()
///
public override string ToString()
{
- return $"Microsoft.Toolkit.HighPerformance.Memory.Span2D<{typeof(T)}>[{Height}, {this.width}]";
+ return $"Microsoft.Toolkit.HighPerformance.Span2D<{typeof(T)}>[{Height}, {this.width}]";
}
///
diff --git a/Microsoft.Toolkit.Uwp.Notifications/Microsoft.Toolkit.Uwp.Notifications.csproj b/Microsoft.Toolkit.Uwp.Notifications/Microsoft.Toolkit.Uwp.Notifications.csproj
index c980f7c4205..2d7fa085a7a 100644
--- a/Microsoft.Toolkit.Uwp.Notifications/Microsoft.Toolkit.Uwp.Notifications.csproj
+++ b/Microsoft.Toolkit.Uwp.Notifications/Microsoft.Toolkit.Uwp.Notifications.csproj
@@ -35,6 +35,11 @@
+
+
+
+
+
diff --git a/Microsoft.Toolkit.Uwp.Notifications/Properties/Microsoft.Toolkit.Uwp.Notifications.rd.xml b/Microsoft.Toolkit.Uwp.Notifications/Properties/Microsoft.Toolkit.Uwp.Notifications.rd.xml
new file mode 100644
index 00000000000..f4e4b4f2b45
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.Notifications/Properties/Microsoft.Toolkit.Uwp.Notifications.rd.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.Notifications/Toasts/Builder/ToastContentBuilder.Actions.cs b/Microsoft.Toolkit.Uwp.Notifications/Toasts/Builder/ToastContentBuilder.Actions.cs
index 32e50870076..8e6c55a86c9 100644
--- a/Microsoft.Toolkit.Uwp.Notifications/Toasts/Builder/ToastContentBuilder.Actions.cs
+++ b/Microsoft.Toolkit.Uwp.Notifications/Toasts/Builder/ToastContentBuilder.Actions.cs
@@ -112,7 +112,7 @@ public ToastContentBuilder AddButton(string content, ToastActivationType activat
/// The current instance of
public ToastContentBuilder AddButton(IToastButton button)
{
- if (button is ToastButton toastButton && toastButton.Content == null)
+ if (button is ToastButton toastButton && toastButton.Content == null && toastButton.NeedsContent())
{
throw new InvalidOperationException("Content is required on button.");
}
diff --git a/Microsoft.Toolkit.Uwp.Notifications/Toasts/ToastButton.cs b/Microsoft.Toolkit.Uwp.Notifications/Toasts/ToastButton.cs
index a564d274984..feb023faefe 100644
--- a/Microsoft.Toolkit.Uwp.Notifications/Toasts/ToastButton.cs
+++ b/Microsoft.Toolkit.Uwp.Notifications/Toasts/ToastButton.cs
@@ -20,6 +20,17 @@ public sealed class ToastButton :
private bool _usingCustomArguments;
+ private bool _usingSnoozeActivation;
+ private string _snoozeSelectionBoxId;
+
+ private bool _usingDismissActivation;
+
+ internal bool NeedsContent()
+ {
+ // Snooze/dismiss buttons don't need content (the system will auto-add the localized strings).
+ return !_usingDismissActivation && !_usingSnoozeActivation;
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -213,6 +224,11 @@ private ToastButton AddArgumentHelper(string key, string value)
throw new InvalidOperationException("You cannot use the AddArgument methods when using protocol activation.");
}
+ if (_usingDismissActivation || _usingSnoozeActivation)
+ {
+ throw new InvalidOperationException("You cannot use the AddArgument methods when using dismiss or snooze activation.");
+ }
+
bool alreadyExists = _arguments.ContainsKey(key);
_arguments[key] = value;
@@ -314,6 +330,48 @@ public ToastButton SetAfterActivationBehavior(ToastAfterActivationBehavior after
return this;
}
+ ///
+ /// Configures the button to use system snooze activation when the button is clicked, using the default system snooze time.
+ ///
+ /// The current instance of
+ public ToastButton SetSnoozeActivation()
+ {
+ return SetSnoozeActivation(null);
+ }
+
+ ///
+ /// Configures the button to use system snooze activation when the button is clicked, with a snooze time defined by the specified selection box.
+ ///
+ /// The ID of an existing which allows the user to pick a custom snooze time. The ID's of the s inside the selection box must represent the snooze interval in minutes. For example, if the user selects an item that has an ID of "120", then the notification will be snoozed for 2 hours. When the user clicks this button, if you specified a SelectionBoxId, the system will parse the ID of the selected item and snooze by that amount of minutes.
+ /// The current instance of
+ public ToastButton SetSnoozeActivation(string selectionBoxId)
+ {
+ if (_arguments.Count > 0)
+ {
+ throw new InvalidOperationException($"{nameof(SetSnoozeActivation)} cannot be used in conjunction with ${nameof(AddArgument)}.");
+ }
+
+ _usingSnoozeActivation = true;
+ _snoozeSelectionBoxId = selectionBoxId;
+
+ return this;
+ }
+
+ ///
+ /// Configures the button to use system dismiss activation when the button is clicked (the toast will simply dismiss rather than activating).
+ ///
+ /// The current instance of
+ public ToastButton SetDismissActivation()
+ {
+ if (_arguments.Count > 0)
+ {
+ throw new InvalidOperationException($"{nameof(SetDismissActivation)} cannot be used in conjunction with ${nameof(AddArgument)}.");
+ }
+
+ _usingDismissActivation = true;
+ return this;
+ }
+
///
/// Sets an identifier used in telemetry to identify your category of action. This should be something like "Delete", "Reply", or "Archive". In the upcoming toast telemetry dashboard in Dev Center, you will be able to view how frequently your actions are being clicked.
///
@@ -349,7 +407,7 @@ public ToastButton SetTextBoxId(string textBoxId)
internal bool CanAddArguments()
{
- return ActivationType != ToastActivationType.Protocol && !_usingCustomArguments;
+ return ActivationType != ToastActivationType.Protocol && !_usingCustomArguments && !_usingDismissActivation && !_usingSnoozeActivation;
}
internal bool ContainsArgument(string key)
@@ -362,13 +420,44 @@ internal Element_ToastAction ConvertToElement()
var el = new Element_ToastAction()
{
Content = Content,
- Arguments = Arguments,
- ActivationType = Element_Toast.ConvertActivationType(ActivationType),
ImageUri = ImageUri,
InputId = TextBoxId,
HintActionId = HintActionId
};
+ if (_usingSnoozeActivation)
+ {
+ el.ActivationType = Element_ToastActivationType.System;
+ el.Arguments = "snooze";
+
+ if (_snoozeSelectionBoxId != null)
+ {
+ el.InputId = _snoozeSelectionBoxId;
+ }
+
+ // Content needs to be specified as empty for auto-generated Snooze content
+ if (el.Content == null)
+ {
+ el.Content = string.Empty;
+ }
+ }
+ else if (_usingDismissActivation)
+ {
+ el.ActivationType = Element_ToastActivationType.System;
+ el.Arguments = "dismiss";
+
+ // Content needs to be specified as empty for auto-generated Dismiss content
+ if (el.Content == null)
+ {
+ el.Content = string.Empty;
+ }
+ }
+ else
+ {
+ el.ActivationType = Element_Toast.ConvertActivationType(ActivationType);
+ el.Arguments = Arguments;
+ }
+
ActivationOptions?.PopulateElement(el);
return el;
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
index 23cb14bd7ac..d55194bd8b5 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
+++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj
@@ -272,12 +272,14 @@
+
+
@@ -496,6 +498,7 @@
AutoFocusBehaviorPage.xaml
+
ColorPickerButtonPage.xaml
@@ -505,6 +508,9 @@
EnumValuesExtensionPage.xaml
+
+ CanvasPathGeometryPage.xaml
+
FocusBehaviorPage.xaml
@@ -990,6 +996,13 @@
MSBuild:Compile
Designer
+
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
MSBuild:Compile
Designer
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png
new file mode 100644
index 00000000000..5c909a581e1
Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png differ
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml
new file mode 100644
index 00000000000..44a17d85327
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs
new file mode 100644
index 00000000000..b0acb1f0de5
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs
@@ -0,0 +1,330 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Graphics.Canvas.Geometry;
+using Microsoft.Graphics.Canvas.UI.Xaml;
+using Microsoft.Toolkit.Uwp.Extensions;
+using Microsoft.Toolkit.Uwp.UI.Media.Geometry;
+using Windows.System;
+using Windows.UI;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Media;
+
+namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class CanvasPathGeometryPage : Page
+ {
+ private const string Sample1 =
+ "F0 M 656.500,400.500 C 656.500,350.637 598.572,307.493 514.292,286.708 C 493.507,202.428 450.363,144.500 400.500,144.500 C 350.637,144.500 307.493,202.428 286.708,286.708 C 202.428,307.493 144.500,350.637 144.500,400.500 C 144.500,450.363 202.428,493.507 286.708,514.292 C 307.493,598.572 350.637,656.500 400.500,656.500 C 450.363,656.500 493.507,598.572 514.292,514.292 C 598.572,493.507 656.500,450.363 656.500,400.500 ZM 581.519,219.481 C 546.261,184.222 474.793,194.676 400.500,239.574 C 326.207,194.676 254.739,184.222 219.481,219.481 C 184.222,254.739 194.676,326.207 239.574,400.500 C 194.676,474.792 184.222,546.261 219.481,581.519 C 254.739,616.778 326.207,606.324 400.500,561.426 C 474.793,606.324 546.261,616.778 581.519,581.519 C 616.778,546.261 606.324,474.792 561.426,400.500 C 606.324,326.207 616.778,254.739 581.519,219.481 ZU 112.5 112.5 570 570 36 36";
+
+ private const string Sample2 =
+ "F1 M 331.341,81.975 L 398.766,218.593 L 549.533,240.500 L 440.437,346.842 L 466.191,497.000 L 331.341,426.105 L 196.491,497.000 L 222.245,346.842 L 113.150,240.500 L 263.916,218.593 L 331.341,81.975 Z";
+
+ private const string Sample3 =
+ "F1 M 545.497,397.058 C 454.492,512.882 286.824,533.003 171.000,441.998 C 78.340,369.194 62.244,235.059 135.048,142.400 C 193.291,68.272 300.599,55.395 374.726,113.639 C 434.028,160.233 444.330,246.079 397.736,305.381 C 360.460,352.823 291.783,361.064 244.341,323.788 C 206.388,293.968 199.795,239.026 229.616,201.073 C 253.472,170.711 297.425,165.436 327.788,189.293 C 352.078,208.378 356.297,243.540 337.212,267.830 C 321.944,287.262 293.814,290.638 274.382,275.370 C 258.836,263.155 256.136,240.651 268.350,225.106 C 278.122,212.669 296.125,210.509 308.562,220.280 C 318.511,228.098 320.239,242.500 312.422,252.449";
+
+ private const string Sample4 =
+ "F1 M 311.717,332.110 C 285.669,332.110 264.552,310.994 264.552,284.945 C 264.552,258.897 285.669,237.781 311.717,237.781 C 337.765,237.781 358.881,258.897 358.881,284.945 C 358.881,310.994 337.765,332.110 311.717,332.110 Z M 505.712,232.846 C 634.939,203.833 411.705,171.395 371.772,213.383 C 411.705,171.395 311.872,-30.889 311.872,92.013 C 311.872,-30.889 212.038,171.395 251.972,213.383 C 212.038,171.395 -11.196,203.833 118.031,232.846 C -11.196,203.833 150.338,361.289 214.951,327.320 C 150.338,361.289 112.205,583.622 192.072,460.719 C 112.205,583.622 311.872,478.651 311.872,397.737 C 311.872,478.651 511.538,583.622 431.672,460.719 C 511.538,583.622 473.405,361.289 408.792,327.320 C 473.405,361.289 634.939,203.833 505.712,232.846 Z";
+
+ private const string Sample5 =
+ "F1 M 391.853,348.284 C 391.853,357.113 384.696,364.271 375.867,364.271 L 301.927,364.271 C 293.098,364.271 285.940,357.113 285.940,348.284 L 285.940,274.345 C 285.940,265.515 293.098,258.358 301.927,258.358 L 375.867,258.358 C 384.696,258.358 391.853,265.515 391.853,274.345 L 391.853,348.284 Z M 544.748,282.990 L 485.488,267.081 C 472.521,263.600 466.301,248.839 472.866,237.128 L 502.867,183.604 C 512.642,166.166 494.336,146.433 476.214,154.872 L 420.592,180.776 C 408.421,186.445 394.169,179.136 391.670,165.944 L 380.248,105.658 C 376.526,86.017 349.819,82.667 341.362,100.780 L 315.403,156.378 C 309.723,168.543 294.107,172.105 283.714,163.607 L 236.213,124.767 C 220.737,112.113 198.125,126.714 203.289,146.025 L 219.141,205.301 C 222.610,218.271 212.937,231.038 199.512,231.208 L 138.159,231.988 C 118.170,232.242 110.233,257.962 126.602,269.436 L 176.847,304.655 C 187.841,312.361 188.638,328.358 178.464,337.118 L 131.965,377.153 C 116.816,390.196 127.269,415.001 147.184,413.268 L 208.312,407.950 C 221.687,406.786 232.580,418.529 230.417,431.779 L 220.531,492.336 C 217.310,512.066 241.261,524.348 255.403,510.220 L 298.811,466.854 C 308.310,457.365 324.202,459.358 331.062,470.899 L 362.415,523.643 C 372.629,540.827 398.872,534.840 400.624,514.927 L 406.001,453.804 C 407.178,440.430 420.634,431.742 433.307,436.173 L 491.227,456.425 C 510.098,463.022 526.353,441.568 514.895,425.187 L 479.725,374.908 C 472.030,363.906 476.753,348.601 489.310,343.850 L 546.697,322.133 C 565.393,315.057 564.054,288.173 544.748,282.990 Z";
+
+ private const string ErrorString =
+ "F1 M 19.648,24.605 L 19.648,30.220 L 29.404,30.220 L 29.404,28.149 C 29.404,27.229 29.581,26.573 29.936,26.181 C 30.290,25.790 30.753,25.594 31.325,25.594 C 31.885,25.594 " +
+ "32.342,25.790 32.696,26.181 C 33.051,26.573 33.228,27.229 33.228,28.149 L 33.228,34.044 L 15.227,34.044 C 14.307,34.044 13.651,33.867 13.259,33.512 C 12.867,33.158 12.672,32.695 " +
+ "12.672,32.122 C 12.672,31.563 12.870,31.106 13.268,30.751 C 13.666,30.397 14.319,30.220 15.227,30.220 L 15.824,30.220 L 15.824,15.260 L 15.227,15.260 C 14.307,15.260 13.651,15.082 " +
+ "13.259,14.728 C 12.867,14.373 12.672,13.910 12.672,13.338 C 12.672,12.766 12.867,12.303 13.259,11.948 C 13.651,11.594 14.307,11.417 15.227,11.417 L 32.388,11.436 L 32.388,17.255 C " +
+ "32.388,18.163 32.214,18.813 31.866,19.205 C 31.518,19.596 31.058,19.792 30.486,19.792 C 29.914,19.792 29.451,19.600 29.096,19.214 C 28.742,18.829 28.564,18.176 28.564,17.255 L " +
+ "28.564,15.260 L 19.648,15.260 L 19.648,20.781 L 23.006,20.781 C 23.006,19.786 23.099,19.146 23.285,18.860 C 23.671,18.250 24.218,17.946 24.927,17.946 C 25.487,17.946 25.944,18.142 " +
+ "26.298,18.533 C 26.652,18.925 26.830,19.581 26.830,20.501 L 26.830,24.903 C 26.830,25.737 26.730,26.297 26.531,26.582 C 26.133,27.167 25.599,27.459 24.927,27.459 C 24.218,27.459 " +
+ "23.671,27.155 23.285,26.545 C 23.099,26.259 23.006,25.612 23.006,24.605 L 19.648,24.605 ZM 45.707,17.106 L 45.707,19.494 C 47.311,18.337 48.577,17.567 49.503,17.181 C 50.430,16.795 " +
+ "51.297,16.603 52.105,16.603 C 53.349,16.603 54.555,17.063 55.724,17.983 C 56.520,18.605 56.918,19.239 56.918,19.886 C 56.918,20.433 56.728,20.896 56.349,21.275 C 55.970,21.655 " +
+ "55.513,21.844 54.978,21.844 C 54.505,21.844 54.008,21.608 53.486,21.135 C 52.963,20.663 52.497,20.427 52.087,20.427 C 51.552,20.427 50.753,20.762 49.690,21.434 C 48.626,22.105 " +
+ "47.299,23.113 45.707,24.456 L 45.707,30.220 L 51.154,30.220 C 52.074,30.220 52.730,30.397 53.122,30.751 C 53.514,31.106 53.710,31.569 53.710,32.141 C 53.710,32.701 53.514,33.158 " +
+ "53.122,33.512 C 52.730,33.867 52.074,34.044 51.154,34.044 L 39.607,34.044 C 38.687,34.044 38.031,33.867 37.639,33.512 C 37.248,33.158 37.052,32.695 37.052,32.122 C 37.052,31.563 " +
+ "37.248,31.106 37.639,30.751 C 38.031,30.397 38.687,30.220 39.607,30.220 L 41.883,30.220 L 41.883,20.930 L 40.503,20.930 C 39.582,20.930 38.927,20.753 38.535,20.399 C 38.143,20.044 " +
+ "37.947,19.581 37.947,19.009 C 37.947,18.449 38.143,17.992 38.535,17.638 C 38.927,17.283 39.582,17.106 40.503,17.106 L 45.707,17.106 ZM 68.633,17.106 L 68.633,19.494 C 70.237,18.337 " +
+ "71.502,17.567 72.429,17.181 C 73.355,16.795 74.222,16.603 75.031,16.603 C 76.274,16.603 77.480,17.063 78.650,17.983 C 79.445,18.605 79.843,19.239 79.843,19.886 C 79.843,20.433 " +
+ "79.654,20.896 79.274,21.275 C 78.895,21.655 78.438,21.844 77.903,21.844 C 77.431,21.844 76.933,21.608 76.411,21.135 C 75.889,20.663 75.423,20.427 75.012,20.427 C 74.477,20.427 " +
+ "73.678,20.762 72.615,21.434 C 71.552,22.105 70.224,23.113 68.633,24.456 L 68.633,30.220 L 74.079,30.220 C 74.999,30.220 75.656,30.397 76.047,30.751 C 76.439,31.106 76.635,31.569 " +
+ "76.635,32.141 C 76.635,32.701 76.439,33.158 76.047,33.512 C 75.656,33.867 74.999,34.044 74.079,34.044 L 62.533,34.044 C 61.612,34.044 60.957,33.867 60.565,33.512 C 60.173,33.158 " +
+ "59.977,32.695 59.977,32.122 C 59.977,31.563 60.173,31.106 60.565,30.751 C 60.957,30.397 61.612,30.220 62.533,30.220 L 64.809,30.220 L 64.809,20.930 L 63.428,20.930 C 62.508,20.930 " +
+ "61.852,20.753 61.460,20.399 C 61.069,20.044 60.873,19.581 60.873,19.009 C 60.873,18.449 61.069,17.992 61.460,17.638 C 61.852,17.283 62.508,17.106 63.428,17.106 L 68.633,17.106 ZM " +
+ "98.460,25.911 C 98.460,24.680 98.018,23.548 97.135,22.516 C 95.929,21.123 94.343,20.427 92.379,20.427 C 90.650,20.427 89.208,20.980 88.051,22.087 C 86.895,23.194 86.316,24.474 " +
+ "86.316,25.929 C 86.316,27.123 86.901,28.239 88.070,29.278 C 89.239,30.316 90.675,30.835 92.379,30.835 C 94.095,30.835 95.537,30.316 96.706,29.278 C 97.875,28.239 98.460,27.117 " +
+ "98.460,25.911 Z M 102.284,25.892 C 102.284,27.360 101.876,28.780 101.062,30.154 C 100.247,31.529 99.035,32.623 97.425,33.438 C 95.814,34.252 94.132,34.659 92.379,34.659 C " +
+ "90.638,34.659 88.971,34.258 87.380,33.456 C 85.788,32.654 84.575,31.563 83.742,30.182 C 82.909,28.802 82.492,27.360 82.492,25.855 C 82.492,24.325 82.915,22.824 83.761,21.350 C " +
+ "84.606,19.876 85.822,18.717 87.408,17.871 C 88.993,17.026 90.650,16.603 92.379,16.603 C 94.119,16.603 95.795,17.035 97.406,17.899 C 99.016,18.763 100.232,19.926 101.053,21.387 C " +
+ "101.873,22.849 102.284,24.350 102.284,25.892 ZM 114.483,17.106 L 114.483,19.494 C 116.088,18.337 117.353,17.567 118.279,17.181 C 119.206,16.795 120.073,16.603 120.882,16.603 C " +
+ "122.125,16.603 123.331,17.063 124.500,17.983 C 125.296,18.605 125.694,19.239 125.694,19.886 C 125.694,20.433 125.504,20.896 125.125,21.275 C 124.746,21.655 124.289,21.844 " +
+ "123.754,21.844 C 123.282,21.844 122.784,21.608 122.262,21.135 C 121.740,20.663 121.273,20.427 120.863,20.427 C 120.328,20.427 119.529,20.762 118.466,21.434 C 117.403,22.105 " +
+ "116.075,23.113 114.483,24.456 L 114.483,30.220 L 119.930,30.220 C 120.850,30.220 121.506,30.397 121.898,30.751 C 122.290,31.106 122.486,31.569 122.486,32.141 C 122.486,32.701 " +
+ "122.290,33.158 121.898,33.512 C 121.506,33.867 120.850,34.044 119.930,34.044 L 108.384,34.044 C 107.463,34.044 106.807,33.867 106.416,33.512 C 106.024,33.158 105.828,32.695 " +
+ "105.828,32.122 C 105.828,31.563 106.024,31.106 106.416,30.751 C 106.807,30.397 107.463,30.220 108.384,30.220 L 110.659,30.220 L 110.659,20.930 L 109.279,20.930 C 108.359,20.930 " +
+ "107.703,20.753 107.311,20.399 C 106.919,20.044 106.723,19.581 106.723,19.009 C 106.723,18.449 106.919,17.992 107.311,17.638 C 107.703,17.283 108.359,17.106 109.279,17.106 L " +
+ "114.483,17.106 ZM 140.431,32.645 C 140.431,33.192 140.225,33.655 139.815,34.034 C 139.405,34.414 138.838,34.603 138.118,34.603 C 137.396,34.603 136.830,34.414 136.420,34.034 C " +
+ "136.010,33.655 135.804,33.192 135.804,32.645 C 135.804,32.110 136.006,31.653 136.411,31.274 C 136.815,30.895 137.384,30.705 138.118,30.705 C 138.851,30.705 139.420,30.891 " +
+ "139.824,31.264 C 140.228,31.637 140.431,32.098 140.431,32.645 Z M 141.046,13.655 L 139.983,25.183 C 139.933,25.780 139.734,26.244 139.386,26.573 C 139.038,26.903 138.603,27.067 " +
+ "138.080,27.067 C 137.558,27.067 137.123,26.903 136.774,26.573 C 136.426,26.244 136.227,25.780 136.178,25.183 L 135.096,13.655 C 135.046,13.071 135.021,12.685 135.021,12.499 C " +
+ "135.021,11.529 135.313,10.749 135.898,10.158 C 136.482,9.567 137.210,9.272 138.080,9.272 C 138.938,9.272 139.662,9.570 140.253,10.167 C 140.844,10.764 141.139,11.516 141.139,12.424 " +
+ "C 141.139,12.611 141.108,13.021 141.046,13.655 Z";
+
+ private const string ParseError1 = "Parameter \"(pathData matches.Count == 0)\" must be false, was true: ";
+ private const string ParseError2 = "Parameter \"(pathData matches.Count > 1)\" must be false, was true: ";
+ private const string ParseError3 = "PATH_ERR003";
+
+ private DispatcherQueueTimer _typeTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
+
+ private List _colors;
+ private List _samples;
+
+ private string _data = string.Empty;
+ private StringBuilder _logger;
+
+ private float _strokeThickness;
+ private Color _strokeColor;
+ private Color _fillColor;
+ private bool _selectionChanged = false;
+ private bool _isParsing = false;
+
+ private CanvasGeometry _errorGeometry;
+ private GeometryStreamReader _reader;
+
+ private SolidColorBrush _commandBrush;
+ private SolidColorBrush _commandErrorBrush;
+
+ public string InputText { get; set; }
+
+ public CanvasPathGeometryPage()
+ {
+ this.InitializeComponent();
+ _reader = new GeometryStreamReader();
+ _logger = new StringBuilder();
+ _colors = new List()
+ {
+ Colors.Transparent,
+ Colors.Black,
+ Colors.White,
+ Colors.Crimson,
+ CanvasPathGeometry.CreateColor("#bf5af2"),
+ CanvasPathGeometry.CreateColor("#0a84ff"),
+ CanvasPathGeometry.CreateColor("#32d74b"),
+ CanvasPathGeometry.CreateColor("#ff9500"),
+ CanvasPathGeometry.CreateColor("#ffd60a")
+ };
+
+ _commandBrush = new SolidColorBrush(Colors.White);
+ _commandErrorBrush = new SolidColorBrush(Colors.Red);
+
+ var colorList = new List()
+ {
+ "Transparent",
+ "Black",
+ "White",
+ "Crimson",
+ "Purple",
+ "LightBlue",
+ "LightGreen",
+ "Orange",
+ "Yellow"
+ };
+
+ this._samples = new List()
+ {
+ string.Empty,
+ Sample1,
+ Sample2,
+ Sample3,
+ Sample4,
+ Sample5
+ };
+
+ var sampleList = new List()
+ {
+ "None",
+ "Sample 1",
+ "Sample 2",
+ "Sample 3",
+ "Sample 4",
+ "Sample 5",
+ };
+
+ StrokeList.ItemsSource = colorList;
+ FillList.ItemsSource = colorList;
+
+ StrokeThickness.Value = 1;
+ StrokeList.SelectedIndex = 1;
+ FillList.SelectedIndex = 0;
+
+ _selectionChanged = false;
+ }
+
+ private void ParseData()
+ {
+ _data = InputData.Text;
+ _isParsing = true;
+ RenderCanvas.Invalidate();
+ _isParsing = false;
+ }
+
+ private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
+ {
+ if (string.IsNullOrWhiteSpace(_data))
+ {
+ CommandsList.Text = string.Empty;
+ return;
+ }
+
+ this._errorGeometry ??= CanvasPathGeometry.CreateGeometry(sender, ErrorString);
+
+ _logger?.Clear();
+ CommandsList.Text = string.Empty;
+
+ try
+ {
+ _logger?.AppendLine("// The following commands represent the CanvasPathBuilder command(s) needed");
+ _logger?.AppendLine("// to create the CanvasGeometry from the specified Win2d Path Mini Language.");
+ var geometry = CanvasPathGeometry.CreateGeometry(sender, _data);
+ _reader.StartLogging();
+ geometry.SendPathTo(_reader);
+ _logger?.AppendLine(_reader.EndLogging());
+ CommandsList.Text = _logger?.ToString() ?? string.Empty;
+
+ args.DrawingSession.FillGeometry(geometry, _fillColor);
+ args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
+ RootPivot.SelectedIndex = 0;
+ CommandsList.Foreground = _commandBrush;
+ }
+ catch (ArgumentException argEx)
+ {
+ var message = argEx.Message;
+ var errorCode = message.Substring(0, 11);
+ var parseError = string.Empty;
+ if (message.StartsWith(ParseError1))
+ {
+ parseError = "Parse Error: No matching data!";
+ }
+ else if (message.StartsWith(ParseError2))
+ {
+ parseError = "Parse Error: Multiple FillRule elements present in Path Data!";
+ }
+ else if (message.StartsWith(ParseError3))
+ {
+ var tokens = message.Split('\n', StringSplitOptions.RemoveEmptyEntries);
+ if (tokens.Length == 3)
+ {
+ parseError = $"Parse Error at {tokens[1]}. Cannot parse '{tokens[2]}'.";
+ }
+ }
+ else
+ {
+ parseError = "Parsing error! Invalid input data!";
+ }
+
+ args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
+ CommandsList.Text = parseError;
+ RootPivot.SelectedIndex = 1;
+ CommandsList.Foreground = _commandErrorBrush;
+ }
+ catch (Exception)
+ {
+ args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
+ CommandsList.Text = "Parsing error! Invalid input data!";
+ RootPivot.SelectedIndex = 1;
+ CommandsList.Foreground = _commandErrorBrush;
+ }
+ }
+
+ private void OnStrokeThicknessChanged(object sender, RangeBaseValueChangedEventArgs e)
+ {
+ _strokeThickness = (float)StrokeThickness.Value;
+ _selectionChanged = true;
+ RenderCanvas.Invalidate();
+ }
+
+ private void OnStrokeColorChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (StrokeList.SelectedIndex > -1)
+ {
+ _strokeColor = _colors[StrokeList.SelectedIndex];
+ _selectionChanged = true;
+ }
+
+ RenderCanvas.Invalidate();
+ }
+
+ private void OnFillColorChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (FillList.SelectedIndex > -1)
+ {
+ _fillColor = _colors[FillList.SelectedIndex];
+ _selectionChanged = true;
+ }
+
+ RenderCanvas.Invalidate();
+ }
+
+ private void ShowSample(int index)
+ {
+ InputData.Text = _samples.ElementAt(index);
+
+ if (!_selectionChanged)
+ {
+ StrokeThickness.Value = 4;
+ StrokeList.SelectedIndex = 1;
+ FillList.SelectedIndex = 5;
+ _selectionChanged = false;
+ }
+
+ _data = InputData.Text;
+ RenderCanvas.Invalidate();
+ }
+
+ private void OnClearCanvas(object sender, RoutedEventArgs e)
+ {
+ InputData.Text = string.Empty;
+ ParseData();
+ }
+
+ private void OnShowRoundedStarSample(object sender, RoutedEventArgs e)
+ {
+ ShowSample(1);
+ }
+
+ private void OnShowStarSample(object sender, RoutedEventArgs e)
+ {
+ ShowSample(2);
+ }
+
+ private void OnShowSpiralSample(object sender, RoutedEventArgs e)
+ {
+ ShowSample(3);
+ }
+
+ private void OnShowFlowerSample(object sender, RoutedEventArgs e)
+ {
+ ShowSample(4);
+ }
+
+ private void OnShowGearSample(object sender, RoutedEventArgs e)
+ {
+ ShowSample(5);
+ }
+
+ public void OnInputTextChanged(object sender, RoutedEventArgs e)
+ {
+ // Call the ParseData method only after 0.3 seconds have elapsed since last trigger.
+ _typeTimer.Debounce(ParseData, TimeSpan.FromSeconds(0.3));
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/GeometryStreamReader.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/GeometryStreamReader.cs
new file mode 100644
index 00000000000..a0b3dbed959
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/GeometryStreamReader.cs
@@ -0,0 +1,127 @@
+// 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.Numerics;
+using System.Text;
+using Microsoft.Graphics.Canvas.Geometry;
+
+namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
+{
+ ///
+ /// Class to read the path data.
+ ///
+ internal class GeometryStreamReader : ICanvasPathReceiver
+ {
+ private readonly StringBuilder _cmdBuilder;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GeometryStreamReader()
+ {
+ _cmdBuilder = new StringBuilder();
+ }
+
+ ///
+ /// Starts logging the data for the sample app
+ ///
+ public void StartLogging()
+ {
+ _cmdBuilder.Clear();
+ _cmdBuilder.AppendLine($"using (var pathBuilder = new CanvasPathBuilder(null))");
+ _cmdBuilder.AppendLine("{\n");
+ }
+
+ ///
+ /// Finishes reading the geometry path data and returns the data as formatted string.
+ ///
+ /// commands to create the CanvasGeometry
+ public string EndLogging()
+ {
+ _cmdBuilder.AppendLine("}");
+ return _cmdBuilder.ToString();
+ }
+
+ ///
+ /// Starts a new figure at the specified point, with the specified figure fill option.
+ ///
+ /// Start point
+ ///
+ public void BeginFigure(Vector2 point, CanvasFigureFill fill)
+ {
+ _cmdBuilder.AppendLine($"\n pathBuilder.BeginFigure(new Vector2({point.X}, {point.Y}));");
+ }
+
+ ///
+ /// Adds a single arc to the path, specified by start and end points through which an ellipse will be fitted.
+ ///
+ /// Start Point
+ /// radiusX
+ /// radiusY
+ /// rotationAngle
+ ///
+ ///
+ public void AddArc(Vector2 point, float x, float y, float z, CanvasSweepDirection sweepDirection, CanvasArcSize arcSize)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.AddArc(new Vector2({point.X}, {point.Y}), {x}, {y}, {z}, {sweepDirection}, {arcSize});");
+ }
+
+ ///
+ /// Adds a cubic bezier to the path. The bezier starts where the path left off, and has the specified control points and end point.
+ ///
+ /// First ControlPoint
+ /// Second Control Point
+ /// EndPoint
+ public void AddCubicBezier(Vector2 controlPoint1, Vector2 controlPoint2, Vector2 endPoint)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.AddCubicBezier(new Vector2({controlPoint1.X}, {controlPoint1.Y}), new Vector2({controlPoint2.X}, {controlPoint2.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
+ }
+
+ ///
+ /// Adds a line segment to the path, with the specified end point.
+ ///
+ /// EndPoint
+ public void AddLine(Vector2 endPoint)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.AddLine(new Vector2({endPoint.X}, {endPoint.Y}));");
+ }
+
+ ///
+ /// Adds a quadratic bezier to the path. The bezier starts where the path left off, and has the specified control point and end point.
+ ///
+ /// Control Point
+ /// EndPoint
+ public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.AddQuadraticBezier(new Vector2({controlPoint.X}, {controlPoint.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
+ }
+
+ ///
+ /// Specifies the method used to determine which points are inside the geometry described by this path builder, and which points are outside.
+ ///
+ ///
+ public void SetFilledRegionDetermination(CanvasFilledRegionDetermination filledRegionDetermination)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.{filledRegionDetermination});");
+ }
+
+ ///
+ /// Specifies stroke and join options to be applied to new segments added to the path builder.
+ ///
+ ///
+ public void SetSegmentOptions(CanvasFigureSegmentOptions figureSegmentOptions)
+ {
+ // Do nothing
+ }
+
+ ///
+ /// >Ends the current figure; optionally, closes it.
+ ///
+ ///
+ public void EndFigure(CanvasFigureLoop figureLoop)
+ {
+ _cmdBuilder.AppendLine($" pathBuilder.EndFigure({(figureLoop == CanvasFigureLoop.Closed ? "CanvasFigureLoop.Closed" : "CanvasFigureLoop.Open")});");
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.bind
new file mode 100644
index 00000000000..66dd11951a9
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.bind
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.png
new file mode 100644
index 00000000000..6477997a952
Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabbedCommandBar/TabbedCommandBar.png differ
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastCode.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastCode.bind
index 7d5a3bafa44..431aa671248 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastCode.bind
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastCode.bind
@@ -13,7 +13,9 @@ private void PopToast()
("60", "1 hour"),
("240", "4 hours"),
("1440", "1 day"))
- .AddButton(new ToastButtonSnooze() { SelectionBoxId = "snoozeTime" })
- .AddButton(new ToastButtonDismiss())
+ .AddButton(new ToastButton()
+ .SetSnoozeActivation("snoozeTime"))
+ .AddButton(new ToastButton()
+ .SetDismissActivation())
.Show();
}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastPage.xaml.cs
index 896795bec19..0d732b0269e 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastPage.xaml.cs
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Toast/ToastPage.xaml.cs
@@ -40,8 +40,10 @@ public static ToastContent GenerateToastContent()
("60", "1 hour"),
("240", "4 hours"),
("1440", "1 day"))
- .AddButton(new ToastButtonSnooze() { SelectionBoxId = "snoozeTime" })
- .AddButton(new ToastButtonDismiss());
+ .AddButton(new ToastButton()
+ .SetSnoozeActivation("snoozeTime"))
+ .AddButton(new ToastButton()
+ .SetDismissActivation());
return builder.Content;
}
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml
index 3b7a093050c..c354b2293be 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/XamlOnlyPage.xaml
@@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations"
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors"
+ xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
@@ -17,6 +18,8 @@
+
+
@@ -34,17 +37,17 @@
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
index 3e500284001..760cffb9d42 100644
--- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
+++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json
@@ -454,6 +454,15 @@
"XamlCodeFile": "TokenizingTextBoxXaml.bind",
"Icon": "/SamplePages/TokenizingTextBox/TokenizingTextBox.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/TokenizingTextBox.md"
+ },
+ {
+ "Name": "TabbedCommandBar",
+ "Subcategory": "Menus and Toolbars",
+ "About": "A control for displaying multiple CommandBars in the same space, like Microsoft Office's ribbon.",
+ "CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/TabbedCommandBar",
+ "XamlCodeFile": "/SamplePages/TabbedCommandBar/TabbedCommandBar.bind",
+ "Icon": "/SamplePages/TabbedCommandBar/TabbedCommandBar.png",
+ "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/TabbedCommandBar.md"
}
]
},
@@ -901,6 +910,14 @@
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/MarkdownParser.md"
},
+ {
+ "Name": "Win2d Path Mini Language Parser",
+ "Type": "CanvasPathGeometryPage",
+ "Subcategory": "Parser",
+ "About": "CanvasPathGeometry class allows you to convert Win2d Path Mini Language string to CanvasGeometry, Brushes, CanvasStrokes or CanvasStrokeStyles.",
+ "Icon": "/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png",
+ "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/CanvasPathGeometry.md"
+ },
{
"Name": "LiveTile",
"Type": "LiveTilePage",
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Blur.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Blur.cs
deleted file mode 100644
index 3a56da53c15..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Blur.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Performs an blur animation using composition.
- ///
- ///
- /// Microsoft.Xaml.Interactivity.Behavior{Windows.UI.Xaml.UIElement}
- ///
- public class Blur : CompositionBehaviorBase
- {
- ///
- /// The Blur value of the associated object
- ///
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Blur), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// Gets or sets the Blur.
- ///
- ///
- /// The Blur.
- ///
- public double Value
- {
- get { return (double)GetValue(ValueProperty); }
- set { SetValue(ValueProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject?.Blur(duration: Duration, delay: Delay, value: (float)Value, easingType: EasingType, easingMode: EasingMode)?.Start();
- }
- }
-}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.cs
deleted file mode 100644
index 3dd912d33a7..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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 Microsoft.Toolkit.Uwp.UI.Behaviors;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Media.Animation;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// A base class for all behaviors using composition.It contains some of the common properties to set on a visual.
- ///
- /// The type of the associated object.
- ///
- public abstract class CompositionBehaviorBase : BehaviorBase
- where T : UIElement
- {
- ///
- /// Called when the associated object has been loaded.
- ///
- protected override void OnAssociatedObjectLoaded()
- {
- base.OnAssociatedObjectLoaded();
-
- if (AutomaticallyStart)
- {
- StartAnimation();
- }
- }
-
- ///
- /// The duration of the animation.
- ///
- public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(nameof(Duration), typeof(double), typeof(CompositionBehaviorBase), new PropertyMetadata(1d, PropertyChangedCallback));
-
- ///
- /// The delay of the animation.
- ///
- public static readonly DependencyProperty DelayProperty = DependencyProperty.Register(nameof(Delay), typeof(double), typeof(CompositionBehaviorBase), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The property sets if the animation should automatically start.
- ///
- public static readonly DependencyProperty AutomaticallyStartProperty = DependencyProperty.Register(nameof(AutomaticallyStart), typeof(bool), typeof(CompositionBehaviorBase), new PropertyMetadata(true, PropertyChangedCallback));
-
- ///
- /// The used to generate the easing function of the animation.
- ///
- public static readonly DependencyProperty EasingTypeProperty = DependencyProperty.Register(nameof(EasingType), typeof(EasingType), typeof(CompositionBehaviorBase), new PropertyMetadata(EasingType.Default, PropertyChangedCallback));
-
- ///
- /// The used to generate the easing function of the animation.
- ///
- public static readonly DependencyProperty EasingModeProperty = DependencyProperty.Register(nameof(EasingMode), typeof(EasingMode), typeof(CompositionBehaviorBase), new PropertyMetadata(EasingMode.EaseOut, PropertyChangedCallback));
-
- ///
- /// Gets or sets a value indicating whether [automatically start] on the animation is set.
- ///
- ///
- /// true if [automatically start]; otherwise, false.
- ///
- public bool AutomaticallyStart
- {
- get { return (bool)GetValue(AutomaticallyStartProperty); }
- set { SetValue(AutomaticallyStartProperty, value); }
- }
-
- ///
- /// Gets or sets the delay.
- ///
- ///
- /// The delay.
- ///
- public double Delay
- {
- get { return (double)GetValue(DelayProperty); }
- set { SetValue(DelayProperty, value); }
- }
-
- ///
- /// Gets or sets the duration.
- ///
- ///
- /// The duration.
- ///
- public double Duration
- {
- get { return (double)GetValue(DurationProperty); }
- set { SetValue(DurationProperty, value); }
- }
-
- ///
- /// Gets or sets the used to generate the easing function of the animation.
- ///
- ///
- /// The easing function
- ///
- public EasingType EasingType
- {
- get { return (EasingType)GetValue(EasingTypeProperty); }
- set { SetValue(EasingTypeProperty, value); }
- }
-
- ///
- /// Gets or sets the used to generate the easing function of the animation.
- ///
- ///
- /// The easing mode
- ///
- public EasingMode EasingMode
- {
- get { return (EasingMode)GetValue(EasingModeProperty); }
- set { SetValue(EasingModeProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public abstract void StartAnimation();
-
- ///
- /// If any of the properties are changed then the animation is automatically started depending on the AutomaticallyStart property.
- ///
- /// The dependency object.
- /// The instance containing the event data.
- protected static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
- {
- var behavior = dependencyObject as CompositionBehaviorBase;
- if (behavior == null)
- {
- return;
- }
-
- if (behavior.AutomaticallyStart)
- {
- behavior.StartAnimation();
- }
- }
- }
-}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.nongeneric.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.nongeneric.cs
deleted file mode 100644
index c68a8180bac..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/CompositionBehaviorBase.nongeneric.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Non-generic convenience implementation to provide backwards compatibility.
- ///
- ///
- public abstract class CompositionBehaviorBase : CompositionBehaviorBase
- {
- }
-}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Fade.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Fade.cs
deleted file mode 100644
index fe9d4bab165..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Fade.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Performs an fade animation using composition.
- ///
- ///
- /// Microsoft.Xaml.Interactivity.Behavior{Windows.UI.Xaml.UIElement}
- ///
- public class Fade : CompositionBehaviorBase
- {
- ///
- /// The Opacity value of the associated object
- ///
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Fade), new PropertyMetadata(1d, PropertyChangedCallback));
-
- ///
- /// Gets or sets the Opacity.
- ///
- ///
- /// The Opacity.
- ///
- public double Value
- {
- get { return (double)GetValue(ValueProperty); }
- set { SetValue(ValueProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject.Fade((float)Value, Duration, Delay, EasingType, EasingMode)?.Start();
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Light.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Light.cs
deleted file mode 100644
index 556972c1dae..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Light.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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;
-using Windows.UI;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Media;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Applies a basic point light to a UIElement. You control the intensity by setting the distance of the light.
- ///
- ///
- [Obsolete("The Light effect will be removed in a future major release. Please use XamlLight instead")]
-
- public class Light : CompositionBehaviorBase
- {
- ///
- /// The Blur value of the associated object
- ///
- public static readonly DependencyProperty DistanceProperty = DependencyProperty.Register(nameof(Distance), typeof(double), typeof(Light), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The Color of the spotlight no the associated object.
- ///
- public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Brush), typeof(Light), new PropertyMetadata(new SolidColorBrush(Colors.White)));
-
- ///
- /// Gets or sets the Blur.
- ///
- ///
- /// The Blur.
- ///
- public double Distance
- {
- get { return (double)GetValue(DistanceProperty); }
- set { SetValue(DistanceProperty, value); }
- }
-
- ///
- /// Gets or sets the color of the spotlight.
- ///
- public Brush Color
- {
- get { return (Brush)GetValue(ColorProperty); }
- set { SetValue(ColorProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject?.Light(
- duration: Duration,
- delay: Delay,
- easingType: EasingType,
- easingMode: EasingMode,
- distance: (float)Distance,
- color: ((SolidColorBrush)Color).Color)?.Start();
- }
- }
-}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Offset.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Offset.cs
deleted file mode 100644
index 16e1e7d7c47..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Offset.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Performs an offset animation using composition.
- ///
- ///
- ///
- /// Microsoft.Xaml.Interactivity.Behavior{Windows.UI.Xaml.UIElement}
- ///
- public class Offset : CompositionBehaviorBase
- {
- ///
- /// The Offset on the x axis of the associated object
- ///
- public static readonly DependencyProperty OffsetXProperty = DependencyProperty.Register(nameof(OffsetX), typeof(double), typeof(Offset), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The Offset on the y axis of the associated object
- ///
- public static readonly DependencyProperty OffsetYProperty = DependencyProperty.Register(nameof(OffsetY), typeof(double), typeof(Offset), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// Gets or sets the Offset x.
- ///
- ///
- /// The Offset x.
- ///
- public double OffsetX
- {
- get { return (double)GetValue(OffsetXProperty); }
- set { SetValue(OffsetXProperty, value); }
- }
-
- ///
- /// Gets or sets the Offset y.
- ///
- ///
- /// The Offset y.
- ///
- public double OffsetY
- {
- get { return (double)GetValue(OffsetYProperty); }
- set { SetValue(OffsetYProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject.Offset(
- duration: Duration,
- delay: Delay,
- easingType: EasingType,
- easingMode: EasingMode,
- offsetX: (float)OffsetX,
- offsetY: (float)OffsetY)?.Start();
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Rotate.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Rotate.cs
deleted file mode 100644
index d1117c791c0..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Rotate.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Performs a rotation animation using composition.
- ///
- public class Rotate : CompositionBehaviorBase
- {
- ///
- /// The rotation of the associated object in degrees
- ///
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Rotate), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The center (x axis) of rotation for associated object
- ///
- public static readonly DependencyProperty CenterXProperty = DependencyProperty.Register(nameof(CenterX), typeof(double), typeof(Rotate), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The center (y axis) of rotation for associated object
- ///
- public static readonly DependencyProperty CenterYProperty = DependencyProperty.Register(nameof(CenterY), typeof(double), typeof(Rotate), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// Gets or sets the center point (x axis) of the associated object.
- ///
- ///
- /// The center point (x axis) of the associated object.
- ///
- public double CenterX
- {
- get { return (double)GetValue(CenterXProperty); }
- set { SetValue(CenterXProperty, value); }
- }
-
- ///
- /// Gets or sets the center point (y axis) of the associated object.
- ///
- ///
- /// The center point (y axis) of the associated object.
- ///
- public double CenterY
- {
- get { return (double)GetValue(CenterYProperty); }
- set { SetValue(CenterYProperty, value); }
- }
-
- ///
- /// Gets or sets the Rotation in degrees.
- ///
- ///
- /// The Rotation of the associated object in degrees.
- ///
- public double Value
- {
- get { return (double)GetValue(ValueProperty); }
- set { SetValue(ValueProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject.Rotate(
- duration: Duration,
- delay: Delay,
- easingType: EasingType,
- easingMode: EasingMode,
- value: (float)Value,
- centerX: (float)CenterX,
- centerY: (float)CenterY)?
- .Start();
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Saturation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Saturation.cs
deleted file mode 100644
index 61e0050fcb0..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Saturation.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// A behavior to allow Saturation changes to a UI Element.
- ///
- public class Saturation : CompositionBehaviorBase
- {
- ///
- /// The Saturation value of the associated object
- ///
- public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Saturation), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The _framework element
- ///
- private FrameworkElement _frameworkElement;
-
- ///
- /// Gets or sets the Saturation.
- ///
- ///
- /// The Saturation.
- ///
- public double Value
- {
- get { return (double)GetValue(ValueProperty); }
- set { SetValue(ValueProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- _frameworkElement?.Saturation(
- duration: Duration,
- delay: Delay,
- easingType: EasingType,
- easingMode: EasingMode,
- value: (float)Value)?.StartAsync();
- }
-
- ///
- /// Called after the behavior is attached to the .
- ///
- ///
- /// Override this to hook up functionality to the
- ///
- protected override void OnAttached()
- {
- base.OnAttached();
- _frameworkElement = AssociatedObject as FrameworkElement;
- }
- }
-}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Scale.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Scale.cs
deleted file mode 100644
index dc56e81a0e9..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Behaviors/Scale.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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 Windows.UI.Xaml;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Behaviors
-{
- ///
- /// Performs a scale animation using composition.
- ///
- public class Scale : CompositionBehaviorBase
- {
- ///
- /// The scale (x axis) of the associated object
- ///
- public static readonly DependencyProperty ScaleXProperty = DependencyProperty.Register(nameof(ScaleX), typeof(double), typeof(Scale), new PropertyMetadata(1d, PropertyChangedCallback));
-
- ///
- /// The scale (y axis) of the associated object
- ///
- public static readonly DependencyProperty ScaleYProperty = DependencyProperty.Register(nameof(ScaleY), typeof(double), typeof(Scale), new PropertyMetadata(1d, PropertyChangedCallback));
-
- ///
- /// The center (x axis) of scale for associated object
- ///
- public static readonly DependencyProperty CenterXProperty = DependencyProperty.Register(nameof(CenterX), typeof(double), typeof(Scale), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// The center (y axis) of scale for associated object
- ///
- public static readonly DependencyProperty CenterYProperty = DependencyProperty.Register(nameof(CenterY), typeof(double), typeof(Scale), new PropertyMetadata(0d, PropertyChangedCallback));
-
- ///
- /// Gets or sets the scale on the x axis.
- ///
- ///
- /// The scale on the x axis.
- ///
- public double ScaleX
- {
- get { return (double)GetValue(ScaleXProperty); }
- set { SetValue(ScaleXProperty, value); }
- }
-
- ///
- /// Gets or sets the scale on the y axis.
- ///
- ///
- /// The scale on the y axis.
- ///
- public double ScaleY
- {
- get { return (double)GetValue(ScaleYProperty); }
- set { SetValue(ScaleYProperty, value); }
- }
-
- ///
- /// Gets or sets the scale (x axis) of the associated object.
- ///
- ///
- /// The scale (x axis) of the associated object.
- ///
- public double CenterX
- {
- get { return (double)GetValue(CenterXProperty); }
- set { SetValue(CenterXProperty, value); }
- }
-
- ///
- /// Gets or sets the scale (y axis) of the associated object.
- ///
- ///
- /// The scale (y axis) of the associated object.
- ///
- public double CenterY
- {
- get { return (double)GetValue(CenterYProperty); }
- set { SetValue(CenterYProperty, value); }
- }
-
- ///
- /// Starts the animation.
- ///
- public override void StartAnimation()
- {
- AssociatedObject.Scale(
- duration: Duration,
- delay: Delay,
- easingType: EasingType,
- easingMode: EasingMode,
- centerX: (float)CenterX,
- centerY: (float)CenterY,
- scaleX: (float)ScaleX,
- scaleY: (float)ScaleY)?
- .Start();
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Default.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Default.cs
index 5e15a7de772..1dcbee0c547 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Default.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Default.cs
@@ -6,7 +6,6 @@
using System;
using System.Numerics;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
@@ -605,7 +604,7 @@ public AnimationBuilder Transform(
{
if (!Matrix4x4.Decompose(to, out Vector3 toScale, out Quaternion toRotation, out Vector3 toTranslation))
{
- ThrowHelper.ThrowArgumentException("The destination matrix could not be decomposed");
+ ThrowThrowArgumentExceptionForToDecompose();
}
Vector3? fromScale = null;
@@ -616,7 +615,7 @@ public AnimationBuilder Transform(
{
if (!Matrix4x4.Decompose(from.GetValueOrDefault(), out Vector3 scale3, out Quaternion rotation4, out Vector3 translation3))
{
- ThrowHelper.ThrowArgumentException("The initial matrix could not be decomposed");
+ ThrowThrowArgumentExceptionForFromDecompose();
}
fromScale = scale3;
@@ -629,6 +628,9 @@ public AnimationBuilder Transform(
Translation(toTranslation, fromTranslation, delay, duration, easingType, easingMode);
return this;
+
+ static void ThrowThrowArgumentExceptionForToDecompose() => throw new ArgumentException("The destination matrix could not be decomposed");
+ static void ThrowThrowArgumentExceptionForFromDecompose() => throw new ArgumentException("The initial matrix could not be decomposed");
}
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Factories.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Factories.cs
index 455603b2aea..890420badd8 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Factories.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/AnimationBuilder.Factories.cs
@@ -6,7 +6,6 @@
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
-using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Composition;
@@ -161,7 +160,7 @@ public CompositionAnimation GetAnimation(CompositionObject targetHint, out Compo
easingFunction);
}
- return ThrowHelper.ThrowInvalidOperationException("Invalid animation type");
+ throw new InvalidOperationException("Invalid animation type");
}
///
@@ -216,7 +215,7 @@ public Timeline GetAnimation(DependencyObject targetHint)
easingFunction);
}
- return ThrowHelper.ThrowInvalidOperationException("Invalid animation type");
+ throw new InvalidOperationException("Invalid animation type");
}
///
@@ -229,9 +228,33 @@ public Timeline GetAnimation(DependencyObject targetHint)
private TValue GetToAs()
where TValue : unmanaged
{
- T to = To;
-
- return Unsafe.As(ref to);
+ // We employ this (T2)(object)t1 pattern multiple times in this library to alter generics.
+ // This is an equivalent but safer alternative to using Unsafe.As(ref TFrom).
+ // For instance, this method will result in the following IL being emitted:
+ // =============================
+ // IL_0000: ldarg.0
+ // IL_0001: call instance !0 class AnimationFactory`1::get_To()
+ // IL_0006: box !T
+ // IL_000b: unbox.any !!TValue
+ // IL_0010: ret
+ // =============================
+ // The key point is that the JIT (and AOT compilers such as .NET Native) can recognize this
+ // pattern and optimize the boxing away in case the types match. This is the case whenever
+ // the generic arguments are value types, which due to generic types in .NET being reified
+ // results in a completely different generic instantiation of the same method, making the
+ // type arguments effectively constant values known at compile time, ie. at JIT time.
+ // As a result of this, the boxing is completely avoided and the value is returned directly.
+ // Leveraging this pattern lets us keep the same optimal codegen while avoiding the extra
+ // NuGet package dependency on UWP, and the more dangerous path using the Unsafe APIs.
+ // As an example, assuming T is float, the JIT will produce the following codegen on x64:
+ // =============================
+ // L0000: vzeroupper
+ // L0003: vmovss xmm0, [rcx+8]
+ // L0008: ret
+ // =============================
+ // We can see how the property value is loaded directly from the underlying field and
+ // then returned to the caller: no boxing or unwanted overhead is introduced at all.
+ return (TValue)(object)To;
}
///
@@ -251,7 +274,7 @@ private TValue GetToAs()
T from = From.GetValueOrDefault();
- return Unsafe.As(ref from);
+ return (TValue)(object)from;
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/Helpers/ListBuilder{T}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/Helpers/ListBuilder{T}.cs
index df9bc575253..b22634fdb0e 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/Helpers/ListBuilder{T}.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/Helpers/ListBuilder{T}.cs
@@ -9,7 +9,7 @@
namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
{
///
- /// A small generic builder type that allows to create instances.
+ /// A small generic builder type that allows to create instances.
///
/// The type of items to create a sequence of.
internal struct ListBuilder
@@ -56,14 +56,14 @@ public void Append(T item)
}
///
- /// Gets a instance with the current items.
+ /// Gets a instance with the current items.
///
- /// A instance with the current items.
+ /// A instance with the current items.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan AsSpan()
+ public ArraySegment GetArraySegment()
{
- return this.array.AsSpan(0, this.index);
+ return new(this.array, 0, this.index);
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Composition.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Composition.cs
index e5dc6fb44e8..718ee6781ae 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Composition.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Composition.cs
@@ -4,7 +4,6 @@
using System;
using System.Numerics;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml.Media.Animation;
@@ -33,7 +32,7 @@ public static CompositionAnimation GetAnimation(
TimeSpan? delay,
TimeSpan duration,
RepeatOption repeat,
- ReadOnlySpan keyFrames)
+ ArraySegment keyFrames)
where TKeyFrame : struct, IKeyFrameInfo
{
KeyFrameAnimation animation;
@@ -42,7 +41,7 @@ public static CompositionAnimation GetAnimation(
{
BooleanKeyFrameAnimation boolAnimation = target.Compositor.CreateBooleanKeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(boolAnimation, duration))
{
@@ -58,7 +57,7 @@ public static CompositionAnimation GetAnimation(
{
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
{
@@ -83,7 +82,7 @@ public static CompositionAnimation GetAnimation(
{
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
{
@@ -108,7 +107,7 @@ public static CompositionAnimation GetAnimation(
{
Vector2KeyFrameAnimation vector2Animation = target.Compositor.CreateVector2KeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector2Animation, duration))
{
@@ -133,7 +132,7 @@ public static CompositionAnimation GetAnimation(
{
Vector3KeyFrameAnimation vector3Animation = target.Compositor.CreateVector3KeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector3Animation, duration))
{
@@ -158,7 +157,7 @@ public static CompositionAnimation GetAnimation(
{
Vector4KeyFrameAnimation vector4Animation = target.Compositor.CreateVector4KeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector4Animation, duration))
{
@@ -183,7 +182,7 @@ public static CompositionAnimation GetAnimation(
{
ColorKeyFrameAnimation colorAnimation = target.Compositor.CreateColorKeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(colorAnimation, duration))
{
@@ -208,7 +207,7 @@ public static CompositionAnimation GetAnimation(
{
QuaternionKeyFrameAnimation quaternionAnimation = target.Compositor.CreateQuaternionKeyFrameAnimation();
- foreach (ref readonly var keyFrame in keyFrames)
+ foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(quaternionAnimation, duration))
{
@@ -231,7 +230,7 @@ public static CompositionAnimation GetAnimation(
}
else
{
- return ThrowHelper.ThrowInvalidOperationException("Invalid animation type");
+ throw new InvalidOperationException("Invalid animation type");
}
animation.Duration = duration;
@@ -284,7 +283,7 @@ public CompositionAnimation GetAnimation(CompositionObject targetHint, out Compo
this.delay,
this.duration,
this.repeat,
- this.keyFrames.AsSpan());
+ this.keyFrames.GetArraySegment());
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Xaml.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Xaml.cs
index 23665b9544b..a5727d42d0b 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Xaml.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.Xaml.cs
@@ -45,7 +45,7 @@ public Timeline GetAnimation(DependencyObject targetHint)
this.delay,
this.duration,
this.repeat,
- this.keyFrames.AsSpan());
+ this.keyFrames.GetArraySegment());
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.cs
index ef707c415d6..4542c36551b 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/NormalizedKeyFrameAnimationBuilder{T}.cs
@@ -150,7 +150,7 @@ public KeyFrameInfo(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue GetValueAs()
{
- return Unsafe.As(ref Unsafe.AsRef(in this.value));
+ return (TValue)(object)this.value;
}
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Composition.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Composition.cs
index 066e7ad6c1a..772aa7f90a7 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Composition.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Composition.cs
@@ -47,8 +47,8 @@ public CompositionAnimation GetAnimation(CompositionObject targetHint, out Compo
// We can retrieve the total duration from the last timed keyframe, and then set
// this as the target duration and use it to normalize the keyframe progresses.
- ReadOnlySpan keyFrames = this.keyFrames.AsSpan();
- TimeSpan duration = keyFrames[keyFrames.Length - 1].GetTimedProgress(default);
+ ArraySegment keyFrames = this.keyFrames.GetArraySegment();
+ TimeSpan duration = keyFrames[keyFrames.Count - 1].GetTimedProgress(default);
return NormalizedKeyFrameAnimationBuilder.GetAnimation(
targetHint,
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Xaml.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Xaml.cs
index ee6042a7a07..1c73463697b 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Xaml.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.Xaml.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
@@ -32,7 +31,7 @@ public static Timeline GetAnimation(
TimeSpan? delay,
TimeSpan duration,
RepeatOption repeat,
- ReadOnlySpan keyFrames)
+ ArraySegment keyFrames)
where TKeyFrame : struct, IKeyFrameInfo
{
Timeline animation;
@@ -118,7 +117,9 @@ public static Timeline GetAnimation(
}
else
{
- return ThrowHelper.ThrowInvalidOperationException("Invalid animation type");
+ static Timeline ThrowInvalidOperationException() => throw new InvalidOperationException("Invalid animation type");
+
+ return ThrowInvalidOperationException();
}
animation.BeginTime = delay;
@@ -163,7 +164,7 @@ public Timeline GetAnimation(DependencyObject targetHint)
this.delay,
default,
this.repeat,
- this.keyFrames.AsSpan());
+ this.keyFrames.GetArraySegment());
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.cs
index 6c0368d6f55..8a55f7d296f 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Builders/TimedKeyFrameAnimationBuilder{T}.cs
@@ -143,7 +143,7 @@ public KeyFrameInfo(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue GetValueAs()
{
- return Unsafe.As(ref Unsafe.AsRef(in this.value));
+ return (TValue)(object)this.value;
}
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/AnimationEffect.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Effects/AnimationEffect.cs
deleted file mode 100644
index 724e1e35fec..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/AnimationEffect.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-// 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;
-using System.Numerics;
-using Windows.UI.Composition;
-using Windows.UI.Xaml;
-using Windows.UI.Xaml.Hosting;
-using Windows.UI.Xaml.Media.Animation;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Effects
-{
- ///
- /// An abstract class that provides the mechanism to create
- /// an effect using composition.
- ///
- public abstract class AnimationEffect
- {
- private static string[] _effectProperties;
-
- ///
- /// Gets the name of the effect.
- ///
- ///
- /// The name of the effect.
- ///
- public abstract string EffectName { get; }
-
- ///
- /// Gets or sets the compositor.
- ///
- ///
- /// The compositor.
- ///
- public Compositor Compositor { get; set; }
-
- ///
- /// Gets or sets the effect brush.
- ///
- ///
- /// The effect brush.
- ///
- public CompositionEffectBrush EffectBrush { get; set; }
-
- ///
- /// Applies the effect.
- ///
- /// An array of strings of the effect properties to change.
- public abstract string[] ApplyEffect();
-
- ///
- /// An animation which will apply the derived effect.
- ///
- /// The animation set.
- /// The value.
- /// The duration in milliseconds.
- /// The delay in milliseconds.
- /// The easing function to use
- /// The easing mode to use
- /// An animation set with the effect added to it.
- public AnimationSet EffectAnimation(
- AnimationSet animationSet,
- double value = 0d,
- double duration = 500d,
- double delay = 0d,
- EasingType easingType = EasingType.Default,
- EasingMode easingMode = EasingMode.EaseOut)
- {
- if (animationSet == null)
- {
- return null;
- }
-
- var visual = animationSet.Visual;
- var associatedObject = animationSet.Element as FrameworkElement;
-
- if (associatedObject == null)
- {
- return animationSet;
- }
-
- Compositor = visual?.Compositor;
-
- if (Compositor == null)
- {
- return null;
- }
-
- // check to see if the visual already has an effect applied.
- var spriteVisual = ElementCompositionPreview.GetElementChildVisual(associatedObject) as SpriteVisual;
- EffectBrush = spriteVisual?.Brush as CompositionEffectBrush;
-
- if (EffectBrush == null || EffectBrush?.Comment != EffectName)
- {
- _effectProperties = ApplyEffect();
- EffectBrush.Comment = EffectName;
-
- var sprite = Compositor.CreateSpriteVisual();
- sprite.Brush = EffectBrush;
- ElementCompositionPreview.SetElementChildVisual(associatedObject, sprite);
-
- sprite.Size = new Vector2((float)associatedObject.ActualWidth, (float)associatedObject.ActualHeight);
-
- associatedObject.SizeChanged +=
- (s, e) =>
- {
- sprite.Size = new Vector2(
- (float)associatedObject.ActualWidth,
- (float)associatedObject.ActualHeight);
- };
- }
-
- if (duration <= 0)
- {
- foreach (var effectProperty in _effectProperties)
- {
- animationSet.AddEffectDirectPropertyChange(EffectBrush, (float)value, effectProperty);
- }
- }
- else
- {
- foreach (var effectProperty in _effectProperties)
- {
- var animation = Compositor.CreateScalarKeyFrameAnimation();
- animation.InsertKeyFrame(1f, (float)value, AnimationExtensions.GetCompositionEasingFunction(easingType, Compositor, easingMode));
-
- animation.Duration = TimeSpan.FromMilliseconds(duration);
- animation.DelayTime = TimeSpan.FromMilliseconds(delay);
-
- animationSet.AddCompositionEffectAnimation(EffectBrush, animation, effectProperty);
- }
- }
-
- // Saturation starts from 1 to 0, instead of 0 to 1 so this makes sure the
- // the brush isn't removed from the UI element incorrectly. Completing on
- // Saturation as it's reusing the same sprite visual. Removing the Sprite removes the effect.
- if (EffectName != "Saturation" && value == 0)
- {
- animationSet.Completed += AnimationSet_Completed;
- }
-
- return animationSet;
- }
-
- ///
- /// Handles the Completed event of the AnimationSet control.
- /// When an animation is completed the brush is removed from the sprite.
- ///
- /// The source of the event.
- /// The instance containing the event data.
- private void AnimationSet_Completed(object sender, EventArgs e)
- {
- var animationSet = sender as AnimationSet;
-
- if (animationSet != null)
- {
- animationSet.Completed -= AnimationSet_Completed;
-
- var spriteVisual = ElementCompositionPreview.GetElementChildVisual(animationSet.Element) as SpriteVisual;
- var brush = spriteVisual?.Brush as CompositionEffectBrush;
-
- if (brush != null && brush.Comment == EffectName)
- {
- spriteVisual.Brush = null;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Blur.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Blur.cs
deleted file mode 100644
index aa60fd5e059..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Blur.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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 Microsoft.Graphics.Canvas.Effects;
-using Windows.UI.Composition;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Effects
-{
- ///
- /// An animation effect that applies blur.
- ///
- ///
- public class Blur : AnimationEffect
- {
- ///
- /// Gets the name of the effect.
- ///
- ///
- /// The name of the effect.
- ///
- public override string EffectName { get; } = "Blur";
-
- ///
- /// Applies the effect.
- ///
- ///
- /// An array of strings of the effect properties to change.
- ///
- public override string[] ApplyEffect()
- {
- var gaussianBlur = new GaussianBlurEffect
- {
- Name = EffectName,
- BlurAmount = 0f,
- Optimization = EffectOptimization.Balanced,
- BorderMode = EffectBorderMode.Hard,
- Source = new CompositionEffectSourceParameter("source")
- };
-
- var propertyToChange = $"{EffectName}.BlurAmount";
- var propertiesToAnimate = new[] { propertyToChange };
-
- EffectBrush = Compositor.CreateEffectFactory(gaussianBlur, propertiesToAnimate).CreateBrush();
- EffectBrush.SetSourceParameter("source", Compositor.CreateBackdropBrush());
-
- return propertiesToAnimate;
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Saturation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Saturation.cs
deleted file mode 100644
index 30289f925a3..00000000000
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Effects/Saturation.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 Microsoft.Graphics.Canvas.Effects;
-using Windows.UI.Composition;
-
-namespace Microsoft.Toolkit.Uwp.UI.Animations.Effects
-{
- ///
- /// An animation effect that applies saturation.
- ///
- ///
- public class Saturation : AnimationEffect
- {
- ///
- /// Gets the name of the effect.
- ///
- ///
- /// The name of the effect.
- ///
- public override string EffectName { get; } = "Saturation";
-
- ///
- /// Applies the effect.
- ///
- ///
- /// An array of strings of the effect properties to change.
- ///
- public override string[] ApplyEffect()
- {
- var saturationEffect = new SaturationEffect
- {
- Saturation = 1f,
- Name = EffectName,
- Source = new CompositionEffectSourceParameter("source")
- };
-
- var propertyToChange = $"{EffectName}.Saturation";
- var propertiesToAnimate = new[] { propertyToChange };
-
- EffectBrush = Compositor.CreateEffectFactory(saturationEffect, propertiesToAnimate).CreateBrush();
- EffectBrush.SetSourceParameter("source", Compositor.CreateBackdropBrush());
-
- return propertiesToAnimate;
- }
- }
-}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/AnimationExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/AnimationExtensions.cs
index e40c9033170..ae4081f56f1 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/AnimationExtensions.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/AnimationExtensions.cs
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Numerics;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
@@ -111,7 +110,7 @@ public static class Composition
Axis.X => "AnchorPoint.X",
Axis.Y => "AnchorPoint.Y",
Axis.Z => "AnchorPoint.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -132,7 +131,7 @@ public static class Composition
Axis.X => "Translation.X",
Axis.Y => "Translation.Y",
Axis.Z => "Translation.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -153,7 +152,7 @@ public static class Composition
Axis.X => "Offset.X",
Axis.Y => "Offset.Y",
Axis.Z => "Offset.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -174,7 +173,7 @@ public static class Composition
Axis.X => "Scale.X",
Axis.Y => "Scale.Y",
Axis.Z => "Scale.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -195,7 +194,7 @@ public static class Composition
Axis.X => "CenterPoint.X",
Axis.Y => "CenterPoint.Y",
Axis.Z => "CenterPoint.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -217,7 +216,7 @@ public static class Composition
Side.Bottom => nameof(InsetClip.BottomInset),
Side.Right => nameof(InsetClip.RightInset),
Side.Left => nameof(InsetClip.LeftInset),
- _ => ThrowHelper.ThrowArgumentException("Invalid clip side")
+ _ => ThrowArgumentException("Invalid clip side")
};
///
@@ -238,7 +237,7 @@ public static class Composition
Axis.X => "Size.X",
Axis.Y => "Size.Y",
Axis.Z => "Size.Z",
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
}
@@ -257,7 +256,7 @@ public static class Xaml
{
Axis.X => nameof(CompositeTransform.TranslateX),
Axis.Y => nameof(CompositeTransform.TranslateY),
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -270,7 +269,7 @@ public static class Xaml
{
Axis.X => nameof(CompositeTransform.ScaleX),
Axis.Y => nameof(CompositeTransform.ScaleY),
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -283,7 +282,7 @@ public static class Xaml
{
Axis.X => nameof(CompositeTransform.CenterX),
Axis.Y => nameof(CompositeTransform.CenterY),
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
///
@@ -296,9 +295,17 @@ public static class Xaml
{
Axis.X => nameof(FrameworkElement.Width),
Axis.Y => nameof(FrameworkElement.Height),
- _ => ThrowHelper.ThrowArgumentException("Invalid axis")
+ _ => ThrowArgumentException("Invalid axis")
};
}
+
+ ///
+ /// Throws a new with a given message.
+ ///
+ private static T ThrowArgumentException(string message)
+ {
+ throw new ArgumentException(message);
+ }
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/EasingTypeExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/EasingTypeExtensions.cs
index 039f807f4f5..325a0cb6814 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/EasingTypeExtensions.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/EasingTypeExtensions.cs
@@ -4,8 +4,8 @@
#nullable enable
+using System;
using System.Diagnostics.Contracts;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml.Media.Animation;
using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions;
@@ -46,8 +46,10 @@ public static class EasingTypeExtensions
EasingType.Quintic => new QuinticEase { EasingMode = easingMode },
EasingType.Sine => new SineEase { EasingMode = easingMode },
- _ => ThrowHelper.ThrowArgumentException("Invalid easing type")
+ _ => ThrowArgumentException()
};
+
+ static EasingFunctionBase ThrowArgumentException() => throw new ArgumentException("Invalid easing type");
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/ScrollViewerExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/ScrollViewerExtensions.cs
index a11b6b27b11..09bf2781bf4 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/ScrollViewerExtensions.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Extensions/ScrollViewerExtensions.cs
@@ -2,8 +2,8 @@
// 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;
using System.Numerics;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -66,11 +66,13 @@ public static ExpressionAnimation StartExpressionAnimation(
visual.StartAnimation($"{nameof(Visual.Offset)}.{targetAxis}", animation);
break;
default:
- ThrowHelper.ThrowArgumentException("Invalid target property");
+ ThrowArgumentException();
break;
}
return animation;
+
+ static ExpressionAnimation ThrowArgumentException() => throw new ArgumentException("Invalid target property");
}
}
}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj
index 77c82601938..3bc4c0b8b6c 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj
@@ -18,22 +18,6 @@
9.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Options/RepeatOption.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Options/RepeatOption.cs
index 62515f7a833..58ee14c03fd 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Options/RepeatOption.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Options/RepeatOption.cs
@@ -4,7 +4,6 @@
using System;
using System.Diagnostics.Contracts;
-using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation.Metadata;
using Windows.UI.Composition;
using Windows.UI.Xaml.Media.Animation;
@@ -52,7 +51,10 @@ private RepeatOption(int value)
[Pure]
public static RepeatOption Count(int count)
{
- Guard.IsGreaterThanOrEqualTo(count, 0, nameof(count));
+ if (count < 0)
+ {
+ ThrowArgumentOutOfRangeForCount();
+ }
return new(count);
}
@@ -76,7 +78,7 @@ public static RepeatOption Parse(string text)
return Forever;
}
- return ThrowHelper.ThrowArgumentException("Invalid input text");
+ return ThrowArgumentExceptionForText();
}
///
@@ -109,5 +111,21 @@ public RepeatBehavior ToRepeatBehavior()
return (AnimationIterationBehavior.Count, this.value);
}
+
+ ///
+ /// Throws a new when the constructor is invoked with an incorrect parameter.
+ ///
+ private static void ThrowArgumentOutOfRangeForCount()
+ {
+ throw new ArgumentOutOfRangeException("The parameter \"count\" must be greater than or equal to 0.");
+ }
+
+ ///
+ /// Throws a new when the constructor is invoked with an incorrect parameter.
+ ///
+ private static RepeatOption ThrowArgumentExceptionForText()
+ {
+ throw new ArgumentException("The input text is not valid to parse a new RepeatOption instance. It must be either a natural number or \"Forever\".");
+ }
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StartAnimationActivity.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StartAnimationActivity.cs
index 3abc6a08e84..c74a4c5c533 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StartAnimationActivity.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StartAnimationActivity.cs
@@ -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.
+using System;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@@ -53,7 +53,10 @@ public UIElement TargetObject
///
public override async Task InvokeAsync(UIElement element, CancellationToken token)
{
- Guard.IsNotNull(Animation, nameof(Animation));
+ if (Animation is null)
+ {
+ ThrowArgumentNullException();
+ }
await base.InvokeAsync(element, token);
@@ -72,6 +75,8 @@ public override async Task InvokeAsync(UIElement element, CancellationToken toke
{
await Animation.StartAsync(token);
}
+
+ static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StopAnimationActivity.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StopAnimationActivity.cs
index 58a174ea7f2..6fb686ccb82 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StopAnimationActivity.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Activities/StopAnimationActivity.cs
@@ -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.
+using System;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@@ -53,7 +53,10 @@ public UIElement TargetObject
///
public override async Task InvokeAsync(UIElement element, CancellationToken token)
{
- Guard.IsNotNull(Animation, nameof(Animation));
+ if (Animation is null)
+ {
+ ThrowArgumentNullException();
+ }
await base.InvokeAsync(element, token);
@@ -69,6 +72,8 @@ public override async Task InvokeAsync(UIElement element, CancellationToken toke
{
Animation.Stop();
}
+
+ static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs
index 22441c8409f..44a211cb0f0 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/AnimationSet.cs
@@ -9,7 +9,6 @@
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@@ -33,16 +32,9 @@ public sealed class AnimationSet : DependencyObjectCollection
public event EventHandler? Started;
///
- /// Raised whenever the current animation ends.
+ /// Raised whenever the current animation completes.
///
- public event EventHandler? Ended;
-
- ///
- /// An interface representing a node in an instance.
- ///
- public interface INode
- {
- }
+ public event EventHandler? Completed;
///
/// Gets or sets a value indicating whether top level animation nodes in this collection are invoked
@@ -74,7 +66,7 @@ public async void Start()
// Here we're using an async void method on purpose, in order to be able to await
// the completion of the animation and rethrow exceptions. We can't just use the
// synchronous AnimationBuilder.Start method here, as we also need to await for the
- // animation to complete in either case in order to raise the Ended event when that
+ // animation to complete in either case in order to raise the Completed event when that
// happens. So we add an async state machine here to work around this.
await StartAsync();
}
@@ -125,7 +117,7 @@ public async Task StartAsync(UIElement element, CancellationToken token)
if (IsSequential)
{
- foreach (INode node in this)
+ foreach (object node in this)
{
if (node is ITimeline timeline)
{
@@ -152,6 +144,10 @@ public async Task StartAsync(UIElement element, CancellationToken token)
break;
}
}
+ else
+ {
+ ThrowArgumentException();
+ }
// This should in theory only be necessary in the timeline branch, but doing this check
// after running activities too help guard against 3rd party activities that might not
@@ -166,7 +162,7 @@ public async Task StartAsync(UIElement element, CancellationToken token)
{
var builder = AnimationBuilder.Create();
- foreach (INode node in this)
+ foreach (object node in this)
{
switch (node)
{
@@ -176,13 +172,18 @@ public async Task StartAsync(UIElement element, CancellationToken token)
case IActivity activity:
_ = activity.InvokeAsync(element, token);
break;
+ default:
+ ThrowArgumentException();
+ break;
}
}
await builder.StartAsync(element, token);
}
- Ended?.Invoke(this, EventArgs.Empty);
+ Completed?.Invoke(this, EventArgs.Empty);
+
+ static void ThrowArgumentException() => throw new ArgumentException($"An animation set can only contain nodes implementing either ITimeline or IActivity");
}
///
@@ -218,10 +219,12 @@ private UIElement GetParent()
if (ParentReference?.TryGetTarget(out parent) != true)
{
- ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
+ ThrowInvalidOperationException();
}
return parent!;
+
+ static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current AnimationSet object isn't bound to a parent UIElement instance.");
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs
index 2cbf0da37c2..eda7836e7f4 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs
@@ -6,7 +6,6 @@
using System;
using System.Diagnostics.Contracts;
-using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Hosting;
@@ -85,10 +84,12 @@ private UIElement GetParent()
if (ParentReference?.TryGetTarget(out parent) != true)
{
- ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
+ ThrowInvalidOperationException();
}
return parent!;
+
+ static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current ImplicitAnimationSet object isn't bound to a parent UIElement instance.");
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IActivity.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IActivity.cs
index 7baa9a47d77..9237b843c85 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IActivity.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IActivity.cs
@@ -11,7 +11,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
///
/// An interface representing a XAML model for a custom activity or action within an .
///
- public interface IActivity : AnimationSet.INode
+ public interface IActivity
{
///
/// Invokes the current activity.
diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs
index 78635101acd..6f3ad7d93d7 100644
--- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/ITimeline.cs
@@ -10,7 +10,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
///
/// An interface representing a XAML model for a custom animation.
///
- public interface ITimeline : AnimationSet.INode
+ public interface ITimeline
{
///
/// Appens the current animation to a target instance.
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationEndBehavior.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationCompletedTriggerBehavior.cs
similarity index 84%
rename from Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationEndBehavior.cs
rename to Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationCompletedTriggerBehavior.cs
index eb0a6442bd9..86848be62f2 100644
--- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationEndBehavior.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationCompletedTriggerBehavior.cs
@@ -10,9 +10,9 @@
namespace Microsoft.Toolkit.Uwp.UI.Behaviors
{
///
- /// A custom that fires whenever a linked ends.
+ /// A custom that fires whenever a linked completes.
///
- public sealed class AnimationEndBehavior : Trigger
+ public sealed class AnimationCompletedTriggerBehavior : Trigger
{
///
/// The current instance in use.
@@ -48,14 +48,14 @@ private void SetResolvedCollection(AnimationSet? animationCollection)
if (this.animationCollection is not null)
{
- this.animationCollection.Ended -= AnimationCollection_Ended;
+ this.animationCollection.Completed -= AnimationCollection_Completed;
}
this.animationCollection = animationCollection;
if (animationCollection is not null)
{
- animationCollection.Ended += AnimationCollection_Ended;
+ animationCollection.Completed += AnimationCollection_Completed;
}
}
@@ -64,7 +64,7 @@ private void SetResolvedCollection(AnimationSet? animationCollection)
///
/// The source instance.
/// The arguments for the event (unused).
- private void AnimationCollection_Ended(object sender, System.EventArgs e)
+ private void AnimationCollection_Completed(object sender, System.EventArgs e)
{
Interaction.ExecuteActions(sender, Actions, e);
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartBehavior.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartedTriggerBehavior.cs
similarity index 96%
rename from Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartBehavior.cs
rename to Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartedTriggerBehavior.cs
index 11c87b9cb52..a436fa21738 100644
--- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartBehavior.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/AnimationStartedTriggerBehavior.cs
@@ -12,7 +12,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
///
/// A custom that fires whenever a linked starts.
///
- public sealed class AnimationStartBehavior : Trigger
+ public sealed class AnimationStartedTriggerBehavior : Trigger
{
///
/// The current instance in use.
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs
index aa75926d7fc..72f65dec6cf 100644
--- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StartAnimationAction.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.Toolkit.Diagnostics;
+using System;
using Microsoft.Toolkit.Uwp.UI.Animations;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
@@ -53,7 +53,10 @@ public UIElement TargetObject
///
public object Execute(object sender, object parameter)
{
- Guard.IsNotNull(Animation, nameof(Animation));
+ if (Animation is null)
+ {
+ ThrowArgumentNullException();
+ }
if (TargetObject is not null)
{
@@ -65,6 +68,8 @@ public object Execute(object sender, object parameter)
}
return null!;
+
+ static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs
index 52f889b6bdd..c8f2a197950 100644
--- a/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Behaviors/Animations/StopAnimationAction.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using Microsoft.Toolkit.Diagnostics;
+using System;
using Microsoft.Toolkit.Uwp.UI.Animations;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
@@ -53,7 +53,10 @@ public UIElement TargetObject
///
public object Execute(object sender, object parameter)
{
- Guard.IsNotNull(Animation, nameof(Animation));
+ if (Animation is null)
+ {
+ ThrowArgumentNullException();
+ }
if (TargetObject is not null)
{
@@ -65,6 +68,8 @@ public object Execute(object sender, object parameter)
}
return null!;
+
+ static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Metadata.cs
new file mode 100644
index 00000000000..557ff70648b
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Metadata.cs
@@ -0,0 +1,28 @@
+// 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.ComponentModel;
+
+using Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties;
+
+using Microsoft.VisualStudio.DesignTools.Extensibility;
+using Microsoft.VisualStudio.DesignTools.Extensibility.Metadata;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
+{
+ internal class TabbedCommandBarMetadata : AttributeTableBuilder
+ {
+ public TabbedCommandBarMetadata()
+ : base()
+ {
+ AddCallback(ControlTypes.TabbedCommandBar,
+ b =>
+ {
+ b.AddCustomAttributes(nameof(TabbedCommandBar.CollapsedState), new CategoryAttribute(Resources.CategoryAppearance));
+ b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false));
+ }
+ );
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Typedata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Typedata.cs
new file mode 100644
index 00000000000..d37d5eae745
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBar.Typedata.cs
@@ -0,0 +1,18 @@
+// 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;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
+{
+ internal static partial class ControlTypes
+ {
+ internal const string TabbedCommandBar = RootNamespace + "." + nameof(TabbedCommandBar);
+ }
+
+ internal static class TabbedCommandBar
+ {
+ internal const string CollapsedState = nameof(CollapsedState);
+ }
+}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Metadata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Metadata.cs
new file mode 100644
index 00000000000..1099909bbf8
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Metadata.cs
@@ -0,0 +1,35 @@
+// 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.ComponentModel;
+
+using Microsoft.Toolkit.Uwp.UI.Controls.Design.Properties;
+
+using Microsoft.VisualStudio.DesignTools.Extensibility;
+using Microsoft.VisualStudio.DesignTools.Extensibility.Metadata;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
+{
+ internal class TabbedCommandBarItemMetadata : AttributeTableBuilder
+ {
+ public TabbedCommandBarItemMetadata()
+ : base()
+ {
+ AddCallback(ControlTypes.TabbedCommandBarItem,
+ b =>
+ {
+ // TODO
+ // b.AddCustomAttributes(nameof(TabbedCommandBarItem.Header), new CategoryAttribute(Resources.CategoryCommon));
+ // b.AddCustomAttributes(nameof(TabbedCommandBarItem.Footer), new CategoryAttribute(Resources.CategoryCommon));
+ b.AddCustomAttributes(nameof(TabbedCommandBarItem.IsContextual), new CategoryAttribute(Resources.CategoryCommon));
+ b.AddCustomAttributes(nameof(TabbedCommandBarItem.OverflowButtonAlignment),
+ new CategoryAttribute(Resources.CategoryLayout),
+ new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
+ );
+ b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false));
+ }
+ );
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Typedata.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Typedata.cs
new file mode 100644
index 00000000000..10a5ec9cdac
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Controls/TabbedCommandBarItem.Typedata.cs
@@ -0,0 +1,21 @@
+// 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;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
+{
+ internal static partial class ControlTypes
+ {
+ internal const string TabbedCommandBarItem = RootNamespace + "." + nameof(TabbedCommandBarItem);
+ }
+
+ internal static class TabbedCommandBarItem
+ {
+ internal const string Header = nameof(Header);
+ internal const string Footer = nameof(Footer);
+ internal const string IsContextual = nameof(IsContextual);
+ internal const string OverflowButtonAlignment = nameof(OverflowButtonAlignment);
+ }
+}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj
index d4aad29f577..8ddeb05d50a 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Microsoft.Toolkit.Uwp.UI.Controls.Core.DesignTools.csproj
@@ -97,6 +97,10 @@
+
+
+
+
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.Designer.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.Designer.cs
index 15da20fa9e5..954ae670563 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.Designer.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.Designer.cs
@@ -95,5 +95,14 @@ internal static string CategoryDropShadow {
return ResourceManager.GetString("CategoryDropShadow", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Layout.
+ ///
+ internal static string CategoryLayout {
+ get {
+ return ResourceManager.GetString("CategoryLayout", resourceCulture);
+ }
+ }
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.resx b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.resx
index aeb265b2d63..55cac3ca36f 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.resx
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core.Design/Properties/Resources.resx
@@ -129,4 +129,7 @@
Drop Shadow
+
+ Layout
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/Microsoft.Toolkit.Uwp.UI.Controls.Core.csproj b/Microsoft.Toolkit.Uwp.UI.Controls.Core/Microsoft.Toolkit.Uwp.UI.Controls.Core.csproj
index b604c5a7219..1bf0967c67d 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/Microsoft.Toolkit.Uwp.UI.Controls.Core.csproj
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/Microsoft.Toolkit.Uwp.UI.Controls.Core.csproj
@@ -14,6 +14,7 @@
- Loading: Helps to show content with animation to the user while the app is doing some calculation.
- RadialProgressBar: Displays progress as a circle getting filled.
- RotatorTile: Rotates through a set of items one-by-one like a live-tile.
+ - TabbedCommandBar: A ribbon-like control that displays a tabbed collection of CommandBars
- TextToolbar: A Toolbar for Editing Text attached to a RichEditBox. It can format RTF, Markdown, or use a Custom Formatter.
- TileControl: A ContentControl that show an image repeated many times.
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs
new file mode 100644
index 00000000000..dcde9b916ad
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.cs
@@ -0,0 +1,101 @@
+// 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.Linq;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Markup;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Media.Animation;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls
+{
+ ///
+ /// A basic ribbon control that houses s
+ ///
+ [ContentProperty(Name = nameof(MenuItems))]
+ [TemplatePart(Name = "PART_RibbonContent", Type = typeof(ContentControl))]
+ [TemplatePart(Name = "PART_RibbonContentBorder", Type = typeof(Border))]
+ [TemplatePart(Name = "PART_TabChangedStoryboard", Type = typeof(Storyboard))]
+ public class TabbedCommandBar : NavigationView
+ {
+ private ContentControl _ribbonContent = null;
+ private Border _ribbonContentBorder = null;
+ private Storyboard _tabChangedStoryboard = null;
+
+ ///
+ /// The last selected .
+ ///
+ private TabbedCommandBarItem _previousSelectedItem = null;
+ private long _visibilityChangedToken;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TabbedCommandBar()
+ {
+ DefaultStyleKey = typeof(TabbedCommandBar);
+
+ SelectionChanged += SelectedItemChanged;
+ }
+
+ ///
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if (_ribbonContent != null)
+ {
+ _ribbonContent.Content = null;
+ }
+
+ // Get RibbonContent first, since setting SelectedItem requires it
+ _ribbonContent = GetTemplateChild("PART_RibbonContent") as ContentControl;
+ _ribbonContentBorder = GetTemplateChild("PART_RibbonContentBorder") as Border;
+ _tabChangedStoryboard = GetTemplateChild("TabChangedStoryboard") as Storyboard;
+
+ SelectedItem = MenuItems.FirstOrDefault();
+ }
+
+ private void SelectedItemChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
+ {
+ var item = sender.SelectedItem as TabbedCommandBarItem;
+ if (item == null || item.Visibility == Visibility.Collapsed)
+ {
+ // If the item is now hidden, select the first item instead.
+ // I can't think of any way that the visibiltiy would be null
+ // and still be selectable, but let's handle it just in case.
+ sender.SelectedItem = sender.MenuItems.FirstOrDefault();
+ return;
+ }
+
+ // Remove the visibility PropertyChanged handler from the
+ // previously selected item
+ if (_previousSelectedItem != null)
+ {
+ _previousSelectedItem.UnregisterPropertyChangedCallback(TabbedCommandBarItem.VisibilityProperty, _visibilityChangedToken);
+ }
+
+ // Register a new visibility PropertyChangedcallback for the
+ // currently selected item
+ _previousSelectedItem = item;
+ _visibilityChangedToken =
+ _previousSelectedItem.RegisterPropertyChangedCallback(TabbedCommandBarItem.VisibilityProperty, SelectedItemVisibilityChanged);
+
+ // Set the ribbon background and start the transition animation
+ _tabChangedStoryboard?.Begin();
+ }
+
+ private void SelectedItemVisibilityChanged(DependencyObject sender, DependencyProperty dp)
+ {
+ // If the item is not visible, default to the first tab
+ if (sender.GetValue(dp) is Visibility vis && vis == Visibility.Collapsed)
+ {
+ // FIXME: This will cause WinUI to throw an exception if run
+ // when the tabs overflow
+ SelectedItem = MenuItems.FirstOrDefault();
+ }
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml
new file mode 100644
index 00000000000..8600e416ae4
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBar.xaml
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.cs
new file mode 100644
index 00000000000..8cf1bb24ba2
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.cs
@@ -0,0 +1,126 @@
+// 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 Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls
+{
+ ///
+ /// A to be displayed in a
+ ///
+ [TemplatePart(Name = "PrimaryItemsControl", Type = typeof(ItemsControl))]
+ [TemplatePart(Name = "MoreButton", Type = typeof(Button))]
+ public class TabbedCommandBarItem : CommandBar
+ {
+ private ItemsControl _primaryItemsControl;
+ private Button _moreButton;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TabbedCommandBarItem()
+ {
+ DefaultStyleKey = typeof(TabbedCommandBarItem);
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
+ nameof(Header),
+ typeof(object),
+ typeof(TabbedCommandBarItem),
+ new PropertyMetadata(string.Empty));
+
+ ///
+ /// Gets or sets the text or to display in the header of this ribbon tab.
+ ///
+ public object Header
+ {
+ get => (object)GetValue(HeaderProperty);
+ set => SetValue(HeaderProperty, value);
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty IsContextualProperty = DependencyProperty.Register(
+ nameof(IsContextual),
+ typeof(bool),
+ typeof(TabbedCommandBarItem),
+ new PropertyMetadata(false));
+
+ ///
+ /// Gets or sets a value indicating whether this tab is contextual.
+ ///
+ public bool IsContextual
+ {
+ get => (bool)GetValue(IsContextualProperty);
+ set => SetValue(IsContextualProperty, value);
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty OverflowButtonAlignmentProperty = DependencyProperty.Register(
+ nameof(OverflowButtonAlignment),
+ typeof(HorizontalAlignment),
+ typeof(TabbedCommandBarItem),
+ new PropertyMetadata(HorizontalAlignment.Left));
+
+ ///
+ /// Gets or sets a value indicating the alignment of the command overflow button.
+ ///
+ public HorizontalAlignment OverflowButtonAlignment
+ {
+ get => (HorizontalAlignment)GetValue(OverflowButtonAlignmentProperty);
+ set => SetValue(OverflowButtonAlignmentProperty, value);
+ }
+
+ ///
+ /// Identifies the property.
+ ///
+ public static readonly DependencyProperty CommandAlignmentProperty = DependencyProperty.Register(
+ nameof(CommandAlignment),
+ typeof(HorizontalAlignment),
+ typeof(TabbedCommandBarItem),
+ new PropertyMetadata(HorizontalAlignment.Stretch));
+
+ ///
+ /// Gets or sets a value indicating the alignment of the commands in the .
+ ///
+ public HorizontalAlignment CommandAlignment
+ {
+ get => (HorizontalAlignment)GetValue(CommandAlignmentProperty);
+ set => SetValue(CommandAlignmentProperty, value);
+ }
+
+ ///
+ protected override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _primaryItemsControl = GetTemplateChild("PrimaryItemsControl") as ItemsControl;
+ if (_primaryItemsControl != null)
+ {
+ _primaryItemsControl.HorizontalAlignment = CommandAlignment;
+ RegisterPropertyChangedCallback(CommandAlignmentProperty, (sender, dp) =>
+ {
+ _primaryItemsControl.HorizontalAlignment = (HorizontalAlignment)sender.GetValue(dp);
+ });
+ }
+
+ _moreButton = GetTemplateChild("MoreButton") as Button;
+ if (_moreButton != null)
+ {
+ _moreButton.HorizontalAlignment = OverflowButtonAlignment;
+ RegisterPropertyChangedCallback(OverflowButtonAlignmentProperty, (sender, dp) =>
+ {
+ _moreButton.HorizontalAlignment = (HorizontalAlignment)sender.GetValue(dp);
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml
new file mode 100644
index 00000000000..d05424ee99e
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItem.xaml
@@ -0,0 +1,368 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItemTemplateSelector.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItemTemplateSelector.cs
new file mode 100644
index 00000000000..0c68a9b35f5
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/TabbedCommandBar/TabbedCommandBarItemTemplateSelector.cs
@@ -0,0 +1,37 @@
+// 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 Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+
+namespace Microsoft.Toolkit.Uwp.UI.Controls
+{
+ ///
+ /// used by for determining the style of normal vs. contextual s.
+ ///
+ public class TabbedCommandBarItemTemplateSelector : DataTemplateSelector
+ {
+ ///
+ /// Gets or sets the of a normal .
+ ///
+ public DataTemplate Normal { get; set; }
+
+ ///
+ /// Gets or sets the of a contextual .
+ ///
+ public DataTemplate Contextual { get; set; }
+
+ ///
+ protected override DataTemplate SelectTemplateCore(object item)
+ {
+ return item is TabbedCommandBarItem t && t.IsContextual ? Contextual : Normal;
+ }
+
+ ///
+ protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
+ {
+ return SelectTemplateCore(item);
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml
index 948dca8dc42..86346a7d1f4 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/Themes/Generic.xaml
@@ -9,6 +9,7 @@
+
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Core/VisualStudioToolsManifest.xml b/Microsoft.Toolkit.Uwp.UI.Controls.Core/VisualStudioToolsManifest.xml
index 34f1bc813a9..161131ea6dd 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Core/VisualStudioToolsManifest.xml
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Core/VisualStudioToolsManifest.xml
@@ -12,6 +12,7 @@
+
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs
index cb7398d9791..8dbe534bf94 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/ColorPicker/ColorPicker.cs
@@ -3,14 +3,11 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
-using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.Toolkit.Uwp.UI.Controls.ColorPickerConverters;
-using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@@ -233,10 +230,12 @@ private T GetTemplateChild(string childName, bool isRequired = false)
T child = this.GetTemplateChild(childName) as T;
if ((child == null) && isRequired)
{
- ThrowHelper.ThrowArgumentNullException(childName);
+ ThrowArgumentNullException();
}
return child;
+
+ static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(childName));
}
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RangeSelector/RangeSelector.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RangeSelector/RangeSelector.cs
index 373db695187..518f0de6790 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/RangeSelector/RangeSelector.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/RangeSelector/RangeSelector.cs
@@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
-using Microsoft.Toolkit.Uwp.UI.Extensions;
+using Microsoft.Toolkit.Uwp.Extensions;
using Windows.Foundation;
using Windows.System;
using Windows.UI.Xaml;
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/InterspersedObservableCollection.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/InterspersedObservableCollection.cs
index 74fc28fd5d1..c0c84fef849 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/InterspersedObservableCollection.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Input/TokenizingTextBox/InterspersedObservableCollection.cs
@@ -5,11 +5,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Collections.Immutable;
using System.Collections.Specialized;
-using System.ComponentModel;
using System.Linq;
-using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.Helpers;
namespace Microsoft.Toolkit.Uwp.UI.Controls
@@ -54,9 +51,12 @@ public object this[int index]
public InterspersedObservableCollection(object itemsSource)
{
- Guard.IsAssignableToType(itemsSource, nameof(itemsSource));
+ if (!(itemsSource is IList list))
+ {
+ ThrowArgumentException();
+ }
- ItemsSource = itemsSource as IList;
+ ItemsSource = list;
if (ItemsSource is INotifyCollectionChanged notifier)
{
@@ -67,6 +67,8 @@ public InterspersedObservableCollection(object itemsSource)
};
notifier.CollectionChanged += weakPropertyChangedListener.OnEvent;
}
+
+ static void ThrowArgumentException() => throw new ArgumentNullException("The input items source must be assignable to the System.Collections.IList type.");
}
private void ItemsSource_CollectionChanged(object source, NotifyCollectionChangedEventArgs eventArgs)
@@ -192,12 +194,20 @@ private void ReadjustKeys()
/// Inner ItemsSource Index.
private int ToInnerIndex(int outerIndex)
{
-#if DEBUG
- Guard.IsInRange(outerIndex, 0, Count, nameof(outerIndex));
- Guard.IsFalse(_interspersedObjects.ContainsKey(outerIndex), nameof(outerIndex)); // We can't map a inserted key to the original collection!
-#endif
+ if ((uint)outerIndex >= Count)
+ {
+ ThrowArgumentOutOfRangeException();
+ }
+
+ if (_interspersedObjects.ContainsKey(outerIndex))
+ {
+ ThrowArgumentException();
+ }
return outerIndex - _interspersedObjects.Keys.Count(key => key.Value <= outerIndex);
+
+ static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(outerIndex));
+ static void ThrowArgumentException() => throw new ArgumentException("The outer index can't be inserted as a key to the original collection.");
}
///
@@ -207,9 +217,10 @@ private int ToInnerIndex(int outerIndex)
/// Index into the entire collection.
private int ToOuterIndex(int innerIndex)
{
-#if DEBUG
- Guard.IsInRange(innerIndex, 0, ItemsSource.Count, nameof(innerIndex));
-#endif
+ if ((uint)innerIndex >= ItemsSource.Count)
+ {
+ ThrowArgumentOutOfRangeException();
+ }
var keys = _interspersedObjects.OrderBy(v => v.Key);
@@ -226,6 +237,8 @@ private int ToOuterIndex(int innerIndex)
}
return innerIndex;
+
+ static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndex));
}
///
@@ -235,9 +248,10 @@ private int ToOuterIndex(int innerIndex)
/// Projected index in the entire collection.
private int ToOuterIndexAfterRemoval(int innerIndexToProject)
{
-#if DEBUG
- Guard.IsInRange(innerIndexToProject, 0, ItemsSource.Count + 1, nameof(innerIndexToProject));
-#endif
+ if ((uint)innerIndexToProject >= ItemsSource.Count + 1)
+ {
+ ThrowArgumentOutOfRangeException();
+ }
//// TODO: Deal with bounds (0 / Count)? Or is it the same?
@@ -256,6 +270,8 @@ private int ToOuterIndexAfterRemoval(int innerIndexToProject)
}
return innerIndexToProject;
+
+ static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndexToProject));
}
///
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs
index d64d1123afd..131baccd3ad 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Layout/GridSplitter/GridSplitter.Events.cs
@@ -148,6 +148,11 @@ protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e)
var horizontalChange = e.Delta.Translation.X;
var verticalChange = e.Delta.Translation.Y;
+ if (this.FlowDirection == FlowDirection.RightToLeft)
+ {
+ horizontalChange *= -1;
+ }
+
if (_resizeDirection == GridResizeDirection.Columns)
{
if (HorizontalMove(horizontalChange))
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/UniformGrid/TakenSpotsReferenceHolder.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/UniformGrid/TakenSpotsReferenceHolder.cs
index 9474082e25e..0092a1275cb 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/UniformGrid/TakenSpotsReferenceHolder.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/UniformGrid/TakenSpotsReferenceHolder.cs
@@ -4,7 +4,6 @@
using System.Collections;
using System.Drawing;
-using Microsoft.Toolkit.Diagnostics;
namespace Microsoft.Toolkit.Uwp.UI.Controls
{
@@ -28,9 +27,6 @@ internal sealed class TakenSpotsReferenceHolder
/// The number of columns to track.
public TakenSpotsReferenceHolder(int rows, int columns)
{
- Guard.IsGreaterThanOrEqualTo(rows, 0, nameof(rows));
- Guard.IsGreaterThanOrEqualTo(columns, 0, nameof(columns));
-
Height = rows;
Width = columns;
diff --git a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/WrapPanel/WrapPanel.Data.cs b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/WrapPanel/WrapPanel.Data.cs
index c3112d9bd96..cf8ac47ed52 100644
--- a/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/WrapPanel/WrapPanel.Data.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Controls.Primitives/WrapPanel/WrapPanel.Data.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI.Xaml.Controls;
@@ -63,8 +62,10 @@ private struct UvRect
{
Orientation.Vertical => new Rect(Position.V, Position.U, Size.V, Size.U),
Orientation.Horizontal => new Rect(Position.U, Position.V, Size.U, Size.V),
- _ => ThrowHelper.ThrowNotSupportedException("unsupported orientation"),
+ _ => ThrowArgumentException()
};
+
+ private static Rect ThrowArgumentException() => throw new ArgumentException("The input orientation is not valid.");
}
private struct Row
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Animations/Abstract/EffectAnimation{TEffect,TValue,TKeyFrame}.cs b/Microsoft.Toolkit.Uwp.UI.Media/Animations/Abstract/EffectAnimation{TEffect,TValue,TKeyFrame}.cs
index b2f2f697824..faaa78dd171 100644
--- a/Microsoft.Toolkit.Uwp.UI.Media/Animations/Abstract/EffectAnimation{TEffect,TValue,TKeyFrame}.cs
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Animations/Abstract/EffectAnimation{TEffect,TValue,TKeyFrame}.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.UI.Media;
using Windows.UI.Composition;
using Windows.UI.Xaml;
@@ -50,14 +49,21 @@ public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeS
{
if (Target is not TEffect target)
{
- return ThrowHelper.ThrowArgumentNullException("The target effect is null, make sure to set the Target property");
+ 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)
{
- return ThrowHelper.ThrowArgumentNullException(
- "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.");
+ 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.Composition keyFrameBuilder = new(
@@ -70,7 +76,7 @@ public override AnimationBuilder AppendToBuilder(AnimationBuilder builder, TimeS
CompositionAnimation animation = keyFrameBuilder.GetAnimation(target.Brush!, out _);
- return builder.ExternalAnimation(target.Brush, animation);
+ return builder.ExternalAnimation(target.Brush!, animation);
}
}
}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs
new file mode 100644
index 00000000000..c1d870f08f9
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs
@@ -0,0 +1,307 @@
+// 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.Numerics;
+using Microsoft.Graphics.Canvas;
+using Microsoft.Graphics.Canvas.Brushes;
+using Microsoft.Graphics.Canvas.Geometry;
+using Windows.Foundation;
+using Windows.UI;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
+{
+ ///
+ /// Extension methods for CanvasDrawingSession.
+ ///
+ public static class CanvasDrawingSessionExtensions
+ {
+ ///
+ /// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Center of the Circle
+ /// Radius of the Circle
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawCircle(this CanvasDrawingSession session, Vector2 centerPoint, float radius, ICanvasStroke stroke)
+ {
+ session.DrawCircle(centerPoint, radius, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the Center in x axis
+ /// Ordinate of the Center in the y axis
+ /// Radius of the Circle
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawCircle(this CanvasDrawingSession session, float x, float y, float radius, ICanvasStroke stroke)
+ {
+ session.DrawCircle(x, y, radius, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Center of the Circle
+ /// Radius in the X axis
+ /// Radius in the Y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawEllipse(this CanvasDrawingSession session, Vector2 centerPoint, float radiusX, float radiusY, ICanvasStroke stroke)
+ {
+ session.DrawEllipse(centerPoint, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the Center on the x axis
+ /// Offset of the Center on the y axis
+ /// Radius in the X axis
+ /// Radius in the Y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawEllipse(this CanvasDrawingSession session, float x, float y, float radiusX, float radiusY, ICanvasStroke stroke)
+ {
+ session.DrawEllipse(x, y, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a geometry relative to the origin, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// CanvasGeometry to render
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, ICanvasStroke stroke)
+ {
+ session.DrawGeometry(geometry, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// CanvasGeometry to render
+ /// Offset
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, Vector2 offset, ICanvasStroke stroke)
+ {
+ session.DrawGeometry(geometry, offset, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// CanvasGeometry to render
+ /// Offset on the x axis
+ /// Offset on the y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, float x, float y, ICanvasStroke stroke)
+ {
+ session.DrawGeometry(geometry, x, y, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Starting position of the line
+ /// Ending position of the line
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawLine(this CanvasDrawingSession session, Vector2 point0, Vector2 point1, ICanvasStroke stroke)
+ {
+ session.DrawLine(point0, point1, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of Starting position of the line on x-axis
+ /// Offset of Starting position of the line on y-axis
+ /// Offset of Ending position of the line on x-axis
+ /// Offset of Ending position of the line on y-axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawLine(this CanvasDrawingSession session, float x0, float y0, float x1, float y1, ICanvasStroke stroke)
+ {
+ session.DrawLine(x0, y0, x1, y1, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Rectangle dimensions
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawRectangle(this CanvasDrawingSession session, Rect rect, ICanvasStroke stroke)
+ {
+ session.DrawRectangle(rect, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Rectangle on the x-axis
+ /// Offset of the top left corner of the Rectangle on the y-axis
+ /// Width of the Rectangle
+ /// Height of the Rectangle
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, ICanvasStroke stroke)
+ {
+ session.DrawRectangle(x, y, w, h, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Rectangle dimensions
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawRoundedRectangle(this CanvasDrawingSession session, Rect rect, float radiusX, float radiusY, ICanvasStroke stroke)
+ {
+ session.DrawRoundedRectangle(rect, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Rounded Rectangle on the x-axis
+ /// Offset of the top left corner of the Rounded Rectangle on the y-axis
+ /// Width of the Rounded Rectangle
+ /// Height of the Rounded Rectangle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawRoundedRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
+ {
+ session.DrawRoundedRectangle(x, y, w, h, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
+ }
+
+ ///
+ /// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.DrawGeometry(geometry, stroke);
+ }
+
+ ///
+ /// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// Offset of the Squircle from the origin.
+ /// CanvasStroke defining the stroke width, the stroke
+ /// color and stroke style.
+ public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasStroke stroke)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.DrawGeometry(geometry, offset, stroke);
+ }
+
+ ///
+ /// Fills a Squircle of the specified dimensions, using the given color.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// Color to fill the Squircle.
+ public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Color color)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.FillGeometry(geometry, color);
+ }
+
+ ///
+ /// Fills a Squircle of the specified dimensions, using the given brush.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// Brush to fill the Squircle.
+ public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasBrush brush)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.FillGeometry(geometry, brush);
+ }
+
+ ///
+ /// Fills a Squircle of the specified dimensions, using the given color at specified offset.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// Offset of the Squircle from the origin.
+ /// Color to fill the Squircle.
+ public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, Color color)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.FillGeometry(geometry, offset, color);
+ }
+
+ ///
+ /// Fills a Squircle of the specified dimensions, using the given brush at specified offset.
+ ///
+ /// CanvasDrawingSession
+ /// Offset of the top left corner of the Squircle on the x-axis
+ /// Offset of the top left corner of the Squircle on the y-axis
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x axis
+ /// Corner Radius on the y axis
+ /// Offset of the Squircle from the origin.
+ /// Brush to fill the Squircle.
+ public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasBrush brush)
+ {
+ using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
+ session.FillGeometry(geometry, offset, brush);
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs
new file mode 100644
index 00000000000..b704ce13f18
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs
@@ -0,0 +1,462 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using Microsoft.Graphics.Canvas.Geometry;
+using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
+{
+ ///
+ /// Defines extension methods for CanvasPathBuilder.
+ ///
+ public static class CanvasPathBuilderExtensions
+ {
+ private const float SquircleFactor = 1.125f;
+ private const float ControlPointFactor = 46f / 64f;
+
+ ///
+ /// Adds a line in the form of a cubic bezier. The control point of the quadratic bezier will be the endpoint of the line itself.
+ ///
+ ///
+ /// Ending location of the line segment.
+ public static void AddLineAsQuadraticBezier(this CanvasPathBuilder pathBuilder, Vector2 end)
+ {
+ pathBuilder.AddQuadraticBezier(end, end);
+ }
+
+ ///
+ /// Adds a line in the form of a cubic bezier. The two control points of the cubic bezier will be the endpoints of the line itself.
+ ///
+ ///
+ /// Starting location of the line segment.
+ /// Ending location of the line segment.
+ public static void AddLineAsCubicBezier(this CanvasPathBuilder pathBuilder, Vector2 start, Vector2 end)
+ {
+ pathBuilder.AddCubicBezier(start, end, end);
+ }
+
+ ///
+ /// Adds a circle figure to the path.
+ ///
+ ///
+ /// Center location of the circle.
+ /// Radius of the circle.
+ public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radius)
+ {
+ pathBuilder.AddEllipseFigure(center.X, center.Y, radius, radius);
+ }
+
+ ///
+ /// Adds a circle figure to the path.
+ ///
+ ///
+ /// X coordinate of the center location of the circle.
+ /// Y coordinate of the center location of the circle.
+ /// Radius of the circle.
+ public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radius)
+ {
+ pathBuilder.AddEllipseFigure(x, y, radius, radius);
+ }
+
+ ///
+ /// Adds an ellipse figure to the path.
+ ///
+ ///
+ /// Center location of the ellipse.
+ /// Radius of the ellipse on the X-axis.
+ /// Radius of the ellipse on the Y-axis.
+ public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radiusX, float radiusY)
+ {
+ pathBuilder.AddEllipseFigure(center.X, center.Y, radiusX, radiusY);
+ }
+
+ ///
+ /// Adds an ellipse figure to the path.
+ ///
+ ///
+ /// X coordinate of the center location of the ellipse.
+ /// Y coordinate of the center location of the ellipse.
+ /// Radius of the ellipse on the X-axis.
+ /// Radius of the ellipse on the Y-axis.
+ public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radiusX, float radiusY)
+ {
+ // Sanitize the radiusX by taking the absolute value
+ radiusX = Math.Abs(radiusX);
+
+ // Sanitize the radiusY by taking the absolute value
+ radiusY = Math.Abs(radiusY);
+
+ try
+ {
+ pathBuilder.BeginFigure(x + radiusX, y);
+ }
+ catch (ArgumentException)
+ {
+ // An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddEllipseFigure() method.
+ static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddEllipseFigure occurred, " +
+ "when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
+ "before calling CanvasPathBuilder.AddEllipseFigure, to end the previous figure.");
+
+ Throw();
+ }
+
+ // First Semi-Ellipse
+ pathBuilder.AddArc(new Vector2(x - radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
+
+ // Second Semi-Ellipse
+ pathBuilder.AddArc(new Vector2(x + radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
+
+ // End Figure
+ pathBuilder.EndFigure(CanvasFigureLoop.Closed);
+ }
+
+ ///
+ /// Adds a n-sided polygon figure to the path.
+ ///
+ ///
+ /// Number of sides of the polygon.
+ /// Center location of the polygon.
+ /// Radius of the circle circumscribing the polygon i.e. the distance
+ /// of each of the vertices of the polygon from the center.
+ public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, Vector2 center, float radius)
+ {
+ pathBuilder.AddPolygonFigure(numSides, center.X, center.Y, radius);
+ }
+
+ ///
+ /// Adds a n-sided polygon figure to the path.
+ ///
+ ///
+ /// Number of sides of the polygon.
+ /// X coordinate of the center location of the polygon.
+ /// Y coordinate of the center location of the polygon.
+ /// Radius of the circle circumscribing the polygon i.e. the distance
+ /// of each of the vertices of the polygon from the center.
+ public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, float x, float y, float radius)
+ {
+ // Sanitize the radius by taking the absolute value
+ radius = Math.Abs(radius);
+
+ // A polygon should have at least 3 sides
+ if (numSides <= 2)
+ {
+ ThrowArgumentOutOfRangeException();
+ }
+
+ // Calculate the first vertex location based on the number of sides
+ var angle = Scalar.TwoPi / numSides;
+ var startAngle = numSides % 2 == 1 ? Scalar.PiByTwo : Scalar.PiByTwo - (angle / 2f);
+
+ var startX = x + (float)(radius * Math.Cos(startAngle));
+ var startY = y - (float)(radius * Math.Sin(startAngle));
+
+ try
+ {
+ pathBuilder.BeginFigure(startX, startY);
+ }
+ catch (ArgumentException)
+ {
+ // An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
+ static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddPolygonFigure occurred, " +
+ "when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
+ "before calling CanvasPathBuilder.AddPolygonFigure, to end the previous figure.");
+
+ Throw();
+ }
+
+ // Add lines to the remaining vertices
+ for (var i = 1; i < numSides; i++)
+ {
+ var posX = x + (float)(radius * Math.Cos(startAngle + (i * angle)));
+ var posY = y - (float)(radius * Math.Sin(startAngle + (i * angle)));
+ pathBuilder.AddLine(posX, posY);
+ }
+
+ // Add a line to the first vertex so that the lines join properly
+ pathBuilder.AddLine(startX, startY);
+
+ // End the Figure
+ pathBuilder.EndFigure(CanvasFigureLoop.Closed);
+
+ static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException($"Parameter {nameof(numSides)} must be greater than 2.");
+ }
+
+ ///
+ /// Adds a Rectangle to the Path.
+ ///
+ ///
+ /// X offset of the TopLeft corner of the Rectangle
+ /// Y offset of the TopLeft corner of the Rectangle
+ /// Width of the Rectangle
+ /// Height of the Rectangle
+ public static void AddRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height)
+ {
+ // Sanitize the width by taking the absolute value
+ width = Math.Abs(width);
+
+ // Sanitize the height by taking the absolute value
+ height = Math.Abs(height);
+
+ try
+ {
+ pathBuilder.BeginFigure(x, y);
+ }
+ catch (ArgumentException)
+ {
+ // An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
+ static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRectangleFigure occurred, " +
+ "when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
+ "before calling CanvasPathBuilder.AddRectangleFigure, to end the previous figure.");
+
+ Throw();
+ }
+
+ // Top Side
+ pathBuilder.AddLine(x + width, y);
+
+ // Right Side
+ pathBuilder.AddLine(x + width, y + height);
+
+ // Bottom Side
+ pathBuilder.AddLine(x, y + height);
+
+ // Left Side
+ pathBuilder.AddLine(x, y);
+
+ // End the Figure
+ pathBuilder.EndFigure(CanvasFigureLoop.Closed);
+ }
+
+ ///
+ /// Adds a RoundedRectangle to the Path.
+ ///
+ ///
+ /// X offset of the TopLeft corner of the RoundedRectangle
+ /// Y offset of the TopLeft corner of the RoundedRectangle
+ /// Width of the RoundedRectangle
+ /// Height of the RoundedRectangle
+ /// Corner Radius on the x-axis
+ /// Corner Radius on the y-axis
+ public static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
+ {
+ // Sanitize the width by taking the absolute value
+ width = Math.Abs(width);
+
+ // Sanitize the height by taking the absolute value
+ height = Math.Abs(height);
+
+ var rect = new CanvasRoundRect(x, y, width, height, radiusX, radiusY);
+ pathBuilder.AddRoundedRectangleFigure(ref rect, true);
+ }
+
+ ///
+ /// Adds a RoundedRectangle to the Path. (To be used internally)
+ ///
+ ///
+ /// CanvasRoundRect
+ /// Flag to indicate whether exception should be raised
+ internal static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
+ {
+ try
+ {
+ // Begin path
+ pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
+ }
+ catch (ArgumentException)
+ {
+ if (!raiseException)
+ {
+ return;
+ }
+
+ // An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
+ static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRoundedRectangleFigure occurred, " +
+ "when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
+ "before calling CanvasPathBuilder.AddRoundedRectangleFigure, to end the previous figure.");
+
+ Throw();
+ }
+
+ // Top line
+ pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
+
+ // Upper-right corner
+ var radiusX = rect.TopRightX - rect.RightTopX;
+ var radiusY = rect.TopRightY - rect.RightTopY;
+ var center = new Vector2(rect.RightTopX, rect.TopRightY);
+ pathBuilder.AddArc(center, radiusX, radiusY, 3f * Scalar.PiByTwo, Scalar.PiByTwo);
+
+ // Right line
+ pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
+
+ // Lower-right corner
+ radiusX = rect.BottomRightX - rect.RightBottomX;
+ radiusY = rect.RightBottomY - rect.BottomRightY;
+ center = new Vector2(rect.RightBottomX, rect.BottomRightY);
+ pathBuilder.AddArc(center, radiusX, radiusY, 0f, Scalar.PiByTwo);
+
+ // Bottom line
+ pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
+
+ // Lower-left corner
+ radiusX = rect.LeftBottomX - rect.BottomLeftX;
+ radiusY = rect.LeftBottomY - rect.BottomLeftY;
+ center = new Vector2(rect.LeftBottomX, rect.BottomLeftY);
+ pathBuilder.AddArc(center, radiusX, radiusY, Scalar.PiByTwo, Scalar.PiByTwo);
+
+ // Left line
+ pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
+
+ // Upper-left corner
+ radiusX = rect.LeftTopX - rect.TopLeftX;
+ radiusY = rect.TopLeftY - rect.LeftTopY;
+ center = new Vector2(rect.LeftTopX, rect.TopLeftY);
+ pathBuilder.AddArc(center, radiusX, radiusY, 2f * Scalar.PiByTwo, Scalar.PiByTwo);
+
+ // End path
+ pathBuilder.EndFigure(CanvasFigureLoop.Closed);
+ }
+
+ ///
+ /// Adds a Squircle to the Path.
+ ///
+ ///
+ /// X offset of the TopLeft corner of the Squircle
+ /// Y offset of the TopLeft corner of the Squircle
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x-axis
+ /// Corner Radius on the y-axis
+ public static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
+ {
+ // Sanitize the width by taking the absolute value
+ width = Math.Abs(width);
+
+ // Sanitize the height by taking the absolute value
+ height = Math.Abs(height);
+
+ var rect = new CanvasRoundRect(x, y, width, height, radiusX * SquircleFactor, radiusY * SquircleFactor);
+ pathBuilder.AddSquircleFigure(ref rect, true);
+ }
+
+ ///
+ /// Adds a Squircle to the Path. (To be used internally)
+ ///
+ ///
+ /// CanvasRoundRect
+ /// Flag to indicate whether exception should be raised
+ internal static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
+ {
+ try
+ {
+ // Begin path
+ pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
+ }
+ catch (ArgumentException)
+ {
+ if (!raiseException)
+ {
+ return;
+ }
+
+ // An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
+ static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddSquircleFigure occurred, " +
+ "when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
+ "before calling CanvasPathBuilder.AddSquircleFigure, to end the previous figure.");
+
+ Throw();
+ }
+
+ // Top line
+ pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
+
+ // Upper-right corner
+ var rightTopControlPoint = new Vector2(rect.RightTopX + ((rect.TopRightX - rect.RightTopX) * ControlPointFactor), rect.RightTopY);
+ var topRightControlPoint = new Vector2(rect.TopRightX, rect.TopRightY - ((rect.TopRightY - rect.RightTopY) * ControlPointFactor));
+
+ // Top Right Curve
+ pathBuilder.AddCubicBezier(rightTopControlPoint, topRightControlPoint, new Vector2(rect.TopRightX, rect.TopRightY));
+
+ // Right line
+ pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
+
+ // Lower-right corner
+ var bottomRightControlPoint = new Vector2(rect.BottomRightX, rect.BottomRightY + ((rect.RightBottomY - rect.BottomRightY) * ControlPointFactor));
+ var rightBottomControlPoint = new Vector2(rect.RightBottomX + ((rect.BottomRightX - rect.RightBottomX) * ControlPointFactor), rect.RightBottomY);
+
+ // Bottom Right Curve
+ pathBuilder.AddCubicBezier(bottomRightControlPoint, rightBottomControlPoint, new Vector2(rect.RightBottomX, rect.RightBottomY));
+
+ // Bottom line
+ pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
+
+ // Lower-left corner
+ var leftBottomControlPoint = new Vector2(rect.LeftBottomX - ((rect.LeftBottomX - rect.BottomLeftX) * ControlPointFactor), rect.LeftBottomY);
+ var bottomLeftControlPoint = new Vector2(rect.BottomLeftX, rect.BottomLeftY + ((rect.LeftBottomY - rect.BottomLeftY) * ControlPointFactor));
+
+ // Bottom Left Curve
+ pathBuilder.AddCubicBezier(leftBottomControlPoint, bottomLeftControlPoint, new Vector2(rect.BottomLeftX, rect.BottomLeftY));
+
+ // Left line
+ pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
+
+ // Upper-left corner
+ var topLeftControlPoint = new Vector2(rect.TopLeftX, rect.TopLeftY - ((rect.TopLeftY - rect.LeftTopY) * ControlPointFactor));
+ var leftTopControlPoint = new Vector2(rect.LeftTopX - ((rect.LeftTopX - rect.TopLeftX) * ControlPointFactor), rect.LeftTopY);
+
+ // Top Left Curve
+ pathBuilder.AddCubicBezier(topLeftControlPoint, leftTopControlPoint, new Vector2(rect.LeftTopX, rect.LeftTopY));
+
+ // End path
+ pathBuilder.EndFigure(CanvasFigureLoop.Closed);
+ }
+
+ ///
+ /// Builds a path with the given collection of points.
+ ///
+ ///
+ /// Specifies whether the figure is open or closed.
+ /// This affects the appearance of fills and strokes, as well as geometry operations.
+ /// Collection of Vector2 points on the path.
+ /// object
+ public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable points)
+ {
+ var first = true;
+
+ foreach (var point in points)
+ {
+ if (first)
+ {
+ builder.BeginFigure(point);
+ first = false;
+ }
+ else
+ {
+ builder.AddLine(point);
+ }
+ }
+
+ builder.EndFigure(canvasFigureLoop);
+ return builder;
+ }
+
+ ///
+ /// Builds a path with the given collection of points in the (x, y) pattern.
+ ///
+ ///
+ /// Specifies whether the figure is open or closed.
+ /// This affects the appearance of fills and strokes, as well as geometry operations.
+ /// Collection of points in the (x, y) pattern on the path.
+ /// object
+ public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable<(float x, float y)> nodes)
+ {
+ var vectors = nodes.Select(n => new Vector2(n.x, n.y));
+ return BuildPathWithLines(builder, canvasFigureLoop, vectors);
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs
new file mode 100644
index 00000000000..5a6bad1c378
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs
@@ -0,0 +1,140 @@
+// 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.Numerics;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Microsoft.Graphics.Canvas;
+using Microsoft.Graphics.Canvas.Brushes;
+using Microsoft.Graphics.Canvas.Geometry;
+using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers;
+using Windows.UI;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
+{
+ ///
+ /// Helper Class for creating Win2d objects.
+ ///
+ public static class CanvasPathGeometry
+ {
+ ///
+ /// Parses the Path data string and converts it to CanvasGeometry.
+ ///
+ /// Path data
+ ///
+ public static CanvasGeometry CreateGeometry(string pathData)
+ {
+ return CreateGeometry(null, pathData);
+ }
+
+ ///
+ /// Parses the Path data string and converts it to CanvasGeometry.
+ ///
+ ///
+ /// Path data
+ ///
+ public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreator, string pathData)
+ {
+ using (new CultureShield("en-US"))
+ {
+ // Get the CanvasGeometry from the path data
+ return CanvasGeometryParser.Parse(resourceCreator, pathData);
+ }
+ }
+
+ ///
+ /// Creates a Squircle geometry with the specified extents.
+ ///
+ /// Resource creator
+ /// X offset of the TopLeft corner of the Squircle
+ /// Y offset of the TopLeft corner of the Squircle
+ /// Width of the Squircle
+ /// Height of the Squircle
+ /// Corner Radius on the x-axis
+ /// Corner Radius on the y-axis
+ ///
+ public static CanvasGeometry CreateSquircle(ICanvasResourceCreator resourceCreator, float x, float y, float width, float height, float radiusX, float radiusY)
+ {
+ using var pathBuilder = new CanvasPathBuilder(resourceCreator);
+ pathBuilder.AddSquircleFigure(x, y, width, height, radiusX, radiusY);
+ return CanvasGeometry.CreatePath(pathBuilder);
+ }
+
+ ///
+ /// Parses the given Brush data string and converts it to ICanvasBrush.
+ ///
+ /// ICanvasResourceCreator
+ /// Brush data in string format
+ ///
+ public static ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator, string brushData)
+ {
+ using (new CultureShield("en-US"))
+ {
+ return CanvasBrushParser.Parse(resourceCreator, brushData);
+ }
+ }
+
+ ///
+ /// Parses the given Stroke data string and converts it to ICanvasStroke.
+ ///
+ /// ICanvasResourceCreator
+ /// Stroke data in string format
+ ///
+ public static ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator, string strokeData)
+ {
+ using (new CultureShield("en-US"))
+ {
+ return CanvasStrokeParser.Parse(resourceCreator, strokeData);
+ }
+ }
+
+ ///
+ /// Parses the give CanvasStrokeStyle data string and converts it to CanvasStrokeStyle.
+ ///
+ /// CanvasStrokeStyle data in string format
+ /// object
+ public static CanvasStrokeStyle CreateStrokeStyle(string styleData)
+ {
+ using (new CultureShield("en-US"))
+ {
+ return CanvasStrokeStyleParser.Parse(styleData);
+ }
+ }
+
+ ///
+ /// Converts the color string in Hexadecimal or HDR color format to the corresponding Color object.
+ /// The hexadecimal color string should be in #RRGGBB or #AARRGGBB format.
+ /// The '#' character is optional.
+ /// The HDR color string should be in R G B A format.
+ /// (R, G, B & A should have value in the range between 0 and 1, inclusive)
+ ///
+ /// Color string in Hexadecimal or HDR format
+ /// Color
+ public static Color CreateColor(string colorString)
+ {
+ using (new CultureShield("en-US"))
+ {
+ return ColorParser.Parse(colorString);
+ }
+ }
+
+ ///
+ /// Converts a Vector4 High Dynamic Range Color to Color object.
+ /// Negative components of the Vector4 will be sanitized by taking the absolute
+ /// value of the component. The HDR Color components should have value in
+ /// the range between 0 and 1, inclusive. If they are more than 1, they
+ /// will be clamped at 1.
+ /// Vector4's X, Y, Z, W components match to Color's R, G, B, A components respectively.
+ ///
+ /// High Dynamic Range Color
+ /// Color
+ public static Color CreateColor(Vector4 hdrColor)
+ {
+ using (new CultureShield("en-US"))
+ {
+ return ColorParser.Parse(hdrColor);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs
new file mode 100644
index 00000000000..5daf59081c1
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs
@@ -0,0 +1,112 @@
+// 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.Numerics;
+using Microsoft.Graphics.Canvas;
+using Microsoft.Graphics.Canvas.Brushes;
+using Microsoft.Graphics.Canvas.Geometry;
+using Windows.UI;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
+{
+ ///
+ /// Class to represent the Stroke which can be used to render an outline on a
+ ///
+ public sealed class CanvasStroke : ICanvasStroke
+ {
+ ///
+ /// Gets or sets the brush with which the stroke will be rendered
+ ///
+ public ICanvasBrush Brush { get; set; }
+
+ ///
+ /// Gets or sets the width of the
+ ///
+ public float Width { get; set; }
+
+ ///
+ /// Gets or sets the Style of the
+ ///
+ public CanvasStrokeStyle Style { get; set; }
+
+ ///
+ /// Gets or sets the Transform matrix of the brush.
+ ///
+ public Matrix3x2 Transform
+ {
+ get => GetTransform();
+
+ set => SetTransform(value);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The brush with which the will be rendered
+ /// Width of the
+ public CanvasStroke(ICanvasBrush brush, float strokeWidth = 1f)
+ : this(brush, strokeWidth, new CanvasStrokeStyle())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The brush with which the will be rendered
+ /// Width of the
+ /// Style of the
+ public CanvasStroke(ICanvasBrush brush, float strokeWidth, CanvasStrokeStyle strokeStyle)
+ {
+ Brush = brush;
+ Width = strokeWidth;
+ Style = strokeStyle;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// ICanvasResourceCreator
+ /// Color of the
+ /// Width of the
+ public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth = 1f)
+ : this(device, strokeColor, strokeWidth, new CanvasStrokeStyle())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// ICanvasResourceCreator
+ /// Color of the
+ /// Width of the
+ /// Style of the
+ public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth, CanvasStrokeStyle strokeStyle)
+ {
+ Brush = new CanvasSolidColorBrush(device, strokeColor);
+ Width = strokeWidth;
+ Style = strokeStyle;
+ }
+
+ ///
+ /// Sets the 's Transform.
+ ///
+ /// Transform matrix to set
+ private void SetTransform(Matrix3x2 value)
+ {
+ if (Brush != null)
+ {
+ Brush.Transform = value;
+ }
+ }
+
+ ///
+ /// Gets the 's Transform. If stroke is null, then returns Matrix3x2.Identity.
+ ///
+ /// Transform matrix of the
+ private Matrix3x2 GetTransform()
+ {
+ return Brush?.Transform ?? Matrix3x2.Identity;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs
new file mode 100644
index 00000000000..1d7c863dab6
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs
@@ -0,0 +1,96 @@
+// 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 Microsoft.Graphics.Canvas.Geometry;
+using Windows.UI.Composition;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
+{
+ ///
+ /// Extension methods for compositor to support Win2d Path Mini Language.
+ ///
+ public static class CompositorGeometryExtensions
+ {
+ ///
+ /// Creates a based on the specified path data.
+ ///
+ ///
+ /// Path data (Win2d Path Mini Language) in string format.
+ ///
+ public static CompositionPath CreatePath(this Compositor compositor, string pathData)
+ {
+ // Create CanvasGeometry
+ var geometry = CanvasPathGeometry.CreateGeometry(pathData);
+
+ // Create CompositionPath
+ return new CompositionPath(geometry);
+ }
+
+ ///
+ /// Creates a based on the given path data.
+ ///
+ ///
+ /// Path data (Win2d Path Mini Language) in string format.
+ ///
+ public static CompositionPathGeometry CreatePathGeometry(this Compositor compositor, string pathData)
+ {
+ // Create CanvasGeometry
+ var geometry = CanvasPathGeometry.CreateGeometry(pathData);
+
+ // Create CompositionPathGeometry
+ return compositor.CreatePathGeometry(new CompositionPath(geometry));
+ }
+
+ ///
+ /// Creates a based on the specified path data.
+ ///
+ ///
+ /// Path data (Win2d Path Mini Language) in string format.
+ ///
+ public static CompositionSpriteShape CreateSpriteShape(this Compositor compositor, string pathData)
+ {
+ // Create CanvasGeometry
+ var geometry = CanvasPathGeometry.CreateGeometry(pathData);
+
+ // Create CompositionPathGeometry
+ var pathGeometry = compositor.CreatePathGeometry(new CompositionPath(geometry));
+
+ // Create CompositionSpriteShape
+ return compositor.CreateSpriteShape(pathGeometry);
+ }
+
+ ///
+ /// Creates a from the specified .
+ ///
+ ///
+ ///
+ /// CompositionGeometricClip
+ public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, CanvasGeometry geometry)
+ {
+ // Create the CompositionPath
+ var path = new CompositionPath(geometry);
+
+ // Create the CompositionPathGeometry
+ var pathGeometry = compositor.CreatePathGeometry(path);
+
+ // Create the CompositionGeometricClip
+ return compositor.CreateGeometricClip(pathGeometry);
+ }
+
+ ///
+ /// Parses the specified path data and converts it to .
+ ///
+ ///
+ /// Path data (Win2d Path Mini Language) in string format.
+ ///
+ public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, string pathData)
+ {
+ // Create the CanvasGeometry from the path data
+ var geometry = CanvasPathGeometry.CreateGeometry(pathData);
+
+ // Create the CompositionGeometricClip
+ return compositor.CreateGeometricClip(geometry);
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs
new file mode 100644
index 00000000000..103ec3628bb
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs
@@ -0,0 +1,280 @@
+// 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;
+using System.Numerics;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
+{
+ ///
+ /// Structure which encapsulates the details of each of the core points of the path of the rounded rectangle which is calculated based on
+ /// either the given (Size, CornerRadius, BorderThickness and Padding) or (Size, RadiusX and RadiusY).
+ ///
+ internal struct CanvasRoundRect
+ {
+ private const float Factor = 0.5f;
+
+ private readonly float _leftTopWidth;
+ private readonly float _topLeftHeight;
+ private readonly float _topRightHeight;
+ private readonly float _rightTopWidth;
+ private readonly float _rightBottomWidth;
+ private readonly float _bottomRightHeight;
+ private readonly float _bottomLeftHeight;
+ private readonly float _leftBottomWidth;
+
+ // This is the location of the properties within the Rect
+ // |--LeftTop----------------------RightTop--|
+ // | |
+ // TopLeft TopRight
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // BottomLeft BottomRight
+ // | |
+ // |--LeftBottom----------------RightBottom--|
+ internal float LeftTopX { get; private set; }
+
+ internal float LeftTopY { get; private set; }
+
+ internal float TopLeftX { get; private set; }
+
+ internal float TopLeftY { get; private set; }
+
+ internal float TopRightX { get; private set; }
+
+ internal float TopRightY { get; private set; }
+
+ internal float RightTopX { get; private set; }
+
+ internal float RightTopY { get; private set; }
+
+ internal float RightBottomX { get; private set; }
+
+ internal float RightBottomY { get; private set; }
+
+ internal float BottomRightX { get; private set; }
+
+ internal float BottomRightY { get; private set; }
+
+ internal float BottomLeftX { get; private set; }
+
+ internal float BottomLeftY { get; private set; }
+
+ internal float LeftBottomX { get; private set; }
+
+ internal float LeftBottomY { get; private set; }
+
+ internal float Width { get; }
+
+ internal float Height { get; }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Origin of the Rect (absolute location of Top Left corner)
+ /// Size of the Rect
+ /// CornerRadius
+ /// BorderThickness
+ /// Padding
+ /// Flag to indicate whether outer or inner border needs
+ /// to be calculated
+ internal CanvasRoundRect(Vector2 origin, Vector2 size, Vector4 cornerRadius, Vector4 borderThickness, Vector4 padding, bool isOuterBorder)
+ : this()
+ {
+ Width = Math.Max(0f, size.X);
+ Height = Math.Max(0f, size.Y);
+
+ var left = Factor * (borderThickness.X + padding.X);
+ var top = Factor * (borderThickness.Y + padding.Y);
+ var right = Factor * (borderThickness.Z + padding.Z);
+ var bottom = Factor * (borderThickness.W + padding.W);
+
+ if (isOuterBorder)
+ {
+ // Top Left corner radius
+ if (cornerRadius.X.IsZero())
+ {
+ _leftTopWidth = _topLeftHeight = 0f;
+ }
+ else
+ {
+ _leftTopWidth = cornerRadius.X + left;
+ _topLeftHeight = cornerRadius.X + top;
+ }
+
+ // Top Right corner radius
+ if (cornerRadius.Y.IsZero())
+ {
+ _topRightHeight = _rightTopWidth = 0f;
+ }
+ else
+ {
+ _topRightHeight = cornerRadius.Y + top;
+ _rightTopWidth = cornerRadius.Y + right;
+ }
+
+ // Bottom Right corner radius
+ if (cornerRadius.Z.IsZero())
+ {
+ _rightBottomWidth = _bottomRightHeight = 0f;
+ }
+ else
+ {
+ _rightBottomWidth = cornerRadius.Z + right;
+ _bottomRightHeight = cornerRadius.Z + bottom;
+ }
+
+ // Bottom Left corner radius
+ if (cornerRadius.W.IsZero())
+ {
+ _bottomLeftHeight = _leftBottomWidth = 0f;
+ }
+ else
+ {
+ _bottomLeftHeight = cornerRadius.W + bottom;
+ _leftBottomWidth = cornerRadius.W + left;
+ }
+ }
+ else
+ {
+ _leftTopWidth = Math.Max(0f, cornerRadius.X - left);
+ _topLeftHeight = Math.Max(0f, cornerRadius.X - top);
+ _topRightHeight = Math.Max(0f, cornerRadius.Y - top);
+ _rightTopWidth = Math.Max(0f, cornerRadius.Y - right);
+ _rightBottomWidth = Math.Max(0f, cornerRadius.Z - right);
+ _bottomRightHeight = Math.Max(0f, cornerRadius.Z - bottom);
+ _bottomLeftHeight = Math.Max(0f, cornerRadius.W - bottom);
+ _leftBottomWidth = Math.Max(0f, cornerRadius.W - left);
+ }
+
+ // Calculate the anchor points
+ ComputeCoordinates(origin.X, origin.Y);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// Top Left corner of the Rounded Rectangle
+ /// Dimensions of the Rounded Rectangle
+ /// Radius of the corners on the x-axis
+ /// Radius of the corners on the y-axis
+ internal CanvasRoundRect(Vector2 origin, Vector2 size, float radiusX, float radiusY)
+ : this(origin.X, origin.Y, size.X, size.Y, radiusX, radiusY)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// X offset of the Top Left corner of the Rounded Rectangle
+ /// Y offset of the Top Left corner of the Rounded Rectangle
+ /// Width of the Rounded Rectangle.
+ /// Height of the Rounded Rectangle.
+ /// Radius of the corners on the x-axis
+ /// Radius of the corners on the y-axis
+ internal CanvasRoundRect(float x, float y, float width, float height, float radiusX, float radiusY)
+ : this()
+ {
+ Width = Math.Max(0f, width);
+ Height = Math.Max(0f, height);
+
+ // Sanitize the radii by taking the absolute value
+ radiusX = Math.Min(Math.Abs(radiusX), width / 2f);
+ radiusY = Math.Min(Math.Abs(radiusY), height / 2);
+
+ _leftTopWidth = radiusX;
+ _rightTopWidth = radiusX;
+ _rightBottomWidth = radiusX;
+ _leftBottomWidth = radiusX;
+ _topLeftHeight = radiusY;
+ _topRightHeight = radiusY;
+ _bottomRightHeight = radiusY;
+ _bottomLeftHeight = radiusY;
+
+ ComputeCoordinates(x, y);
+ }
+
+ ///
+ /// Computes the coordinates of the crucial points on the CanvasRoundRect
+ ///
+ /// X coordinate of the origin.
+ /// Y coordinate of the origin.
+ private void ComputeCoordinates(float originX, float originY)
+ {
+ // compute the coordinates of the key points
+ var leftTopX = _leftTopWidth;
+ var leftTopY = 0f;
+ var rightTopX = Width - _rightTopWidth;
+ var rightTopY = 0f;
+ var topRightX = Width;
+ var topRightY = _topRightHeight;
+ var bottomRightX = Width;
+ var bottomRightY = Height - _bottomRightHeight;
+ var rightBottomX = Width - _rightBottomWidth;
+ var rightBottomY = Height;
+ var leftBottomX = _leftBottomWidth;
+ var leftBottomY = Height;
+ var bottomLeftX = 0f;
+ var bottomLeftY = Height - _bottomLeftHeight;
+ var topLeftX = 0f;
+ var topLeftY = _topLeftHeight;
+
+ // check anchors for overlap and resolve by partitioning corners according to
+ // the percentage of each one.
+ // top edge
+ if (leftTopX > rightTopX)
+ {
+ var v = _leftTopWidth / (_leftTopWidth + _rightTopWidth) * Width;
+ leftTopX = v;
+ rightTopX = v;
+ }
+
+ // right edge
+ if (topRightY > bottomRightY)
+ {
+ var v = _topRightHeight / (_topRightHeight + _bottomRightHeight) * Height;
+ topRightY = v;
+ bottomRightY = v;
+ }
+
+ // bottom edge
+ if (leftBottomX > rightBottomX)
+ {
+ var v = _leftBottomWidth / (_leftBottomWidth + _rightBottomWidth) * Width;
+ rightBottomX = v;
+ leftBottomX = v;
+ }
+
+ // left edge
+ if (topLeftY > bottomLeftY)
+ {
+ var v = _topLeftHeight / (_topLeftHeight + _bottomLeftHeight) * Height;
+ bottomLeftY = v;
+ topLeftY = v;
+ }
+
+ // Apply origin translation
+ LeftTopX = leftTopX + originX;
+ LeftTopY = leftTopY + originY;
+ RightTopX = rightTopX + originX;
+ RightTopY = rightTopY + originY;
+ TopRightX = topRightX + originX;
+ TopRightY = topRightY + originY;
+ BottomRightX = bottomRightX + originX;
+ BottomRightY = bottomRightY + originY;
+ RightBottomX = rightBottomX + originX;
+ RightBottomY = rightBottomY + originY;
+ LeftBottomX = leftBottomX + originX;
+ LeftBottomY = leftBottomY + originY;
+ BottomLeftX = bottomLeftX + originX;
+ BottomLeftY = bottomLeftY + originY;
+ TopLeftX = topLeftX + originX;
+ TopLeftY = topLeftY + originY;
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs
new file mode 100644
index 00000000000..027000a6547
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs
@@ -0,0 +1,59 @@
+// 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.
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
+{
+ ///
+ /// Enum for the various PathFigures.
+ ///
+ internal enum PathFigureType
+ {
+ FillRule,
+ PathFigure,
+ EllipseFigure,
+ PolygonFigure,
+ RectangleFigure,
+ RoundedRectangleFigure
+ }
+
+ ///
+ /// Enum for the various PathElements.
+ ///
+ internal enum PathElementType
+ {
+ MoveTo,
+ Line,
+ HorizontalLine,
+ VerticalLine,
+ QuadraticBezier,
+ SmoothQuadraticBezier,
+ CubicBezier,
+ SmoothCubicBezier,
+ Arc,
+ ClosePath
+ }
+
+ ///
+ /// Enum for the various types of Brushes.
+ ///
+ internal enum BrushType
+ {
+ SolidColor,
+ LinearGradient,
+ RadialGradient,
+ LinearGradientHdr,
+ RadialGradientHdr
+ }
+
+ ///
+ /// Enum for the various types of GradientStop attributes.
+ ///
+ internal enum GradientStopAttributeType
+ {
+ Main,
+ Additional,
+ MainHdr,
+ AdditionalHdr
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs
new file mode 100644
index 00000000000..ee77de7b668
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs
@@ -0,0 +1,157 @@
+// 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;
+using System.Text.RegularExpressions;
+using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path;
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
+{
+ ///
+ /// Factory class to instantiate various PathElements.
+ ///
+ internal static class PathElementFactory
+ {
+ ///
+ /// Creates a default Path Element for the given PathFigureType.
+ ///
+ /// PathFigureType
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreateDefaultPathElement(PathFigureType figureType)
+ {
+ if (figureType == PathFigureType.FillRule)
+ {
+ return new FillRuleElement();
+ }
+
+ static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default FillRuleElement is supported.");
+
+ return Throw();
+ }
+
+ ///
+ /// Creates a default Path Element for the given PathElementType.
+ ///
+ /// PathElementType
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreateDefaultPathElement(PathElementType elementType)
+ {
+ if (elementType == PathElementType.ClosePath)
+ {
+ return new ClosePathElement();
+ }
+
+ static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default ClosePathElement is supported.");
+
+ return Throw();
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathFigureType.
+ ///
+ /// PathFigureType
+ /// Match object
+ /// Index of the path element in the Path data
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreatePathFigure(PathFigureType figureType, Match match, int index)
+ {
+ var element = CreatePathElement(figureType);
+ element?.Initialize(match, index);
+ return element;
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathFigureType.
+ ///
+ /// PathFigureType
+ /// Capture object
+ /// Index of the capture
+ /// Indicates whether the coordinates are absolute or relative
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreateAdditionalPathFigure(PathFigureType figureType, Capture capture, int index, bool isRelative)
+ {
+ var element = CreatePathElement(figureType);
+ element?.InitializeAdditional(capture, index, isRelative);
+ return element;
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathElementType.
+ ///
+ /// PathElementType
+ /// Match object
+ /// Index of the path element in the Path data
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreatePathElement(PathElementType elementType, Match match, int index)
+ {
+ var element = CreatePathElement(elementType);
+ element?.Initialize(match, index);
+ return element;
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathElementType.
+ ///
+ /// PathElementType
+ /// Capture object
+ /// Index of the capture
+ /// Indicates whether the coordinates are absolute or relative
+ /// ICanvasPathElement
+ internal static ICanvasPathElement CreateAdditionalPathElement(PathElementType elementType, Capture capture, int index, bool isRelative)
+ {
+ // Additional attributes in MoveTo Command must be converted
+ // to Line commands
+ if (elementType == PathElementType.MoveTo)
+ {
+ elementType = PathElementType.Line;
+ }
+
+ var element = CreatePathElement(elementType);
+ element?.InitializeAdditional(capture, index, isRelative);
+ return element;
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathFigureType.
+ ///
+ /// PathFigureType
+ /// ICanvasPathElement
+ private static ICanvasPathElement CreatePathElement(PathFigureType figureType)
+ {
+ return figureType switch
+ {
+ PathFigureType.FillRule => new FillRuleElement(),
+ PathFigureType.PathFigure => new CanvasPathFigure(),
+ PathFigureType.EllipseFigure => new CanvasEllipseFigure(),
+ PathFigureType.PolygonFigure => new CanvasPolygonFigure(),
+ PathFigureType.RectangleFigure => new CanvasRectangleFigure(),
+ PathFigureType.RoundedRectangleFigure => new CanvasRoundRectangleFigure(),
+ _ => throw new ArgumentOutOfRangeException(nameof(figureType), figureType, "Invalid PathFigureType!")
+ };
+ }
+
+ ///
+ /// Instantiates a PathElement based on the PathElementType.
+ ///
+ /// PathElementType
+ /// ICanvasPathElement
+ private static ICanvasPathElement CreatePathElement(PathElementType elementType)
+ {
+ return elementType switch
+ {
+ PathElementType.MoveTo => new MoveToElement(),
+ PathElementType.Line => new LineElement(),
+ PathElementType.HorizontalLine => new HorizontalLineElement(),
+ PathElementType.VerticalLine => new VerticalLineElement(),
+ PathElementType.QuadraticBezier => new QuadraticBezierElement(),
+ PathElementType.SmoothQuadraticBezier => new SmoothQuadraticBezierElement(),
+ PathElementType.CubicBezier => new CubicBezierElement(),
+ PathElementType.SmoothCubicBezier => new SmoothCubicBezierElement(),
+ PathElementType.Arc => new ArcElement(),
+ PathElementType.ClosePath => new ClosePathElement(),
+ _ => throw new ArgumentOutOfRangeException(nameof(elementType), elementType, "Invalid PathElementType!")
+ };
+ }
+ }
+}
diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs
new file mode 100644
index 00000000000..79112c46df4
--- /dev/null
+++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs
@@ -0,0 +1,602 @@
+// 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.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+
+[assembly: InternalsVisibleTo("UnitTests.UWP")]
+
+namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
+{
+ ///
+ /// Contains all the Regular Expressions which are used for parsing the Win2d Path Mini Language.
+ ///
+ internal static class RegexFactory
+ {
+ // Whitespace
+ private const string Spacer = @"\s*";
+
+ // Whitespace or comma
+ private const string SpaceOrComma = @"(?:\s+|\s*,\s*)";
+
+ // Whitespace or comma or a minus/plus sign (look ahead)
+ private const string Sep = @"(?:\s+|\s*,\s*|(?=[-+.]))";
+
+ // Whitespace or comma or a '#' sign (look ahead)
+ private const string ColorSep = @"(?:\s+|\s*,\s*|(?=[#]))";
+
+ // Positive Integer
+ private const string Integer = @"[+-]?[0-9]+";
+
+ // Positive Integer
+ private const string PositiveInteger = @"[+]?[0-9]+";
+
+ // Floating point number
+ private const string Float = @"(?:[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
+
+ // Positive Floating point number
+ private const string PositiveFloat = @"(?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
+
+ // Floating point number between 0 and 1, inclusive
+ // private const string Float01 = @"(?:(?[Mm]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
+
+ // Line
+ private static readonly string Line = $"(?[Ll]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
+
+ // Horizontal Line
+ private static readonly string HorizontalLine = $"(?[Hh]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
+
+ // Vertical Line
+ private static readonly string VerticalLine = $"(?[Vv]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
+
+ // Quadratic Bezier
+ private static readonly string QuadraticBezier = $"(?[Qq]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
+
+ // Smooth Quadratic Bezier
+ private static readonly string SmoothQuadraticBezier = $"(?[Tt]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
+
+ // Cubic Bezier
+ private static readonly string CubicBezier = $"(?[Cc]{Spacer}{Pos}{Sep}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*{Spacer})";
+
+ // Smooth Cubic Bezier
+ private static readonly string SmoothCubicBezier = $"(?[Ss]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
+
+ // Arc
+ private static readonly string Arc = $"(?[Aa]{Spacer}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos}" +
+ $"(?:{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*{Spacer})";
+
+ // Close Path
+ private static readonly string ClosePath = $"(?[Zz]{Spacer})";
+
+ // CanvasPathFigure
+ private static readonly string CanvasPathFigureRegexString =
+ $"{MoveTo}" + // M x,y
+ "(" +
+ $"{Line}+|" + // L x,y
+ $"{HorizontalLine}+|" + // H x
+ $"{VerticalLine}+|" + // V y
+ $"{QuadraticBezier}+|" + // Q x1,y1 x,y
+ $"{SmoothQuadraticBezier}+|" + // T x,y
+ $"{CubicBezier}+|" + // C x1,y1 x2,y2 x,y
+ $"{SmoothCubicBezier}+|" + // S x2,y2 x,y
+ $"{Arc}+|" + // A radX, radY, angle, isLargeArc, sweepDirection, x, y
+ ")+" +
+ $"{ClosePath}?"; // Close Path (Optional)
+
+ // Fill Rule
+ private static readonly string FillRule = $"{Spacer}(?[Ff]{Spacer}[01])";
+
+ // PathFigure
+ private static readonly string PathFigure = $"{Spacer}(?{CanvasPathFigureRegexString})";
+
+ // Ellipse Figure
+ private static readonly string EllipseFigure = $"{Spacer}(?[Oo]{Spacer}{Float}{Sep}{Float}{Sep}{Pos}" +
+ $"(?:{Sep}{Float}{Sep}{Float}{Sep}{Pos})*)";
+
+ // Polygon Figure
+ private static readonly string PolygonFigure = $"{Spacer}(?[Pp]{Spacer}{Integer}{Sep}{Float}{Sep}{Pos}" +
+ $"(?:{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*)";
+
+ // Rectangle Figure
+ private static readonly string RectangleFigure = $"{Spacer}(?[Rr]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}" +
+ $"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float})*)";
+
+ // Rounded Rectangle Figure
+ private static readonly string RoundedRectangleFigure = $"{Spacer}(?[Uu]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float}" +
+ $"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*)";
+
+ // CanvasGeometry
+ private static readonly string CanvasGeometryRegexString =
+ $"{FillRule}?" + // F0 or F1
+ "(" +
+ $"{PathFigure}+|" + // Path Figure
+ $"{EllipseFigure}+|" + // O radX, radY, centerX, centerY
+ $"{PolygonFigure}+|" + // P numSides, radius, centerX, centerY
+ $"{RectangleFigure}+|" + // R x, y, width, height
+ $"{RoundedRectangleFigure}+" + // U x, y, width, height, radiusX, radiusY
+ ")+";
+
+ // MoveTo
+ private static readonly string MoveToAttributes = $"(?{Float}){Sep}(?{Float})";
+ private static readonly string MoveToRegexString = $"{Spacer}(?(?[Mm]){Spacer}{MoveToAttributes})" +
+ $"(?{Sep}{Pos})*";
+
+ // Line
+ private static readonly string LineAttributes = $"(?{Float}){Sep}(?{Float})";
+ private static readonly string LineRegexString = $"{Spacer}(?(?[Ll]){Spacer}{LineAttributes})" +
+ $"(?{Sep}{Pos})*";
+
+ // Horizontal Line
+ private static readonly string HorizontalLineAttributes = $"(?{Float})";
+ private static readonly string HorizontalLineRegexString = $"{Spacer}(?(?[Hh]){Spacer}{HorizontalLineAttributes})" +
+ $"(?{Sep}{Float})*";
+
+ // Vertical Line
+ private static readonly string VerticalLineAttributes = $"(?{Float})";
+ private static readonly string VerticalLineRegexString = $"{Spacer}(?(?[Vv]){Spacer}{VerticalLineAttributes})" +
+ $"(?{Sep}{Float})*";
+
+ // Quadratic Bezier
+ private static readonly string QuadraticBezierAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float})";
+ private static readonly string QuadraticBezierRegexString = $"{Spacer}(?(?[Qq]){Spacer}{QuadraticBezierAttributes})" +
+ $"(?{Sep}{Pos}{Sep}{Pos})*";
+
+ // Smooth Quadratic Bezier
+ private static readonly string SmoothQuadraticBezierAttributes = $"(?{Float}){Sep}(?{Float})";
+ private static readonly string SmoothQuadraticBezierRegexString = $"{Spacer}(?(?[Tt]){Spacer}{SmoothQuadraticBezierAttributes})" +
+ $"(?{Sep}{Pos})*";
+
+ // Cubic Bezier
+ private static readonly string CubicBezierAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}" +
+ $"(?{Float}){Sep}(?{Float})";
+
+ private static readonly string CubicBezierRegexString = $"{Spacer}(?(?[Cc]){Spacer}{CubicBezierAttributes})" +
+ $"(?{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*";
+
+ // Smooth Cubic Bezier
+ private static readonly string SmoothCubicBezierAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float})";
+ private static readonly string SmoothCubicBezierRegexString = $"{Spacer}(?(?[Ss]){Spacer}{SmoothCubicBezierAttributes})" +
+ $"(?{Sep}{Pos}{Sep}{Pos})*";
+
+ // Arc
+ private static readonly string ArcAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){SpaceOrComma}" +
+ $"(?[01]){SpaceOrComma}(?[01]){Sep}(?{Float}){Sep}(?{Float})";
+
+ private static readonly string ArcRegexString = $"{Spacer}(?(?[Aa]){Spacer}{ArcAttributes})" +
+ $"(?{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*";
+
+ // Close Path
+ private static readonly string ClosePathRegexString = $"{Spacer}(?(?[Zz])){Spacer}";
+
+ // Fill Rule
+ private static readonly string FillRuleRegexString = $"{Spacer}(?(?[Ff]){Spacer}(?[01]))";
+
+ // Path Figure
+ private static readonly string PathFigureRegexString = $"{Spacer}(?{PathFigure})";
+
+ // Ellipse Figure
+ private static readonly string EllipseFigureAttributes = $"(?{Float}){Sep}(?{Float}){Sep}" +
+ $"(?{Float}){Sep}(?{Float})";
+
+ private static readonly string EllipseFigureRegexString = $"{Spacer}(?(?[Oo]){Spacer}{EllipseFigureAttributes})" +
+ $"(?{Sep}{Float}{Sep}{Float}{Sep}{Pos})*";
+
+ // Polygon Figure
+ private static readonly string PolygonFigureAttributes = $"(?{Integer}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float})";
+ private static readonly string PolygonFigureRegexString = $"{Spacer}(?(?[Pp]){Spacer}{PolygonFigureAttributes})" +
+ $"(?{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*";
+
+ // Rectangle Figure
+ private static readonly string RectangleFigureAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float})";
+ private static readonly string RectangleFigureRegexString = $"{Spacer}(?(?[Rr]){Spacer}{RectangleFigureAttributes})" +
+ $"(?{Sep}{Pos}{Sep}{Float}{Sep}{Float})*";
+
+ // Rectangle Figure
+ private static readonly string RoundedRectangleFigureAttributes = $"(?{Float}){Sep}(?{Float}){Sep}(?{Float}){Sep}(?{Float})" +
+ $"{Sep}(?{Float}){Sep}(?{Float})";
+
+ private static readonly string RoundedRectangleFigureRegexString = $"{Spacer}(?(?[Uu]){Spacer}{RoundedRectangleFigureAttributes})" +
+ $"(?{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*";
+
+ // ARGB Color
+ private static readonly string HexColor = $"(?:#?(?:{Hex}{{2}})?{Hex}{{6}})";
+
+ // Alpha
+ private static readonly string Alpha = $"(?{Hex}{{2}})";
+
+ // Red
+ private static readonly string Red = $"(?{Hex}{{2}})";
+
+ // Green
+ private static readonly string Green = $"(?{Hex}{{2}})";
+
+ // Blue
+ private static readonly string Blue = $"(?{Hex}{{2}})";
+
+ // Hexadecimal Color
+ private static readonly string RgbColor = $"(?{HexColor})";
+
+ // HDR Color (Vector4 in which each component has a value between 0 and 1, inclusive)
+ private static readonly string HdrColor = $"(?{Float01}{Sep}{Float01}{Sep}{Float01}{Sep}{Float01})";
+
+ // Hexadecimal Color Attributes
+ private static readonly string RgbColorAttributes = $"(?#{{0,1}}{Alpha}{{0,1}}{Red}{Green}{Blue})";
+
+ // HDR Color Attributes (Vector4 in which each component has a value between 0 and 1, inclusive)
+ private static readonly string HdrColorAttributes = $"(?(?{Float01}){Sep}(?{Float01}){Sep}(?{Float01}){Sep}(?{Float01}))";
+
+ private static readonly string ColorRegexString = $"(?:{RgbColorAttributes}|{HdrColorAttributes})";
+
+ // Start Point
+ private static readonly string StartPoint = $"(?[Mm]{Spacer}{Pos}{Spacer})";
+
+ // End Point
+ private static readonly string EndPoint = $"(?[Zz]{Spacer}{Pos}{Spacer})";
+
+ // Opacity
+ private static readonly string Opacity = $"(?[Oo]{Spacer}{Float01}{Spacer})";
+
+ // Alpha Mode
+ private static readonly string AlphaMode = $"(?[Aa]{Spacer}[012]{Spacer})";
+
+ // Buffer Precision
+ private static readonly string BufferPrecision = $"(?[Bb]{Spacer}[01234]{Spacer})";
+
+ // Edge Behavior
+ private static readonly string EdgeBehavior = $"(?[Ee]{Spacer}[012]{Spacer})";
+
+ // PreInterpolation Color Space
+ private static readonly string PreColorSpace = $"(?[Pp]{Spacer}[012]{Spacer})";
+
+ // PostInterpolation Color Space
+ private static readonly string PostColorSpace = $"(?