diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj index e0b1dee911..8c3511e128 100644 --- a/Perspex.Controls/Perspex.Controls.csproj +++ b/Perspex.Controls/Perspex.Controls.csproj @@ -71,7 +71,7 @@ - + diff --git a/Perspex.Controls/Presenters/ScrollContentPresenter.cs b/Perspex.Controls/Presenters/ScrollContentPresenter.cs index e7e18590e4..4ec114cacc 100644 --- a/Perspex.Controls/Presenters/ScrollContentPresenter.cs +++ b/Perspex.Controls/Presenters/ScrollContentPresenter.cs @@ -36,8 +36,6 @@ namespace Perspex.Controls.Presenters public ScrollContentPresenter() { - this.GetObservable(ContentProperty).Subscribe(this.ContentChanged); - this.AddHandler( Control.RequestBringIntoViewEvent, new EventHandler(this.BringIntoViewRequested)); @@ -145,21 +143,5 @@ namespace Perspex.Controls.Presenters this.Offset = offset; } - - private void ContentChanged(object content) - { - var scrollInfo = content as IScrollInfo; - - if (this.contentBindings != null) - { - this.contentBindings.Dispose(); - this.contentBindings = null; - } - - if (scrollInfo != null) - { - this.contentBindings = this.Bind(CanScrollHorizontallyProperty, scrollInfo.CanScrollHorizontally); - } - } } } diff --git a/Perspex.Controls/Presenters/TextPresenter.cs b/Perspex.Controls/Presenters/TextPresenter.cs index 4c6abb5d54..da81f29dec 100644 --- a/Perspex.Controls/Presenters/TextPresenter.cs +++ b/Perspex.Controls/Presenters/TextPresenter.cs @@ -16,7 +16,7 @@ namespace Perspex.Controls using Perspex.Media; using Perspex.Threading; - public class TextPresenter : TextBlock, IScrollInfo + public class TextPresenter : TextBlock { public static readonly PerspexProperty AcceptsReturnProperty = TextBox.AcceptsReturnProperty.AddOwner(); @@ -97,16 +97,6 @@ namespace Perspex.Controls get { return base.FormattedText; } } - IObservable IScrollInfo.CanScrollHorizontally - { - get { return this.canScrollHorizontally; } - } - - IObservable IScrollInfo.IsHorizontalScrollBarVisible - { - get { return Observable.Never().StartWith(false); } - } - public int GetCaretIndex(Point point) { var hit = this.FormattedText.HitTestPoint(point); diff --git a/Perspex.Controls/Primitives/IScrollInfo.cs b/Perspex.Controls/Primitives/IScrollInfo.cs deleted file mode 100644 index 7e60b18558..0000000000 --- a/Perspex.Controls/Primitives/IScrollInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright 2014 MIT Licence. See licence.md for more information. -// -// ----------------------------------------------------------------------- - -namespace Perspex.Controls.Primitives -{ - using System; - - /// - /// Allows the child of a to communicate with the scroll viewer. - /// - /// - /// Note that this interface has a different purpose to IScrollInfo in WPF! I would give it - /// a different name, but it does what it suggests - give information about the scrolling - /// requirements of a control. - /// - public interface IScrollInfo - { - IObservable CanScrollHorizontally { get; } - - IObservable IsHorizontalScrollBarVisible { get; } - } -} diff --git a/Perspex.Controls/Primitives/ScrollBar.cs b/Perspex.Controls/Primitives/ScrollBar.cs index 5a8c0f683f..18d48b9fd9 100644 --- a/Perspex.Controls/Primitives/ScrollBar.cs +++ b/Perspex.Controls/Primitives/ScrollBar.cs @@ -7,6 +7,7 @@ namespace Perspex.Controls.Primitives { using System; + using System.Reactive; using System.Reactive.Linq; public class ScrollBar : TemplatedControl @@ -23,6 +24,9 @@ namespace Perspex.Controls.Primitives public static readonly PerspexProperty ViewportSizeProperty = PerspexProperty.Register("ViewportSize", defaultValue: double.NaN); + public static readonly PerspexProperty VisibilityProperty = + PerspexProperty.Register("Visibility"); + public static readonly PerspexProperty OrientationProperty = PerspexProperty.Register("Orientation"); @@ -32,6 +36,17 @@ namespace Perspex.Controls.Primitives Control.PseudoClass(OrientationProperty, x => x == Orientation.Vertical, ":vertical"); } + public ScrollBar() + { + var isVisible = Observable.Merge( + this.GetObservable(MinimumProperty).Select(_ => Unit.Default), + this.GetObservable(MaximumProperty).Select(_ => Unit.Default), + this.GetObservable(ViewportSizeProperty).Select(_ => Unit.Default), + this.GetObservable(VisibilityProperty).Select(_ => Unit.Default)) + .Select(_ => this.CalculateIsVisible()); + this.Bind(IsVisibleProperty, isVisible, BindingPriority.Style); + } + public double Minimum { get { return this.GetValue(MinimumProperty); } @@ -56,6 +71,12 @@ namespace Perspex.Controls.Primitives set { this.SetValue(ViewportSizeProperty, value); } } + public ScrollBarVisibility Visibility + { + get { return this.GetValue(VisibilityProperty); } + set { this.SetValue(VisibilityProperty, value); } + } + public Orientation Orientation { get { return this.GetValue(OrientationProperty); } @@ -66,5 +87,24 @@ namespace Perspex.Controls.Primitives { return base.MeasureOverride(availableSize); } + + private bool CalculateIsVisible() + { + switch (this.Visibility) + { + case ScrollBarVisibility.Visible: + return true; + + case ScrollBarVisibility.Hidden: + return false; + + case ScrollBarVisibility.Auto: + var viewportSize = this.ViewportSize; + return double.IsNaN(viewportSize) || viewportSize < this.Maximum - this.Minimum; + + default: + throw new InvalidOperationException("Invalid value for ScrollBar.Visibility."); + } + } } } diff --git a/Perspex.Controls/Primitives/ScrollBarVisibility.cs b/Perspex.Controls/Primitives/ScrollBarVisibility.cs new file mode 100644 index 0000000000..8f4c275f2f --- /dev/null +++ b/Perspex.Controls/Primitives/ScrollBarVisibility.cs @@ -0,0 +1,15 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2014 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Controls.Primitives +{ + public enum ScrollBarVisibility + { + Visible, + Hidden, + Auto, + } +} diff --git a/Perspex.Controls/ScrollViewer.cs b/Perspex.Controls/ScrollViewer.cs index ae95d38808..9cedfc2442 100644 --- a/Perspex.Controls/ScrollViewer.cs +++ b/Perspex.Controls/ScrollViewer.cs @@ -23,10 +23,7 @@ namespace Perspex.Controls PerspexProperty.Register("Viewport"); public static readonly PerspexProperty CanScrollHorizontallyProperty = - PerspexProperty.Register("CanScrollHorizontally", false); - - public static readonly PerspexProperty IsHorizontalScrollBarVisibleProperty = - PerspexProperty.Register("IsHorizontalScrollBarVisible"); + PerspexProperty.RegisterAttached("CanScrollHorizontally", false); public static readonly PerspexProperty HorizontalScrollBarMaximumProperty = PerspexProperty.Register("HorizontalScrollBarMaximum"); @@ -37,8 +34,8 @@ namespace Perspex.Controls public static readonly PerspexProperty HorizontalScrollBarViewportSizeProperty = PerspexProperty.Register("HorizontalScrollBarViewportSize"); - public static readonly PerspexProperty IsVerticalScrollBarVisibleProperty = - PerspexProperty.Register("IsVerticalScrollBarVisible"); + public static readonly PerspexProperty HorizontalScrollBarVisibilityProperty = + PerspexProperty.RegisterAttached("HorizontalScrollBarVisibility", ScrollBarVisibility.Auto); public static readonly PerspexProperty VerticalScrollBarMaximumProperty = PerspexProperty.Register("VerticalScrollBarMaximum"); @@ -49,6 +46,9 @@ namespace Perspex.Controls public static readonly PerspexProperty VerticalScrollBarViewportSizeProperty = PerspexProperty.Register("VerticalScrollBarViewportSize"); + public static readonly PerspexProperty VerticalScrollBarVisibilityProperty = + PerspexProperty.RegisterAttached("VerticalScrollBarVisibility", ScrollBarVisibility.Auto); + private IDisposable contentBindings; static ScrollViewer() @@ -64,31 +64,21 @@ namespace Perspex.Controls this.GetObservable(ViewportProperty)) .Select(x => new { Extent = x[0], Viewport = x[1] }); - this.Bind( - IsHorizontalScrollBarVisibleProperty, - extentAndViewport.Select(x => x.Extent.Width > x.Viewport.Width), - BindingPriority.Style); - this.Bind( HorizontalScrollBarMaximumProperty, - extentAndViewport.Select(x => x.Extent.Width - x.Viewport.Width)); + extentAndViewport.Select(x => Math.Max(x.Extent.Width - x.Viewport.Width, 0))); this.Bind( HorizontalScrollBarViewportSizeProperty, - extentAndViewport.Select(x => (x.Viewport.Width / x.Extent.Width) * (x.Extent.Width - x.Viewport.Width))); - - this.Bind( - IsVerticalScrollBarVisibleProperty, - extentAndViewport.Select(x => x.Extent.Height > x.Viewport.Height), - BindingPriority.Style); + extentAndViewport.Select(x => Math.Max((x.Viewport.Width / x.Extent.Width) * (x.Extent.Width - x.Viewport.Width), 0))); this.Bind( VerticalScrollBarMaximumProperty, - extentAndViewport.Select(x => x.Extent.Height - x.Viewport.Height)); + extentAndViewport.Select(x => Math.Max(x.Extent.Height - x.Viewport.Height, 0))); this.Bind( VerticalScrollBarViewportSizeProperty, - extentAndViewport.Select(x => (x.Viewport.Height / x.Extent.Height) * (x.Extent.Height - x.Viewport.Height))); + extentAndViewport.Select(x => Math.Max((x.Viewport.Height / x.Extent.Height) * (x.Extent.Height - x.Viewport.Height), 0))); this.GetObservable(OffsetProperty).Subscribe(x => { @@ -101,8 +91,6 @@ namespace Perspex.Controls this.GetObservable(VerticalScrollBarValueProperty)) .Select(x => new Vector(x[0], x[1])) .Subscribe(x => this.Offset = x); - - this.GetObservable(ContentProperty).Subscribe(this.ContentChanged); } public Size Extent @@ -129,6 +117,18 @@ namespace Perspex.Controls set { this.SetValue(CanScrollHorizontallyProperty, value); } } + public ScrollBarVisibility HorizontalScrollBarVisibility + { + get { return this.GetValue(HorizontalScrollBarVisibilityProperty); } + set { this.SetValue(HorizontalScrollBarVisibilityProperty, value); } + } + + public ScrollBarVisibility VerticalScrollBarVisibility + { + get { return this.GetValue(VerticalScrollBarVisibilityProperty); } + set { this.SetValue(VerticalScrollBarVisibilityProperty, value); } + } + protected override Size MeasureOverride(Size availableSize) { return base.MeasureOverride(availableSize); @@ -156,23 +156,5 @@ namespace Perspex.Controls return value; } } - - private void ContentChanged(object content) - { - var scrollInfo = content as IScrollInfo; - - if (this.contentBindings != null) - { - this.contentBindings.Dispose(); - this.contentBindings = null; - } - - if (scrollInfo != null) - { - this.contentBindings = this.Bind( - IsHorizontalScrollBarVisibleProperty, - scrollInfo.IsHorizontalScrollBarVisible); - } - } } } diff --git a/Perspex.Controls/TextBox.cs b/Perspex.Controls/TextBox.cs index 19029cc43a..47eef390fb 100644 --- a/Perspex.Controls/TextBox.cs +++ b/Perspex.Controls/TextBox.cs @@ -7,6 +7,7 @@ namespace Perspex.Controls { using System; + using System.Reactive.Linq; using Perspex.Controls.Primitives; public class TextBox : TemplatedControl @@ -32,6 +33,25 @@ namespace Perspex.Controls public static readonly PerspexProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); + public TextBox() + { + var canScrollHorizontally = this.GetObservable(AcceptsReturnProperty) + .Select(x => !x); + + this.Bind( + ScrollViewer.CanScrollHorizontallyProperty, + canScrollHorizontally, + BindingPriority.Style); + + var horizontalScrollBarVisibility = this.GetObservable(AcceptsReturnProperty) + .Select(x => x ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden); + + this.Bind( + ScrollViewer.HorizontalScrollBarVisibilityProperty, + horizontalScrollBarVisibility, + BindingPriority.Style); + } + public bool AcceptsReturn { get { return this.GetValue(AcceptsReturnProperty); } diff --git a/Perspex.Themes.Default/ScrollViewerStyle.cs b/Perspex.Themes.Default/ScrollViewerStyle.cs index a1ff62f6a7..a1d51c9024 100644 --- a/Perspex.Themes.Default/ScrollViewerStyle.cs +++ b/Perspex.Themes.Default/ScrollViewerStyle.cs @@ -58,20 +58,20 @@ namespace Perspex.Themes.Default { Id = "horizontalScrollBar", Orientation = Orientation.Horizontal, - [~ScrollBar.IsVisibleProperty] = control[~ScrollViewer.IsHorizontalScrollBarVisibleProperty], [~ScrollBar.MaximumProperty] = control[~ScrollViewer.HorizontalScrollBarMaximumProperty], [~~ScrollBar.ValueProperty] = control[~~ScrollViewer.HorizontalScrollBarValueProperty], [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.HorizontalScrollBarViewportSizeProperty], + [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty], [Grid.RowProperty] = 1, }, new ScrollBar { Id = "verticalScrollBar", Orientation = Orientation.Vertical, - [~ScrollBar.IsVisibleProperty] = control[~ScrollViewer.IsVerticalScrollBarVisibleProperty], [~ScrollBar.MaximumProperty] = control[~ScrollViewer.VerticalScrollBarMaximumProperty], [~~ScrollBar.ValueProperty] = control[~~ScrollViewer.VerticalScrollBarValueProperty], [~ScrollBar.ViewportSizeProperty] = control[~ScrollViewer.VerticalScrollBarViewportSizeProperty], + [~ScrollBar.VisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty], [Grid.ColumnProperty] = 1, }, }, diff --git a/Perspex.Themes.Default/TextBoxStyle.cs b/Perspex.Themes.Default/TextBoxStyle.cs index 0c609a9a06..065e2c7831 100644 --- a/Perspex.Themes.Default/TextBoxStyle.cs +++ b/Perspex.Themes.Default/TextBoxStyle.cs @@ -49,6 +49,9 @@ namespace Perspex.Themes.Default [~Border.BorderThicknessProperty] = control[~TextBox.BorderThicknessProperty], Content = new ScrollViewer { + [~ScrollViewer.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty], + [~ScrollViewer.HorizontalScrollBarVisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty], + [~ScrollViewer.VerticalScrollBarVisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty], Content = new TextPresenter { [~TextPresenter.AcceptsReturnProperty] = control[~TextBox.AcceptsReturnProperty],