Mercurial > silverbladetech
diff SilverlightGlimpse/SilverFlow.Controls/Controllers/ResizeController.cs @ 63:536498832a79
Latest version before changing bindings to Listbox
author | Steven Hollidge <stevenhollidge@hotmail.com> |
---|---|
date | Sun, 22 Apr 2012 13:33:42 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SilverlightGlimpse/SilverFlow.Controls/Controllers/ResizeController.cs Sun Apr 22 13:33:42 2012 +0100 @@ -0,0 +1,453 @@ +using System; +using System.Windows; +using System.Windows.Input; +using SilverFlow.Controls.Enums; +using SilverFlow.Controls.Extensions; + +namespace SilverFlow.Controls.Controllers +{ + /// <summary> + /// Calculates resizing element position and size. + /// </summary> + public class ResizeController + { + /// <summary> + /// Thickness of resizing area. + /// </summary> + private const double ResizingAreaDefaultValue = 6; + + /// <summary> + /// Minimal width of the resized window if MinWidth is set to 0. + /// </summary> + private const double MinResizedWidth = 20; + + /// <summary> + /// Resizing bounds, i.e. an area within which the resizing cursor can move. + /// </summary> + private Rect bounds; + + /// <summary> + /// Initial element size when resizing started. + /// </summary> + private Size initialSize; + + /// <summary> + /// Initial position of the element when resizing started. + /// </summary> + private Point initialPosition; + + /// <summary> + /// A corner or edge of the element used for resizing. + /// </summary> + private ResizeAnchor anchor; + + /// <summary> + /// An element implementing IResizableElement interface. + /// </summary> + private IResizableElement element; + + /// <summary> + /// Gets a framework element hosting resizing element. + /// </summary> + /// <value>The host.</value> + private FrameworkElement Host + { + get + { + return element.Parent as FrameworkElement; + } + } + + /// <summary> + /// Gets a value indicating whether the resizing can be started. + /// </summary> + /// <value> + /// <c>true</c> if resizing can be started; otherwise, <c>false</c>. + /// </value> + public bool CanResize + { + get + { + return anchor != ResizeAnchor.None; + } + } + + /// <summary> + /// Gets or sets the width of the resizing area. + /// </summary> + /// <value>The width of the resizing area. Default value is 6.</value> + public double ResizingArea { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance can be resized horizontally. + /// </summary> + /// <value> + /// <c>true</c> if this instance can be resized horizontally; otherwise, <c>false</c>. + /// </value> + private bool CanResizeHorizontally + { + get + { + return !(element.MinWidth == element.MaxWidth && element.MinWidth != 0); + } + } + + /// <summary> + /// Gets a value indicating whether this instance can be resized vertically. + /// </summary> + /// <value> + /// <c>true</c> if this instance can be resized vertically; otherwise, <c>false</c>. + /// </value> + private bool CanResizeVertically + { + get + { + return !(element.MinHeight == element.MaxHeight && element.MinHeight != 0); + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="ResizeController"/> class. + /// </summary> + /// <param name="resizableElement">The resizable element.</param> + public ResizeController(IResizableElement resizableElement) + { + if (resizableElement == null) + throw new ArgumentNullException("resizableElement"); + + element = resizableElement; + anchor = ResizeAnchor.None; + ResizingArea = ResizingAreaDefaultValue; + } + + /// <summary> + /// Starts resizing of the element. + /// </summary> + public void StartResizing() + { + initialSize = new Size(element.ActualWidth, element.ActualHeight); + initialPosition = element.Position; + + CalculateBoundsForResizing(); + } + + /// <summary> + /// Resizes the element. + /// </summary> + /// <param name="dx">Displacement by X-coordinate.</param> + /// <param name="dy">Displacement by Y-coordinate.</param> + public void Resize(double dx, double dy) + { + switch (anchor) + { + case ResizeAnchor.BottomRight: + ResizeBottomRight(dx, dy); + break; + + case ResizeAnchor.Right: + ResizeBottomRight(dx, 0); + break; + + case ResizeAnchor.Bottom: + ResizeBottomRight(0, dy); + break; + + case ResizeAnchor.TopLeft: + ResizeTopLeft(dx, dy); + break; + + case ResizeAnchor.Top: + ResizeTopLeft(0, dy); + break; + + case ResizeAnchor.Left: + ResizeTopLeft(dx, 0); + break; + + case ResizeAnchor.BottomLeft: + ResizeBottomLeft(dx, dy); + break; + + case ResizeAnchor.TopRight: + ResizeTopRight(dx, dy); + break; + } + } + + /// <summary> + /// Determines resizing anchor and sets appropriate Cursor. + /// </summary> + /// <param name="p">Coordinates of the mouse pointer.</param> + public void SetCursor(Point p) + { + if (p.Y < ResizingArea && p.X < ResizingArea) + { + anchor = ResizeAnchor.TopLeft; + element.Cursor = Cursors.SizeNWSE; + } + else if (p.Y < ResizingArea && p.X >= (element.ActualWidth - ResizingArea)) + { + anchor = ResizeAnchor.TopRight; + element.Cursor = Cursors.SizeNESW; + } + else if (p.Y < ResizingArea) + { + if (CanResizeVertically) + { + anchor = ResizeAnchor.Top; + element.Cursor = Cursors.SizeNS; + } + } + else if (p.X < ResizingArea && p.Y >= (element.ActualHeight - ResizingArea)) + { + anchor = ResizeAnchor.BottomLeft; + element.Cursor = Cursors.SizeNESW; + } + else if (p.X < ResizingArea) + { + if (CanResizeHorizontally) + { + anchor = ResizeAnchor.Left; + element.Cursor = Cursors.SizeWE; + } + } + else if (p.X >= (element.ActualWidth - ResizingArea) && p.Y >= (element.ActualHeight - ResizingArea)) + { + anchor = ResizeAnchor.BottomRight; + element.Cursor = Cursors.SizeNWSE; + } + else if (p.X >= (element.ActualWidth - ResizingArea)) + { + if (CanResizeHorizontally) + { + anchor = ResizeAnchor.Right; + element.Cursor = Cursors.SizeWE; + } + } + else if (p.Y >= (element.ActualHeight - ResizingArea)) + { + if (CanResizeVertically) + { + anchor = ResizeAnchor.Bottom; + element.Cursor = Cursors.SizeNS; + } + } + else + { + anchor = ResizeAnchor.None; + element.Cursor = null; + } + } + + /// <summary> + /// Calculates bounds for resizing. + /// </summary> + private void CalculateBoundsForResizing() + { + switch (anchor) + { + case ResizeAnchor.BottomRight: + case ResizeAnchor.Right: + case ResizeAnchor.Bottom: + bounds = GetBoundsForLowerRightCorner(); + break; + + case ResizeAnchor.TopLeft: + case ResizeAnchor.Top: + case ResizeAnchor.Left: + bounds = GetBoundsForUpperLeftCorner(); + break; + + case ResizeAnchor.BottomLeft: + bounds = GetBoundsForLowerLeftCorner(); + break; + + case ResizeAnchor.TopRight: + bounds = GetBoundsForUpperRightCorner(); + break; + } + } + + /// <summary> + /// Calculates bounds for resizing by the lower right corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForLowerRightCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + double maxWidth = Host.ActualWidth - initialPosition.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = Host.ActualHeight - initialPosition.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = initialPosition.Add(minWidth, minHeight); + Point p2 = initialPosition.Add(maxWidth, maxHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the upper left corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForUpperLeftCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(initialSize); + double maxWidth = point.X; + double maxHeight = point.Y; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(-maxWidth, -maxHeight); + Point p2 = point.Add(-minWidth, -minHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the lower left corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForLowerLeftCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(initialSize.Width, 0); + double maxWidth = point.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = Host.ActualHeight - initialPosition.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(-maxWidth, minHeight); + Point p2 = point.Add(-minWidth, maxHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Calculates bounds for resizing by the upper right corner. + /// </summary> + /// <returns>Bounding rectangle.</returns> + private Rect GetBoundsForUpperRightCorner() + { + double minWidth = (element.MinWidth != 0) ? element.MinWidth : MinResizedWidth; + double minHeight = element.MinHeight; + + Point point = initialPosition.Add(0, initialSize.Height); + double maxWidth = Host.ActualWidth - initialPosition.X; + + if (!element.MaxWidth.IsNotSet() && element.MaxWidth > 0) + maxWidth = Math.Min(maxWidth, element.MaxWidth); + + double maxHeight = point.Y; + if (!element.MaxHeight.IsNotSet() && element.MaxHeight > 0) + maxHeight = Math.Min(maxHeight, element.MaxHeight); + + Point p1 = point.Add(minWidth, -maxHeight); + Point p2 = point.Add(maxWidth, -minHeight); + + return new Rect(p1, p2); + } + + /// <summary> + /// Resizes the window by the bottom right corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeBottomRight(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition, initialSize.Add(dx, dy)); + Point point = element.SnapinController.SnapBottomRightCorner(newBounds); + + // If only one coordinate was changed - restore the other after snap in + if (dx == 0) + point.X = newBounds.BottomRight().X; + + if (dy == 0) + point.Y = newBounds.BottomRight().Y; + + point = point.EnsureInBounds(bounds); + + element.Width = point.X - initialPosition.X; + element.Height = point.Y - initialPosition.Y; + } + + /// <summary> + /// Resizes the window by the top left corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeTopLeft(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(dx, dy), initialSize); + Point point = element.SnapinController.SnapTopLeftCorner(newBounds).EnsureInBounds(bounds); + + // If only one coordinate was changed - restore the other after snap in + if (dx == 0) + point.X = newBounds.Position().X; + + if (dy == 0) + point.Y = newBounds.Position().Y; + + element.Position = point; + + Point lowerRight = initialPosition.Add(initialSize); + element.Width = lowerRight.X - point.X; + element.Height = lowerRight.Y - point.Y; + } + + /// <summary> + /// Resizes the window by the lower left corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeBottomLeft(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(dx, 0), initialSize.Add(-dx, dy)); + + Point lowerLeft = element.SnapinController.SnapBottomLeftCorner(newBounds).EnsureInBounds(bounds); + Point topLeft = new Point(lowerLeft.X, initialPosition.Y); + + element.Position = topLeft; + + element.Width = initialSize.Width + initialPosition.X - topLeft.X; + element.Height = lowerLeft.Y - initialPosition.Y; + } + + /// <summary> + /// Resizes the window by the top right corner of the window. + /// </summary> + /// <param name="dx">Increment by X-coordinate.</param> + /// <param name="dy">Increment by Y-coordinate.</param> + private void ResizeTopRight(double dx, double dy) + { + Rect newBounds = new Rect(initialPosition.Add(0, dy), initialSize.Add(dx, -dy)); + + Point topRight = element.SnapinController.SnapTopRightCorner(newBounds).EnsureInBounds(bounds); + Point topLeft = new Point(initialPosition.X, topRight.Y); + + element.Position = topLeft; + + element.Width = topRight.X - initialPosition.X; + element.Height = initialSize.Height + initialPosition.Y - topRight.Y; + } + } +}