From fa97e8f51be1fe21ec94ca81b3495dd61ba63291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Manuel=20Nieto=20S=C3=A1nchez?= Date: Tue, 22 Sep 2015 22:59:03 +0200 Subject: [PATCH] DockPanel Simplification --- .../DockPanelTests/AlignerTests.cs | 44 -- .../DockPanelTests/LeftDockerTests.cs | 38 -- .../DockPanelTests/RectAlignerTests.cs | 40 -- .../DockPanelTests/RightDockerTests.cs | 38 -- src/Perspex.Controls/DockPanel.cs | 448 ++++++++++++++++++ src/Perspex.Controls/DockPanel/Aligner.cs | 21 - src/Perspex.Controls/DockPanel/Alignment.cs | 10 - src/Perspex.Controls/DockPanel/Alignments.cs | 24 - .../DockPanel/CoordinateMixin.cs | 98 ---- src/Perspex.Controls/DockPanel/Dock.cs | 10 - src/Perspex.Controls/DockPanel/DockPanel.cs | 129 ----- src/Perspex.Controls/DockPanel/Docker.cs | 33 -- .../DockPanel/DockingArranger.cs | 54 --- .../DockPanel/EnumerableMixin.cs | 32 -- src/Perspex.Controls/DockPanel/LayoutSizes.cs | 27 -- src/Perspex.Controls/DockPanel/LeftDocker.cs | 21 - src/Perspex.Controls/DockPanel/RectMixin.cs | 51 -- src/Perspex.Controls/DockPanel/RightDocker.cs | 23 - src/Perspex.Controls/DockPanel/Segment.cs | 21 - src/Perspex.Controls/Margins.cs | 8 - src/Perspex.Controls/Perspex.Controls.csproj | 17 +- .../Perspex.Controls.UnitTests.csproj | 4 - 22 files changed, 450 insertions(+), 741 deletions(-) delete mode 100644 Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs delete mode 100644 Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs delete mode 100644 Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs delete mode 100644 Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs create mode 100644 src/Perspex.Controls/DockPanel.cs delete mode 100644 src/Perspex.Controls/DockPanel/Aligner.cs delete mode 100644 src/Perspex.Controls/DockPanel/Alignment.cs delete mode 100644 src/Perspex.Controls/DockPanel/Alignments.cs delete mode 100644 src/Perspex.Controls/DockPanel/CoordinateMixin.cs delete mode 100644 src/Perspex.Controls/DockPanel/Dock.cs delete mode 100644 src/Perspex.Controls/DockPanel/DockPanel.cs delete mode 100644 src/Perspex.Controls/DockPanel/Docker.cs delete mode 100644 src/Perspex.Controls/DockPanel/DockingArranger.cs delete mode 100644 src/Perspex.Controls/DockPanel/EnumerableMixin.cs delete mode 100644 src/Perspex.Controls/DockPanel/LayoutSizes.cs delete mode 100644 src/Perspex.Controls/DockPanel/LeftDocker.cs delete mode 100644 src/Perspex.Controls/DockPanel/RectMixin.cs delete mode 100644 src/Perspex.Controls/DockPanel/RightDocker.cs delete mode 100644 src/Perspex.Controls/DockPanel/Segment.cs delete mode 100644 src/Perspex.Controls/Margins.cs diff --git a/Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs b/Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs deleted file mode 100644 index 4e9c087381..0000000000 --- a/Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Perspex.Controls.UnitTests.DockPanelTests -{ - using Xunit; - - - public class AlignerTests - { - [Fact] - public void ToStartTest() - { - Segment container = new Segment(2, 5); - - var aligned = container.AlignToStart(2); - Assert.Equal(new Segment(2, 4), aligned); - } - - [Fact] - public void ToEndTest() - { - Segment container = new Segment(2, 5); - - var aligned = container.AlignToEnd(2); - Assert.Equal(new Segment(3, 5), aligned); - } - - [Fact] - public void ToMiddleTest() - { - Segment container = new Segment(2, 5); - - var aligned = container.AlignToMiddle(2); - Assert.Equal(new Segment(2.5, 4.5), aligned); - } - - [Fact] - public void ToMiddleTest2() - { - Segment container = new Segment(0, 500); - - var aligned = container.AlignToMiddle(200); - Assert.Equal(new Segment(150, 350), aligned); - } - } -} \ No newline at end of file diff --git a/Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs b/Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs deleted file mode 100644 index 38e12490eb..0000000000 --- a/Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Perspex.Controls.UnitTests.DockPanelTests -{ - using System.Collections.Generic; - using Layout; - using Moq; - using Xunit; - - public class LeftDockerTests - { - private readonly ILayoutable _layoutable; - - public LeftDockerTests() - { - var layoutableMock = new Mock(); - layoutableMock.Setup(l => l.DesiredSize).Returns(new Size(40, 30)); - _layoutable = layoutableMock.Object; - } - - [Theory] - [MemberData("Source")] - public void Dock(Margins margins, Rect expectedRect) - { - var sut = new LeftDocker(new Size(100, 50)); - var actualRect = sut.GetDockingRect(_layoutable.DesiredSize, margins, new Alignments(Alignment.Middle, Alignment.Stretch)); - - Assert.Equal(expectedRect, actualRect); - } - - // ReSharper disable once UnusedMember.Global - public static IEnumerable Source => new[] - { - new object[] { new Margins(), new Rect(0, 0, 40, 50)}, - new object[] { new Margins { VerticalMargin = new Segment(15, 0) }, new Rect(0, 15, 40, 35)}, - new object[] { new Margins { VerticalMargin = new Segment(0, 15) }, new Rect(0, 0, 40, 35)}, - new object[] { new Margins { VerticalMargin = new Segment(20, 15) }, new Rect(0, 20, 40, 15)}, - }; - } -} \ No newline at end of file diff --git a/Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs b/Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs deleted file mode 100644 index 33e18b1c8b..0000000000 --- a/Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace Perspex.Controls.UnitTests.DockPanelTests -{ - using Layout; - using Xunit; - - public class RectAlignerTests - { - private readonly Rect _container = new Rect(0, 0, 40, 40); - private readonly Size _child = new Size(20, 20); - - [Theory] - [MemberData("TestData")] - public void LefTopTest(Alignment horz, Alignment vert, Rect expectedRect) - { - var actualRect = _container.AlignChild(_child, horz, vert); - Assert.Equal(expectedRect, actualRect); - } - - // ReSharper disable once UnusedMember.Global - public static object[] TestData => new object[] - { - new object[] {Alignment.Start, Alignment.Start, new Rect(0, 0, 20, 20)}, - new object[] {Alignment.Middle, Alignment.Start, new Rect(10, 0, 20, 20)}, - new object[] {Alignment.End, Alignment.Start, new Rect(20, 0, 20, 20)}, - new object[] {Alignment.Stretch, Alignment.Start, new Rect(0, 0, 40, 20)}, - - new object[] {Alignment.Start, Alignment.Middle, new Rect(0, 10, 20, 20)}, - new object[] {Alignment.Middle, Alignment.Middle, new Rect(10, 10, 20, 20)}, - new object[] {Alignment.End, Alignment.Middle, new Rect(20, 10, 20, 20)}, - new object[] {Alignment.Stretch, Alignment.Middle, new Rect(0, 10, 40, 20)}, - - new object[] {Alignment.Start, VerticalAlignment.Bottom, new Rect(0, 20, 20, 20)}, - new object[] {Alignment.Middle, VerticalAlignment.Bottom, new Rect(10, 20, 20, 20)}, - new object[] {Alignment.End, VerticalAlignment.Bottom, new Rect(20, 20, 20, 20)}, - new object[] {Alignment.Stretch, VerticalAlignment.Bottom, new Rect(0, 20, 40, 20)}, - - new object[] {Alignment.Stretch, VerticalAlignment.Stretch, new Rect(0, 0, 40, 40)}, - }; - } -} \ No newline at end of file diff --git a/Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs b/Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs deleted file mode 100644 index b08676dd71..0000000000 --- a/Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Perspex.Controls.UnitTests.DockPanelTests -{ - using System.Collections.Generic; - using Layout; - using Moq; - using Xunit; - - public class RightDockerTests - { - private readonly ILayoutable _layoutable; - - public RightDockerTests() - { - var layoutableMock = new Mock(); - layoutableMock.Setup(l => l.DesiredSize).Returns(new Size(40, 30)); - _layoutable = layoutableMock.Object; - } - - [Theory] - [MemberData("Source")] - public void Dock(Margins margins, Rect expectedRect) - { - var sut = new RightDocker(new Size(100, 50)); - var actualRect = sut.GetDockingRect(_layoutable.DesiredSize, margins, new Alignments(Alignment.Middle, Alignment.Stretch)); - - Assert.Equal(expectedRect, actualRect); - } - - // ReSharper disable once UnusedMember.Global - public static IEnumerable Source => new[] - { - new object[] { new Margins(), new Rect(60, 0, 40, 50)}, - new object[] { new Margins { VerticalMargin = new Segment(0, 15) }, new Rect(60, 0, 40, 35)}, - new object[] { new Margins { VerticalMargin = new Segment(15, 0) }, new Rect(60, 15, 40, 35)}, - new object[] { new Margins { VerticalMargin = new Segment(20, 15) }, new Rect(60, 20, 40, 15)}, - }; - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel.cs b/src/Perspex.Controls/DockPanel.cs new file mode 100644 index 0000000000..efded15d40 --- /dev/null +++ b/src/Perspex.Controls/DockPanel.cs @@ -0,0 +1,448 @@ +namespace Perspex.Controls +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using Layout; + + public class DockPanel : Panel + { + public static readonly PerspexProperty DockProperty = PerspexProperty.RegisterAttached("Dock"); + + static DockPanel() + { + AffectsArrange(DockProperty); + } + + // ReSharper disable once UnusedMember.Global + public static Dock GetDock(PerspexObject perspexObject) + { + return perspexObject.GetValue(DockProperty); + } + + // ReSharper disable once UnusedMember.Global + public static void SetDock(PerspexObject element, Dock dock) + { + element.SetValue(DockProperty, dock); + } + + public static readonly PerspexProperty LastChildFillProperty = PerspexProperty.Register(nameof(LastChildFillProperty), defaultValue: true); + + public bool LastChildFill + { + get { return GetValue(LastChildFillProperty); } + set { SetValue(LastChildFillProperty, value); } + } + + protected override Size MeasureOverride(Size availableSize) + { + if (!LastChildFill) + { + return MeasureItemsThatWillBeDocked(availableSize, Children); + } + + var sizeRequiredByDockingItems = MeasureItemsThatWillBeDocked(availableSize, Children.WithoutLast()); + var elementThatWillFill = Children.Last(); + elementThatWillFill.Measure(availableSize - sizeRequiredByDockingItems); + var finalSize = sizeRequiredByDockingItems.Inflate(new Thickness(elementThatWillFill.DesiredSize.Width, elementThatWillFill.DesiredSize.Height)); + return finalSize; + } + + private static Size MeasureItemsThatWillBeDocked(Size availableSize, IEnumerable children) + { + var requiredHorizontalLength = 0D; + var requiredVerticalLength = 0D; + + foreach (var control in children) + { + control.Measure(availableSize); + + var dock = control.GetValue(DockProperty); + if (IsHorizontal(dock)) + { + requiredHorizontalLength += control.DesiredSize.Width; + } + else + { + requiredVerticalLength += control.DesiredSize.Height; + } + } + + return new Size(requiredHorizontalLength, requiredVerticalLength); + } + + private static bool IsHorizontal(Dock dock) + { + return dock == Dock.Left || dock == Dock.Right; + } + + protected override Size ArrangeOverride(Size finalSize) + { + if (!LastChildFill) + { + return ArrangeAllChildren(finalSize); + } + else + { + return ArrangeChildrenAndFillLastChild(finalSize); + } + } + + private Size ArrangeChildrenAndFillLastChild(Size finalSize) + { + var docker = new DockingArranger(); + var requiredSize = docker.ArrangeAndGetUsedSize(finalSize, Children.WithoutLast()); + ArrangeToFill(Children.Last(), finalSize, docker.UsedMargin); + return requiredSize; + } + + private Size ArrangeAllChildren(Size finalSize) + { + return new DockingArranger().ArrangeAndGetUsedSize(finalSize, Children); + } + + private static void ArrangeToFill(ILayoutable layoutable, Size containerSize, Margin margin) + { + var containerRect = new Rect(new Point(0, 0), containerSize); + var marginsCutout = margin.AsThickness(); + var withoutMargins = containerRect.Deflate(marginsCutout); + + layoutable.Arrange(withoutMargins); + } + + private class DockingArranger + { + public Margin UsedMargin { get; private set; } + + public Size ArrangeAndGetUsedSize(Size availableSize, IEnumerable children) + { + var leftArranger = new LeftDocker(availableSize); + var rightArranger = new RightDocker(availableSize); + var topArranger = new LeftDocker(availableSize.Swap()); + var bottomArranger = new RightDocker(availableSize.Swap()); + + UsedMargin = new Margin(); + + foreach (var control in children) + { + Rect dockedRect; + var dock = control.GetValue(DockProperty); + switch (dock) + { + case Dock.Left: + dockedRect = leftArranger.GetDockedRect(control.DesiredSize, UsedMargin, control.GetAlignments()); + break; + + case Dock.Top: + UsedMargin.Swap(); + dockedRect = topArranger.GetDockedRect(control.DesiredSize.Swap(), UsedMargin, control.GetAlignments().Swap()).Swap(); + UsedMargin.Swap(); + break; + + case Dock.Right: + dockedRect = rightArranger.GetDockedRect(control.DesiredSize, UsedMargin, control.GetAlignments()); + break; + + case Dock.Bottom: + UsedMargin.Swap(); + dockedRect = bottomArranger.GetDockedRect(control.DesiredSize.Swap(), UsedMargin, control.GetAlignments().Swap()).Swap(); + UsedMargin.Swap(); + break; + + default: + throw new InvalidOperationException($"Invalid dock value {dock}"); + } + + control.Arrange(dockedRect); + } + + return availableSize; + } + } + + private class LeftDocker : Docker + { + public LeftDocker(Size availableSize) : base(availableSize) + { + } + + public override Rect GetDockedRect(Size childSize, Margin margin, Alignments alignments) + { + var marginsCutout = margin.AsThickness(); + var availableRect = OriginalRect.Deflate(marginsCutout); + var alignedRect = AlignToLeft(availableRect, childSize, alignments.Vertical); + + AccumulatedOffset += childSize.Width; + margin.Horizontal = margin.Horizontal.Offset(childSize.Width, 0); + + return alignedRect; + } + + private static Rect AlignToLeft(Rect availableRect, Size childSize, Alignment verticalAlignment) + { + return availableRect.AlignChild(childSize, Alignment.Start, verticalAlignment); + } + } + + private class RightDocker : Docker + { + public RightDocker(Size availableSize) : base(availableSize) + { + } + + public override Rect GetDockedRect(Size childSize, Margin margin, Alignments alignments) + { + var marginsCutout = margin.AsThickness(); + var withoutMargins = OriginalRect.Deflate(marginsCutout); + var finalRect = withoutMargins.AlignChild(childSize, Alignment.End, alignments.Vertical); + + AccumulatedOffset += childSize.Width; + margin.Horizontal = margin.Horizontal.Offset(0, childSize.Width); + + return finalRect; + } + } + + private abstract class Docker + { + protected Docker(Size availableSize) + { + OriginalRect = new Rect(new Point(0, 0), availableSize); + } + + protected double AccumulatedOffset { get; set; } + + protected Rect OriginalRect { get; } + + public abstract Rect GetDockedRect(Size childSize, Margin margin, Alignments alignments); + } + } + + public class Margin + { + public Segment Horizontal { get; set; } + public Segment Vertical { get; set; } + } + + public enum Alignment + { + Stretch, Start, Middle, End, + } + + public static class SegmentMixin + { + public static Segment AlignToStart(this Segment container, double length) + { + return new Segment(container.Start, container.Start + length); + } + + public static Segment AlignToEnd(this Segment container, double length) + { + return new Segment(container.End - length, container.End); + } + + public static Segment AlignToMiddle(this Segment container, double length) + { + var start = container.Start + (container.Length - length) / 2; + return new Segment(start, start + length); + } + } + + public struct Alignments + { + private readonly Alignment _horizontal; + private readonly Alignment _vertical; + + public Alignments(Alignment horizontal, Alignment vertical) + { + _horizontal = horizontal; + _vertical = vertical; + } + + public Alignment Horizontal => _horizontal; + + public Alignment Vertical => _vertical; + } + + public static class CoordinateMixin + { + private static Point Swap(this Point p) + { + return new Point(p.Y, p.X); + } + + public static Size Swap(this Size s) + { + return new Size(s.Height, s.Width); + } + + public static Rect Swap(this Rect r) + { + return new Rect(r.Position.Swap(), r.Size.Swap()); + } + + public static Segment Offset(this Segment l, double startOffset, double endOffset) + { + return new Segment(l.Start + startOffset, l.End + endOffset); + } + + public static void Swap(this Margin m) + { + var v = m.Vertical; + m.Vertical = m.Horizontal; + m.Horizontal = v; + } + + public static Thickness AsThickness(this Margin margin) + { + return new Thickness(margin.Horizontal.Start, margin.Vertical.Start, margin.Horizontal.End, margin.Vertical.End); + } + + private static Alignment AsAlignment(this HorizontalAlignment horz) + { + switch (horz) + { + case HorizontalAlignment.Stretch: + return Alignment.Stretch; + case HorizontalAlignment.Left: + return Alignment.Start; + case HorizontalAlignment.Center: + return Alignment.Middle; + case HorizontalAlignment.Right: + return Alignment.End; + default: + throw new ArgumentOutOfRangeException(nameof(horz), horz, null); + } + } + + private static Alignment AsAlignment(this VerticalAlignment vert) + { + switch (vert) + { + case VerticalAlignment.Stretch: + return Alignment.Stretch; + case VerticalAlignment.Top: + return Alignment.Start; + case VerticalAlignment.Center: + return Alignment.Middle; + case VerticalAlignment.Bottom: + return Alignment.End; + default: + throw new ArgumentOutOfRangeException(nameof(vert), vert, null); + } + } + + public static Alignments GetAlignments(this ILayoutable layoutable) + { + return new Alignments(layoutable.HorizontalAlignment.AsAlignment(), layoutable.VerticalAlignment.AsAlignment()); + } + + public static Alignments Swap(this Alignments alignments) + { + return new Alignments(alignments.Vertical, alignments.Horizontal); + } + } + + public enum Dock + { + Left = 0, + Bottom, + Right, + Top + } + + public static class RectMixin + { + public static Rect AlignChild(this Rect container, Size childSize, Alignment horizontalAlignment, Alignment verticalAlignment) + { + var horzSegment = container.GetHorizontalCoordinates(); + var vertSegment = container.GetVerticalCoordinates(); + + var horzResult = GetAlignedSegment(childSize.Width, horizontalAlignment, horzSegment); + var vertResult = GetAlignedSegment(childSize.Height, verticalAlignment, vertSegment); + + return FromSegments(horzResult, vertResult); + } + + private static Rect FromSegments(Segment horzSegment, Segment vertSegment) + { + return new Rect(horzSegment.Start, vertSegment.Start, horzSegment.Length, vertSegment.Length); + } + + private static Segment GetAlignedSegment(double width, Alignment alignment, Segment horzSegment) + { + switch (alignment) + { + case Alignment.Start: + return horzSegment.AlignToStart(width); + + case Alignment.Middle: + return horzSegment.AlignToMiddle(width); + + case Alignment.End: + return horzSegment.AlignToEnd(width); + + default: + return new Segment(horzSegment.Start, horzSegment.End); + } + } + + private static Segment GetHorizontalCoordinates(this Rect rect) + { + return new Segment(rect.X, rect.Right); + } + + private static Segment GetVerticalCoordinates(this Rect rect) + { + return new Segment(rect.Y, rect.Bottom); + } + } + + public struct Segment + { + public Segment(double start, double end) + { + Start = start; + End = end; + } + + public double Start { get; } + public double End { get; } + + public double Length => End - Start; + + public override string ToString() + { + return $"Start: {Start}, End: {End}"; + } + } + + public static class EnumerableMixin + { + private static IEnumerable Shrink(this IEnumerable source, int left, int right) + { + int i = 0; + var buffer = new Queue(right + 1); + + foreach (T x in source) + { + if (i >= left) // Read past left many elements at the start + { + buffer.Enqueue(x); + if (buffer.Count > right) // Build a buffer to drop right many elements at the end + yield return buffer.Dequeue(); + } + else i++; + } + } + public static IEnumerable WithoutLast(this IEnumerable source, int n = 1) + { + return source.Shrink(0, n); + } + public static IEnumerable WithoutFirst(this IEnumerable source, int n = 1) + { + return source.Shrink(n, 0); + } + } +} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Aligner.cs b/src/Perspex.Controls/DockPanel/Aligner.cs deleted file mode 100644 index 821d9000f3..0000000000 --- a/src/Perspex.Controls/DockPanel/Aligner.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Perspex.Controls -{ - public static class LinearMarginMixin - { - public static Segment AlignToStart(this Segment container, double length) - { - return new Segment(container.Start, container.Start + length); - } - - public static Segment AlignToEnd(this Segment container, double length) - { - return new Segment(container.End - length, container.End); - } - - public static Segment AlignToMiddle(this Segment container, double length) - { - var start = container.Start + (container.Length - length) / 2; - return new Segment(start, start + length); - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Alignment.cs b/src/Perspex.Controls/DockPanel/Alignment.cs deleted file mode 100644 index d710a6bd60..0000000000 --- a/src/Perspex.Controls/DockPanel/Alignment.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Perspex.Controls -{ - public enum Alignment - { - Stretch, - Start, - Middle, - End, - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Alignments.cs b/src/Perspex.Controls/DockPanel/Alignments.cs deleted file mode 100644 index 4eefde9719..0000000000 --- a/src/Perspex.Controls/DockPanel/Alignments.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Perspex.Controls -{ - public struct Alignments - { - private readonly Alignment _horizontal; - private readonly Alignment _vertical; - - public Alignments(Alignment horizontal, Alignment vertical) - { - _horizontal = horizontal; - _vertical = vertical; - } - - public Alignment Horizontal - { - get { return _horizontal; } - } - - public Alignment Vertical - { - get { return _vertical; } - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/CoordinateMixin.cs b/src/Perspex.Controls/DockPanel/CoordinateMixin.cs deleted file mode 100644 index 829b2d3de2..0000000000 --- a/src/Perspex.Controls/DockPanel/CoordinateMixin.cs +++ /dev/null @@ -1,98 +0,0 @@ -namespace Perspex.Controls -{ - using System; - using Layout; - - public static class CoordinateMixin - { - private static Point Swap(this Point p) - { - return new Point(p.Y, p.X); - } - - public static Size Swap(this Size s) - { - return new Size(s.Height, s.Width); - } - - public static Rect Swap(this Rect r) - { - return new Rect(r.Position.Swap(), r.Size.Swap()); - } - - public static Segment Offset(this Segment l, double startOffset, double endOffset) - { - return new Segment(l.Start + startOffset, l.End + endOffset); - } - - public static void Swap(this Margins m) - { - var v = m.VerticalMargin; - m.VerticalMargin = m.HorizontalMargin; - m.HorizontalMargin = v; - } - - - public static Thickness AsThickness(this Margins margins) - { - return new Thickness(margins.HorizontalMargin.Start, margins.VerticalMargin.Start, margins.HorizontalMargin.End, margins.VerticalMargin.End); - } - - private static Alignment AsAlignment(this HorizontalAlignment horz) - { - switch (horz) - { - case HorizontalAlignment.Stretch: - return Alignment.Stretch; - case HorizontalAlignment.Left: - return Alignment.Start; - case HorizontalAlignment.Center: - return Alignment.Middle; - case HorizontalAlignment.Right: - return Alignment.End; - default: - throw new ArgumentOutOfRangeException(nameof(horz), horz, null); - } - } - - private static Alignment AsAlignment(this VerticalAlignment vert) - { - switch (vert) - { - case VerticalAlignment.Stretch: - return Alignment.Stretch; - case VerticalAlignment.Top: - return Alignment.Start; - case VerticalAlignment.Center: - return Alignment.Middle; - case VerticalAlignment.Bottom: - return Alignment.End; - default: - throw new ArgumentOutOfRangeException(nameof(vert), vert, null); - } - } - - public static Alignments GetAlignments(this ILayoutable layoutable) - { - return new Alignments(layoutable.HorizontalAlignment.AsAlignment(), layoutable.VerticalAlignment.AsAlignment()); - } - - public static Alignments Swap(this Alignments alignments) - { - return new Alignments(alignments.Vertical, alignments.Horizontal); - } - - public static LayoutSizes GetLayoutSizes(this ILayoutable layoutable) - { - return new LayoutSizes( - new Size(layoutable.Width, layoutable.Height), - new Size(layoutable.MaxWidth, layoutable.MaxHeight), - new Size(layoutable.MinWidth, layoutable.MinHeight)); - } - - public static LayoutSizes Swap(this LayoutSizes l) - { - return new LayoutSizes(l.Size.Swap(), l.MaxSize.Swap(), l.MinSize.Swap()); - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Dock.cs b/src/Perspex.Controls/DockPanel/Dock.cs deleted file mode 100644 index 69a2e0fe66..0000000000 --- a/src/Perspex.Controls/DockPanel/Dock.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Perspex.Controls -{ - public enum Dock - { - Left = 0, - Bottom, - Right, - Top - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/DockPanel.cs b/src/Perspex.Controls/DockPanel/DockPanel.cs deleted file mode 100644 index e71ac4ee9e..0000000000 --- a/src/Perspex.Controls/DockPanel/DockPanel.cs +++ /dev/null @@ -1,129 +0,0 @@ -namespace Perspex.Controls -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using Layout; - - // ReSharper disable once UnusedMember.Global - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public class DockPanel : Panel - { - public static readonly PerspexProperty DockProperty = PerspexProperty.RegisterAttached("Dock"); - - static DockPanel() - { - AffectsArrange(DockProperty); - } - - public static Dock GetDock(PerspexObject element) - { - return element.GetValue(DockProperty); - } - - public static void SetDock(PerspexObject element, Dock dock) - { - element.SetValue(DockProperty, dock); - } - - public static readonly PerspexProperty LastChildFillProperty = PerspexProperty.Register(nameof(DataContext), defaultValue: true); - - public bool LastChildFill - { - get { return GetValue(LastChildFillProperty); } - set { SetValue(LastChildFillProperty, value); } - } - - protected override Size MeasureOverride(Size availableSize) - { - if (!LastChildFill) - { - return MeasureItemsThatWillBeDocked(availableSize, Children); - } - - var sizeRequiredByDockingItems = MeasureItemsThatWillBeDocked(availableSize, Children.WithoutLast()); - var elementThatWillFill = Children.Last(); - elementThatWillFill.Measure(availableSize - sizeRequiredByDockingItems); - var finalSize = sizeRequiredByDockingItems.Inflate(new Thickness(elementThatWillFill.DesiredSize.Width, elementThatWillFill.DesiredSize.Height)); - return finalSize; - } - - private static Size MeasureItemsThatWillBeDocked(Size availableSize, IEnumerable children) - { - var requiredHorizontalLength = 0D; - var requiredVerticalLength = 0D; - - foreach (var control in children) - { - control.Measure(availableSize); - - var dock = control.GetValue(DockProperty); - if (IsHorizontal(dock)) - { - requiredHorizontalLength += control.DesiredSize.Width; - } - else - { - requiredVerticalLength += control.DesiredSize.Height; - } - } - - return new Size(requiredHorizontalLength, requiredVerticalLength); - } - - private static bool IsHorizontal(Dock dock) - { - return dock == Dock.Left || dock == Dock.Right; - } - - protected override Size ArrangeOverride(Size finalSize) - { - var docker = new DockingArranger(); - - if (!LastChildFill) - { - return docker.ArrangeChildren(finalSize, Children); - } - - var requiredSize = docker.ArrangeChildren(finalSize, Children.WithoutLast()); - - ArrangeToFill(finalSize, docker.Margins, Children.Last()); - - return requiredSize; - } - - private static void ArrangeToFill(Size availableSize, Margins margins, ILayoutable layoutable) - { - var containerRect = new Rect(new Point(0,0), availableSize); - var marginsCutout = margins.AsThickness(); - var withoutMargins = containerRect.Deflate(marginsCutout); - - var finalSize = GetConstrainedSize(layoutable, withoutMargins); - - var finalRect = withoutMargins.AlignChild(finalSize, Alignment.Middle, Alignment.Middle); - - layoutable.Arrange(finalRect); - } - - private static Size GetConstrainedSize(ILayoutable layoutable, Rect withoutMargins) - { - var width = GetWidth(layoutable.GetLayoutSizes(), withoutMargins); - var height = GetWidth(layoutable.GetLayoutSizes().Swap(), withoutMargins.Swap()); - var finalSize = new Size(width, height); - return finalSize; - } - - private static double GetWidth(LayoutSizes layoutSizes, Rect withoutMargins) - { - return layoutSizes.IsWidthSpecified - ? layoutSizes.Size.Width - : GetConstrainedDimension(withoutMargins.Width, layoutSizes.MaxSize.Width, layoutSizes.MinSize.Width); - } - - private static double GetConstrainedDimension(double toConstrain, double maximum, double minimum) - { - return Math.Max(Math.Min(toConstrain, maximum), minimum); - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Docker.cs b/src/Perspex.Controls/DockPanel/Docker.cs deleted file mode 100644 index 04fea052b2..0000000000 --- a/src/Perspex.Controls/DockPanel/Docker.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Perspex.Controls -{ - public class Docker - { - private Size _availableSize; - private double _accumulatedOffset; - private Rect _originalRect; - - protected Docker(Size availableSize) - { - AvailableSize = availableSize; - OriginalRect = new Rect(new Point(0, 0), AvailableSize); - } - - protected Size AvailableSize - { - get { return _availableSize; } - set { _availableSize = value; } - } - - protected double AccumulatedOffset - { - get { return _accumulatedOffset; } - set { _accumulatedOffset = value; } - } - - protected Rect OriginalRect - { - get { return _originalRect; } - set { _originalRect = value; } - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/DockingArranger.cs b/src/Perspex.Controls/DockPanel/DockingArranger.cs deleted file mode 100644 index 2ce9c429ae..0000000000 --- a/src/Perspex.Controls/DockPanel/DockingArranger.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using Perspex; -using Perspex.Controls; - -internal class DockingArranger -{ - public Margins Margins { get; private set; } - - public Size ArrangeChildren(Size finalSize, IEnumerable controls) - { - var leftArranger = new LeftDocker(finalSize); - var rightArranger = new RightDocker(finalSize); - var topArranger = new LeftDocker(finalSize.Swap()); - var bottomArranger = new RightDocker(finalSize.Swap()); - - Margins = new Margins(); - - foreach (var control in controls) - { - Rect dockedRect; - var dock = control.GetValue(DockPanel.DockProperty); - switch (dock) - { - case Dock.Left: - dockedRect = leftArranger.GetDockingRect(control.DesiredSize, Margins, control.GetAlignments()); - break; - - case Dock.Top: - Margins.Swap(); - dockedRect = topArranger.GetDockingRect(control.DesiredSize.Swap(), Margins, control.GetAlignments().Swap()).Swap(); - Margins.Swap(); - break; - - case Dock.Right: - dockedRect = rightArranger.GetDockingRect(control.DesiredSize, Margins, control.GetAlignments()); - break; - - case Dock.Bottom: - Margins.Swap(); - dockedRect = bottomArranger.GetDockingRect(control.DesiredSize.Swap(), Margins, control.GetAlignments().Swap()).Swap(); - Margins.Swap(); - break; - - default: - throw new InvalidOperationException($"Invalid dock value {dock}"); - } - - control.Arrange(dockedRect); - } - - return finalSize; - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/EnumerableMixin.cs b/src/Perspex.Controls/DockPanel/EnumerableMixin.cs deleted file mode 100644 index d1543e4fac..0000000000 --- a/src/Perspex.Controls/DockPanel/EnumerableMixin.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Perspex.Controls -{ - using System.Collections.Generic; - - public static class EnumerableMixin - { - public static IEnumerable Shrink(this IEnumerable source, int left, int right) - { - int i = 0; - var buffer = new Queue(right + 1); - - foreach (T x in source) - { - if (i >= left) // Read past left many elements at the start - { - buffer.Enqueue(x); - if (buffer.Count > right) // Build a buffer to drop right many elements at the end - yield return buffer.Dequeue(); - } - else i++; - } - } - public static IEnumerable WithoutLast(this IEnumerable source, int n = 1) - { - return source.Shrink(0, n); - } - public static IEnumerable WithoutFirst(this IEnumerable source, int n = 1) - { - return source.Shrink(n, 0); - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/LayoutSizes.cs b/src/Perspex.Controls/DockPanel/LayoutSizes.cs deleted file mode 100644 index 62862ac958..0000000000 --- a/src/Perspex.Controls/DockPanel/LayoutSizes.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Perspex.Controls -{ - using System; - - public struct LayoutSizes - { - private readonly Size _size; - private readonly Size _maxSize; - private readonly Size _minSize; - - public LayoutSizes(Size size, Size maxSize, Size minSize) - { - _size = size; - _maxSize = maxSize; - _minSize = minSize; - } - - public Size MinSize => _minSize; - - public Size MaxSize => _maxSize; - - public Size Size => _size; - - public bool IsWidthSpecified => !double.IsNaN(_size.Width); - public bool IsHeightSpecified => !double.IsNaN(_size.Height); - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/LeftDocker.cs b/src/Perspex.Controls/DockPanel/LeftDocker.cs deleted file mode 100644 index 063774f752..0000000000 --- a/src/Perspex.Controls/DockPanel/LeftDocker.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Perspex.Controls -{ - public class LeftDocker : Docker - { - public LeftDocker(Size availableSize) : base(availableSize) - { - } - - public Rect GetDockingRect(Size sizeToDock, Margins margins, Alignments alignments) - { - var marginsCutout = margins.AsThickness(); - var withoutMargins = OriginalRect.Deflate(marginsCutout); - var finalRect = withoutMargins.AlignChild(sizeToDock, Alignment.Start, alignments.Vertical); - - AccumulatedOffset += sizeToDock.Width; - margins.HorizontalMargin = margins.HorizontalMargin.Offset(sizeToDock.Width, 0); - - return finalRect; - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/RectMixin.cs b/src/Perspex.Controls/DockPanel/RectMixin.cs deleted file mode 100644 index a0bbe52045..0000000000 --- a/src/Perspex.Controls/DockPanel/RectMixin.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace Perspex.Controls -{ - using Layout; - - public static class RectMixin - { - public static Rect AlignChild(this Rect container, Size childSize, Alignment horizontalAlignment, Alignment verticalAlignment) - { - var horzSegment = container.GetHorizontalCoordinates(); - var vertSegment = container.GetVerticalCoordinates(); - - var horzResult = GetAlignedSegment(childSize.Width, horizontalAlignment, horzSegment); - var vertResult = GetAlignedSegment(childSize.Height, verticalAlignment, vertSegment); - - return FromSegments(horzResult, vertResult); - } - - public static Rect FromSegments(Segment horzSegment, Segment vertSegment) - { - return new Rect(horzSegment.Start, vertSegment.Start, horzSegment.Length, vertSegment.Length); - } - - private static Segment GetAlignedSegment(double width, Alignment alignment, Segment horzSegment) - { - switch (alignment) - { - case Alignment.Start: - return horzSegment.AlignToStart(width); - - case Alignment.Middle: - return horzSegment.AlignToMiddle(width); - - case Alignment.End: - return horzSegment.AlignToEnd(width); - - default: - return new Segment(horzSegment.Start, horzSegment.End); - } - } - - public static Segment GetHorizontalCoordinates(this Rect rect) - { - return new Segment(rect.X, rect.Right); - } - - public static Segment GetVerticalCoordinates(this Rect rect) - { - return new Segment(rect.Y, rect.Bottom); - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/RightDocker.cs b/src/Perspex.Controls/DockPanel/RightDocker.cs deleted file mode 100644 index 5c8abe0e0f..0000000000 --- a/src/Perspex.Controls/DockPanel/RightDocker.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Perspex.Controls -{ - using Layout; - - public class RightDocker : Docker - { - public RightDocker(Size availableSize) : base(availableSize) - { - } - - public Rect GetDockingRect(Size sizeToDock, Margins margins, Alignments alignments) - { - var marginsCutout = margins.AsThickness(); - var withoutMargins = OriginalRect.Deflate(marginsCutout); - var finalRect = withoutMargins.AlignChild(sizeToDock, Alignment.End, alignments.Vertical); - - AccumulatedOffset += sizeToDock.Width; - margins.HorizontalMargin = margins.HorizontalMargin.Offset(0, sizeToDock.Width); - - return finalRect; - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/DockPanel/Segment.cs b/src/Perspex.Controls/DockPanel/Segment.cs deleted file mode 100644 index 56afae9bb5..0000000000 --- a/src/Perspex.Controls/DockPanel/Segment.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Perspex.Controls -{ - public struct Segment - { - public Segment(double start, double end) - { - Start = start; - End = end; - } - - public double Start { get; set; } - public double End { get; set; } - - public double Length => End - Start; - - public override string ToString() - { - return $"Start: {Start}, End: {End}"; - } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/Margins.cs b/src/Perspex.Controls/Margins.cs deleted file mode 100644 index 12075f61f0..0000000000 --- a/src/Perspex.Controls/Margins.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Perspex.Controls -{ - public class Margins - { - public Segment HorizontalMargin { get; set; } - public Segment VerticalMargin { get; set; } - } -} \ No newline at end of file diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj index 38d5486118..51cb6c6f1e 100644 --- a/src/Perspex.Controls/Perspex.Controls.csproj +++ b/src/Perspex.Controls/Perspex.Controls.csproj @@ -41,17 +41,7 @@ Properties\SharedAssemblyInfo.cs - - - - - - - - - - - + @@ -59,9 +49,6 @@ - - - @@ -79,7 +66,6 @@ - @@ -223,6 +209,7 @@ Perspex.Styling +