Browse Source

Merge branch 'dockpanel-simplification'

Steven Kirk 10 years ago
parent
commit
b130a14bee

+ 0 - 44
Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs

@@ -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);
-        }
-    }
-}

+ 0 - 38
Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs

@@ -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<ILayoutable>();
-            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<object[]> 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)},
-        };
-    }
-}

+ 0 - 40
Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs

@@ -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)},
-        };
-    }
-}

+ 0 - 38
Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs

@@ -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<ILayoutable>();
-            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<object[]> 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)},
-        };
-    }
-}

+ 448 - 0
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<Dock> DockProperty = PerspexProperty.RegisterAttached<DockPanel, Control, Dock>("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<bool> LastChildFillProperty = PerspexProperty.Register<DockPanel, bool>(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<IControl> 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<IControl> 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<T> Shrink<T>(this IEnumerable<T> source, int left, int right)
+        {
+            int i = 0;
+            var buffer = new Queue<T>(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<T> WithoutLast<T>(this IEnumerable<T> source, int n = 1)
+        {
+            return source.Shrink(0, n);
+        }
+        public static IEnumerable<T> WithoutFirst<T>(this IEnumerable<T> source, int n = 1)
+        {
+            return source.Shrink(n, 0);
+        }
+    }
+}

+ 0 - 21
src/Perspex.Controls/DockPanel/Aligner.cs

@@ -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);
-        }
-    }
-}

+ 0 - 10
src/Perspex.Controls/DockPanel/Alignment.cs

@@ -1,10 +0,0 @@
-namespace Perspex.Controls
-{
-    public enum Alignment
-    {
-        Stretch,
-        Start,
-        Middle,
-        End,
-    }
-}

+ 0 - 24
src/Perspex.Controls/DockPanel/Alignments.cs

@@ -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; }
-        }
-    }
-}

+ 0 - 98
src/Perspex.Controls/DockPanel/CoordinateMixin.cs

@@ -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());
-        }
-    }
-}

+ 0 - 10
src/Perspex.Controls/DockPanel/Dock.cs

@@ -1,10 +0,0 @@
-namespace Perspex.Controls
-{
-    public enum Dock
-    {
-        Left = 0,
-        Bottom,
-        Right,
-        Top
-    }
-}

+ 0 - 129
src/Perspex.Controls/DockPanel/DockPanel.cs

@@ -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<Dock> DockProperty = PerspexProperty.RegisterAttached<DockPanel, Control, Dock>("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<bool> LastChildFillProperty = PerspexProperty.Register<DockPanel, bool>(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<IControl> 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);
-        }
-    }
-}

+ 0 - 33
src/Perspex.Controls/DockPanel/Docker.cs

@@ -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; }
-        }
-    }
-}

+ 0 - 54
src/Perspex.Controls/DockPanel/DockingArranger.cs

@@ -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<IControl> 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;
-    }
-}

+ 0 - 32
src/Perspex.Controls/DockPanel/EnumerableMixin.cs

@@ -1,32 +0,0 @@
-namespace Perspex.Controls
-{
-    using System.Collections.Generic;
-
-    public static class EnumerableMixin
-    {
-        public static IEnumerable<T> Shrink<T>(this IEnumerable<T> source, int left, int right)
-        {
-            int i = 0;
-            var buffer = new Queue<T>(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<T> WithoutLast<T>(this IEnumerable<T> source, int n = 1)
-        {
-            return source.Shrink(0, n);
-        }
-        public static IEnumerable<T> WithoutFirst<T>(this IEnumerable<T> source, int n = 1)
-        {
-            return source.Shrink(n, 0);
-        }
-    }
-}

+ 0 - 27
src/Perspex.Controls/DockPanel/LayoutSizes.cs

@@ -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);
-    }
-}

+ 0 - 21
src/Perspex.Controls/DockPanel/LeftDocker.cs

@@ -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;
-        }
-    }
-}

+ 0 - 51
src/Perspex.Controls/DockPanel/RectMixin.cs

@@ -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);
-        }
-    }
-}

+ 0 - 23
src/Perspex.Controls/DockPanel/RightDocker.cs

@@ -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;
-        }
-    }
-}

+ 0 - 21
src/Perspex.Controls/DockPanel/Segment.cs

@@ -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}";
-        }
-    }
-}

+ 0 - 8
src/Perspex.Controls/Margins.cs

@@ -1,8 +0,0 @@
-namespace Perspex.Controls
-{
-    public class Margins
-    {
-        public Segment HorizontalMargin { get; set; }
-        public Segment VerticalMargin { get; set; }
-    }
-}

+ 2 - 15
src/Perspex.Controls/Perspex.Controls.csproj

@@ -41,18 +41,8 @@
     <Compile Include="..\Shared\SharedAssemblyInfo.cs">
       <Link>Properties\SharedAssemblyInfo.cs</Link>
     </Compile>
+    <Compile Include="DockPanel.cs" />
     <Compile Include="SystemDialog.cs" />
-    <Compile Include="DockPanel\Aligner.cs" />
-    <Compile Include="DockPanel\Alignment.cs" />
-    <Compile Include="DockPanel\Alignments.cs" />
-    <Compile Include="DockPanel\CoordinateMixin.cs" />
-    <Compile Include="DockPanel\Dock.cs" />
-    <Compile Include="DockPanel\Docker.cs" />
-    <Compile Include="DockPanel\DockingArranger.cs" />
-    <Compile Include="DockPanel\DockPanel.cs" />
-    <Compile Include="DockPanel\EnumerableMixin.cs" />
-    <Compile Include="DockPanel\LayoutSizes.cs" />
-    <Compile Include="DockPanel\RectMixin.cs" />
     <Compile Include="Generators\ITreeItemContainerGenerator.cs" />
     <Compile Include="Generators\ItemContainers.cs" />
     <Compile Include="IContentControl.cs" />
@@ -60,9 +50,6 @@
     <Compile Include="IPanel.cs" />
     <Compile Include="IReparentingHost.cs" />
     <Compile Include="ISetLogicalParent.cs" />
-    <Compile Include="DockPanel\LeftDocker.cs" />
-    <Compile Include="DockPanel\Segment.cs" />
-    <Compile Include="Margins.cs" />
     <Compile Include="MenuItemAccessKeyHandler.cs" />
     <Compile Include="Mixins\SelectableMixin.cs" />
     <Compile Include="Platform\ISystemDialogImpl.cs" />
@@ -81,7 +68,6 @@
     <Compile Include="Primitives\IScrollInfo.cs" />
     <Compile Include="Primitives\Popup.cs" />
     <Compile Include="Primitives\ScrollInfoAdapter.cs" />
-    <Compile Include="DockPanel\RightDocker.cs" />
     <Compile Include="Canvas.cs" />
     <Compile Include="Templates\ControlTemplate`2.cs" />
     <Compile Include="Templates\DataTemplate`1.cs" />
@@ -222,6 +208,7 @@
       <Name>Perspex.Styling</Name>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 0 - 4
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@@ -81,13 +81,9 @@
     <Otherwise />
   </Choose>
   <ItemGroup>
-    <Compile Include="DockPanelTests\AlignerTests.cs" />
-    <Compile Include="DockPanelTests\RectAlignerTests.cs" />
-    <Compile Include="DockPanelTests\RightDockerTests.cs" />
     <Compile Include="GridLengthTests.cs" />
     <Compile Include="ContentPresenterTests.cs" />
     <Compile Include="BorderTests.cs" />
-    <Compile Include="DockPanelTests\LeftDockerTests.cs" />
     <Compile Include="Mixins\SelectableMixinTests.cs" />
     <Compile Include="Primitives\TrackTests.cs" />
     <Compile Include="Primitives\PopupTests.cs" />