Skip to content

Commit d8a0794

Browse files
Merge branch 'master' into bugfix/tile-content-builder
2 parents 6b28b94 + 0bb6eb8 commit d8a0794

File tree

9 files changed

+338
-12
lines changed

9 files changed

+338
-12
lines changed

Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,10 @@ public bool TryGetSpan(out ReadOnlySpan<T> span)
906906
// Without Span<T> runtime support, we can only get a Span<T> from a T[] instance
907907
if (this.instance.GetType() == typeof(T[]))
908908
{
909-
span = Unsafe.As<T[]>(this.instance).AsSpan((int)this.offset, (int)Length);
909+
T[] array = Unsafe.As<T[]>(this.instance)!;
910+
int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt<T>(array, this.offset));
911+
912+
span = array.AsSpan(index, (int)Length);
910913

911914
return true;
912915
}

Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,10 @@ public bool TryGetSpan(out Span<T> span)
10621062
// Without Span<T> runtime support, we can only get a Span<T> from a T[] instance
10631063
if (this.Instance.GetType() == typeof(T[]))
10641064
{
1065-
span = Unsafe.As<T[]>(this.Instance).AsSpan((int)this.Offset, (int)Length);
1065+
T[] array = Unsafe.As<T[]>(this.Instance)!;
1066+
int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt<T>(array, this.Offset));
1067+
1068+
span = array.AsSpan(index, (int)Length);
10661069

10671070
return true;
10681071
}

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Visual Extensions/VisualExtensionsCode.bind

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,49 @@
55
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
66
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
77
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
8+
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
9+
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
10+
xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations"
11+
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors"
812
mc:Ignorable="d">
913

10-
<Grid>
14+
<StackPanel Spacing="120" VerticalAlignment="Center">
15+
16+
<!--This is a static element, with some Visual properties being modified through
17+
the VisualExtensions class. You can modify their values to see how the position,
18+
orientation and alignment of the Border element changes.-->
1119
<Border Height="100"
1220
Width="100"
1321
Background="Purple"
22+
ui:VisualExtensions.CenterPoint="50,50,0"
23+
ui:VisualExtensions.Offset="50"
1424
ui:VisualExtensions.Opacity="0.5"
1525
ui:VisualExtensions.RotationAngleInDegrees="80"
1626
ui:VisualExtensions.Scale="2, 0.5, 1"
17-
ui:VisualExtensions.NormalizedCenterPoint="0.5" />
18-
</Grid>
27+
ui:VisualExtensions.NormalizedCenterPoint="0.5"
28+
ui:VisualExtensions.Translation="20,12,0"/>
29+
30+
<!--This Button demonstrates the VisualExtensions.Translation property in combination with a translation
31+
animation. The Translation property is set to indicate the starting position of the element (relative
32+
to its offset), and the animation will modify that to reach the specified translation value. Note how
33+
the animation doesn't have an explicit starting value, as it will just start animating the translation
34+
from the current value set via the VisualExtensions.Translation attached property.-->
35+
<Button Height="120"
36+
Width="360"
37+
Background="Green"
38+
Content="Click me!"
39+
FontSize="32"
40+
ui:VisualExtensions.Translation="20,-40,0">
41+
<ani:Explicit.Animations>
42+
<ani:AnimationSet x:Name="MoveAnimation">
43+
<ani:TranslationAnimation To="480,80,0" Duration="0:0:2"/>
44+
</ani:AnimationSet>
45+
</ani:Explicit.Animations>
46+
<interactivity:Interaction.Behaviors>
47+
<interactions:EventTriggerBehavior EventName="Click">
48+
<behaviors:StartAnimationAction Animation="{Binding ElementName=MoveAnimation}"/>
49+
</interactions:EventTriggerBehavior>
50+
</interactivity:Interaction.Behaviors>
51+
</Button>
52+
</StackPanel>
1953
</Page>

Microsoft.Toolkit.Uwp.SampleApp/SamplePages/Visual Extensions/VisualExtensionsPage.xaml

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
44
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
55
xmlns:ui="using:Microsoft.Toolkit.Uwp.UI"
6+
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
7+
xmlns:interactions="using:Microsoft.Xaml.Interactions.Core"
8+
xmlns:ani="using:Microsoft.Toolkit.Uwp.UI.Animations"
9+
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Behaviors"
610
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
711
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
812
mc:Ignorable="d">
913

10-
<Grid>
14+
<StackPanel Spacing="120" VerticalAlignment="Center">
1115
<Border Height="100"
1216
Width="100"
1317
Background="Purple"
@@ -16,6 +20,25 @@
1620
ui:VisualExtensions.Opacity="0.5"
1721
ui:VisualExtensions.RotationAngleInDegrees="80"
1822
ui:VisualExtensions.Scale="2, 0.5, 1"
19-
ui:VisualExtensions.NormalizedCenterPoint="0.5"/>
20-
</Grid>
23+
ui:VisualExtensions.NormalizedCenterPoint="0.5"
24+
ui:VisualExtensions.Translation="20,12,0"/>
25+
<Button
26+
Height="120"
27+
Width="360"
28+
Background="Green"
29+
Content="Click me!"
30+
FontSize="32"
31+
ui:VisualExtensions.Translation="20,-40,0">
32+
<ani:Explicit.Animations>
33+
<ani:AnimationSet x:Name="MoveAnimation">
34+
<ani:TranslationAnimation To="480,80,0" Duration="0:0:2"/>
35+
</ani:AnimationSet>
36+
</ani:Explicit.Animations>
37+
<interactivity:Interaction.Behaviors>
38+
<interactions:EventTriggerBehavior EventName="Click">
39+
<behaviors:StartAnimationAction Animation="{Binding ElementName=MoveAnimation}"/>
40+
</interactions:EventTriggerBehavior>
41+
</interactivity:Interaction.Behaviors>
42+
</Button>
43+
</StackPanel>
2144
</Page>

Microsoft.Toolkit.Uwp.UI/Extensions/VisualExtensions.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Globalization;
56
using System.Numerics;
67
using Windows.UI.Composition;
78
using Windows.UI.Xaml;
@@ -114,6 +115,36 @@ public static void SetOffset(DependencyObject obj, string value)
114115
obj.SetValue(OffsetProperty, value);
115116
}
116117

118+
/// <summary>
119+
/// Gets the <c>"Translation"</c> property of the underlying <see cref="Visual"/> object for a <see cref="UIElement"/>, in <see cref="string"/> form.
120+
/// </summary>
121+
/// <param name="obj">The <see cref="DependencyObject"/> instance.</param>
122+
/// <returns>The <see cref="string"/> representation of the <c>"Translation"</c> property property.</returns>
123+
public static string GetTranslation(DependencyObject obj)
124+
{
125+
if (!DesignTimeHelpers.IsRunningInLegacyDesignerMode && obj is UIElement element)
126+
{
127+
return GetTranslationForElement(element);
128+
}
129+
130+
return (string)obj.GetValue(TranslationProperty);
131+
}
132+
133+
/// <summary>
134+
/// Sets the <c>"Translation"</c> property of the underlying <see cref="Visual"/> object for a <see cref="UIElement"/>, in <see cref="string"/> form.
135+
/// </summary>
136+
/// <param name="obj">The <see cref="DependencyObject"/> instance.</param>
137+
/// <param name="value">The <see cref="string"/> representation of the <c>"Translation"</c> property property to be set.</param>
138+
public static void SetTranslation(DependencyObject obj, string value)
139+
{
140+
if (!DesignTimeHelpers.IsRunningInLegacyDesignerMode && obj is UIElement element)
141+
{
142+
SetTranslationForElement(value, element);
143+
}
144+
145+
obj.SetValue(TranslationProperty, value);
146+
}
147+
117148
/// <summary>
118149
/// Gets the <see cref="Visual.Opacity"/> of a UIElement
119150
/// </summary>
@@ -334,6 +365,12 @@ public static void SetNormalizedCenterPoint(DependencyObject obj, string value)
334365
public static readonly DependencyProperty OffsetProperty =
335366
DependencyProperty.RegisterAttached("Offset", typeof(string), typeof(VisualExtensions), new PropertyMetadata(null, OnOffsetChanged));
336367

368+
/// <summary>
369+
/// Identifies the Translation attached property.
370+
/// </summary>
371+
public static readonly DependencyProperty TranslationProperty =
372+
DependencyProperty.RegisterAttached("Translation", typeof(string), typeof(VisualExtensions), new PropertyMetadata(null, OnTranslationChanged));
373+
337374
/// <summary>
338375
/// Identifies the Opacity attached property.
339376
/// </summary>
@@ -400,6 +437,14 @@ private static void OnOffsetChanged(DependencyObject d, DependencyPropertyChange
400437
}
401438
}
402439

440+
private static void OnTranslationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
441+
{
442+
if (e.NewValue is string str)
443+
{
444+
SetTranslation(d, str);
445+
}
446+
}
447+
403448
private static void OnOpacityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
404449
{
405450
if (e.NewValue is double dbl)
@@ -503,6 +548,37 @@ private static void SetOffsetForElement(string value, UIElement element)
503548
visual.Offset = value.ToVector3();
504549
}
505550

551+
private static string GetTranslationForElement(UIElement element)
552+
{
553+
CompositionGetValueStatus result = GetVisual(element).Properties.TryGetVector3("Translation", out Vector3 translation);
554+
555+
return result switch
556+
{
557+
// The ("G", CultureInfo.InvariantCulture) combination produces a string with the default numeric
558+
// formatting style, and using ',' as component separator, so that the resulting text can safely
559+
// be parsed back if needed with the StringExtensions.ToVector3(string) extension, which uses
560+
// the invariant culture mode by default so that the syntax will always match that from XAML.
561+
CompositionGetValueStatus.Succeeded => translation.ToString("G", CultureInfo.InvariantCulture),
562+
_ => "<0, 0, 0>"
563+
};
564+
}
565+
566+
private static void SetTranslationForElement(string value, UIElement element)
567+
{
568+
ElementCompositionPreview.SetIsTranslationEnabled(element, true);
569+
570+
// The "Translation" attached property refers to the "hidden" property that is enabled
571+
// through "ElementCompositionPreview.SetIsTranslationEnabled". The value for this property
572+
// is not available directly on the Visual class and can only be accessed through its property
573+
// set. Note that this "Translation" value is not the same as Visual.TransformMatrix.Translation.
574+
// In fact, the latter doesn't require to be explicitly enabled and is actually combined with
575+
// this at runtime (ie. the whole transform matrix is combined with the additional translation
576+
// from the "Translation" property, if any), and the two can be set and animated independently.
577+
// In this case we're just interested in the "Translation" property, which is more commonly used
578+
// as it can also be animated directly with a Vector3 animation instead of a Matrix4x4 one.
579+
GetVisual(element).Properties.InsertVector3("Translation", value.ToVector3());
580+
}
581+
506582
private static double GetOpacityForElement(UIElement element)
507583
{
508584
var visual = GetVisual(element);

UnitTests/UnitTests.HighPerformance.Shared/Memory/Test_ReadOnlySpan2D{T}.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,51 @@ ref Unsafe.AsRef(span[2]),
586586

587587
[TestCategory("ReadOnlySpan2DT")]
588588
[TestMethod]
589-
public void Test_ReadOnlySpan2DT_TryGetReadOnlySpan_1()
589+
public void Test_ReadOnlySpan2DT_TryGetSpan_From1DArray_1()
590+
{
591+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
592+
593+
ReadOnlySpan2D<int> span2d = new ReadOnlySpan2D<int>(array, 3, 3);
594+
595+
bool success = span2d.TryGetSpan(out ReadOnlySpan<int> span);
596+
597+
Assert.IsTrue(success);
598+
Assert.AreEqual(span.Length, span2d.Length);
599+
Assert.IsTrue(Unsafe.AreSame(ref array[0], ref Unsafe.AsRef(in span[0])));
600+
}
601+
602+
[TestCategory("ReadOnlySpan2DT")]
603+
[TestMethod]
604+
public void Test_ReadOnlySpan2DT_TryGetSpan_From1DArray_2()
605+
{
606+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
607+
608+
ReadOnlySpan2D<int> span2d = new ReadOnlySpan2D<int>(array, 3, 3).Slice(1, 0, 2, 3);
609+
610+
bool success = span2d.TryGetSpan(out ReadOnlySpan<int> span);
611+
612+
Assert.IsTrue(success);
613+
Assert.AreEqual(span.Length, span2d.Length);
614+
Assert.IsTrue(Unsafe.AreSame(ref array[3], ref Unsafe.AsRef(in span[0])));
615+
}
616+
617+
[TestCategory("ReadOnlySpan2DT")]
618+
[TestMethod]
619+
public void Test_ReadOnlySpan2DT_TryGetSpan_From1DArray_3()
620+
{
621+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
622+
623+
ReadOnlySpan2D<int> span2d = new ReadOnlySpan2D<int>(array, 3, 3).Slice(0, 1, 3, 2);
624+
625+
bool success = span2d.TryGetSpan(out ReadOnlySpan<int> span);
626+
627+
Assert.IsFalse(success);
628+
Assert.AreEqual(span.Length, 0);
629+
}
630+
631+
[TestCategory("ReadOnlySpan2DT")]
632+
[TestMethod]
633+
public void Test_ReadOnlySpan2DT_TryGetReadOnlySpan_From2DArray_1()
590634
{
591635
int[,] array =
592636
{
@@ -610,7 +654,7 @@ public void Test_ReadOnlySpan2DT_TryGetReadOnlySpan_1()
610654

611655
[TestCategory("ReadOnlySpan2DT")]
612656
[TestMethod]
613-
public void Test_ReadOnlySpan2DT_TryGetReadOnlySpan_2()
657+
public void Test_ReadOnlySpan2DT_TryGetReadOnlySpan_From2DArray_2()
614658
{
615659
int[,] array =
616660
{

UnitTests/UnitTests.HighPerformance.Shared/Memory/Test_Span2D{T}.cs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,66 @@ public void Test_Span2DT_GetRowSpan()
761761

762762
[TestCategory("Span2DT")]
763763
[TestMethod]
764-
public void Test_Span2DT_TryGetSpan_1()
764+
public void Test_Span2DT_TryGetSpan_From1DArray_1()
765+
{
766+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
767+
768+
Span2D<int> span2d = new Span2D<int>(array, 3, 3);
769+
770+
bool success = span2d.TryGetSpan(out Span<int> span);
771+
772+
Assert.IsTrue(success);
773+
Assert.AreEqual(span.Length, span2d.Length);
774+
Assert.IsTrue(Unsafe.AreSame(ref array[0], ref span[0]));
775+
}
776+
777+
[TestCategory("Span2DT")]
778+
[TestMethod]
779+
public void Test_Span2DT_TryGetSpan_From1DArray_2()
780+
{
781+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
782+
783+
Span2D<int> span2d = new Span2D<int>(array, 3, 3).Slice(1, 0, 2, 3);
784+
785+
bool success = span2d.TryGetSpan(out Span<int> span);
786+
787+
Assert.IsTrue(success);
788+
Assert.AreEqual(span.Length, span2d.Length);
789+
Assert.IsTrue(Unsafe.AreSame(ref array[3], ref span[0]));
790+
}
791+
792+
[TestCategory("Span2DT")]
793+
[TestMethod]
794+
public void Test_Span2DT_TryGetSpan_From1DArray_3()
795+
{
796+
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
797+
798+
Span2D<int> span2d = new Span2D<int>(array, 3, 3).Slice(0, 1, 3, 2);
799+
800+
bool success = span2d.TryGetSpan(out Span<int> span);
801+
802+
Assert.IsFalse(success);
803+
Assert.AreEqual(span.Length, 0);
804+
}
805+
806+
// See https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3947
807+
[TestCategory("Span2DT")]
808+
[TestMethod]
809+
public void Test_Span2DT_TryGetSpan_From1DArray_4()
810+
{
811+
int[] array = new int[128];
812+
Span2D<int> span2d = new Span2D<int>(array, 8, 16);
813+
814+
bool success = span2d.TryGetSpan(out Span<int> span);
815+
816+
Assert.IsTrue(success);
817+
Assert.AreEqual(span.Length, span2d.Length);
818+
Assert.IsTrue(Unsafe.AreSame(ref array[0], ref span[0]));
819+
}
820+
821+
[TestCategory("Span2DT")]
822+
[TestMethod]
823+
public void Test_Span2DT_TryGetSpan_From2DArray_1()
765824
{
766825
int[,] array =
767826
{
@@ -790,7 +849,7 @@ public void Test_Span2DT_TryGetSpan_1()
790849

791850
[TestCategory("Span2DT")]
792851
[TestMethod]
793-
public void Test_Span2DT_TryGetSpan_2()
852+
public void Test_Span2DT_TryGetSpan_From2DArray_2()
794853
{
795854
int[,] array =
796855
{

0 commit comments

Comments
 (0)