Browse Source

DockPanel

pull/174/head
José Manuel Nieto Sánchez 11 years ago
parent
commit
d94bbfc2c3
  1. 1
      Perspex.sln.DotSettings
  2. 44
      Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs
  3. 38
      Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs
  4. 40
      Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs
  5. 38
      Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs
  6. 21
      src/Perspex.Controls/DockPanel/Aligner.cs
  7. 10
      src/Perspex.Controls/DockPanel/Alignment.cs
  8. 24
      src/Perspex.Controls/DockPanel/Alignments.cs
  9. 98
      src/Perspex.Controls/DockPanel/CoordinateMixin.cs
  10. 10
      src/Perspex.Controls/DockPanel/Dock.cs
  11. 129
      src/Perspex.Controls/DockPanel/DockPanel.cs
  12. 33
      src/Perspex.Controls/DockPanel/Docker.cs
  13. 54
      src/Perspex.Controls/DockPanel/DockingArranger.cs
  14. 32
      src/Perspex.Controls/DockPanel/EnumerableMixin.cs
  15. 27
      src/Perspex.Controls/DockPanel/LayoutSizes.cs
  16. 21
      src/Perspex.Controls/DockPanel/LeftDocker.cs
  17. 51
      src/Perspex.Controls/DockPanel/RectMixin.cs
  18. 23
      src/Perspex.Controls/DockPanel/RightDocker.cs
  19. 21
      src/Perspex.Controls/DockPanel/Segment.cs
  20. 8
      src/Perspex.Controls/Margins.cs
  21. 15
      src/Perspex.Controls/Perspex.Controls.csproj
  22. 10
      src/Perspex.SceneGraph/Size.cs
  23. 4
      tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

1
Perspex.sln.DotSettings

@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=EnumMember/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=EnumMember/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>

44
Tests/Perspex.Controls.UnitTests/DockPanelTests/AlignerTests.cs

@ -0,0 +1,44 @@
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);
}
}
}

38
Tests/Perspex.Controls.UnitTests/DockPanelTests/LeftDockerTests.cs

@ -0,0 +1,38 @@
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)},
};
}
}

40
Tests/Perspex.Controls.UnitTests/DockPanelTests/RectAlignerTests.cs

@ -0,0 +1,40 @@
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)},
};
}
}

38
Tests/Perspex.Controls.UnitTests/DockPanelTests/RightDockerTests.cs

@ -0,0 +1,38 @@
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)},
};
}
}

21
src/Perspex.Controls/DockPanel/Aligner.cs

@ -0,0 +1,21 @@
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);
}
}
}

10
src/Perspex.Controls/DockPanel/Alignment.cs

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

24
src/Perspex.Controls/DockPanel/Alignments.cs

@ -0,0 +1,24 @@
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; }
}
}
}

98
src/Perspex.Controls/DockPanel/CoordinateMixin.cs

@ -0,0 +1,98 @@
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());
}
}
}

10
src/Perspex.Controls/DockPanel/Dock.cs

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

129
src/Perspex.Controls/DockPanel/DockPanel.cs

@ -0,0 +1,129 @@
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);
}
}
}

33
src/Perspex.Controls/DockPanel/Docker.cs

@ -0,0 +1,33 @@
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; }
}
}
}

54
src/Perspex.Controls/DockPanel/DockingArranger.cs

@ -0,0 +1,54 @@
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;
}
}

32
src/Perspex.Controls/DockPanel/EnumerableMixin.cs

@ -0,0 +1,32 @@
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);
}
}
}

27
src/Perspex.Controls/DockPanel/LayoutSizes.cs

@ -0,0 +1,27 @@
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);
}
}

21
src/Perspex.Controls/DockPanel/LeftDocker.cs

@ -0,0 +1,21 @@
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;
}
}
}

51
src/Perspex.Controls/DockPanel/RectMixin.cs

@ -0,0 +1,51 @@
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);
}
}
}

23
src/Perspex.Controls/DockPanel/RightDocker.cs

@ -0,0 +1,23 @@
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;
}
}
}

21
src/Perspex.Controls/DockPanel/Segment.cs

@ -0,0 +1,21 @@
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}";
}
}
}

8
src/Perspex.Controls/Margins.cs

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

15
src/Perspex.Controls/Perspex.Controls.csproj

@ -41,6 +41,17 @@
<Compile Include="..\Shared\SharedAssemblyInfo.cs"> <Compile Include="..\Shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link> <Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile> </Compile>
<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\ITreeItemContainerGenerator.cs" />
<Compile Include="Generators\ItemContainers.cs" /> <Compile Include="Generators\ItemContainers.cs" />
<Compile Include="IContentControl.cs" /> <Compile Include="IContentControl.cs" />
@ -48,6 +59,9 @@
<Compile Include="IPanel.cs" /> <Compile Include="IPanel.cs" />
<Compile Include="IReparentingHost.cs" /> <Compile Include="IReparentingHost.cs" />
<Compile Include="ISetLogicalParent.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="MenuItemAccessKeyHandler.cs" />
<Compile Include="Mixins\SelectableMixin.cs" /> <Compile Include="Mixins\SelectableMixin.cs" />
<Compile Include="Presenters\IContentPresenter.cs" /> <Compile Include="Presenters\IContentPresenter.cs" />
@ -65,6 +79,7 @@
<Compile Include="Primitives\IScrollInfo.cs" /> <Compile Include="Primitives\IScrollInfo.cs" />
<Compile Include="Primitives\Popup.cs" /> <Compile Include="Primitives\Popup.cs" />
<Compile Include="Primitives\ScrollInfoAdapter.cs" /> <Compile Include="Primitives\ScrollInfoAdapter.cs" />
<Compile Include="DockPanel\RightDocker.cs" />
<Compile Include="Templates\ControlTemplate`2.cs" /> <Compile Include="Templates\ControlTemplate`2.cs" />
<Compile Include="Templates\DataTemplate`1.cs" /> <Compile Include="Templates\DataTemplate`1.cs" />
<Compile Include="Templates\IControlTemplate.cs" /> <Compile Include="Templates\IControlTemplate.cs" />

10
src/Perspex.SceneGraph/Size.cs

@ -96,6 +96,16 @@ namespace Perspex
return new Size(size._width / scale.X, size._height / scale.Y); return new Size(size._width / scale.X, size._height / scale.Y);
} }
public static Size operator +(Size size, Size toAdd)
{
return new Size(size._width + toAdd._width, size._height + toAdd._height);
}
public static Size operator -(Size size, Size toSubstract)
{
return new Size(size._width + toSubstract._width, size._height + toSubstract._height);
}
/// <summary> /// <summary>
/// Constrains the size. /// Constrains the size.
/// </summary> /// </summary>

4
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

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

Loading…
Cancel
Save