Skip to content

Commit

Permalink
feat: Add CounterAxisAlignment
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert-Louis committed Jan 12, 2024
1 parent 448c708 commit 1f09dd1
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 16 deletions.
5 changes: 3 additions & 2 deletions doc/controls/AutoLayoutControl.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ Property|Type|Description
Orientation | `Orientation` | Gets or sets the dimension by which the items are stacked
Spacing | `double` | Gets or sets a uniform distance (in pixels) between stacked items. It is applied in the direction of the `AutoLayout`'s Orientation
Justify | `AutoLayoutJustify` | Gets or sets the value to determine how items are justified within the container. Options are `Stack` or `SpaceBetween`. Note: if a child has its `PrimaryAlignment` set to `Stretch`, it will default to `Stack`
PrimaryAxisAlignment | `AutoLayoutAlignment` | Gets or sets the alignment characteristics that are applied to the content, based on the value of the `Orientation` property. Options are `Start`, `Center`, and `End`. The default is `Start`
PrimaryAxisAlignment | `AutoLayoutAlignment` | Gets or sets the alignment characteristics that are applied to the content, based on the value of the `Orientation` property. Options are `Start`, `Center`, and `End`. The default is `Start`.
CounterAxisAlignment | `AutoLayoutAlignment` | Gets or sets the alignment characteristics that are applied to the content, based on the inverse value of the `Orientation` property. Options are `Start`, `Center`, and `End`. The default is `Start`. If already set in `CounterAlignment` CounterAlignment will have priority.
IsReverseZIndex | `bool` | Gets or sets whether or not the ZIndex of the children should be reversed. The default is `false`
Padding | `Thickness` | **WARNING:** Padding for `AutoLayout` behaves the same as it does within the Figma Plugin: The anchor points determine which sides of the Padding will be taken into consideration. For example, items that are aligned to the Right and Top positions will only take the `Tickness.Right` and `Thickness.Top` values of `Padding` into consideration

Expand All @@ -54,7 +55,7 @@ Padding | `Thickness` | **WARNING:** Padding for `AutoLayout` behaves the same a
Property|Type|Description
-|-|-
PrimaryAlignment|`AutoLayoutPrimaryAlignment`| Gets or sets the alignment characteristics that are applied to this element when it is composed in an `AutoLayout` parent, based on the value of the `Orientation` property. Options are `Auto` and `Stretch`. The default is `Auto`
CounterAlignment|`AutoLayoutAlignment`| Gets or sets the counter-alignment characteristics that are applied to this element when it is composed in an `AutoLayout` parent, based on the inverse value of the `Orientation` property. Options are `Start`, `Center`, `End`, and `Stretch`. The default is `Start`
CounterAlignment|`AutoLayoutAlignment`| Gets or sets the counter-alignment characteristics that are applied to this element when it is composed in an `AutoLayout` parent, based on the inverse value of the `Orientation` property. Options are `Start`, `Center`, `End`, and `Stretch`. The default is `Start`. If already set in `CounterAxisAlignment` CounterAlignment will have priority.
PrimaryLength|`double`| Gets or sets the height or width of the child depending on the `Orientation`. If `Height` or `Width` are already set they will have priority.
CounterLength|`double`| Gets or sets the height or width of the child depending on the inverse of `Orientation`. If `Height` or `Width` are already set they will have priority.
IsIndependentLayout | `bool` | **WARNING:** Should not be used with other Attached Properties. Gets or sets whether the element is independent of the `AutoLayout` positioning. Reflects the `Absolute Position` option from Figma
117 changes: 117 additions & 0 deletions src/Uno.Toolkit.RuntimeTests/Tests/AutoLayoutTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,84 @@ public async Task When_Padding(bool isStretch, Orientation orientation, AutoLayo
Assert.AreEqual(rec2expected, border1Transform!.X);
}

[TestMethod]
[RequiresFullWindow]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.Center, 100, 25)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.Center, 25, 25)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.Start, 25, 100)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.Start, 100, 25)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.Center, 25, 25)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.Center, 25, 100)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.Center, 100, 25)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.Center, 100, 25)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.Center, 100, 25)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.Center, 25, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.Center, 25, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.Center, 25, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.Start, 100, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.Start, 100, 100)]

// Issue with TransformToVisual not having the same result in iOS and Android and WinIU uno issue #11774
//https://github.com/unoplatform/uno/issues/11774
#if !(__IOS__ || __ANDROID__)
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.End, 100, -50)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.End, 25, -50)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.End, -50, -50)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.Center, -50, 25)]
[DataRow(false, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.Start, -50, 100)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.End, -50, 25)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.Start, 100, -50)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.Center, 25, -50)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.End, -50, -50)]
[DataRow(false, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.End, -50, 100)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Start, AutoLayoutAlignment.End, 100, -50)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.Center, AutoLayoutAlignment.End, 100, -50)]
[DataRow(true, Orientation.Vertical, AutoLayoutAlignment.End, AutoLayoutAlignment.End, 100, -50)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Start, AutoLayoutAlignment.End, -50, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.Center, AutoLayoutAlignment.End, -50, 100)]
[DataRow(true, Orientation.Horizontal, AutoLayoutAlignment.End, AutoLayoutAlignment.End, -50, 100)]
#endif

public async Task When_Padding_CounterAxis(bool isStretch, Orientation orientation, AutoLayoutAlignment primaryAxisAlignment, AutoLayoutAlignment counterAlignment, double rec1expected, double rec2expected)
{
var SUT = new AutoLayout()
{
Orientation = orientation,
Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)),
Padding = new Thickness(100),
Width = 400,
Height = 400,
PrimaryAxisAlignment = primaryAxisAlignment,
CounterAxisAlignment = counterAlignment,
};
var border1 = new Border()
{
Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255)),
Width = orientation is Orientation.Horizontal && isStretch ? double.NaN : 350,
Height = orientation is Orientation.Vertical && isStretch ? double.NaN : 350,
};

if (isStretch)
{
AutoLayout.SetPrimaryAlignment(border1, AutoLayoutPrimaryAlignment.Stretch);
}

SUT.Children.Add(border1);

await UnitTestUIContentHelperEx.SetContentAndWait(SUT);

var border1Transform = border1.TransformToVisual(SUT).TransformPoint(new Windows.Foundation.Point(0, 0));

Assert.AreEqual(rec1expected, border1Transform!.Y);
Assert.AreEqual(rec2expected, border1Transform!.X);
}

[TestMethod]
[RequiresFullWindow]
[DataRow(Orientation.Vertical, 10, 340, 280)]
Expand Down Expand Up @@ -590,6 +668,7 @@ public async Task When_Hug_With_Padding()
var autolayout = new AutoLayout()
{
PrimaryAxisAlignment = AutoLayoutAlignment.Center,
Background = new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)),
};

var textBlock = new TextBlock()
Expand All @@ -612,4 +691,42 @@ public async Task When_Hug_With_Padding()

Assert.AreEqual(Math.Ceiling(autoLayoutAcutalWidth - textBlockCenter), Math.Ceiling(textBlockTransform!.X));
}

[TestMethod]
[RequiresFullWindow]
public async Task When_Hug_With_Padding_CounterAxis()
{
var SUT = new AutoLayout()
{
PrimaryAxisAlignment = AutoLayoutAlignment.Center,
CounterAxisAlignment = AutoLayoutAlignment.Center,
Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255)),
Width = 300,
Height = 300,
Padding = new Thickness(50)
};

var autolayout = new AutoLayout()
{
PrimaryAxisAlignment = AutoLayoutAlignment.Center,
Background = new SolidColorBrush(Color.FromArgb(255, 0, 255, 255)),
};

var textBlock = new TextBlock()
{
Text = "should be center",
};

SUT.Children.Add(autolayout);
autolayout.Children.Add(textBlock);

await UnitTestUIContentHelperEx.SetContentAndWait(SUT);

var textBlockTransform = textBlock.TransformToVisual(SUT).TransformPoint(new Windows.Foundation.Point(0, 0));

var autoLayoutAcutalWidth = SUT.ActualWidth / 2;
var textBlockCenter = textBlock.ActualWidth / 2;

Assert.AreEqual(Math.Ceiling(autoLayoutAcutalWidth - textBlockCenter), Math.Ceiling(textBlockTransform!.X));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,13 @@ protected override Size ArrangeOverride(Size finalSize)
EnsureZeroFloor(ref childLength);

// Calculate the position of the child by applying the alignment instructions
var counterAlignment = GetCounterAlignment(child.Element);
var isStretch = counterAlignment is AutoLayoutAlignment.Stretch || (child.Element is FrameworkElement fe && fe.GetCounterLength(orientation) is double.NaN) && child.Role is not AutoLayoutRole.Hug;
var counterA = child.Element.ReadLocalValue(CounterAlignmentProperty);
var isCounterAlignemntDefined = counterA != DependencyProperty.UnsetValue;

var childCounterAlignment = GetCounterAlignment(child.Element);
var isStretch = childCounterAlignment is AutoLayoutAlignment.Stretch;
var useCounterAxisAlignement = isStretch || !isCounterAlignemntDefined;
var counterAlignment = useCounterAxisAlignement ? this.CounterAxisAlignment : childCounterAlignment;
var haveCounterStartPadding = isStretch || counterAlignment is AutoLayoutAlignment.Start;
var counterStartPadding = haveCounterStartPadding ? (isHorizontal ? padding.Top : padding.Left) : 0;

Expand Down Expand Up @@ -287,7 +292,7 @@ private static void ApplyMinMaxValues(UIElement element, Orientation orientation

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double ComputeCounterAlignmentOffset(
AutoLayoutAlignment counterAlignment,
AutoLayoutAlignment? counterAlignment,
double childCounterLength,
double availableCounterLength,
Thickness padding,
Expand All @@ -301,9 +306,10 @@ private static void ApplyMinMaxValues(UIElement element, Orientation orientation
var alignmentOffsetSize = availableCounterLength - (counterStartPadding + counterEndPadding);
var relativeOffset = Math.Abs(alignmentOffsetSize) / 2;

return alignmentOffsetSize > 0 ?
var test = alignmentOffsetSize > 0 ?
(relativeOffset + counterStartPadding) - (childCounterLength / 2) :
relativeOffset + (availableCounterLength - counterEndPadding) - (childCounterLength / 2);
return test;
case AutoLayoutAlignment.End:
return availableCounterLength - childCounterLength;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ private void ApplyMinMaxValues(ref Size desiredSize)
fixedSize, // The available size for the child is its defined fixed size
availableCounterSize,
ref desiredCounterSize,
counterPaddingSize);
counterPaddingSize,
CounterAxisAlignment);

Decrement(ref remainingSize, fixedSize);
}
Expand Down Expand Up @@ -255,7 +256,8 @@ private void ApplyMinMaxValues(ref Size desiredSize)
double.PositiveInfinity, // We don't want the child to limit its own desired size to available one
availableCounterSize,
ref desiredCounterSize,
counterPaddingSize);
counterPaddingSize,
CounterAxisAlignment);

calculatedChild.MeasuredLength = desiredSize;

Expand Down Expand Up @@ -305,7 +307,7 @@ private void ApplyMinMaxValues(ref Size desiredSize)
continue;
}

MeasureChild(child.Element, orientation, filledSize, availableCounterSize, ref desiredCounterSize, counterPaddingSize);
MeasureChild(child.Element, orientation, filledSize, availableCounterSize, ref desiredCounterSize, counterPaddingSize, CounterAxisAlignment);

child.MeasuredLength = child.Element is FrameworkElement fe ? Math.Min(fe.GetMaxLength(orientation), double.MaxValue) : double.MaxValue;
}
Expand All @@ -319,15 +321,18 @@ private void ApplyMinMaxValues(ref Size desiredSize)
double availableSize,
double availableCounterSize,
ref double desiredCounterSize,
double counterPaddingSize)
double counterPaddingSize,
AutoLayoutAlignment counterAxisAlignment)
{
var isOrientationHorizontal = orientation is Orientation.Horizontal;
var isPrimaryAlignmentStretch = GetPrimaryAlignment(child) is AutoLayoutPrimaryAlignment.Stretch;
var isCounterAlignmentStretch = GetCounterAlignment(child) is AutoLayoutAlignment.Stretch;

if (child as FrameworkElement is { } frameworkElement)
{
UpdateCounterAlignment(ref frameworkElement, isOrientationHorizontal, isPrimaryAlignmentStretch, GetCounterAlignment(child));
var test = frameworkElement.ReadLocalValue(CounterAlignmentProperty) == DependencyProperty.UnsetValue ? counterAxisAlignment : GetCounterAlignment(child);

UpdateCounterAlignment(ref frameworkElement, isOrientationHorizontal, isPrimaryAlignmentStretch, test);

var isStretch = isOrientationHorizontal ?
frameworkElement.VerticalAlignment is VerticalAlignment.Stretch :
Expand Down
23 changes: 18 additions & 5 deletions src/Uno.Toolkit.UI/Controls/AutoLayout/AutoLayout.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ public AutoLayoutAlignment PrimaryAxisAlignment
set => SetValue(PrimaryAxisAlignmentProperty, value);
}

// -- PrimaryAxisAlignment DependencyProperty --
public static readonly DependencyProperty CounterAxisAlignmentProperty = DependencyProperty.Register(
nameof(CounterAxisAlignment),
typeof(AutoLayoutAlignment),
typeof(AutoLayout),
new PropertyMetadata(default(AutoLayoutAlignment), propertyChangedCallback: InvalidateArrangeCallback));

public AutoLayoutAlignment CounterAxisAlignment
{
get => (AutoLayoutAlignment)GetValue(CounterAxisAlignmentProperty);
set => SetValue(CounterAxisAlignmentProperty, value);
}

// -- PrimaryAlignment Attached Property --
[DynamicDependency(nameof(GetPrimaryAlignment))]
public static readonly DependencyProperty PrimaryAlignmentProperty = DependencyProperty.RegisterAttached(
Expand All @@ -98,20 +111,20 @@ public static AutoLayoutPrimaryAlignment GetPrimaryAlignment(DependencyObject el
}

// -- CounterAlignment Attached Property --
[DynamicDependency(nameof(GetCounterAlignment))]
//[DynamicDependency(nameof(GetCounterAlignment))]
public static readonly DependencyProperty CounterAlignmentProperty = DependencyProperty.RegisterAttached(
"CounterAlignment",
"CounterAlignment",
typeof(AutoLayoutAlignment),
typeof(AutoLayout),
new PropertyMetadata(AutoLayoutAlignment.Stretch, propertyChangedCallback: InvalidateArrangeCallback));
new PropertyMetadata(default(AutoLayoutAlignment), propertyChangedCallback: InvalidateArrangeCallback));

[DynamicDependency(nameof(GetCounterAlignment))]
//[DynamicDependency(nameof(GetCounterAlignment))]
public static void SetCounterAlignment(DependencyObject element, AutoLayoutAlignment value)
{
element.SetValue(CounterAlignmentProperty, value);
}

[DynamicDependency(nameof(SetCounterAlignment))]
//[DynamicDependency(nameof(SetCounterAlignment))]
public static AutoLayoutAlignment GetCounterAlignment(DependencyObject element)
{
return (AutoLayoutAlignment)element.GetValue(CounterAlignmentProperty);
Expand Down

0 comments on commit 1f09dd1

Please sign in to comment.