diff --git a/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Controls/AutoLayoutPage.xaml b/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Controls/AutoLayoutPage.xaml
index 9c7495b3c..a0e9b5855 100644
--- a/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Controls/AutoLayoutPage.xaml
+++ b/samples/Uno.Toolkit.Samples/Uno.Toolkit.Samples.Shared/Content/Controls/AutoLayoutPage.xaml
@@ -27,7 +27,7 @@
-
+
diff --git a/src/Uno.Toolkit.RuntimeTests/Tests/AutoLayoutTest.cs b/src/Uno.Toolkit.RuntimeTests/Tests/AutoLayoutTest.cs
index 0545727c7..55a347d42 100644
--- a/src/Uno.Toolkit.RuntimeTests/Tests/AutoLayoutTest.cs
+++ b/src/Uno.Toolkit.RuntimeTests/Tests/AutoLayoutTest.cs
@@ -10,6 +10,8 @@
using Uno.Toolkit.UI;
using Uno.UI.RuntimeTests;
using Windows.UI;
+using FluentAssertions;
+using FluentAssertions.Execution;
#if IS_WINUI
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -64,34 +66,43 @@ public async Task When_SpaceBetween_with_spacing(Orientation orientation, double
var layoutRect1Actual = LayoutInformation.GetLayoutSlot(SUT.Children[1] as FrameworkElement);
var layoutRect2Actual = LayoutInformation.GetLayoutSlot(SUT.Children[2] as FrameworkElement);
+ using var _ = new AssertionScope();
+
if (orientation is Orientation.Vertical)
{
- Assert.AreEqual(expectedResult1, layoutRect0Actual.Y);
- Assert.AreEqual(expectedResult2, layoutRect1Actual.Y);
- Assert.AreEqual(expectedResult3, layoutRect2Actual.Y);
+ layoutRect0Actual.Y.Should().Be(expectedResult1);
+ layoutRect1Actual.Y.Should().Be(expectedResult2);
+ layoutRect2Actual.Y.Should().Be(expectedResult3);
}
else
{
- Assert.AreEqual(expectedResult1, layoutRect0Actual.X);
- Assert.AreEqual(expectedResult2, layoutRect1Actual.X);
- Assert.AreEqual(expectedResult3, layoutRect2Actual.X);
+ layoutRect0Actual.X.Should().Be(expectedResult1);
+ layoutRect1Actual.X.Should().Be(expectedResult2);
+ layoutRect2Actual.X.Should().Be(expectedResult3);
}
}
+
[TestMethod]
[RequiresFullWindow]
- [DataRow(true, Orientation.Vertical, VerticalAlignment.Bottom, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 0, 0, 10 }, 10, 298, 110, 12, 185)]
- [DataRow(true, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, 10, 12, 110, 12, 185)]
- [DataRow(true, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, -30, 12, 110, 12, 165)]
- [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, 10, 12, 12, 12, 105)]
- [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, 10, 12, 12, 12, 105)]
- [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, -30, 12, 12, 12, 85)]
- [DataRow(false, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, 10, 12, 110, 138, 248)]
- [DataRow(false, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, 10, 12, 110, 78, 138)]
- [DataRow(false, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, -20, 12, 110, 168, 248)]
- [DataRow(false, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 108, 10, 0, 0 }, -20, 12, 110, 108, 138)]
+ [DataRow(true, Orientation.Vertical, VerticalAlignment.Bottom, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 0, 0, 10 }, 10, 300, 110, 12, 185)]
+ [DataRow(true, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, 10, 10, 110, 12, 185)]
+ [DataRow(true, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, -30, 10, 110, 12, 165)]
+ [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, 10, 10, 10, 12, 105)]
+ [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, 10, 10, 10, 12, 105)]
+ [DataRow(true, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 10, 10, 0, 0 }, -30, 10, 10, 12, 85)]
+ [DataRow(false, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, 10, 10, 110, 138, 248)]
+ [DataRow(false, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, 10, 10, 110, 78, 138)]
+ [DataRow(false, Orientation.Vertical, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, -20, 10, 110, 168, 248)]
+ [DataRow(false, Orientation.Horizontal, VerticalAlignment.Top, HorizontalAlignment.Left, new[] { 10, 10, 10, 10 }, new[] { 110, 10, 0, 0 }, -20, 10, 110, 108, 138)]
public async Task When_AbsolutePosition_WithPadding(bool isStretch, Orientation orientation, VerticalAlignment vAlign, HorizontalAlignment hAlign, int[] padding, int[] margin, int spacing, double expectedY, double expectedX, double rec1expected, double rec2expected)
{
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")))
+ {
+ Assert.Inconclusive("This test is not valid on Wasm");
+ return;
+ }
+
var SUT = new AutoLayout()
{
Orientation = orientation,
@@ -229,8 +240,8 @@ public async Task When_Padding(bool isStretch, Orientation orientation, AutoLayo
var border1 = new Border()
{
Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255)),
- Width = 350,
- Height = 350,
+ Width = orientation is Orientation.Horizontal && isStretch ? double.NaN : 350,
+ Height = orientation is Orientation.Vertical && isStretch ? double.NaN : 350,
};
if (isStretch)
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Arrange.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Arrange.cs
new file mode 100644
index 000000000..34b95fa67
--- /dev/null
+++ b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Arrange.cs
@@ -0,0 +1,344 @@
+using System;
+using System.Runtime.CompilerServices;
+using Windows.Foundation;
+
+#if IS_WINUI
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+#else
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+#endif
+
+namespace Uno.Toolkit.UI;
+
+partial class AutoLayout
+{
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ // Local cache of DependencyProperties
+ var children = Children;
+ var orientation = Orientation;
+ var justify = Justify;
+ var primaryAxisAlignment = PrimaryAxisAlignment;
+ var padding = Padding;
+ var spacing = Spacing.FiniteOrDefault(0d);
+ var isHorizontal = orientation == Orientation.Horizontal;
+ var borderThickness = BorderBrush is null ? default : BorderThickness;
+
+ var borderThicknessLength = borderThickness.GetLength(orientation);
+
+ var totalNonFilledStackedSize = 0d;
+ var numberOfFilledChildren = 0;
+ var numberOfStackedChildren = 0;
+ var haveStartPadding = false;
+ var haveEndPadding = false;
+
+ if (_calculatedChildren is null || children.Count != _calculatedChildren.Length)
+ {
+ // Children list changed, invalidate measure and wait for next pass
+ InvalidateMeasure();
+ return finalSize;
+ }
+
+ // 1. Calculate the total size of non-filled and filled children
+ for (var i = 0; i < children.Count; i++)
+ {
+ var child = children[i];
+ var calculatedChild = _calculatedChildren[i];
+
+ if (!ReferenceEquals(child, calculatedChild.Element))
+ {
+ // Invalid calculated child, invalidate measure and wait for next pass to fix this.
+ InvalidateMeasure();
+ return finalSize;
+ }
+
+ switch (calculatedChild.Role)
+ {
+ case AutoLayoutRole.Independent:
+ continue; // Independent children are not stacked, skip it from the calculation
+ case AutoLayoutRole.Filled:
+ numberOfFilledChildren++;
+ break;
+ case AutoLayoutRole.Hug:
+ case AutoLayoutRole.Fixed:
+ default:
+ totalNonFilledStackedSize += calculatedChild.MeasuredLength;
+ break;
+ }
+
+ numberOfStackedChildren++;
+ }
+
+ var atLeastOneFilledChild = numberOfFilledChildren > 0;
+
+ switch (primaryAxisAlignment)
+ {
+ case AutoLayoutAlignment.Start:
+ haveStartPadding = true;
+ haveEndPadding = atLeastOneFilledChild || justify == AutoLayoutJustify.SpaceBetween;
+ break;
+ case AutoLayoutAlignment.End:
+ haveStartPadding = atLeastOneFilledChild || justify == AutoLayoutJustify.SpaceBetween;
+ haveEndPadding = true;
+ break;
+ case AutoLayoutAlignment.Center:
+ var havePadding = atLeastOneFilledChild || justify == AutoLayoutJustify.SpaceBetween;
+ haveEndPadding = havePadding;
+ haveStartPadding = havePadding;
+ break;
+ }
+
+ var startPadding = haveStartPadding
+ ? isHorizontal ? padding.Left : padding.Top
+ : 0d;
+ var endPadding = haveEndPadding
+ ? isHorizontal ? padding.Right : padding.Bottom
+ : 0d;
+
+ var totalPaddingSize = startPadding + endPadding;
+
+ // Available Size is the final size minus the border thickness and the padding
+ var availableSizeForStackedChildren = finalSize.GetLength(orientation) - (borderThicknessLength + totalPaddingSize);
+ EnsureZeroFloor(ref availableSizeForStackedChildren);
+
+ // Start the offset at the border + start padding
+ var currentOffset = borderThickness.GetStartLength(orientation) + startPadding;
+
+ // Calculate the defined inter-element spacing size (if any, not taking care of SpaceBetween yet)
+ var totalSpacingSize = spacing * (numberOfStackedChildren - 1);
+
+ // Calculate the remaining size for each filled children (if any)
+ var filledChildrenSize = atLeastOneFilledChild
+ ? (availableSizeForStackedChildren - (totalNonFilledStackedSize + totalSpacingSize)) / numberOfFilledChildren
+ : 0;
+
+ EnsureZeroFloor(ref filledChildrenSize);
+
+ // Establish actual inter-element spacing.
+ var spacingOffset = justify == AutoLayoutJustify.SpaceBetween && !atLeastOneFilledChild
+ // When SpaceBetween is defined and there's no filled children
+ ? ComputeSpaceBetween(availableSizeForStackedChildren, totalNonFilledStackedSize, numberOfStackedChildren)
+ // Fallback to the Spacing property
+ : spacing;
+
+ var primaryAxisAlignmentOffset = PrimaryAxisAlignmentOffsetSize(
+ primaryAxisAlignment,
+ availableSizeForStackedChildren,
+ totalNonFilledStackedSize,
+ atLeastOneFilledChild,
+ spacing,
+ numberOfStackedChildren);
+
+ currentOffset += primaryAxisAlignmentOffset;
+
+ // 3. Arrange children, one by one (in reverse order if IsReverseZIndex is true)
+ var isReverse = IsReverseZIndex;
+ var start = isReverse ? children.Count - 1 : 0;
+ var stop = isReverse ? -1 : children.Count;
+ var increment = isReverse ? -1 : 1;
+ for (var i = start; i != stop; i += increment)
+ {
+ var child = _calculatedChildren[i];
+
+ if (child.Role == AutoLayoutRole.Independent)
+ {
+ // Independent is given all the available space,
+ // because it's not stacking with others.
+ // No padding is applied to it either.
+ child.Element.Arrange(new Rect(default, finalSize));
+
+ continue; // next child, current offset remains unchanged
+ }
+
+ // Calculate the length of the child (size in the panel orientation)
+
+ // Length depends on the role of the child
+ var childLength = child.Role == AutoLayoutRole.Filled
+ ? filledChildrenSize
+ : child.MeasuredLength;
+
+ var offsetRelativeToPadding = currentOffset - (startPadding + borderThicknessLength);
+
+ if (childLength > availableSizeForStackedChildren - offsetRelativeToPadding)
+ {
+ // Child is too big, truncate it to the remaining space
+ childLength = availableSizeForStackedChildren - offsetRelativeToPadding;
+ }
+
+ EnsureZeroFloor(ref childLength);
+
+ // Calculate the counter length of the child (size in the other dimension than
+ var childCounterLength = GetCounterLength(child.Element);
+
+ var childFinalCounterLength = isHorizontal
+ ? double.IsNaN(childCounterLength) ? child.Element.DesiredSize.Height : childCounterLength
+ : double.IsNaN(childCounterLength) ? child.Element.DesiredSize.Width : childCounterLength;
+
+ EnsureZeroFloor(ref childFinalCounterLength);
+
+ // Calculate the position of the child by applying the alignment instructions
+ var counterAlignment = GetCounterAlignment(child.Element);
+
+ var isPrimaryAlignmentStretch = GetPrimaryAlignment(child.Element) is AutoLayoutPrimaryAlignment.Stretch;
+ var isCounterAlignmentStretch = counterAlignment is AutoLayoutAlignment.Stretch;
+
+ if (child.Element is FrameworkElement frameworkElement)
+ {
+ UpdateCounterAlignmentToStretch(ref frameworkElement, isHorizontal, isPrimaryAlignmentStretch, isCounterAlignmentStretch);
+ }
+
+ var haveCounterStartPadding = counterAlignment is AutoLayoutAlignment.Stretch or AutoLayoutAlignment.Start;
+ var counterStartPadding = haveCounterStartPadding ? (isHorizontal ? padding.Top : padding.Left) : 0;
+
+ var haveCounterEndPadding = counterAlignment is AutoLayoutAlignment.Stretch or AutoLayoutAlignment.End;
+ var counterEndPadding = haveCounterEndPadding ? (isHorizontal ? padding.Bottom : padding.Right) : 0;
+
+ var test = borderThickness.GetCounterLength(orientation);
+ var availableCounterLength = finalSize.GetCounterLength(orientation) - (counterStartPadding + counterEndPadding + test);
+
+ EnsureZeroFloor(ref availableCounterLength);
+
+ var childSize = isHorizontal
+ ? new Size(childLength, counterAlignment is AutoLayoutAlignment.Stretch ? availableCounterLength : childFinalCounterLength)
+ : new Size(counterAlignment is AutoLayoutAlignment.Stretch ? availableCounterLength : childFinalCounterLength, childLength);
+
+ ApplyMinMaxValues(child.Element, orientation, ref childSize);
+
+ var counterAlignmentOffset =
+ ComputeCounterAlignmentOffset(counterAlignment, childFinalCounterLength, availableCounterLength, counterStartPadding, borderThickness.GetCounterStartLength(orientation));
+
+ var childOffsetPosition = new Point(
+ isHorizontal ? currentOffset : counterAlignmentOffset,
+ isHorizontal ? counterAlignmentOffset : currentOffset);
+
+ // Arrange the child to its final position, determined by the calculated offset and size
+ child.Element.Arrange(new Rect(childOffsetPosition, childSize));
+
+ // Increment the offset for the next child
+ currentOffset += (childLength + spacingOffset);
+ }
+
+ return finalSize;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void UpdateCounterAlignmentToStretch(ref FrameworkElement frameworkElement, bool isHorizontal, bool isPrimaryAlignmentStretch, bool isCounterAlignmentStretch)
+ {
+ if (isHorizontal)
+ {
+ frameworkElement.HorizontalAlignment = isPrimaryAlignmentStretch ? HorizontalAlignment.Stretch : frameworkElement.HorizontalAlignment;
+ frameworkElement.VerticalAlignment = isCounterAlignmentStretch ? VerticalAlignment.Stretch : frameworkElement.VerticalAlignment;
+ }
+ else
+ {
+ frameworkElement.VerticalAlignment = isPrimaryAlignmentStretch ? VerticalAlignment.Stretch : frameworkElement.VerticalAlignment;
+ frameworkElement.HorizontalAlignment = isCounterAlignmentStretch ? HorizontalAlignment.Stretch : frameworkElement.HorizontalAlignment;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ApplyMinMaxValues(UIElement element, Orientation orientation, ref Size desiredSize)
+ {
+ if (element is not FrameworkElement frameworkElement)
+ {
+ return;
+ }
+
+ // Cache local values to avoid multiple calls to the DP system
+ var width = frameworkElement.Width;
+ var height = frameworkElement.Height;
+ var maxWidth = frameworkElement.MaxWidth;
+ var maxHeight = frameworkElement.MaxHeight;
+ var minWidth = frameworkElement.MinWidth;
+ var minHeight = frameworkElement.MinHeight;
+
+ var primaryLength = GetPrimaryLength(element);
+ var counterLength = GetCounterLength(element);
+
+ // Apply primaryLength and counterLength constraints, if defined
+ if (orientation is Orientation.Horizontal)
+ {
+ if (!double.IsNaN(primaryLength)) desiredSize.Width = primaryLength;
+ if (!double.IsNaN(counterLength)) desiredSize.Height = counterLength;
+ }
+ else
+ {
+ if (!double.IsNaN(primaryLength)) desiredSize.Height = primaryLength;
+ if (!double.IsNaN(counterLength)) desiredSize.Width = counterLength;
+ }
+
+ // Apply Width and Height constraints, if defined
+ if (!double.IsNaN(width)) desiredSize.Width = width;
+ if (!double.IsNaN(height)) desiredSize.Height = height;
+
+ // Apply MaxWidth and MaxHeight constraints, if defined
+ if (!double.IsNaN(maxWidth) && desiredSize.Width > maxWidth) desiredSize.Width = maxWidth;
+ if (!double.IsNaN(maxHeight) && desiredSize.Height > maxHeight) desiredSize.Height = maxHeight;
+
+ // Apply MinWidth and MinHeight constraints
+ if (!double.IsNaN(minWidth) && desiredSize.Width < minWidth) desiredSize.Width = minWidth;
+ if (!double.IsNaN(minHeight) && desiredSize.Height < minHeight) desiredSize.Height = minHeight;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static double ComputeCounterAlignmentOffset(
+ AutoLayoutAlignment autoLayoutAlignment,
+ double childCounterLength,
+ double availableCounterLength,
+ double counterStartPadding,
+ double counterBorderThickness)
+ {
+ var alignmentOffsetSize = availableCounterLength - childCounterLength;
+
+ var calculatedOffset = autoLayoutAlignment switch
+ {
+ AutoLayoutAlignment.End => alignmentOffsetSize,
+ AutoLayoutAlignment.Center when alignmentOffsetSize > 0 => alignmentOffsetSize / 2,
+ _ => 0
+ };
+
+ return calculatedOffset + counterStartPadding + counterBorderThickness;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static double ComputeSpaceBetween(
+ double availableSizeForStackedChildren,
+ double totalNonFilledStackedSize,
+ int numberOfStackedChildren)
+ {
+ var availableSizeForStackedSpaceBetween = availableSizeForStackedChildren - totalNonFilledStackedSize;
+
+ EnsureZeroFloor(ref availableSizeForStackedSpaceBetween);
+
+ return availableSizeForStackedSpaceBetween / (numberOfStackedChildren - 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static double PrimaryAxisAlignmentOffsetSize(
+ AutoLayoutAlignment autoLayoutAlignment,
+ double availableSizeForStackedChildren,
+ double totalNonFilledStackedSize,
+ bool atLeastOneFilledChild,
+ double spacing,
+ int numberOfStackedChildren)
+ {
+ if (atLeastOneFilledChild || autoLayoutAlignment is AutoLayoutAlignment.Start)
+ {
+ return 0;
+ }
+
+ var totalSpacingSize = spacing * (numberOfStackedChildren - 1);
+
+ var alignmentOffsetSize = availableSizeForStackedChildren -
+ (totalNonFilledStackedSize + totalSpacingSize);
+
+ return autoLayoutAlignment switch
+ {
+ AutoLayoutAlignment.End => alignmentOffsetSize,
+ AutoLayoutAlignment.Center when alignmentOffsetSize > 0 => alignmentOffsetSize / 2,
+ _ => 0
+ };
+ }
+}
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Measure.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Measure.cs
new file mode 100644
index 000000000..408110b41
--- /dev/null
+++ b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Layouting.Measure.cs
@@ -0,0 +1,452 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Windows.Foundation;
+#if IS_WINUI
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+#else
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+#endif
+
+namespace Uno.Toolkit.UI;
+
+partial class AutoLayout
+{
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ // - Each children can be one of the following role
+ // 1. Hug: The child is measured according to its own constraints, using its desired size.
+ // 2. Fixed: The child is measured according to its own constraints, using its fixed size.
+ // 3. Filled: The child will be given the remaining space, evenly distributed through all
+ // other "filled" children.
+ // 4. Independent: The child will follow the z-axis of its position in the children list,
+ // but won't be affected by the other children.
+
+ // -1. Local cache of DependencyProperties
+ var orientation = Orientation;
+ var children = Children;
+ var justify = Justify;
+ var spacing = Spacing.FiniteOrDefault(0d);
+ var borderThickness = BorderBrush is null ? default : BorderThickness;
+
+ // 0. Establish start remaining sizes to dispatch, according to the orientation
+ var remainingSize = availableSize.GetLength(orientation) - borderThickness.GetLength(orientation);
+ var availableCounterSize = availableSize.GetCounterLength(orientation) - borderThickness.GetCounterLength(orientation);
+ var desiredCounterSize = 0d;
+
+ // 1. Establish the list of children roles
+ var filledAsHug = double.IsPositiveInfinity(remainingSize);
+ var (numberOfStackedChildren, atLeastOneFilledChild) = EstablishChildrenRoles(children, filledAsHug, orientation);
+
+ // 2. Remove applicable padding and spacing from the remaining size
+ var paddingSize = Padding.GetLength(orientation);
+ Decrement(ref remainingSize, paddingSize);
+
+ // 3. Measure fixed children
+ MeasureFixedChildren(orientation, availableCounterSize, ref remainingSize, ref desiredCounterSize);
+
+ // 4. Establish the total spacing size, if applicable
+ var totalSpacingSize = 0d;
+ if (numberOfStackedChildren > 0
+ && justify != AutoLayoutJustify.SpaceBetween
+ && spacing != 0
+ && spacing < double.PositiveInfinity)
+ {
+ totalSpacingSize = (numberOfStackedChildren - 1) * spacing;
+ Decrement(ref remainingSize, totalSpacingSize);
+ }
+
+ // 5. Calculate the size of Hug children
+ MeasureHugChildren(orientation, availableCounterSize, ref remainingSize, ref desiredCounterSize);
+
+ // 6. Calculate the size of Filled children
+ MeasureFilledChildren(orientation, availableCounterSize, ref remainingSize, ref desiredCounterSize);
+
+ // 7. Measure independent children, independently of the remaining size
+ var independentDesiredSize = MeasureIndependentChildren(availableSize, borderThickness, orientation, ref desiredCounterSize);
+
+ EnsureZeroFloor(ref desiredCounterSize);
+
+ // 8. Calculate the desired size of the panel
+ Size desiredSize;
+
+ if ((atLeastOneFilledChild
+ || justify == AutoLayoutJustify.SpaceBetween)
+ && remainingSize is > 0 and < double.PositiveInfinity)
+ {
+ // 8a. Calculated the spacing size, when justify is SpaceBetween or there's at least one filled child
+
+ // We don't need to calculate the spacing since it's the remaining size
+ // and the final spacing will be calculated in the arrange pass.
+ desiredSize = orientation switch
+ {
+ Orientation.Horizontal => new Size(availableSize.Width, desiredCounterSize),
+ Orientation.Vertical => new Size(desiredCounterSize, availableSize.Height),
+ _ => throw new ArgumentOutOfRangeException(),
+ };
+ }
+ else
+ {
+ // 8b. Calculate the desired size of the panel, when there's at least one filled child
+ var stackedChildrenDesiredSize =
+ _calculatedChildren!
+ .Select(c => c.MeasuredLength)
+ .Sum()
+ + totalSpacingSize;
+
+ var desiredSizeInPrimaryOrientation = Math.Max(independentDesiredSize, stackedChildrenDesiredSize);
+
+ EnsureZeroFloor(ref desiredSizeInPrimaryOrientation);
+
+ desiredSize = orientation switch
+ {
+ Orientation.Horizontal => new Size(
+ width: desiredSizeInPrimaryOrientation + paddingSize,
+ height: desiredCounterSize + Padding.GetCounterLength(orientation)),
+ Orientation.Vertical => new Size(
+ width: desiredCounterSize + Padding.GetCounterLength(orientation),
+ height: desiredSizeInPrimaryOrientation + paddingSize),
+ _ => throw new ArgumentOutOfRangeException(),
+ };
+ }
+
+ // 9. Apply Width and Height constraints, if any
+ ApplyMinMaxValues(ref desiredSize);
+
+ return desiredSize;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ApplyMinMaxValues(ref Size desiredSize)
+ {
+ // Cache local values to avoid multiple calls to the DP system
+ var width = Width;
+ var height = Height;
+ var maxWidth = MaxWidth;
+ var maxHeight = MaxHeight;
+ var minWidth = MinWidth;
+ var minHeight = MinHeight;
+
+ // Apply Width and Height constraints, if defined
+ if (!double.IsNaN(width)) desiredSize.Width = width;
+ if (!double.IsNaN(height)) desiredSize.Height = height;
+
+ // Apply MaxWidth and MaxHeight constraints, if defined
+ if (!double.IsNaN(maxWidth) && desiredSize.Width > maxWidth) desiredSize.Width = maxWidth;
+ if (!double.IsNaN(maxHeight) && desiredSize.Height > maxHeight) desiredSize.Height = maxHeight;
+
+ // Apply MinWidth and MinHeight constraints
+ if (!double.IsNaN(minWidth) && desiredSize.Width < minWidth) desiredSize.Width = minWidth;
+ if (!double.IsNaN(minHeight) && desiredSize.Height < minHeight) desiredSize.Height = minHeight;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private (int, bool) EstablishChildrenRoles(IList children, bool filledAsHug, Orientation orientation)
+ {
+ if(_calculatedChildren == null || _calculatedChildren.Length != children.Count)
+ {
+ _calculatedChildren = new CalculatedChildren[children.Count];
+ }
+
+ var numberOfStackedChildren = children.Count;
+ var atLeastOneFilledChild = false;
+
+ for (var i = 0; i < children.Count; i++)
+ {
+ var child = children[i];
+
+ AutoLayoutRole role;
+ var length = 0d;
+
+ if (GetIsIndependentLayout(child))
+ {
+ role = AutoLayoutRole.Independent;
+ numberOfStackedChildren--;
+ }
+ else if (GetPrimaryAlignment(child) == AutoLayoutPrimaryAlignment.Stretch && !filledAsHug)
+ {
+ atLeastOneFilledChild = true;
+ role = AutoLayoutRole.Filled;
+ }
+ else if(GetPrimaryLength(child) is var l and >= 0 and < double.PositiveInfinity)
+ {
+ role = AutoLayoutRole.Fixed;
+ length = l;
+ }
+ else
+ {
+ var size = child is FrameworkElement frameworkElement ?
+ Math.Max(frameworkElement.GetLength(orientation), frameworkElement.GetMinLength(orientation)) :
+ -1;
+
+ if (size >= 0)
+ {
+ role = AutoLayoutRole.Fixed;
+ length = size;
+ }
+ else
+ {
+ role = AutoLayoutRole.Hug;
+ }
+ }
+
+ _calculatedChildren[i] = new CalculatedChildren(child, role, length);
+ }
+
+ return (numberOfStackedChildren, atLeastOneFilledChild);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void MeasureFixedChildren(
+ Orientation orientation,
+ double availableCounterSize,
+ ref double remainingSize,
+ ref double desiredCounterSize)
+ {
+ for (var i = 0; i < _calculatedChildren!.Length; i++)
+ {
+ var calculatedChild = _calculatedChildren[i];
+
+ if (calculatedChild.Role != AutoLayoutRole.Fixed)
+ {
+ continue;
+ }
+
+ var fixedSize = calculatedChild.MeasuredLength;
+
+ MeasureChild(
+ calculatedChild.Element,
+ orientation,
+ fixedSize, // The available size for the child is its defined fixed size
+ availableCounterSize,
+ ref desiredCounterSize);
+
+ Decrement(ref remainingSize, fixedSize);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void MeasureHugChildren(
+ Orientation orientation,
+ double availableCounterSize,
+ ref double remainingSize,
+ ref double desiredCounterSize)
+ {
+ for (var i = 0; i < _calculatedChildren!.Length; i++)
+ {
+ var calculatedChild = _calculatedChildren![i];
+
+ if (calculatedChild.Role != AutoLayoutRole.Hug)
+ {
+ continue;
+ }
+
+ var desiredSize = MeasureChild(
+ calculatedChild.Element,
+ orientation,
+ double.PositiveInfinity, // We don't want the child to limit its own desired size to available one
+ availableCounterSize,
+ ref desiredCounterSize);
+
+ calculatedChild.MeasuredLength = desiredSize;
+
+ // We don't care about its desired size, because it's fixed, so we're using fixed size
+ Decrement(ref remainingSize, desiredSize);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool MeasureFilledChildren(
+ Orientation orientation,
+ double availableCounterSize,
+ ref double remainingSize,
+ ref double desiredCounterSize)
+ {
+ if (double.IsInfinity(remainingSize))
+ {
+ return false; // Filled children act as Hug children when there's infinite available size
+ }
+
+ // Calculated the number of filled children using a for loop, to avoid allocating an array
+ var filledChildrenCount = 0;
+ for (var i = 0; i < _calculatedChildren!.Length; i++)
+ {
+ var child = _calculatedChildren![i];
+
+ if (child.Role == AutoLayoutRole.Filled)
+ {
+ filledChildrenCount++;
+ }
+ }
+
+ if (filledChildrenCount <= 0)
+ {
+ return false; // no filled children
+ }
+
+ var filledSize = remainingSize / filledChildrenCount;
+ EnsureZeroFloor(ref filledSize);
+
+ for (var i = 0; i < _calculatedChildren!.Length; i++)
+ {
+ var child = _calculatedChildren![i];
+ if (child.Role != AutoLayoutRole.Filled)
+ {
+ continue;
+ }
+
+ MeasureChild(child.Element, orientation, filledSize, availableCounterSize, ref desiredCounterSize);
+
+ child.MeasuredLength = filledSize;
+ }
+
+ return true; // at least one filled child
+ }
+
+ private static double MeasureChild(
+ UIElement child,
+ Orientation orientation,
+ double availableSize,
+ double availableCounterSize,
+ ref double desiredCounterSize)
+ {
+ var availableSizeForChild = orientation == Orientation.Horizontal
+ ? new Size(availableSize, availableCounterSize)
+ : new Size(availableCounterSize, availableSize);
+
+ child.Measure(availableSizeForChild);
+
+ double desiredSize;
+ if (orientation == Orientation.Horizontal)
+ {
+ desiredSize = child.DesiredSize.Width;
+ desiredCounterSize = Math.Max(desiredCounterSize, child.DesiredSize.Height);
+ }
+ else
+ {
+ desiredSize = child.DesiredSize.Height;
+ desiredCounterSize = Math.Max(desiredCounterSize, child.DesiredSize.Width);
+ }
+
+ EnsureZeroFloor(ref desiredSize);
+ return desiredSize;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Decrement(ref double value, double decrement)
+ {
+ if (double.IsInfinity(value))
+ {
+ return;
+ }
+
+ value -= decrement;
+ EnsureZeroFloor(ref value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void EnsureZeroFloor(ref double sizeValue)
+ {
+ if (sizeValue < 0)
+ {
+ sizeValue = 0;
+ }
+ }
+
+ private double MeasureIndependentChildren(
+ Size availableSize,
+ Thickness borderThickness,
+ Orientation orientation,
+ ref double desiredCounterSize)
+ {
+ var resultSize = new Size();
+ var anyIndependent = false;
+
+ // Actual available size for independent children is the available size minus the border thickness
+ availableSize = new Size(
+ availableSize.Width - (borderThickness.Left + borderThickness.Right),
+ availableSize.Height - (borderThickness.Top + borderThickness.Bottom));
+
+ for (var i = 0; i < _calculatedChildren!.Length; i++)
+ {
+ var child = _calculatedChildren[i];
+
+ if (child.Role != AutoLayoutRole.Independent)
+ {
+ continue;
+ }
+
+ anyIndependent = true;
+
+ child.Element.Measure(availableSize);
+ var desiredSize = child.Element.DesiredSize;
+
+ // Adjust resulting desired size to the largest of the children
+ resultSize.Width = Math.Max(resultSize.Width, desiredSize.Width);
+ resultSize.Height = Math.Max(resultSize.Height, desiredSize.Height);
+ }
+
+ if (!anyIndependent)
+ {
+ return -1;
+ }
+
+ desiredCounterSize = Math.Max(
+ desiredCounterSize,
+ orientation switch
+ {
+ Orientation.Horizontal => resultSize.Height,
+ Orientation.Vertical => resultSize.Width,
+ _ => throw new NotSupportedException()
+ });
+
+ return orientation switch
+ {
+ Orientation.Horizontal => resultSize.Width,
+ Orientation.Vertical => resultSize.Height,
+ _ => throw new NotSupportedException()
+ };
+ }
+
+ private CalculatedChildren[]? _calculatedChildren;
+
+ private class CalculatedChildren
+ {
+ public CalculatedChildren(UIElement element, AutoLayoutRole role, double measuredLength = 0d)
+ {
+ Element = element;
+ Role = role;
+ MeasuredLength = measuredLength;
+ }
+
+ internal UIElement Element { get; }
+
+ ///
+ /// How the element is layouted in the AutoLayout.
+ ///
+ ///
+ /// When the element is absolutely positioned (Independent), it is not stacked with others.
+ ///
+ internal AutoLayoutRole Role { get; }
+
+ ///
+ /// Measured length of the element, when it is stacked.
+ ///
+ ///
+ /// Will be zero when the element is absolutely positioned, because it is not stacked
+ /// with others.
+ ///
+ internal double MeasuredLength { get; set; }
+ }
+
+ private enum AutoLayoutRole : byte
+ {
+ Hug,
+ Fixed,
+ Filled,
+ Independent
+ }
+}
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Properties.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Properties.cs
new file mode 100644
index 000000000..1c415dd6f
--- /dev/null
+++ b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Properties.cs
@@ -0,0 +1,195 @@
+using System.Diagnostics.CodeAnalysis;
+
+#if IS_WINUI
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+#else
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+#endif
+
+namespace Uno.Toolkit.UI;
+
+partial class AutoLayout
+{
+ // -- IsReverseZIndex DependencyProperty --
+ public static readonly DependencyProperty IsReverseZIndexProperty = DependencyProperty.Register(
+ nameof(IsReverseZIndex),
+ typeof(bool),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(bool), propertyChangedCallback: InvalidateArrangeCallback));
+
+ public bool IsReverseZIndex
+ {
+ get => (bool)GetValue(IsReverseZIndexProperty);
+ set => SetValue(IsReverseZIndexProperty, value);
+ }
+
+ // -- Orientation DependencyProperty --
+ public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
+ nameof(Orientation),
+ typeof(Orientation),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(Orientation), propertyChangedCallback: InvalidateMeasureCallback));
+
+ public Orientation Orientation
+ {
+ get => (Orientation)GetValue(OrientationProperty);
+ set => SetValue(OrientationProperty, value);
+ }
+
+ // -- Spacing DependencyProperty --
+ public static readonly DependencyProperty SpacingProperty = DependencyProperty.Register(
+ nameof(Spacing),
+ typeof(double),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(double), propertyChangedCallback: InvalidateMeasureCallback));
+
+ public double Spacing
+ {
+ get => (double)GetValue(SpacingProperty);
+ set => SetValue(SpacingProperty, value);
+ }
+
+ // -- Justify DependencyProperty --
+ public static readonly DependencyProperty JustifyProperty = DependencyProperty.Register(
+ nameof(Justify),
+ typeof(AutoLayoutJustify),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(AutoLayoutJustify), propertyChangedCallback: InvalidateArrangeCallback));
+
+ public AutoLayoutJustify Justify
+ {
+ get => (AutoLayoutJustify)GetValue(JustifyProperty);
+ set => SetValue(JustifyProperty, value);
+ }
+
+ // -- PrimaryAxisAlignment DependencyProperty --
+ public static readonly DependencyProperty PrimaryAxisAlignmentProperty = DependencyProperty.Register(
+ nameof(PrimaryAxisAlignment),
+ typeof(AutoLayoutAlignment),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(AutoLayoutAlignment), propertyChangedCallback: InvalidateArrangeCallback));
+
+ public AutoLayoutAlignment PrimaryAxisAlignment
+ {
+ get => (AutoLayoutAlignment)GetValue(PrimaryAxisAlignmentProperty);
+ set => SetValue(PrimaryAxisAlignmentProperty, value);
+ }
+
+ // -- PrimaryAlignment Attached Property --
+ [DynamicDependency(nameof(GetPrimaryAlignment))]
+ public static readonly DependencyProperty PrimaryAlignmentProperty = DependencyProperty.RegisterAttached(
+ "PrimaryAlignment",
+ typeof(AutoLayoutPrimaryAlignment),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(AutoLayoutPrimaryAlignment), propertyChangedCallback: InvalidateArrangeCallback));
+
+ [DynamicDependency(nameof(GetPrimaryAlignment))]
+ public static void SetPrimaryAlignment(DependencyObject element, AutoLayoutPrimaryAlignment value)
+ {
+ element.SetValue(PrimaryAlignmentProperty, value);
+ }
+
+ [DynamicDependency(nameof(GetPrimaryAlignment))]
+ public static AutoLayoutPrimaryAlignment GetPrimaryAlignment(DependencyObject element)
+ {
+ return (AutoLayoutPrimaryAlignment)element.GetValue(PrimaryAlignmentProperty);
+ }
+
+ // -- CounterAlignment Attached Property --
+ [DynamicDependency(nameof(GetCounterAlignment))]
+ public static readonly DependencyProperty CounterAlignmentProperty = DependencyProperty.RegisterAttached(
+ "CounterAlignment",
+ typeof(AutoLayoutAlignment),
+ typeof(AutoLayout),
+ new PropertyMetadata(AutoLayoutAlignment.Stretch, propertyChangedCallback: InvalidateArrangeCallback));
+
+ [DynamicDependency(nameof(GetCounterAlignment))]
+ public static void SetCounterAlignment(DependencyObject element, AutoLayoutAlignment value)
+ {
+ element.SetValue(CounterAlignmentProperty, value);
+ }
+
+ [DynamicDependency(nameof(SetCounterAlignment))]
+ public static AutoLayoutAlignment GetCounterAlignment(DependencyObject element)
+ {
+ return (AutoLayoutAlignment)element.GetValue(CounterAlignmentProperty);
+ }
+
+ // -- IsIndependentLayout Attached Property --
+ [DynamicDependency(nameof(GetIsIndependentLayout))]
+ public static readonly DependencyProperty IsIndependentLayoutProperty = DependencyProperty.RegisterAttached(
+ "IsIndependentLayout",
+ typeof(bool),
+ typeof(AutoLayout),
+ new PropertyMetadata(default(bool), propertyChangedCallback: InvalidateMeasureCallback));
+
+ [DynamicDependency(nameof(GetIsIndependentLayout))]
+ public static void SetIsIndependentLayout(DependencyObject element, bool value)
+ {
+ element.SetValue(IsIndependentLayoutProperty, value);
+ }
+
+ [DynamicDependency(nameof(SetIsIndependentLayout))]
+ public static bool GetIsIndependentLayout(DependencyObject element)
+ {
+ return (bool)element.GetValue(IsIndependentLayoutProperty);
+ }
+
+ // -- PrimaryLength Attached Property --
+ [DynamicDependency(nameof(GetPrimaryLength))]
+ public static readonly DependencyProperty PrimaryLengthProperty = DependencyProperty.RegisterAttached(
+ "PrimaryLength",
+ typeof(double),
+ typeof(AutoLayout),
+ new PropertyMetadata(double.NaN, propertyChangedCallback: InvalidateMeasureCallback));
+
+ [DynamicDependency(nameof(GetPrimaryLength))]
+ public static void SetPrimaryLength(DependencyObject element, double value)
+ {
+ element.SetValue(PrimaryLengthProperty, value);
+ }
+
+ [DynamicDependency(nameof(SetPrimaryLength))]
+ public static double GetPrimaryLength(DependencyObject element)
+ {
+ return (double)element.GetValue(PrimaryLengthProperty);
+ }
+
+ // -- CounterLength Attached Property --
+ [DynamicDependency(nameof(GetCounterLength))]
+ public static readonly DependencyProperty CounterLengthProperty = DependencyProperty.RegisterAttached(
+ "CounterLength",
+ typeof(double),
+ typeof(AutoLayout),
+ new PropertyMetadata(double.NaN, propertyChangedCallback: InvalidateMeasureCallback));
+
+ [DynamicDependency(nameof(GetCounterLength))]
+ public static void SetCounterLength(DependencyObject element, double value)
+ {
+ element.SetValue(CounterLengthProperty, value);
+ }
+
+ [DynamicDependency(nameof(SetCounterLength))]
+ public static double GetCounterLength(DependencyObject element)
+ {
+ return (double)element.GetValue(CounterLengthProperty);
+ }
+
+ private static void InvalidateMeasureCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is UIElement element)
+ {
+ element.InvalidateMeasure();
+ }
+ }
+
+ private static void InvalidateArrangeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is UIElement element)
+ {
+ element.InvalidateArrange();
+ }
+ }
+}
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.cs
index ddf60fd30..59002548c 100644
--- a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.cs
+++ b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.cs
@@ -1,606 +1,11 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-#if IS_WINUI
-using Microsoft.UI.Xaml;
+#if IS_WINUI
using Microsoft.UI.Xaml.Controls;
-using Microsoft.UI.Xaml.Markup;
-using Microsoft.UI.Xaml.Media;
#else
-using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
-using Windows.UI.Xaml.Markup;
-using Windows.UI.Xaml.Media;
#endif
-namespace Uno.Toolkit.UI
-{
- [ContentProperty(Name = nameof(Children))]
- [TemplatePart(Name = PART_RootGrid, Type = typeof(Grid))]
- public partial class AutoLayout : Control
- {
- private Grid? _grid;
- private const string PART_RootGrid = nameof(PART_RootGrid);
-
- public AutoLayout()
- {
- DefaultStyleKey = typeof(AutoLayout);
- Children = new AutoLayoutChildren();
-
- Loaded += OnLoaded;
-
- this.RegisterDisposablePropertyChangedCallback(HorizontalAlignmentProperty, (snd, e) => UpdateAutoLayout());
- this.RegisterDisposablePropertyChangedCallback(VerticalAlignmentProperty, (snd, e) => UpdateAutoLayout());
- this.RegisterDisposablePropertyChangedCallback(PaddingProperty, (snd, e) => UpdateAutoLayout());
- }
-
- protected override void OnApplyTemplate()
- {
- _grid = GetTemplateChild(PART_RootGrid) as Grid;
-
- base.OnApplyTemplate();
-
- UpdateAutoLayout();
- }
-
- private static void OnLoaded(object sender, RoutedEventArgs e)
- {
- (sender as AutoLayout)?.UpdateAutoLayout();
- }
-
- public static readonly DependencyProperty IsReverseZIndexProperty = DependencyProperty.Register(
- "IsReverseZIndex", typeof(bool), typeof(AutoLayout), new PropertyMetadata(default(bool), propertyChangedCallback: UpdateCallback));
-
- public bool IsReverseZIndex
- {
- get => (bool)GetValue(IsReverseZIndexProperty);
- set => SetValue(IsReverseZIndexProperty, value);
- }
-
- private bool IsHorizontalHug => HorizontalAlignment is not HorizontalAlignment.Stretch && Width is double.NaN;
-
- private bool IsVerticalHug => VerticalAlignment is not VerticalAlignment.Stretch && Height is double.NaN;
-
- public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
- "Orientation", typeof(Orientation), typeof(AutoLayout), new PropertyMetadata(default(Orientation), propertyChangedCallback: UpdateCallback));
-
- public Orientation Orientation
- {
- get => (Orientation)GetValue(OrientationProperty);
- set => SetValue(OrientationProperty, value);
- }
-
- public static readonly DependencyProperty SpacingProperty = DependencyProperty.Register(
- "Spacing", typeof(double), typeof(AutoLayout), new PropertyMetadata(default(double), propertyChangedCallback: UpdateCallback));
-
- public double Spacing
- {
- get => (double)GetValue(SpacingProperty);
- set => SetValue(SpacingProperty, value);
- }
-
- public static readonly DependencyProperty JustifyProperty = DependencyProperty.Register(
- "Justify", typeof(AutoLayoutJustify), typeof(AutoLayout), new PropertyMetadata(default(AutoLayoutJustify), propertyChangedCallback: UpdateCallback));
-
- public AutoLayoutJustify Justify
- {
- get => (AutoLayoutJustify)GetValue(JustifyProperty);
- set => SetValue(JustifyProperty, value);
- }
-
- public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register(
- "Children", typeof(AutoLayoutChildren), typeof(AutoLayout), new PropertyMetadata(default(AutoLayoutChildren), propertyChangedCallback: UpdateCallback));
-
- public AutoLayoutChildren Children
- {
- get => (AutoLayoutChildren)GetValue(ChildrenProperty);
- set => SetValue(ChildrenProperty, value);
- }
-
- [DynamicDependency(nameof(GetPrimaryAlignment))]
- public static readonly DependencyProperty PrimaryAlignmentProperty = DependencyProperty.RegisterAttached(
- "PrimaryAlignment",
- typeof(AutoLayoutPrimaryAlignment),
- typeof(AutoLayout),
- new PropertyMetadata(default(AutoLayoutPrimaryAlignment), propertyChangedCallback: UpdateAttachedCallback));
-
- [DynamicDependency(nameof(GetPrimaryAlignment))]
- public static void SetPrimaryAlignment(DependencyObject element, AutoLayoutPrimaryAlignment value)
- {
- element.SetValue(PrimaryAlignmentProperty, value);
- }
-
- [DynamicDependency(nameof(GetPrimaryAlignment))]
- public static AutoLayoutPrimaryAlignment GetPrimaryAlignment(DependencyObject element)
- {
- return (AutoLayoutPrimaryAlignment)element.GetValue(PrimaryAlignmentProperty);
- }
-
- [DynamicDependency(nameof(GetCounterAlignment))]
- public static readonly DependencyProperty CounterAlignmentProperty = DependencyProperty.RegisterAttached(
- "CounterAlignment",
- typeof(AutoLayoutAlignment),
- typeof(AutoLayout),
- new PropertyMetadata(AutoLayoutAlignment.Stretch, propertyChangedCallback: UpdateAttachedCallback));
-
- [DynamicDependency(nameof(GetCounterAlignment))]
- public static void SetCounterAlignment(DependencyObject element, AutoLayoutAlignment value)
- {
- element.SetValue(CounterAlignmentProperty, value);
- }
-
- [DynamicDependency(nameof(SetCounterAlignment))]
- public static AutoLayoutAlignment GetCounterAlignment(DependencyObject element)
- {
- return (AutoLayoutAlignment)element.GetValue(CounterAlignmentProperty);
- }
-
- [DynamicDependency(nameof(GetIsIndependentLayout))]
- public static readonly DependencyProperty IsIndependentLayoutProperty = DependencyProperty.RegisterAttached(
- "IsIndependentLayout", typeof(bool), typeof(AutoLayout), new PropertyMetadata(default(bool), propertyChangedCallback: UpdateAttachedCallback));
-
- [DynamicDependency(nameof(GetIsIndependentLayout))]
- public static void SetIsIndependentLayout(DependencyObject element, bool value)
- {
- element.SetValue(IsIndependentLayoutProperty, value);
- }
-
- [DynamicDependency(nameof(SetIsIndependentLayout))]
- public static bool GetIsIndependentLayout(DependencyObject element)
- {
- return (bool)element.GetValue(IsIndependentLayoutProperty);
- }
-
- [DynamicDependency(nameof(GetPrimaryLength))]
- public static readonly DependencyProperty PrimaryLengthProperty = DependencyProperty.RegisterAttached(
- "PrimaryLength", typeof(double), typeof(AutoLayout), new PropertyMetadata(default(double), propertyChangedCallback: UpdateAttachedCallback));
-
- [DynamicDependency(nameof(GetPrimaryLength))]
- public static void SetPrimaryLength(DependencyObject element, double value)
- {
- element.SetValue(PrimaryLengthProperty, value);
- }
-
- [DynamicDependency(nameof(SetPrimaryLength))]
- public static double GetPrimaryLength(DependencyObject element)
- {
- return (double)element.GetValue(PrimaryLengthProperty);
- }
-
- [DynamicDependency(nameof(GetCounterLength))]
- public static readonly DependencyProperty CounterLengthProperty = DependencyProperty.RegisterAttached(
- "CounterLength", typeof(double), typeof(AutoLayout), new PropertyMetadata(default(double), propertyChangedCallback: UpdateAttachedCallback));
-
- [DynamicDependency(nameof(GetCounterLength))]
- public static void SetCounterLength(DependencyObject element, double value)
- {
- element.SetValue(CounterLengthProperty, value);
- }
-
- [DynamicDependency(nameof(SetCounterLength))]
- public static double GetCounterLength(DependencyObject element)
- {
- return (double)element.GetValue(CounterLengthProperty);
- }
-
- public static readonly DependencyProperty PrimaryAxisAlignmentProperty = DependencyProperty.Register(
- "PrimaryAxisAlignment",
- typeof(AutoLayoutAlignment),
- typeof(AutoLayout),
- new PropertyMetadata(default(AutoLayoutAlignment), propertyChangedCallback: UpdateCallback));
-
- public AutoLayoutAlignment PrimaryAxisAlignment
- {
- get => (AutoLayoutAlignment)GetValue(PrimaryAxisAlignmentProperty);
- set => SetValue(PrimaryAxisAlignmentProperty, value);
- }
-
- private static void UpdateCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is AutoLayout al)
- {
- al.UpdateAutoLayout();
- }
- }
-
- private static void UpdateAttachedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is FrameworkElement fe)
- {
- if (fe.Parent is AutoLayout al)
- {
- al.UpdateAutoLayout();
- }
- }
- }
-
- private void UpdateAutoLayout()
- {
- if (_grid == null || IsLoaded is false)
- {
- return;
- }
-
- var children = Children;
- var childrenCount = children.Count;
-
- if (childrenCount == 0)
- {
- _grid.Children.Clear();
- _grid.RowDefinitions.Clear();
- _grid.ColumnDefinitions.Clear();
- return;
- }
-
- var padding = Padding;
- var isVertical = Orientation is Orientation.Vertical;
- var spacing = Spacing;
- var hasSpace = Spacing != 0 || Justify == AutoLayoutJustify.SpaceBetween;
- var hasSpaceBetween = Justify == AutoLayoutJustify.SpaceBetween;
- var isSpacing = spacing != 0 && !hasSpaceBetween;
-
- var childrenCounterALignment = children.Any() ? GetCounterAlignment(children.First()) : AutoLayoutAlignment.Start;
-
- bool atLeastOneChildFillAvailableSpaceInPrimaryAxis = children.Where(child => GetPrimaryAlignment(child) is AutoLayoutPrimaryAlignment.Stretch).Any();
- bool atLeastOneChildFillAvailableSpaceInCounterAxis = children.Where(child => GetCounterAlignment(child) is AutoLayoutAlignment.Stretch).Any();
-
- var childrenVerticallAlignment = Orientation == Orientation.Vertical ?
- atLeastOneChildFillAvailableSpaceInPrimaryAxis ? VerticalAlignment.Stretch : PrimaryAxisAlignment.ToVerticalAlignment() :
- atLeastOneChildFillAvailableSpaceInCounterAxis ? VerticalAlignment.Stretch : childrenCounterALignment.ToVerticalAlignment();
- var childrenHorizontalAlignment = Orientation == Orientation.Vertical ?
- atLeastOneChildFillAvailableSpaceInCounterAxis ? HorizontalAlignment.Stretch : childrenCounterALignment.ToHorizontalAlignment() :
- atLeastOneChildFillAvailableSpaceInPrimaryAxis ? HorizontalAlignment.Stretch : PrimaryAxisAlignment.ToHorizontalAlignment();
-
- var independentLayoutCount = children.Where(child => GetIsIndependentLayout(child)).Count();
- var hasIndependentLayout = independentLayoutCount > 0;
+namespace Uno.Toolkit.UI;
- var hasPadding = !padding.Equals(default(Thickness));
-
- var gridRowIndexOffSet = 0;
- var gridColumnIndexOffSet = 0;
-
- var shouldAddInBetweenRow = (hasSpaceBetween && atLeastOneChildFillAvailableSpaceInPrimaryAxis is false) || spacing > 0;
- var gridDefinitionsCount = shouldAddInBetweenRow ? ((childrenCount - independentLayoutCount) * 2) - 1 : (childrenCount - independentLayoutCount);
-
- var isPrimaryAxisAlignmentCenterOrEnd = hasIndependentLayout && PrimaryAxisAlignment is AutoLayoutAlignment.Center or AutoLayoutAlignment.End;
-
- // Inject & Move elements in the inner grid
- for (var i = 0; i < childrenCount; i++)
- {
- var child = children[IsReverseZIndex ? childrenCount-i-1 : i];
-
- if (child.Parent != null)
- {
- // We need to move instead of adding the element
- var currentIndexInGrid = _grid.Children.IndexOf(child);
- if (currentIndexInGrid == i)
- {
- // nothing to do, already at right place
- continue;
- }
-
- _grid.Children.Move((uint)currentIndexInGrid, (uint)i);
- }
- else
- {
- if (_grid.Children.Count > i)
- {
- _grid.Children.Insert(i, child);
- }
- else
- {
- _grid.Children.Add(child);
- }
- }
- }
-
- // remove any extra children on grid
- while (childrenCount < _grid.Children.Count)
- {
- _grid.Children.RemoveAt(_grid.Children.Count - 1);
- }
-
- // Set inter-element spacing
- if (hasSpace)
- {
- _grid.ClearValue(Grid.ColumnSpacingProperty);
- _grid.ClearValue(Grid.RowSpacingProperty);
- }
-
- if (isVertical)
- {
- _grid.ColumnDefinitions.Clear();
- _grid.RowDefinitions.Clear();
- // Ensure definitions is of right size
- while (_grid.RowDefinitions.Count < gridDefinitionsCount)
- {
- _grid.RowDefinitions.Add(new RowDefinition());
- }
- }
- else
- {
- _grid.RowDefinitions.Clear();
- _grid.ColumnDefinitions.Clear();
-
- // Ensure definitions is of right size
- while (_grid.ColumnDefinitions.Count < gridDefinitionsCount)
- {
- _grid.ColumnDefinitions.Add(new ColumnDefinition());
- }
- }
-
- if (hasPadding)
- {
- _grid.Padding = new Thickness(0);
-
- if (IsVerticalHug || childrenVerticallAlignment is VerticalAlignment.Top or VerticalAlignment.Stretch || hasSpaceBetween)
- {
- var topPaddingSize = spacing < 0 ? padding.Top - spacing : padding.Top;
- _grid.RowDefinitions.Insert(0, new RowDefinition() { Height = new GridLength(topPaddingSize), MaxHeight = topPaddingSize });
- gridRowIndexOffSet += 1;
- }
-
- if (IsHorizontalHug || childrenHorizontalAlignment is HorizontalAlignment.Left or HorizontalAlignment.Stretch || hasSpaceBetween)
- {
- var firstColumnWidthSize = spacing < 0 ? padding.Left - spacing : padding.Left;
- _grid.ColumnDefinitions.Insert(0, new ColumnDefinition() { Width = new GridLength(firstColumnWidthSize), MaxWidth = firstColumnWidthSize });
- gridColumnIndexOffSet += 1;
- }
- }
-
- if (isVertical)
- {
-
- if (hasPadding)
- {
- _grid.ColumnDefinitions.Insert(gridColumnIndexOffSet, new ColumnDefinition() { Width = new GridLength(1, IsHorizontalHug ? GridUnitType.Auto : GridUnitType.Star) });
- }
-
- var rawChildIndex = 0;
-
- if (hasSpaceBetween is false && atLeastOneChildFillAvailableSpaceInPrimaryAxis is false && isPrimaryAxisAlignmentCenterOrEnd)
- {
- _grid.RowDefinitions.Insert(gridRowIndexOffSet, new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRowIndexOffSet += 1;
- }
- // Process children
- for (var i = 0; i < childrenCount; i++)
- {
- var child = children[i];
-
- if (GetIsIndependentLayout(child))
- {
- Grid.SetRow(child, 0);
- // two extra count for the span. Depending of the PrimaryAxisAlignment we need to add two extra grid
- Grid.SetRowSpan(child, gridDefinitionsCount + 4);
- Grid.SetColumnSpan(child, gridDefinitionsCount + 4);
- continue;
- }
-
- var gridIndex = shouldAddInBetweenRow ? rawChildIndex * 2 : rawChildIndex;
- //We add a grid. we need to adjust the index.
- var adjustGridIndex = gridIndex + gridRowIndexOffSet;
- Grid.SetRow(child, adjustGridIndex);
- var gridDefinition = _grid.RowDefinitions[adjustGridIndex];
-
- // Get relative alignment or default if not set + set on child element
- var primaryAlignment = GetPrimaryAlignment(child);
- if (primaryAlignment is AutoLayoutPrimaryAlignment.Stretch)
- {
- gridDefinition.Height = new GridLength(1, GridUnitType.Star);
- child.VerticalAlignment = VerticalAlignment.Stretch;
- }
- else if (GetPrimaryLength(child) is var height and > 0)
- {
- gridDefinition.Height = new GridLength(height);
- child.VerticalAlignment = VerticalAlignment.Stretch;
- }
- else
- {
- gridDefinition.Height = GridLength.Auto;
- child.VerticalAlignment = VerticalAlignment.Top;
- }
-
- var counterAlignment = GetCounterAlignment(child).ToHorizontalAlignment();
-
- child.HorizontalAlignment = counterAlignment;
-
- if (gridColumnIndexOffSet > 0)
- {
- Grid.SetColumn(child, gridColumnIndexOffSet);
- }
-
- if (GetCounterLength(child) is var width and > 0)
- {
- child.Width = width;
- }
- rawChildIndex++;
- }
-
- // Process "space between"
- if (hasSpace)
- {
- if (isSpacing)
- {
- if (spacing < 0)
- {
- _grid.RowSpacing = spacing;
- _grid.ClearValue(Grid.ColumnSpacingProperty);
- }
- else
- {
- for (var i = 1 + gridRowIndexOffSet; i < gridDefinitionsCount + gridRowIndexOffSet; i += 2)
- {
- _grid.RowDefinitions[i].Height = new GridLength(spacing);
- }
- }
- }
- else if (hasSpaceBetween)
- {
- for (var i = 1 + gridRowIndexOffSet; i < gridDefinitionsCount + gridRowIndexOffSet; i += 2)
- {
- _grid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Star);
- }
- }
- }
-
- if (hasIndependentLayout && atLeastOneChildFillAvailableSpaceInPrimaryAxis is not true && PrimaryAxisAlignment != AutoLayoutAlignment.End && !hasSpaceBetween)
- {
- //We need to make sure that the independent layout can span all across his parent
- _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- }
-
- var paddingCondition = atLeastOneChildFillAvailableSpaceInPrimaryAxis || PrimaryAxisAlignment is not AutoLayoutAlignment.Start || IsVerticalHug;
-
- }
- else // Horizontal
- {
- if (hasPadding)
- {
- _grid.RowDefinitions.Insert(gridRowIndexOffSet ,new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- }
-
- var rawChildIndex = 0;
-
- if (hasSpaceBetween is false && atLeastOneChildFillAvailableSpaceInPrimaryAxis is false && isPrimaryAxisAlignmentCenterOrEnd )
- {
- _grid.ColumnDefinitions.Insert(gridColumnIndexOffSet, new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
- gridColumnIndexOffSet += 1;
- }
-
- // Process children
- for (var i = 0; i < childrenCount; i++)
- {
- var child = children[i];
- if (GetIsIndependentLayout(child))
- {
- Grid.SetColumn(child, 0);
- // two extra count for the span. Depending of the PrimaryAxisAlignment we need to add two extra grid
- Grid.SetColumnSpan(child, gridDefinitionsCount + 4);
- Grid.SetRowSpan(child, gridDefinitionsCount + 4);
- continue;
- }
-
- var gridIndex = shouldAddInBetweenRow ? rawChildIndex * 2 : rawChildIndex;
- //We add a grid. we need to adjust the index.
- var adjustGridIndex = gridIndex + gridColumnIndexOffSet;
- var gridDefinition = _grid.ColumnDefinitions[adjustGridIndex];
- Grid.SetColumn(child, adjustGridIndex);
-
- // Get relative alignment or default if not set + set on child element
- var primaryAlignment = GetPrimaryAlignment(child);
- if (primaryAlignment is AutoLayoutPrimaryAlignment.Stretch)
- {
- gridDefinition.Width = new GridLength(1, GridUnitType.Star);
- child.HorizontalAlignment = HorizontalAlignment.Stretch;
- }
- else if (GetPrimaryLength(child) is var width and > 0)
- {
- gridDefinition.Width = new GridLength(width);
- child.HorizontalAlignment = HorizontalAlignment.Stretch;
- }
- else
- {
- gridDefinition.Width = GridLength.Auto;
- child.HorizontalAlignment = HorizontalAlignment.Left;
- }
-
- var counterAlignment = GetCounterAlignment(child).ToVerticalAlignment();
-
- child.VerticalAlignment = counterAlignment;
-
-
- if (gridRowIndexOffSet > 0)
- {
- Grid.SetRow(child, 1);
- }
-
- if (GetCounterLength(child) is var height and > 0)
- {
- child.Height = height;
- }
- rawChildIndex++;
- }
-
- // Process "space between"
- if (hasSpace)
- {
- if (isSpacing)
- {
-
- if (spacing < 0)
- {
- _grid.ColumnSpacing = spacing;
- _grid.ClearValue(Grid.RowSpacingProperty);
- }
- else
- {
- for (var i = 1 + gridColumnIndexOffSet; i < gridDefinitionsCount + gridColumnIndexOffSet; i += 2)
- {
- _grid.ColumnDefinitions[i].Width = new GridLength(spacing);
- }
- }
- }
- else if (hasSpaceBetween)
- {
- for (var i = 1 + gridColumnIndexOffSet; i < gridDefinitionsCount + gridColumnIndexOffSet; i += 2)
- {
- _grid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star);
- }
- }
- }
-
- if (hasIndependentLayout && atLeastOneChildFillAvailableSpaceInPrimaryAxis is not true && PrimaryAxisAlignment != AutoLayoutAlignment.End && !hasSpaceBetween)
- {
- //We need to make sure that the independent layout can span all across his parent
- _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
- }
-
- var paddingCondition = atLeastOneChildFillAvailableSpaceInPrimaryAxis || PrimaryAxisAlignment is not AutoLayoutAlignment.Start || IsHorizontalHug;
- }
-
- if (hasPadding)
- {
- if (IsVerticalHug || childrenVerticallAlignment is VerticalAlignment.Bottom or VerticalAlignment.Stretch || hasSpaceBetween)
- {
- var bottomPaddingSize = spacing < 0 ? padding.Bottom - spacing : padding.Bottom;
- _grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(bottomPaddingSize), MaxHeight = bottomPaddingSize });
- }
-
- if (IsHorizontalHug || childrenHorizontalAlignment is HorizontalAlignment.Right or HorizontalAlignment.Stretch || hasSpaceBetween)
- {
- var rightPaddingsize = spacing < 0 ? padding.Right - spacing : padding.Right;
- _grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(rightPaddingsize), MaxWidth = rightPaddingsize });
- }
- }
-
- var shouldUsePrimaryAxisAlignment = atLeastOneChildFillAvailableSpaceInPrimaryAxis || hasSpaceBetween || hasIndependentLayout;
-
- // Set container alignments
- if (isVertical)
- {
- if (shouldUsePrimaryAxisAlignment)
- {
- _grid.ClearValue(VerticalAlignmentProperty);
- }
- else
- {
- _grid.VerticalAlignment = PrimaryAxisAlignment.ToVerticalAlignment();
- }
- _grid.ClearValue(HorizontalAlignmentProperty);
- }
- else
- {
- if (shouldUsePrimaryAxisAlignment)
- {
- _grid.ClearValue(HorizontalAlignmentProperty);
- }
- else
- {
- _grid.HorizontalAlignment = PrimaryAxisAlignment.ToHorizontalAlignment();
- }
- _grid.ClearValue(VerticalAlignmentProperty);
- }
- }
- }
+public sealed partial class AutoLayout : RelativePanel
+{
}
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.xaml b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.xaml
deleted file mode 100644
index 2768d8430..000000000
--- a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.xaml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutChildren.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutChildren.cs
deleted file mode 100644
index 6fb15b6b3..000000000
--- a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutChildren.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Collections.Generic;
-
-#if IS_WINUI
-using Microsoft.UI.Xaml;
-#else
-using Windows.UI.Xaml;
-#endif
-
-namespace Uno.Toolkit.UI
-{
- public partial class AutoLayoutChildren : List
- {
- public AutoLayoutChildren()
- : base(4)
- {
- }
- }
-}
diff --git a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutExtensions.cs b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutExtensions.cs
index d686d21c5..e6954b2a2 100644
--- a/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutExtensions.cs
+++ b/src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayoutExtensions.cs
@@ -1,15 +1,121 @@
using System;
+using System.Runtime.CompilerServices;
+using Windows.Foundation;
#if IS_WINUI
using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
#else
using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
#endif
namespace Uno.Toolkit.UI
{
internal static class AutoLayoutExtensions
{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetLength(this Size size, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => size.Width,
+ Orientation.Vertical => size.Height,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetLength(this Thickness thickness, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => thickness.Left + thickness.Right,
+ Orientation.Vertical => thickness.Top + thickness.Bottom,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetLength(this FrameworkElement frameworkElement, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => frameworkElement.Width,
+ Orientation.Vertical => frameworkElement.Height,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetStartLength(this Thickness thickness, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => thickness.Left,
+ Orientation.Vertical => thickness.Top,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetCounterStartLength(this Thickness thickness, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => thickness.Top,
+ Orientation.Vertical => thickness.Left,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetEndLength(this Thickness thickness, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => thickness.Right,
+ Orientation.Vertical => thickness.Bottom,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetMinLength(this FrameworkElement frameworkElement, Orientation orientation)
+ {
+ return orientation switch
+ {
+ Orientation.Horizontal => frameworkElement.MinWidth,
+ Orientation.Vertical => frameworkElement.MinHeight,
+ _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null),
+ };
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetCounterLength(this Size size, Orientation orientation)
+ {
+ return size.GetLength(orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetCounterLength(this Thickness thickness, Orientation orientation)
+ {
+ return thickness.GetLength(orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetCounterLength(this FrameworkElement frameworkElement, Orientation orientation)
+ {
+ return frameworkElement.GetLength(orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static double GetMinCounterLength(this FrameworkElement frameworkElement, Orientation orientation)
+ {
+ return frameworkElement.GetMinLength(orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal);
+ }
+
internal static HorizontalAlignment ToHorizontalAlignment(this AutoLayoutAlignment layoutAlignment)
{
return layoutAlignment switch