diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs
index f425ae3f01..002c5ea3f2 100644
--- a/src/Avalonia.Controls/Border.cs
+++ b/src/Avalonia.Controls/Border.cs
@@ -108,18 +108,7 @@ namespace Avalonia.Controls
/// The desired size of the control.
protected override Size MeasureOverride(Size availableSize)
{
- var child = Child;
- var padding = Padding + new Thickness(BorderThickness);
-
- if (child != null)
- {
- child.Measure(availableSize.Deflate(padding));
- return child.DesiredSize.Inflate(padding);
- }
- else
- {
- return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
- }
+ return MeasureOverrideImpl(availableSize, Child, Padding, BorderThickness);
}
///
@@ -129,15 +118,32 @@ namespace Avalonia.Controls
/// The space taken.
protected override Size ArrangeOverride(Size finalSize)
{
- var child = Child;
-
- if (child != null)
+ if (Child != null)
{
var padding = Padding + new Thickness(BorderThickness);
- child.Arrange(new Rect(finalSize).Deflate(padding));
+ Child.Arrange(new Rect(finalSize).Deflate(padding));
}
return finalSize;
}
+
+ internal static Size MeasureOverrideImpl(
+ Size availableSize,
+ IControl child,
+ Thickness padding,
+ double borderThickness)
+ {
+ padding += new Thickness(borderThickness);
+
+ if (child != null)
+ {
+ child.Measure(availableSize.Deflate(padding));
+ return child.DesiredSize.Inflate(padding);
+ }
+ else
+ {
+ return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index a97fdf8784..d0a438cc2b 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -2,14 +2,12 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
-using System.Reactive.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
-using Avalonia.VisualTree;
namespace Avalonia.Controls.Presenters
{
@@ -340,94 +338,121 @@ namespace Avalonia.Controls.Presenters
///
protected override Size MeasureOverride(Size availableSize)
{
- var child = Child;
- var padding = Padding + new Thickness(BorderThickness);
+ return Border.MeasureOverrideImpl(availableSize, Child, Padding, BorderThickness);
+ }
+
+ ///
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ return ArrangeOverrideImpl(finalSize, new Vector());
+ }
+
+ ///
+ /// Called when the property changes.
+ ///
+ /// The event args.
+ private void ContentChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ _createdChild = false;
- if (child != null)
+ if (((ILogical)this).IsAttachedToLogicalTree)
{
- child.Measure(availableSize.Deflate(padding));
- return child.DesiredSize.Inflate(padding);
+ UpdateChild();
}
- else
+ else if (Child != null)
{
- return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
+ VisualChildren.Remove(Child);
+ LogicalChildren.Remove(Child);
+ Child = null;
+ _dataTemplate = null;
}
+
+ InvalidateMeasure();
}
- ///
- protected override Size ArrangeOverride(Size finalSize)
+ internal Size ArrangeOverrideImpl(Size finalSize, Vector offset)
{
- var child = Child;
-
- if (child != null)
+ if (Child != null)
{
- var padding = Padding + new Thickness(BorderThickness);
- var sizeMinusPadding = finalSize.Deflate(padding);
- var size = sizeMinusPadding;
- var horizontalAlignment = HorizontalContentAlignment;
- var verticalAlignment = VerticalContentAlignment;
- var originX = padding.Left;
- var originY = padding.Top;
-
- if (horizontalAlignment != HorizontalAlignment.Stretch)
+ var padding = Padding;
+ var borderThickness = BorderThickness;
+ var horizontalContentAlignment = HorizontalContentAlignment;
+ var verticalContentAlignment = VerticalContentAlignment;
+ var useLayoutRounding = UseLayoutRounding;
+ var availableSizeMinusMargins = new Size(
+ Math.Max(0, finalSize.Width - padding.Left - padding.Right - borderThickness),
+ Math.Max(0, finalSize.Height - padding.Top - padding.Bottom - borderThickness));
+ var size = availableSizeMinusMargins;
+ var scale = GetLayoutScale();
+ var originX = offset.X + padding.Left + borderThickness;
+ var originY = offset.Y + padding.Top + borderThickness;
+
+ if (horizontalContentAlignment != HorizontalAlignment.Stretch)
{
- size = size.WithWidth(child.DesiredSize.Width);
+ size = size.WithWidth(Math.Min(size.Width, DesiredSize.Width - padding.Left - padding.Right));
}
- if (verticalAlignment != VerticalAlignment.Stretch)
+ if (verticalContentAlignment != VerticalAlignment.Stretch)
{
- size = size.WithHeight(child.DesiredSize.Height);
+ size = size.WithHeight(Math.Min(size.Height, DesiredSize.Height - padding.Top - padding.Bottom));
}
- switch (horizontalAlignment)
+ size = LayoutHelper.ApplyLayoutConstraints(Child, size);
+
+ if (useLayoutRounding)
+ {
+ size = new Size(
+ Math.Ceiling(size.Width * scale) / scale,
+ Math.Ceiling(size.Height * scale) / scale);
+ availableSizeMinusMargins = new Size(
+ Math.Ceiling(availableSizeMinusMargins.Width * scale) / scale,
+ Math.Ceiling(availableSizeMinusMargins.Height * scale) / scale);
+ }
+
+ switch (horizontalContentAlignment)
{
- case HorizontalAlignment.Stretch:
case HorizontalAlignment.Center:
- originX += (sizeMinusPadding.Width - size.Width) / 2;
+ case HorizontalAlignment.Stretch:
+ originX += (availableSizeMinusMargins.Width - size.Width) / 2;
break;
case HorizontalAlignment.Right:
- originX = size.Width - child.DesiredSize.Width;
+ originX += availableSizeMinusMargins.Width - size.Width;
break;
}
- switch (verticalAlignment)
+ switch (verticalContentAlignment)
{
- case VerticalAlignment.Stretch:
case VerticalAlignment.Center:
- originY += (sizeMinusPadding.Height - size.Height) / 2;
+ case VerticalAlignment.Stretch:
+ originY += (availableSizeMinusMargins.Height - size.Height) / 2;
break;
case VerticalAlignment.Bottom:
- originY = size.Height - child.DesiredSize.Height;
+ originY += availableSizeMinusMargins.Height - size.Height;
break;
}
- child.Arrange(new Rect(originX, originY, size.Width, size.Height));
+ if (useLayoutRounding)
+ {
+ originX = Math.Floor(originX * scale) / scale;
+ originY = Math.Floor(originY * scale) / scale;
+ }
+
+ Child.Arrange(new Rect(originX, originY, size.Width, size.Height));
}
return finalSize;
}
- ///
- /// Called when the property changes.
- ///
- /// The event args.
- private void ContentChanged(AvaloniaPropertyChangedEventArgs e)
+ private double GetLayoutScale()
{
- _createdChild = false;
+ var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;
- if (((ILogical)this).IsAttachedToLogicalTree)
+ if (result == 0 || double.IsNaN(result) || double.IsInfinity(result))
{
- UpdateChild();
- }
- else if (Child != null)
- {
- VisualChildren.Remove(Child);
- LogicalChildren.Remove(Child);
- Child = null;
- _dataTemplate = null;
+ throw new Exception($"Invalid LayoutScaling returned from {VisualRoot.GetType()}");
}
- InvalidateMeasure();
+ return result;
}
private void TemplatedParentChanged(AvaloniaPropertyChangedEventArgs e)
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index d982ed4ac4..75b1602919 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -48,7 +48,6 @@ namespace Avalonia.Controls.Presenters
ScrollViewer.CanScrollHorizontallyProperty.AddOwner();
private Size _extent;
- private Size _measuredExtent;
private Vector _offset;
private IDisposable _logicalScrollSubscription;
private Size _viewport;
@@ -176,63 +175,36 @@ namespace Avalonia.Controls.Presenters
///
protected override Size MeasureOverride(Size availableSize)
{
- var child = Child;
-
- if (child != null)
+ if (_logicalScrollSubscription != null || Child == null)
{
- var measureSize = availableSize;
-
- if (_logicalScrollSubscription == null)
- {
- measureSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
+ return base.MeasureOverride(availableSize);
+ }
- if (!CanScrollHorizontally)
- {
- measureSize = measureSize.WithWidth(availableSize.Width);
- }
- }
+ var constraint = new Size(
+ CanScrollHorizontally ? double.PositiveInfinity : availableSize.Width,
+ double.PositiveInfinity);
- child.Measure(measureSize);
- var size = child.DesiredSize;
- _measuredExtent = size;
- return size.Constrain(availableSize);
- }
- else
- {
- return Extent = new Size();
- }
+ Child.Measure(constraint);
+ return Child.DesiredSize.Constrain(availableSize);
}
///
protected override Size ArrangeOverride(Size finalSize)
{
- var child = this.GetVisualChildren().SingleOrDefault() as ILayoutable;
- var logicalScroll = _logicalScrollSubscription != null;
-
- if (!logicalScroll)
- {
- Viewport = finalSize;
- Extent = _measuredExtent;
-
- if (child != null)
- {
- var size = new Size(
- CanScrollHorizontally ?
- Math.Max(finalSize.Width, child.DesiredSize.Width) :
- Math.Min(finalSize.Width, child.DesiredSize.Width),
- Math.Max(finalSize.Height, child.DesiredSize.Height));
-
- child.Arrange(new Rect((Point)(-Offset), size));
- return finalSize;
- }
- }
- else if (child != null)
+ if (_logicalScrollSubscription != null || Child == null)
{
- child.Arrange(new Rect(finalSize));
- return finalSize;
+ return base.ArrangeOverride(finalSize);
}
- return new Size();
+ var size = new Size(
+ CanScrollHorizontally ?
+ Math.Max(Child.DesiredSize.Width, finalSize.Width) :
+ finalSize.Width,
+ Math.Max(Child.DesiredSize.Height, finalSize.Height));
+ ArrangeOverrideImpl(size, -Offset);
+ Viewport = finalSize;
+ Extent = Child.Bounds.Size;
+ return finalSize;
}
///
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs
new file mode 100644
index 0000000000..450b85696e
--- /dev/null
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Layout.cs
@@ -0,0 +1,194 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Avalonia.Controls.Presenters;
+using Avalonia.Layout;
+using Xunit;
+
+namespace Avalonia.Controls.UnitTests.Presenters
+{
+ public class ContentPresenterTests_Layout
+ {
+ [Theory]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Stretch, 0, 0, 100, 100)]
+ [InlineData(HorizontalAlignment.Left, VerticalAlignment.Stretch, 0, 0, 16, 100)]
+ [InlineData(HorizontalAlignment.Right, VerticalAlignment.Stretch, 84, 0, 16, 100)]
+ [InlineData(HorizontalAlignment.Center, VerticalAlignment.Stretch, 42, 0, 16, 100)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Top, 0, 0, 100, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Bottom, 0, 84, 100, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Center, 0, 42, 100, 16)]
+ public void Content_Alignment_Is_Applied_To_Child_Bounds(
+ HorizontalAlignment h,
+ VerticalAlignment v,
+ double expectedX,
+ double expectedY,
+ double expectedWidth,
+ double expectedHeight)
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ HorizontalContentAlignment = h,
+ VerticalContentAlignment = v,
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), content.Bounds);
+ }
+
+ [Theory]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Stretch, 10, 10, 80, 80)]
+ [InlineData(HorizontalAlignment.Left, VerticalAlignment.Stretch, 10, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Right, VerticalAlignment.Stretch, 74, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Center, VerticalAlignment.Stretch, 42, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Top, 10, 10, 80, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Bottom, 10, 74, 80, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Center, 10, 42, 80, 16)]
+ public void Content_Alignment_And_Padding_Are_Applied_To_Child_Bounds(
+ HorizontalAlignment h,
+ VerticalAlignment v,
+ double expectedX,
+ double expectedY,
+ double expectedWidth,
+ double expectedHeight)
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ HorizontalContentAlignment = h,
+ VerticalContentAlignment = v,
+ Padding = new Thickness(10),
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), content.Bounds);
+ }
+
+ [Fact]
+ public void Content_Can_Be_Stretched()
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(0, 0, 100, 100), content.Bounds);
+ }
+
+ [Fact]
+ public void Content_Can_Be_Right_Aligned()
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ HorizontalAlignment = HorizontalAlignment.Right
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(84, 0, 16, 100), content.Bounds);
+ }
+
+ [Fact]
+ public void Content_Can_Be_Bottom_Aligned()
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ VerticalAlignment = VerticalAlignment.Bottom,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(0, 84, 100, 16), content.Bounds);
+ }
+
+ [Fact]
+ public void Content_Can_Be_TopLeft_Aligned()
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ HorizontalAlignment = HorizontalAlignment.Right,
+ VerticalAlignment = VerticalAlignment.Top,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds);
+ }
+
+ [Fact]
+ public void Content_Can_Be_TopRight_Aligned()
+ {
+ Border content;
+ var target = new ContentPresenter
+ {
+ Content = content = new Border
+ {
+ MinWidth = 16,
+ MinHeight = 16,
+ HorizontalAlignment = HorizontalAlignment.Right,
+ VerticalAlignment = VerticalAlignment.Top,
+ },
+ };
+
+ target.UpdateChild();
+ target.Measure(new Size(100, 100));
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds);
+ }
+
+ [Fact]
+ public void Padding_Is_Applied_To_TopLeft_Aligned_Content()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
index 58f4127cdc..ece79d3a95 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
@@ -204,7 +204,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
scroll.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(20, target.Panel.Children.Count);
- Assert.Equal(new Size(10, 200), scroll.Extent);
+ Assert.Equal(new Size(100, 200), scroll.Extent);
Assert.Equal(new Size(100, 100), scroll.Viewport);
target.VirtualizationMode = ItemVirtualizationMode.Simple;
@@ -266,7 +266,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
scroll.Arrange(new Rect(0, 0, 100, 100));
Assert.Equal(20, target.Panel.Children.Count);
- Assert.Equal(new Size(10, 200), scroll.Extent);
+ Assert.Equal(new Size(100, 200), scroll.Extent);
Assert.Equal(new Size(100, 100), scroll.Viewport);
}
diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
index d8d1f127f8..1830c3e5c1 100644
--- a/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
@@ -12,55 +12,32 @@ namespace Avalonia.Controls.UnitTests.Presenters
{
public class ScrollContentPresenterTests
{
- [Fact]
- public void Content_Can_Be_Left_Aligned()
- {
- Border content;
- var target = new ScrollContentPresenter
- {
- Content = content = new Border
- {
- Padding = new Thickness(8),
- HorizontalAlignment = HorizontalAlignment.Left
- },
- };
-
- target.UpdateChild();
- target.Measure(new Size(100, 100));
- target.Arrange(new Rect(0, 0, 100, 100));
-
- Assert.Equal(new Rect(0, 0, 16, 100), content.Bounds);
- }
-
- [Fact]
- public void Content_Can_Be_Stretched()
- {
- Border content;
- var target = new ScrollContentPresenter
- {
- Content = content = new Border
- {
- Padding = new Thickness(8),
- },
- };
-
- target.UpdateChild();
- target.Measure(new Size(100, 100));
- target.Arrange(new Rect(0, 0, 100, 100));
-
- Assert.Equal(new Rect(0, 0, 100, 100), content.Bounds);
- }
-
- [Fact]
- public void Content_Can_Be_Right_Aligned()
+ [Theory]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Stretch, 10, 10, 80, 80)]
+ [InlineData(HorizontalAlignment.Left, VerticalAlignment.Stretch, 10, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Right, VerticalAlignment.Stretch, 74, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Center, VerticalAlignment.Stretch, 42, 10, 16, 80)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Top, 10, 10, 80, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Bottom, 10, 74, 80, 16)]
+ [InlineData(HorizontalAlignment.Stretch, VerticalAlignment.Center, 10, 42, 80, 16)]
+ public void Alignment_And_Padding_Are_Applied_To_Child_Bounds(
+ HorizontalAlignment h,
+ VerticalAlignment v,
+ double expectedX,
+ double expectedY,
+ double expectedWidth,
+ double expectedHeight)
{
Border content;
var target = new ScrollContentPresenter
{
+ Padding = new Thickness(10),
Content = content = new Border
{
- Padding = new Thickness(8),
- HorizontalAlignment = HorizontalAlignment.Right
+ MinWidth = 16,
+ MinHeight = 16,
+ HorizontalAlignment = h,
+ VerticalAlignment = v,
},
};
@@ -68,19 +45,19 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
- Assert.Equal(new Rect(84, 0, 16, 100), content.Bounds);
+ Assert.Equal(new Rect(expectedX, expectedY, expectedWidth, expectedHeight), content.Bounds);
}
[Fact]
- public void Content_Can_Be_Bottom_Aligned()
+ public void DesiredSize_Is_Content_Size_When_Smaller_Than_AvailableSize()
{
- Border content;
var target = new ScrollContentPresenter
{
- Content = content = new Border
+ Padding = new Thickness(10),
+ Content = new Border
{
- Padding = new Thickness(8),
- VerticalAlignment = VerticalAlignment.Bottom,
+ MinWidth = 16,
+ MinHeight = 16,
},
};
@@ -88,20 +65,19 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
- Assert.Equal(new Rect(0, 84, 100, 16), content.Bounds);
+ Assert.Equal(new Size(16, 16), target.DesiredSize);
}
[Fact]
- public void Content_Can_Be_TopRight_Aligned()
+ public void DesiredSize_Is_AvailableSize_When_Content_Larger_Than_AvailableSize()
{
- Border content;
var target = new ScrollContentPresenter
{
- Content = content = new Border
+ Padding = new Thickness(10),
+ Content = new Border
{
- Padding = new Thickness(8),
- HorizontalAlignment = HorizontalAlignment.Right,
- VerticalAlignment = VerticalAlignment.Top,
+ MinWidth = 160,
+ MinHeight = 160,
},
};
@@ -109,7 +85,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
- Assert.Equal(new Rect(84, 0, 16, 16), content.Bounds);
+ Assert.Equal(new Size(100, 100), target.DesiredSize);
}
[Fact]
@@ -201,6 +177,19 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(new[] { "Viewport", "Extent" }, set);
}
+ [Fact]
+ public void Should_Correctly_Arrange_Child_Larger_Than_Viewport()
+ {
+ var child = new Canvas { MinWidth = 150, MinHeight = 150 };
+ var target = new ScrollContentPresenter { Content = child, };
+
+ target.UpdateChild();
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Size(150, 150), child.Bounds.Size);
+ }
+
[Fact]
public void Arrange_Should_Constrain_Child_Width_When_CanScrollHorizontally_False()
{
@@ -227,6 +216,32 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(100, child.Bounds.Width);
}
+ [Fact]
+ public void Extent_Width_Should_Be_Arrange_Width_When_CanScrollHorizontally_False()
+ {
+ var child = new WrapPanel
+ {
+ Children =
+ {
+ new Border { Width = 40, Height = 50 },
+ new Border { Width = 40, Height = 50 },
+ new Border { Width = 40, Height = 50 },
+ }
+ };
+
+ var target = new ScrollContentPresenter
+ {
+ Content = child,
+ CanScrollHorizontally = false,
+ };
+
+ target.UpdateChild();
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Size(100, 100), target.Extent);
+ }
+
[Fact]
public void Setting_Offset_Should_Invalidate_Arrange()
{
diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
index dcc65edc74..410b2ffb2e 100644
--- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
+++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
@@ -7,6 +7,97 @@ namespace Avalonia.Layout.UnitTests
{
public class LayoutableTests
{
+ [Theory]
+ [InlineData(0, 0, 0, 0, 100, 100)]
+ [InlineData(10, 0, 0, 0, 90, 100)]
+ [InlineData(10, 0, 5, 0, 85, 100)]
+ [InlineData(0, 10, 0, 0, 100, 90)]
+ [InlineData(0, 10, 0, 5, 100, 85)]
+ [InlineData(4, 4, 6, 7, 90, 89)]
+ public void Margin_Is_Applied_To_MeasureOverride_Size(
+ double l,
+ double t,
+ double r,
+ double b,
+ double expectedWidth,
+ double expectedHeight)
+ {
+ var target = new TestLayoutable
+ {
+ Margin = new Thickness(l, t, r, b),
+ };
+
+ target.Measure(new Size(100, 100));
+
+ Assert.Equal(new Size(expectedWidth, expectedHeight), target.MeasureSize);
+ }
+
+ [Theory]
+ [InlineData(HorizontalAlignment.Stretch, 100)]
+ [InlineData(HorizontalAlignment.Left, 10)]
+ [InlineData(HorizontalAlignment.Center, 10)]
+ [InlineData(HorizontalAlignment.Right, 10)]
+ public void HorizontalAlignment_Is_Applied_To_ArrangeOverride_Size(
+ HorizontalAlignment h,
+ double expectedWidth)
+ {
+ var target = new TestLayoutable
+ {
+ HorizontalAlignment = h,
+ };
+
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Size(expectedWidth, 100), target.ArrangeSize);
+ }
+
+ [Theory]
+ [InlineData(VerticalAlignment.Stretch, 100)]
+ [InlineData(VerticalAlignment.Top, 10)]
+ [InlineData(VerticalAlignment.Center, 10)]
+ [InlineData(VerticalAlignment.Bottom, 10)]
+ public void VerticalAlignment_Is_Applied_To_ArrangeOverride_Size(
+ VerticalAlignment v,
+ double expectedHeight)
+ {
+ var target = new TestLayoutable
+ {
+ VerticalAlignment = v,
+ };
+
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Size(100, expectedHeight), target.ArrangeSize);
+ }
+
+ [Theory]
+ [InlineData(0, 0, 0, 0, 100, 100)]
+ [InlineData(10, 0, 0, 0, 90, 100)]
+ [InlineData(10, 0, 5, 0, 85, 100)]
+ [InlineData(0, 10, 0, 0, 100, 90)]
+ [InlineData(0, 10, 0, 5, 100, 85)]
+ [InlineData(4, 4, 6, 7, 90, 89)]
+ public void Margin_Is_Applied_To_ArrangeOverride_Size(
+ double l,
+ double t,
+ double r,
+ double b,
+ double expectedWidth,
+ double expectedHeight)
+ {
+ var target = new TestLayoutable
+ {
+ Margin = new Thickness(l, t, r, b),
+ };
+
+ target.Measure(Size.Infinity);
+ target.Arrange(new Rect(0, 0, 100, 100));
+
+ Assert.Equal(new Size(expectedWidth, expectedHeight), target.ArrangeSize);
+ }
+
[Fact]
public void Only_Calls_LayoutManager_InvalidateMeasure_Once()
{
@@ -86,5 +177,24 @@ namespace Avalonia.Layout.UnitTests
AvaloniaLocator.CurrentMutable.Bind().ToConstant(layoutManager);
return result;
}
+
+ private class TestLayoutable : Layoutable
+ {
+ public Size ArrangeSize { get; private set; }
+ public Size MeasureResult { get; set; } = new Size(10, 10);
+ public Size MeasureSize { get; private set; }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ MeasureSize = availableSize;
+ return MeasureResult;
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ ArrangeSize = finalSize;
+ return base.ArrangeOverride(finalSize);
+ }
+ }
}
}