From a91d21d0a4e1454c1c501be4b8d1973f070283b7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 1 Apr 2016 21:14:47 +0200 Subject: [PATCH] Make some TextBox/Block properties direct. --- src/Perspex.Base/DirectProperty.cs | 20 ++++- src/Perspex.Base/PerspexProperty.cs | 11 ++- src/Perspex.Base/PerspexProperty`1.cs | 8 +- src/Perspex.Base/StyledPropertyBase.cs | 2 +- .../Presenters/TextPresenter.cs | 69 ++++++++++++---- src/Perspex.Controls/TextBlock.cs | 19 ++--- src/Perspex.Controls/TextBox.cs | 81 +++++++++++++------ 7 files changed, 151 insertions(+), 59 deletions(-) diff --git a/src/Perspex.Base/DirectProperty.cs b/src/Perspex.Base/DirectProperty.cs index 7ae19ec520..82d47e7c1d 100644 --- a/src/Perspex.Base/DirectProperty.cs +++ b/src/Perspex.Base/DirectProperty.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using Perspex.Data; namespace Perspex { @@ -44,11 +45,13 @@ namespace Perspex /// The property to copy. /// Gets the current value of the property. /// Sets the value of the property. May be null. + /// Optional overridden metadata. private DirectProperty( PerspexProperty source, Func getter, - Action setter) - : base(source, typeof(TOwner)) + Action setter, + PropertyMetadata metadata) + : base(source, typeof(TOwner), metadata) { Contract.Requires(getter != null); @@ -76,16 +79,25 @@ namespace Perspex /// Registers the direct property on another type. /// /// The type of the additional owner. + /// Gets the current value of the property. + /// Sets the value of the property. + /// + /// The value to use when the property is set to + /// + /// The default binding mode for the property. /// The property. public DirectProperty AddOwner( Func getter, - Action setter = null) + Action setter = null, + TValue unsetValue = default(TValue), + BindingMode defaultBindingMode = BindingMode.OneWay) where TNewOwner : PerspexObject { var result = new DirectProperty( this, getter, - setter); + setter, + new DirectPropertyMetadata(unsetValue, defaultBindingMode)); PerspexPropertyRegistry.Instance.Register(typeof(TNewOwner), result); return result; diff --git a/src/Perspex.Base/PerspexProperty.cs b/src/Perspex.Base/PerspexProperty.cs index e88c7f4418..36c534266e 100644 --- a/src/Perspex.Base/PerspexProperty.cs +++ b/src/Perspex.Base/PerspexProperty.cs @@ -71,7 +71,11 @@ namespace Perspex /// /// The direct property to copy. /// The new owner type. - protected PerspexProperty(PerspexProperty source, Type ownerType) + /// Optional overridden metadata. + protected PerspexProperty( + PerspexProperty source, + Type ownerType, + PropertyMetadata metadata) { Contract.Requires(source != null); Contract.Requires(ownerType != null); @@ -86,6 +90,11 @@ namespace Perspex Notifying = source.Notifying; Id = source.Id; _defaultMetadata = source._defaultMetadata; + + if (metadata != null) + { + _metadata.Add(ownerType, metadata); + } } /// diff --git a/src/Perspex.Base/PerspexProperty`1.cs b/src/Perspex.Base/PerspexProperty`1.cs index 101aeaed22..1ad1cef2e1 100644 --- a/src/Perspex.Base/PerspexProperty`1.cs +++ b/src/Perspex.Base/PerspexProperty`1.cs @@ -32,8 +32,12 @@ namespace Perspex /// /// The property to copy. /// The new owner type. - protected PerspexProperty(PerspexProperty source, Type ownerType) - : base(source, ownerType) + /// Optional overridden metadata. + protected PerspexProperty( + PerspexProperty source, + Type ownerType, + PropertyMetadata metadata) + : base(source, ownerType, metadata) { } } diff --git a/src/Perspex.Base/StyledPropertyBase.cs b/src/Perspex.Base/StyledPropertyBase.cs index 2f5311d706..e77c5704eb 100644 --- a/src/Perspex.Base/StyledPropertyBase.cs +++ b/src/Perspex.Base/StyledPropertyBase.cs @@ -46,7 +46,7 @@ namespace Perspex /// The property to add the owner to. /// The type of the class that registers the property. protected StyledPropertyBase(StyledPropertyBase source, Type ownerType) - : base(source, ownerType) + : base(source, ownerType, null) { _inherits = source.Inherits; } diff --git a/src/Perspex.Controls/Presenters/TextPresenter.cs b/src/Perspex.Controls/Presenters/TextPresenter.cs index a08c1c7b5e..3bcc07a9e8 100644 --- a/src/Perspex.Controls/Presenters/TextPresenter.cs +++ b/src/Perspex.Controls/Presenters/TextPresenter.cs @@ -12,24 +12,28 @@ namespace Perspex.Controls.Presenters { public class TextPresenter : TextBlock { - public static readonly StyledProperty CaretIndexProperty = - TextBox.CaretIndexProperty.AddOwner(); + public static readonly DirectProperty CaretIndexProperty = + TextBox.CaretIndexProperty.AddOwner( + o => o.CaretIndex, + (o, v) => o.CaretIndex = v); - public static readonly StyledProperty SelectionStartProperty = - TextBox.SelectionStartProperty.AddOwner(); + public static readonly DirectProperty SelectionStartProperty = + TextBox.SelectionStartProperty.AddOwner( + o => o.SelectionStart, + (o, v) => o.SelectionStart = v); - public static readonly StyledProperty SelectionEndProperty = - TextBox.SelectionEndProperty.AddOwner(); + public static readonly DirectProperty SelectionEndProperty = + TextBox.SelectionEndProperty.AddOwner( + o => o.SelectionEnd, + (o, v) => o.SelectionEnd = v); private readonly DispatcherTimer _caretTimer; + private int _caretIndex; + private int _selectionStart; + private int _selectionEnd; private bool _caretBlink; private IBrush _highlightBrush; - static TextPresenter() - { - CaretIndexProperty.OverrideValidation((o, v) => v); - } - public TextPresenter() { _caretTimer = new DispatcherTimer(); @@ -47,20 +51,44 @@ namespace Perspex.Controls.Presenters public int CaretIndex { - get { return GetValue(CaretIndexProperty); } - set { SetValue(CaretIndexProperty, value); } + get + { + return _caretIndex; + } + + set + { + value = CoerceCaretIndex(value); + SetAndRaise(CaretIndexProperty, ref _caretIndex, value); + } } public int SelectionStart { - get { return GetValue(SelectionStartProperty); } - set { SetValue(SelectionStartProperty, value); } + get + { + return _selectionStart; + } + + set + { + value = CoerceCaretIndex(value); + SetAndRaise(SelectionStartProperty, ref _selectionStart, value); + } } public int SelectionEnd { - get { return GetValue(SelectionEndProperty); } - set { SetValue(SelectionEndProperty, value); } + get + { + return _selectionEnd; + } + + set + { + value = CoerceCaretIndex(value); + SetAndRaise(SelectionEndProperty, ref _selectionEnd, value); + } } public int GetCaretIndex(Point point) @@ -206,6 +234,13 @@ namespace Perspex.Controls.Presenters } } + private int CoerceCaretIndex(int value) + { + var text = Text; + var length = text?.Length ?? 0; + return Math.Max(0, Math.Min(length, value)); + } + private void CaretTimerTick(object sender, EventArgs e) { _caretBlink = !_caretBlink; diff --git a/src/Perspex.Controls/TextBlock.cs b/src/Perspex.Controls/TextBlock.cs index 0ddc3c30ee..162774e732 100644 --- a/src/Perspex.Controls/TextBlock.cs +++ b/src/Perspex.Controls/TextBlock.cs @@ -72,8 +72,11 @@ namespace Perspex.Controls /// /// Defines the property. /// - public static readonly StyledProperty TextProperty = - PerspexProperty.Register(nameof(Text)); + public static readonly DirectProperty TextProperty = + PerspexProperty.RegisterDirect( + nameof(Text), + o => o.Text, + (o, v) => o.Text = v); /// /// Defines the property. @@ -87,14 +90,8 @@ namespace Perspex.Controls public static readonly StyledProperty TextWrappingProperty = PerspexProperty.Register(nameof(TextWrapping)); - /// - /// The formatted text used for rendering. - /// + private string _text; private FormattedText _formattedText; - - /// - /// Stores the last constraint passed to MeasureOverride. - /// private Size _constraint; /// @@ -140,8 +137,8 @@ namespace Perspex.Controls [Content] public string Text { - get { return GetValue(TextProperty); } - set { SetValue(TextProperty, value); } + get { return _text; } + set { SetAndRaise(TextProperty, ref _text, value); } } /// diff --git a/src/Perspex.Controls/TextBox.cs b/src/Perspex.Controls/TextBox.cs index d44401b1a3..3985dbef60 100644 --- a/src/Perspex.Controls/TextBox.cs +++ b/src/Perspex.Controls/TextBox.cs @@ -29,18 +29,29 @@ namespace Perspex.Controls public static readonly DirectProperty CanScrollHorizontallyProperty = PerspexProperty.RegisterDirect("CanScrollHorizontally", o => o.CanScrollHorizontally); - // TODO: Should CaretIndex, SelectionStart/End and Text be direct properties? - public static readonly StyledProperty CaretIndexProperty = - PerspexProperty.Register("CaretIndex", validate: ValidateCaretIndex); - - public static readonly StyledProperty SelectionStartProperty = - PerspexProperty.Register("SelectionStart", validate: ValidateCaretIndex); - - public static readonly StyledProperty SelectionEndProperty = - PerspexProperty.Register("SelectionEnd", validate: ValidateCaretIndex); - - public static readonly StyledProperty TextProperty = - TextBlock.TextProperty.AddOwner(); + public static readonly DirectProperty CaretIndexProperty = + PerspexProperty.RegisterDirect( + nameof(CaretIndex), + o => o.CaretIndex, + (o, v) => o.CaretIndex = v); + + public static readonly DirectProperty SelectionStartProperty = + PerspexProperty.RegisterDirect( + nameof(SelectionStart), + o => o.SelectionStart, + (o, v) => o.SelectionStart = v); + + public static readonly DirectProperty SelectionEndProperty = + PerspexProperty.RegisterDirect( + nameof(SelectionEnd), + o => o.SelectionEnd, + (o, v) => o.SelectionEnd = v); + + public static readonly DirectProperty TextProperty = + TextBlock.TextProperty.AddOwner( + o => o.Text, + (o, v) => o.Text = v, + defaultBindingMode: BindingMode.TwoWay); public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); @@ -71,6 +82,10 @@ namespace Perspex.Controls public bool Equals(UndoRedoState other) => ReferenceEquals(Text, other.Text) || Equals(Text, other.Text); } + private string _text; + private int _caretIndex; + private int _selectionStart; + private int _selectionEnd; private bool _canScrollHorizontally; private TextPresenter _presenter; private UndoRedoHelper _undoRedoHelper; @@ -78,7 +93,6 @@ namespace Perspex.Controls static TextBox() { FocusableProperty.OverrideDefaultValue(typeof(TextBox), true); - TextProperty.OverrideMetadata(new StyledPropertyMetadata(defaultBindingMode: BindingMode.TwoWay)); } public TextBox() @@ -117,10 +131,15 @@ namespace Perspex.Controls public int CaretIndex { - get { return GetValue(CaretIndexProperty); } + get + { + return _caretIndex; + } + set { - SetValue(CaretIndexProperty, value); + value = CoerceCaretIndex(value); + SetAndRaise(CaretIndexProperty, ref _caretIndex, value); if (_undoRedoHelper.IsLastState && _undoRedoHelper.LastState.Text == Text) _undoRedoHelper.UpdateLastState(); } @@ -128,21 +147,37 @@ namespace Perspex.Controls public int SelectionStart { - get { return GetValue(SelectionStartProperty); } - set { SetValue(SelectionStartProperty, value); } + get + { + return _selectionStart; + } + + set + { + value = CoerceCaretIndex(value); + SetAndRaise(SelectionStartProperty, ref _selectionStart, value); + } } public int SelectionEnd { - get { return GetValue(SelectionEndProperty); } - set { SetValue(SelectionEndProperty, value); } + get + { + return _selectionEnd; + } + + set + { + value = CoerceCaretIndex(value); + SetAndRaise(SelectionEndProperty, ref _selectionEnd, value); + } } [Content] public string Text { - get { return GetValue(TextProperty); } - set { SetValue(TextProperty, value); } + get { return _text; } + set { SetAndRaise(TextProperty, ref _text, value); } } public TextAlignment TextAlignment @@ -426,9 +461,9 @@ namespace Perspex.Controls } } - private static int ValidateCaretIndex(PerspexObject o, int value) + private int CoerceCaretIndex(int value) { - var text = o.GetValue(TextProperty); + var text = Text; var length = text?.Length ?? 0; return Math.Max(0, Math.Min(length, value)); }