From f1a15335aacf58846ad742bee0449699f4def4b8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 26 Aug 2015 19:08:50 +0200 Subject: [PATCH] Documented Layoutable and friends. --- Perspex.Layout/ILayoutRoot.cs | 9 + Perspex.Layout/ILayoutable.cs | 77 +++++- Perspex.Layout/Layoutable.cs | 358 +++++++++++++++++++++------ Perspex.Layout/Perspex.Layout.csproj | 1 + 4 files changed, 373 insertions(+), 72 deletions(-) diff --git a/Perspex.Layout/ILayoutRoot.cs b/Perspex.Layout/ILayoutRoot.cs index 4962760a21..6a7a0fee8d 100644 --- a/Perspex.Layout/ILayoutRoot.cs +++ b/Perspex.Layout/ILayoutRoot.cs @@ -6,10 +6,19 @@ namespace Perspex.Layout { + /// + /// Defines the root of a layoutable tree. + /// public interface ILayoutRoot : ILayoutable { + /// + /// The size available to layout the controls. + /// Size ClientSize { get; } + /// + /// The layout manager to use for laying out the tree. + /// ILayoutManager LayoutManager { get; } } } diff --git a/Perspex.Layout/ILayoutable.cs b/Perspex.Layout/ILayoutable.cs index 2af9ec7572..50add8b5f2 100644 --- a/Perspex.Layout/ILayoutable.cs +++ b/Perspex.Layout/ILayoutable.cs @@ -1,48 +1,119 @@ // ----------------------------------------------------------------------- // -// Copyright 2013 MIT Licence. See licence.md for more information. +// Copyright 2015 MIT Licence. See licence.md for more information. // // ----------------------------------------------------------------------- namespace Perspex.Layout { - // TODO: Probably want to move width/height/etc properties to different interface. + /// + /// Defines layout-related functionality for a control. + /// public interface ILayoutable : IVisual { + /// + /// Gets the size that this element computed during the measure pass of the layout process. + /// Size DesiredSize { get; } - double Width { get; } + /// + /// Gets the width of the element. + /// + double Width { get; } + /// + /// Gets the height of the element. + /// double Height { get; } + /// + /// Gets the minimum width of the element. + /// double MinWidth { get; } + /// + /// Gets the maximum width of the element. + /// double MaxWidth { get; } + /// + /// Gets the minimum height of the element. + /// double MinHeight { get; } + /// + /// Gets the maximum height of the element. + /// double MaxHeight { get; } + /// + /// Gets the margin around the element. + /// + Thickness Margin { get; } + + /// + /// Gets the element's preferred horizontal alignment in its parent. + /// HorizontalAlignment HorizontalAlignment { get; } + /// + /// Gets the element's preferred vertical alignment in its parent. + /// VerticalAlignment VerticalAlignment { get; } + /// + /// Gets a value indicating whether the control's layout measure is valid. + /// bool IsMeasureValid { get; } + /// + /// Gets a value indicating whether the control's layouts arrange is valid. + /// bool IsArrangeValid { get; } + /// + /// Gets the available size passed in the previous layout pass, if any. + /// Size? PreviousMeasure { get; } + /// + /// Gets the layout rect passed in the previous layout pass, if any. + /// Rect? PreviousArrange { get; } + /// + /// Creates the visual children of the control, if necessary + /// void ApplyTemplate(); + /// + /// Carries out a measure of the control. + /// + /// The available size for the control. + /// + /// If true, the control will be measured even if has not + /// changed from the last measure. + /// void Measure(Size availableSize, bool force = false); + /// + /// Arranges the control and its children. + /// + /// The control's new bounds. + /// + /// If true, the control will be arranged even if has not changed + /// from the last arrange. + /// void Arrange(Rect rect, bool force = false); + /// + /// Invalidates the measurement of the control and queues a new layout pass. + /// void InvalidateMeasure(); + /// + /// Invalidates the arrangement of the control and queues a new layout pass. + /// void InvalidateArrange(); } } diff --git a/Perspex.Layout/Layoutable.cs b/Perspex.Layout/Layoutable.cs index ffcbeaed02..1f0087d9f8 100644 --- a/Perspex.Layout/Layoutable.cs +++ b/Perspex.Layout/Layoutable.cs @@ -12,53 +12,122 @@ namespace Perspex.Layout using Serilog; using Serilog.Core.Enrichers; + /// + /// Defines how a control aligns itself horizontally in its parent control. + /// public enum HorizontalAlignment { + /// + /// The control stretches to fill the width of the parent control. + /// Stretch, + + /// + /// The control aligns itself to the left of the parent control. + /// Left, + + /// + /// The control centers itself in the parent control. + /// Center, + + /// + /// The control aligns itself to the right of the parent control. + /// Right, } + /// + /// Defines how a control aligns itself vertically in its parent control. + /// public enum VerticalAlignment { + /// + /// The control stretches to fill the height of the parent control. + /// Stretch, + + /// + /// The control aligns itself to the top of the parent control. + /// Top, + + /// + /// The control centers itself within the parent control. + /// Center, + + /// + /// The control aligns itself to the bottom of the parent control. + /// Bottom, } + /// + /// Implements layout-related functionality for a control. + /// public class Layoutable : Visual, ILayoutable { + /// + /// Defines the property. + /// public static readonly PerspexProperty WidthProperty = - PerspexProperty.Register("Width", double.NaN); + PerspexProperty.Register(nameof(Width), double.NaN); + /// + /// Defines the property. + /// public static readonly PerspexProperty HeightProperty = - PerspexProperty.Register("Height", double.NaN); + PerspexProperty.Register(nameof(Height), double.NaN); + /// + /// Defines the property. + /// public static readonly PerspexProperty MinWidthProperty = - PerspexProperty.Register("MinWidth"); + PerspexProperty.Register(nameof(MinWidth)); + /// + /// Defines the property. + /// public static readonly PerspexProperty MaxWidthProperty = - PerspexProperty.Register("MaxWidth", double.PositiveInfinity); + PerspexProperty.Register(nameof(MaxWidth), double.PositiveInfinity); + /// + /// Defines the property. + /// public static readonly PerspexProperty MinHeightProperty = - PerspexProperty.Register("MinHeight"); + PerspexProperty.Register(nameof(MinHeight)); + /// + /// Defines the property. + /// public static readonly PerspexProperty MaxHeightProperty = - PerspexProperty.Register("MaxHeight", double.PositiveInfinity); + PerspexProperty.Register(nameof(MaxHeight), double.PositiveInfinity); + /// + /// Defines the property. + /// public static readonly PerspexProperty MarginProperty = - PerspexProperty.Register("Margin"); + PerspexProperty.Register(nameof(Margin)); + /// + /// Defines the property. + /// public static readonly PerspexProperty HorizontalAlignmentProperty = - PerspexProperty.Register("HorizontalAlignment"); + PerspexProperty.Register(nameof(HorizontalAlignment)); + /// + /// Defines the property. + /// public static readonly PerspexProperty VerticalAlignmentProperty = - PerspexProperty.Register("VerticalAlignment"); + PerspexProperty.Register(nameof(VerticalAlignment)); + /// + /// Defines the property. + /// public static readonly PerspexProperty UseLayoutRoundingProperty = - PerspexProperty.Register("UseLayoutRounding", defaultValue: true, inherits: true); + PerspexProperty.Register(nameof(UseLayoutRounding), defaultValue: true, inherits: true); private Size? previousMeasure; @@ -66,6 +135,9 @@ namespace Perspex.Layout private ILogger layoutLog; + /// + /// Initializes static members of the class. + /// static Layoutable() { Layoutable.AffectsMeasure(Visual.IsVisibleProperty); @@ -80,6 +152,9 @@ namespace Perspex.Layout Layoutable.AffectsMeasure(Layoutable.VerticalAlignmentProperty); } + /// + /// Initializes a new instance of the class. + /// public Layoutable() { this.layoutLog = Log.ForContext(new[] @@ -90,98 +165,155 @@ namespace Perspex.Layout }); } + /// + /// Gets or sets the width of the element. + /// public double Width { get { return this.GetValue(WidthProperty); } set { this.SetValue(WidthProperty, value); } } + /// + /// Gets or sets the height of the element. + /// public double Height { get { return this.GetValue(HeightProperty); } set { this.SetValue(HeightProperty, value); } } + /// + /// Gets or sets the minimum width of the element. + /// public double MinWidth { get { return this.GetValue(MinWidthProperty); } set { this.SetValue(MinWidthProperty, value); } } + /// + /// Gets or sets the maximum width of the element. + /// public double MaxWidth { get { return this.GetValue(MaxWidthProperty); } set { this.SetValue(MaxWidthProperty, value); } } + /// + /// Gets or sets the minimum height of the element. + /// public double MinHeight { get { return this.GetValue(MinHeightProperty); } set { this.SetValue(MinHeightProperty, value); } } + /// + /// Gets or sets the maximum height of the element. + /// public double MaxHeight { get { return this.GetValue(MaxHeightProperty); } set { this.SetValue(MaxHeightProperty, value); } } + /// + /// Gets or sets the margin around the element. + /// public Thickness Margin { get { return this.GetValue(MarginProperty); } set { this.SetValue(MarginProperty, value); } } + /// + /// Gets or sets the element's preferred horizontal alignment in its parent. + /// public HorizontalAlignment HorizontalAlignment { get { return this.GetValue(HorizontalAlignmentProperty); } set { this.SetValue(HorizontalAlignmentProperty, value); } } + /// + /// Gets or sets the element's preferred vertical alignment in its parent. + /// public VerticalAlignment VerticalAlignment { get { return this.GetValue(VerticalAlignmentProperty); } set { this.SetValue(VerticalAlignmentProperty, value); } } + /// + /// Gets the size that this element computed during the measure pass of the layout process. + /// public Size DesiredSize { get; set; } + /// + /// Gets a value indicating whether the control's layout measure is valid. + /// public bool IsMeasureValid { get; private set; } + /// + /// Gets a value indicating whether the control's layouts arrange is valid. + /// public bool IsArrangeValid { get; private set; } + /// + /// Gets or sets a value that determines whether the element should be snapped to pixel + /// boundaries at layout time. + /// public bool UseLayoutRounding { get { return this.GetValue(UseLayoutRoundingProperty); } set { this.SetValue(UseLayoutRoundingProperty, value); } } + /// + /// Gets the available size passed in the previous layout pass, if any. + /// Size? ILayoutable.PreviousMeasure { get { return this.previousMeasure; } } + /// + /// Gets the layout rect passed in the previous layout pass, if any. + /// Rect? ILayoutable.PreviousArrange { get { return this.previousArrange; } } + /// + /// Creates the visual children of the control, if necessary + /// public virtual void ApplyTemplate() { } + /// + /// Carries out a measure of the control. + /// + /// The available size for the control. + /// + /// If true, the control will be measured even if has not + /// changed from the last measure. + /// public void Measure(Size availableSize, bool force = false) { if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height)) @@ -207,6 +339,14 @@ namespace Perspex.Layout } } + /// + /// Arranges the control and its children. + /// + /// The control's new bounds. + /// + /// If true, the control will be arranged even if has not changed + /// from the last arrange. + /// public void Arrange(Rect rect, bool force = false) { if (IsInvalidRect(rect)) @@ -231,6 +371,9 @@ namespace Perspex.Layout } } + /// + /// Invalidates the measurement of the control and queues a new layout pass. + /// public void InvalidateMeasure() { var parent = this.GetVisualParent(); @@ -260,6 +403,9 @@ namespace Perspex.Layout } } + /// + /// Invalidates the arrangement of the control and queues a new layout pass. + /// public void InvalidateArrange() { var root = this.GetLayoutRoot(); @@ -278,16 +424,107 @@ namespace Perspex.Layout } } + /// + /// Marks a property as affecting the control's measurement. + /// + /// The property. + /// + /// After a call to this method in a control's static constructor, any change to the + /// property will cause to be called on the element. + /// + protected static void AffectsMeasure(PerspexProperty property) + { + property.Changed.Subscribe(AffectsMeasureInvalidate); + } + + /// + /// Marks a property as affecting the control's arrangement. + /// + /// The property. + /// + /// After a call to this method in a control's static constructor, any change to the + /// property will cause to be called on the element. + /// protected static void AffectsArrange(PerspexProperty property) { property.Changed.Subscribe(AffectsArrangeInvalidate); } - protected static void AffectsMeasure(PerspexProperty property) + /// + /// The default implementation of the control's measure pass. + /// + /// The size available to the control. + /// The desired size for the control. + /// + /// This method calls which is probably the method you + /// want to override in order to modify a control's arrangement. + /// + protected virtual Size MeasureCore(Size availableSize) { - property.Changed.Subscribe(AffectsMeasureInvalidate); + if (this.IsVisible) + { + this.ApplyTemplate(); + + var constrained = LayoutHelper + .ApplyLayoutConstraints(this, availableSize) + .Deflate(this.Margin); + + var measured = this.MeasureOverride(constrained); + var width = measured.Width; + var height = measured.Height; + + if (!double.IsNaN(this.Width)) + { + width = this.Width; + } + + width = Math.Min(width, this.MaxWidth); + width = Math.Max(width, this.MinWidth); + + if (!double.IsNaN(this.Height)) + { + height = this.Height; + } + + height = Math.Min(height, this.MaxHeight); + height = Math.Max(height, this.MinHeight); + + return new Size(width, height).Inflate(this.Margin); + } + else + { + return new Size(); + } + } + + /// + /// Measures the control and its child elements as part of a layout pass. + /// + /// The size available to the control. + /// The desired size for the control. + protected virtual Size MeasureOverride(Size availableSize) + { + double width = 0; + double height = 0; + + foreach (ILayoutable child in this.GetVisualChildren().OfType()) + { + child.Measure(availableSize); + width = Math.Max(width, child.DesiredSize.Width); + height = Math.Max(height, child.DesiredSize.Height); + } + + return new Size(width, height); } + /// + /// The default implementation of the control's arrange pass. + /// + /// The control's new bounds. + /// + /// This method calls which is probably the method you + /// want to override in order to modify a control's arrangement. + /// protected virtual void ArrangeCore(Rect finalRect) { if (this.IsVisible) @@ -349,6 +586,11 @@ namespace Perspex.Layout } } + /// + /// Positions child elements as part of a layout pass. + /// + /// The size available to the control. + /// The actual size used. protected virtual Size ArrangeOverride(Size finalSize) { foreach (ILayoutable child in this.GetVisualChildren().OfType()) @@ -359,84 +601,50 @@ namespace Perspex.Layout return finalSize; } - protected virtual Size MeasureCore(Size availableSize) - { - if (this.IsVisible) - { - this.ApplyTemplate(); - - var constrained = LayoutHelper - .ApplyLayoutConstraints(this, availableSize) - .Deflate(this.Margin); - - var measured = this.MeasureOverride(constrained); - var width = measured.Width; - var height = measured.Height; - - if (!double.IsNaN(this.Width)) - { - width = this.Width; - } - - width = Math.Min(width, this.MaxWidth); - width = Math.Max(width, this.MinWidth); - - if (!double.IsNaN(this.Height)) - { - height = this.Height; - } - - height = Math.Min(height, this.MaxHeight); - height = Math.Max(height, this.MinHeight); - - return new Size(width, height).Inflate(this.Margin); - } - else - { - return new Size(); - } - } - - protected virtual Size MeasureOverride(Size availableSize) - { - double width = 0; - double height = 0; - - foreach (ILayoutable child in this.GetVisualChildren().OfType()) - { - child.Measure(availableSize); - width = Math.Max(width, child.DesiredSize.Width); - height = Math.Max(height, child.DesiredSize.Height); - } - - return new Size(width, height); - } - - private static void AffectsArrangeInvalidate(PerspexPropertyChangedEventArgs e) + /// + /// Calls on the control on which a property changed. + /// + /// The event args. + private static void AffectsMeasureInvalidate(PerspexPropertyChangedEventArgs e) { ILayoutable control = e.Sender as ILayoutable; if (control != null) { - control.InvalidateArrange(); + control.InvalidateMeasure(); } } - private static void AffectsMeasureInvalidate(PerspexPropertyChangedEventArgs e) + /// + /// Calls on the control on which a property changed. + /// + /// The event args. + private static void AffectsArrangeInvalidate(PerspexPropertyChangedEventArgs e) { ILayoutable control = e.Sender as ILayoutable; if (control != null) { - control.InvalidateMeasure(); + control.InvalidateArrange(); } } + /// + /// Tests whether a control's size can be changed by a layout pass. + /// + /// The control. + /// True if the control's size can change; otherwise false. private static bool IsResizable(ILayoutable control) { return double.IsNaN(control.Width) || double.IsNaN(control.Height); } + /// + /// Tests whether any of a 's properties incude nagative values, + /// a NaN or Infinity. + /// + /// The rect. + /// True if the rect is invalid; otherwise false. private static bool IsInvalidRect(Rect rect) { return rect.Width < 0 || rect.Height < 0 || @@ -446,6 +654,12 @@ namespace Perspex.Layout double.IsNaN(rect.Width) || double.IsNaN(rect.Height); } + /// + /// Tests whether any of a 's properties incude nagative values, + /// a NaN or Infinity. + /// + /// The size. + /// True if the size is invalid; otherwise false. private static bool IsInvalidSize(Size size) { return size.Width < 0 || size.Height < 0 || @@ -453,6 +667,12 @@ namespace Perspex.Layout double.IsNaN(size.Width) || double.IsNaN(size.Height); } + /// + /// Gets the layout root, together with its distance. + /// + /// + /// A tuple containing the layout root and the root's distance from this control. + /// private Tuple GetLayoutRoot() { var control = (IVisual)this; diff --git a/Perspex.Layout/Perspex.Layout.csproj b/Perspex.Layout/Perspex.Layout.csproj index 3bc42d9e3b..74bbec7aa6 100644 --- a/Perspex.Layout/Perspex.Layout.csproj +++ b/Perspex.Layout/Perspex.Layout.csproj @@ -56,6 +56,7 @@ +