diff --git a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Shapes/PathTestsControl/Path_With_DashStrokeArray.xaml.cs b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Shapes/PathTestsControl/Path_With_DashStrokeArray.xaml.cs index 3a342e775346..8dc1a327b64d 100644 --- a/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Shapes/PathTestsControl/Path_With_DashStrokeArray.xaml.cs +++ b/src/SamplesApp/UITests.Shared/Windows_UI_Xaml_Shapes/PathTestsControl/Path_With_DashStrokeArray.xaml.cs @@ -1,14 +1,13 @@ using Uno.UI.Samples.Controls; using Microsoft.UI.Xaml.Controls; -namespace SamplesApp.Windows_UI_Xaml_Shapes.PathTestsControl +namespace SamplesApp.Windows_UI_Xaml_Shapes.PathTestsControl; + +[Sample("Path", IsManualTest = true)] +public sealed partial class Path_With_DashStrokeArray : UserControl { - [SampleControlInfo("Path", "Path_With_DashStrokeArray")] - public sealed partial class Path_With_DashStrokeArray : UserControl + public Path_With_DashStrokeArray() { - public Path_With_DashStrokeArray() - { - this.InitializeComponent(); - } + this.InitializeComponent(); } } diff --git a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs index d7f68a315db7..232263160c83 100644 --- a/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs +++ b/src/Uno.UI.Composition/Composition/CompositionSpriteShape.skia.cs @@ -1,6 +1,7 @@ #nullable enable using System; +using System.Linq; using SkiaSharp; using Uno; using Uno.Extensions; @@ -56,8 +57,10 @@ internal override void Paint(in Visual.PaintingSession session) // Set stroke thickness strokePaint.StrokeWidth = StrokeThickness; - // TODO: Add support for dashes here - // strokePaint.PathEffect = SKPathEffect.CreateDash(); + if (StrokeDashArray is { Count: > 0 } strokeDashArray) + { + strokePaint.PathEffect = SKPathEffect.CreateDash(strokeDashArray.ToEvenArray(), 0); + } // Generate stroke geometry for bounds that will be passed to a brush. // - [Future]: This generated geometry should also be used for hit testing. @@ -117,6 +120,12 @@ private static SKPaint TryCreateAndClearPaint(in Visual.PaintingSession session, paint.Shader.Dispose(); paint.Shader = null; } + + if (paint.PathEffect != null) + { + paint.PathEffect.Dispose(); + paint.PathEffect = null; + } } paint.ColorFilter = session.Filters.OpacityColorFilter; diff --git a/src/Uno.UI.Composition/Composition/CompositionStrokeDashArray.cs b/src/Uno.UI.Composition/Composition/CompositionStrokeDashArray.cs new file mode 100644 index 000000000000..6b691406d226 --- /dev/null +++ b/src/Uno.UI.Composition/Composition/CompositionStrokeDashArray.cs @@ -0,0 +1,67 @@ +using System.Collections; +using System.Collections.Generic; +using Uno; + +namespace Microsoft.UI.Composition; + +[NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__MACOS__")] +public partial class CompositionStrokeDashArray : CompositionObject, IList, IEnumerable +{ + private readonly List _list; + + internal CompositionStrokeDashArray() + { + _list = new List(); + } + + public uint Size => (uint)_list.Count; + + public int IndexOf(float item) + => _list.IndexOf(item); + + public void Insert(int index, float item) + => _list.Insert(index, item); + + public void RemoveAt(int index) + => _list.RemoveAt(index); + + public float this[int index] + { + get => _list[index]; + set => _list[index] = value; + } + + public void Add(float item) + => _list.Add(item); + + public void Clear() + => _list.Clear(); + + public bool Contains(float item) + => _list.Contains(item); + + public void CopyTo(float[] array, int arrayIndex) + => _list.CopyTo(array, arrayIndex); + + public bool Remove(float item) + => _list.Remove(item); + + public int Count + => _list.Count; + + public bool IsReadOnly + => false; + + public IEnumerator GetEnumerator() + => _list.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _list.GetEnumerator(); + + internal float[] ToEvenArray() + { + return _list.Count % 2 == 0 + ? _list.ToArray() + : [.. _list, .. _list]; + } +} diff --git a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionStrokeDashArray.cs b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionStrokeDashArray.cs index 36479c3b078e..37f1889948f9 100644 --- a/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionStrokeDashArray.cs +++ b/src/Uno.UI.Composition/Generated/3.0.0.0/Microsoft.UI.Composition/CompositionStrokeDashArray.cs @@ -3,26 +3,11 @@ #pragma warning disable 114 // new keyword hiding namespace Microsoft.UI.Composition { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if false || false || false || false || false || false || false [global::Uno.NotImplemented] #endif public partial class CompositionStrokeDashArray : global::Microsoft.UI.Composition.CompositionObject, global::System.Collections.Generic.IList, global::System.Collections.Generic.IEnumerable { -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - internal CompositionStrokeDashArray() - { - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public uint Size - { - get - { - throw new global::System.NotImplementedException("The member uint CompositionStrokeDashArray.Size is not implemented. For more information, visit https://aka.platform.uno/notimplemented#m=uint%20CompositionStrokeDashArray.Size"); - } - } -#endif // Forced skipping of method Microsoft.UI.Composition.CompositionStrokeDashArray.Clear() // Forced skipping of method Microsoft.UI.Composition.CompositionStrokeDashArray.RemoveAt(uint) // Forced skipping of method Microsoft.UI.Composition.CompositionStrokeDashArray.Append(float) @@ -37,122 +22,7 @@ public uint Size // Forced skipping of method Microsoft.UI.Composition.CompositionStrokeDashArray.SetAt(uint, float) // Forced skipping of method Microsoft.UI.Composition.CompositionStrokeDashArray.InsertAt(uint, float) // Processing: System.Collections.Generic.IList -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.IList - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int IndexOf(float item) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.IList - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void Insert(int index, float item) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.IList - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void RemoveAt(int index) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public float this[int index] - { - get - { - throw new global::System.NotSupportedException(); - } - set - { - throw new global::System.NotSupportedException(); - } - } -#endif // Processing: System.Collections.Generic.ICollection -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.ICollection - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void Add(float item) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.ICollection - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void Clear() - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.ICollection - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public bool Contains(float item) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.ICollection - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public void CopyTo(float[] array, int arrayIndex) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.ICollection - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public bool Remove(float item) - { - throw new global::System.NotSupportedException(); - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public int Count - { - get - { - throw new global::System.NotSupportedException(); - } - } -#endif -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public bool IsReadOnly - { - get - { - throw new global::System.NotSupportedException(); - } - } -#endif // Processing: System.Collections.Generic.IEnumerable -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.Generic.IEnumerable - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - public global::System.Collections.Generic.IEnumerator GetEnumerator() - { - throw new global::System.NotSupportedException(); - } -#endif - // Processing: System.Collections.IEnumerable -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ - // DeclaringType: System.Collections.IEnumerable - [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() - { - throw new global::System.NotSupportedException(); - } -#endif } } diff --git a/src/Uno.UI/UI/Xaml/Shapes/Shape.skia.cs b/src/Uno.UI/UI/Xaml/Shapes/Shape.skia.cs index f20f8f7b1016..309a2300a6b6 100644 --- a/src/Uno.UI/UI/Xaml/Shapes/Shape.skia.cs +++ b/src/Uno.UI/UI/Xaml/Shapes/Shape.skia.cs @@ -65,6 +65,7 @@ private void UpdateRender() OnFillBrushChanged(); OnStrokeBrushChanged(); UpdateStrokeThickness(); + UpdateStrokeDashArray(); } private void OnFillBrushChanged() @@ -81,6 +82,24 @@ private void UpdateStrokeThickness() _shape.StrokeThickness = (float)ActualStrokeThickness; } + private void UpdateStrokeDashArray() + { + var compositionStrokeDashArray = new CompositionStrokeDashArray(); + var strokeDashArray = StrokeDashArray; + if (strokeDashArray is null) + { + _shape.StrokeDashArray = null; + return; + } + + for (int i = 0; i < strokeDashArray.Count; i++) + { + compositionStrokeDashArray.Add((float)strokeDashArray[i]); + } + + _shape.StrokeDashArray = compositionStrokeDashArray; + } + private void OnStrokeBrushChanged() { _strokeSubscription.Disposable = null;