From 767cdc1f6f6a63ce997fc9476911e2c361f9d402 Mon Sep 17 00:00:00 2001 From: LukePulverenti Date: Wed, 20 Feb 2013 20:33:05 -0500 Subject: Pushing missing changes --- MediaBrowser.UI.Controls/ScrollingPanel.cs | 404 +++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 MediaBrowser.UI.Controls/ScrollingPanel.cs (limited to 'MediaBrowser.UI.Controls/ScrollingPanel.cs') diff --git a/MediaBrowser.UI.Controls/ScrollingPanel.cs b/MediaBrowser.UI.Controls/ScrollingPanel.cs new file mode 100644 index 000000000..636661f54 --- /dev/null +++ b/MediaBrowser.UI.Controls/ScrollingPanel.cs @@ -0,0 +1,404 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace MediaBrowser.UI.Controls +{ + /// + /// This started from: + /// http://www.switchonthecode.com/tutorials/wpf-tutorial-implementing-iscrollinfo + /// Then, after implementing this, content was being displayed in stack panel like manner. + /// I then reviewed the source code of ScrollContentPresenter and updated MeasureOverride and ArrangeOverride to match. + /// + public class ScrollingPanel : Grid, IScrollInfo + { + /// + /// The infinite size + /// + private static Size InfiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity); + /// + /// The line size + /// + private const double LineSize = 16; + /// + /// The wheel size + /// + private const double WheelSize = 3 * LineSize; + + /// + /// The _ offset + /// + private Vector _Offset; + /// + /// The _ extent + /// + private Size _Extent; + /// + /// The _ viewport + /// + private Size _Viewport; + + /// + /// The _ animation length + /// + private TimeSpan _AnimationLength = TimeSpan.FromMilliseconds(125); + + /// + /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the -derived class. + /// + /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available. + /// The size that this element determines it needs during layout, based on its calculations of child element sizes. + protected override Size MeasureOverride(Size availableSize) + { + if (Children == null || Children.Count == 0) + { + return availableSize; + } + + var constraint2 = availableSize; + if (CanHorizontallyScroll) + { + constraint2.Width = double.PositiveInfinity; + } + if (CanVerticallyScroll) + { + constraint2.Height = double.PositiveInfinity; + } + + var uiElement = Children[0]; + + uiElement.Measure(constraint2); + var size = uiElement.DesiredSize; + + VerifyScrollData(availableSize, size); + + size.Width = Math.Min(availableSize.Width, size.Width); + size.Height = Math.Min(availableSize.Height, size.Height); + + return size; + } + + /// + /// Arranges the content of a element. + /// + /// Specifies the size this element should use to arrange its child elements. + /// that represents the arranged size of this Grid element and its children. + protected override Size ArrangeOverride(Size arrangeSize) + { + this.VerifyScrollData(arrangeSize, _Extent); + + if (this.Children == null || this.Children.Count == 0) + { + return arrangeSize; + } + + TranslateTransform trans = null; + + var uiElement = Children[0]; + + var finalRect = new Rect(uiElement.DesiredSize); + + // ScrollContentPresenter sets these to 0 - current offset + // We need to set it to zero in order to make the animation work + finalRect.X = 0; + finalRect.Y = 0; + + finalRect.Width = Math.Max(finalRect.Width, arrangeSize.Width); + finalRect.Height = Math.Max(finalRect.Height, arrangeSize.Height); + + trans = uiElement.RenderTransform as TranslateTransform; + + if (trans == null) + { + uiElement.RenderTransformOrigin = new Point(0, 0); + trans = new TranslateTransform(); + uiElement.RenderTransform = trans; + } + + uiElement.Arrange(finalRect); + + trans.BeginAnimation(TranslateTransform.XProperty, + GetAnimation(0 - HorizontalOffset), + HandoffBehavior.Compose); + trans.BeginAnimation(TranslateTransform.YProperty, + GetAnimation(0 - VerticalOffset), + HandoffBehavior.Compose); + + return arrangeSize; + } + + /// + /// Gets the animation. + /// + /// To value. + /// DoubleAnimation. + private DoubleAnimation GetAnimation(double toValue) + { + var animation = new DoubleAnimation(toValue, _AnimationLength); + + animation.EasingFunction = new ExponentialEase { EasingMode = EasingMode.EaseInOut }; + + return animation; + } + + #region Movement Methods + /// + /// Scrolls down within content by one logical unit. + /// + public void LineDown() + { SetVerticalOffset(VerticalOffset + LineSize); } + + /// + /// Scrolls up within content by one logical unit. + /// + public void LineUp() + { SetVerticalOffset(VerticalOffset - LineSize); } + + /// + /// Scrolls left within content by one logical unit. + /// + public void LineLeft() + { SetHorizontalOffset(HorizontalOffset - LineSize); } + + /// + /// Scrolls right within content by one logical unit. + /// + public void LineRight() + { SetHorizontalOffset(HorizontalOffset + LineSize); } + + /// + /// Scrolls down within content after a user clicks the wheel button on a mouse. + /// + public void MouseWheelDown() + { SetVerticalOffset(VerticalOffset + WheelSize); } + + /// + /// Scrolls up within content after a user clicks the wheel button on a mouse. + /// + public void MouseWheelUp() + { SetVerticalOffset(VerticalOffset - WheelSize); } + + /// + /// Scrolls left within content after a user clicks the wheel button on a mouse. + /// + public void MouseWheelLeft() + { SetHorizontalOffset(HorizontalOffset - WheelSize); } + + /// + /// Scrolls right within content after a user clicks the wheel button on a mouse. + /// + public void MouseWheelRight() + { SetHorizontalOffset(HorizontalOffset + WheelSize); } + + /// + /// Scrolls down within content by one page. + /// + public void PageDown() + { SetVerticalOffset(VerticalOffset + ViewportHeight); } + + /// + /// Scrolls up within content by one page. + /// + public void PageUp() + { SetVerticalOffset(VerticalOffset - ViewportHeight); } + + /// + /// Scrolls left within content by one page. + /// + public void PageLeft() + { SetHorizontalOffset(HorizontalOffset - ViewportWidth); } + + /// + /// Scrolls right within content by one page. + /// + public void PageRight() + { SetHorizontalOffset(HorizontalOffset + ViewportWidth); } + #endregion + + /// + /// Gets or sets a element that controls scrolling behavior. + /// + /// The scroll owner. + /// A element that controls scrolling behavior. This property has no default value. + public ScrollViewer ScrollOwner { get; set; } + + /// + /// Gets or sets a value that indicates whether scrolling on the horizontal axis is possible. + /// + /// true if this instance can horizontally scroll; otherwise, false. + /// true if scrolling is possible; otherwise, false. This property has no default value. + public bool CanHorizontallyScroll { get; set; } + + /// + /// Gets or sets a value that indicates whether scrolling on the vertical axis is possible. + /// + /// true if this instance can vertically scroll; otherwise, false. + /// true if scrolling is possible; otherwise, false. This property has no default value. + public bool CanVerticallyScroll { get; set; } + + /// + /// Gets the vertical size of the extent. + /// + /// The height of the extent. + /// A that represents, in device independent pixels, the vertical size of the extent.This property has no default value. + public double ExtentHeight + { get { return _Extent.Height; } } + + /// + /// Gets the horizontal size of the extent. + /// + /// The width of the extent. + /// A that represents, in device independent pixels, the horizontal size of the extent. This property has no default value. + public double ExtentWidth + { get { return _Extent.Width; } } + + /// + /// Gets the horizontal offset of the scrolled content. + /// + /// The horizontal offset. + /// A that represents, in device independent pixels, the horizontal offset. This property has no default value. + public double HorizontalOffset + { get { return _Offset.X; } } + + /// + /// Gets the vertical offset of the scrolled content. + /// + /// The vertical offset. + /// A that represents, in device independent pixels, the vertical offset of the scrolled content. Valid values are between zero and the minus the . This property has no default value. + public double VerticalOffset + { get { return _Offset.Y; } } + + /// + /// Gets the vertical size of the viewport for this content. + /// + /// The height of the viewport. + /// A that represents, in device independent pixels, the vertical size of the viewport for this content. This property has no default value. + public double ViewportHeight + { get { return _Viewport.Height; } } + + /// + /// Gets the horizontal size of the viewport for this content. + /// + /// The width of the viewport. + /// A that represents, in device independent pixels, the horizontal size of the viewport for this content. This property has no default value. + public double ViewportWidth + { get { return _Viewport.Width; } } + + /// + /// Forces content to scroll until the coordinate space of a object is visible. + /// + /// A that becomes visible. + /// A bounding rectangle that identifies the coordinate space to make visible. + /// A that is visible. + public Rect MakeVisible(Visual visual, Rect rectangle) + { + if (rectangle.IsEmpty || visual == null + || visual == this || !base.IsAncestorOf(visual)) + { return Rect.Empty; } + + rectangle = visual.TransformToAncestor(this).TransformBounds(rectangle); + + //rectangle.Inflate(50, 50); + rectangle.Scale(1.2, 1.2); + + Rect viewRect = new Rect(HorizontalOffset, + VerticalOffset, ViewportWidth, ViewportHeight); + rectangle.X += viewRect.X; + rectangle.Y += viewRect.Y; + + viewRect.X = CalculateNewScrollOffset(viewRect.Left, + viewRect.Right, rectangle.Left, rectangle.Right); + viewRect.Y = CalculateNewScrollOffset(viewRect.Top, + viewRect.Bottom, rectangle.Top, rectangle.Bottom); + SetHorizontalOffset(viewRect.X); + SetVerticalOffset(viewRect.Y); + rectangle.Intersect(viewRect); + rectangle.X -= viewRect.X; + rectangle.Y -= viewRect.Y; + + return rectangle; + } + + /// + /// Calculates the new scroll offset. + /// + /// The top view. + /// The bottom view. + /// The top child. + /// The bottom child. + /// System.Double. + private static double CalculateNewScrollOffset(double topView, + double bottomView, double topChild, double bottomChild) + { + bool offBottom = topChild < topView && bottomChild < bottomView; + bool offTop = bottomChild > bottomView && topChild > topView; + bool tooLarge = (bottomChild - topChild) > (bottomView - topView); + + if (!offBottom && !offTop) + { return topView; } //Don't do anything, already in view + + if ((offBottom && !tooLarge) || (offTop && tooLarge)) + { return topChild; } + + return (bottomChild - (bottomView - topView)); + } + + /// + /// Verifies the scroll data. + /// + /// The viewport. + /// The extent. + protected void VerifyScrollData(Size viewport, Size extent) + { + if (double.IsInfinity(viewport.Width)) + { viewport.Width = extent.Width; } + + if (double.IsInfinity(viewport.Height)) + { viewport.Height = extent.Height; } + + _Extent = extent; + _Viewport = viewport; + + _Offset.X = Math.Max(0, + Math.Min(_Offset.X, ExtentWidth - ViewportWidth)); + _Offset.Y = Math.Max(0, + Math.Min(_Offset.Y, ExtentHeight - ViewportHeight)); + + if (ScrollOwner != null) + { ScrollOwner.InvalidateScrollInfo(); } + } + + /// + /// Sets the amount of horizontal offset. + /// + /// The degree to which content is horizontally offset from the containing viewport. + public void SetHorizontalOffset(double offset) + { + offset = Math.Max(0, + Math.Min(offset, ExtentWidth - ViewportWidth)); + if (!offset.Equals(_Offset.X)) + { + _Offset.X = offset; + InvalidateArrange(); + } + } + + /// + /// Sets the amount of vertical offset. + /// + /// The degree to which content is vertically offset from the containing viewport. + public void SetVerticalOffset(double offset) + { + offset = Math.Max(0, + Math.Min(offset, ExtentHeight - ViewportHeight)); + if (!offset.Equals(_Offset.Y)) + { + _Offset.Y = offset; + InvalidateArrange(); + } + } + } +} -- cgit v1.2.3