From 2e0e225e2669113ed9d996af629e441affba19db Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 9 Dec 2014 01:14:05 +0100 Subject: [PATCH] Added selection to TextBox. --- Perspex.Controls/TextBox.cs | 73 +++++++++++++------ Perspex.Controls/TextBoxView.cs | 25 +++++++ Perspex.SceneGraph/Media/FormattedText.cs | 4 +- .../Input/WindowsKeyboardDevice.cs | 17 +++-- 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/Perspex.Controls/TextBox.cs b/Perspex.Controls/TextBox.cs index a93e663d7b..58800ba3e6 100644 --- a/Perspex.Controls/TextBox.cs +++ b/Perspex.Controls/TextBox.cs @@ -20,11 +20,18 @@ namespace Perspex.Controls public static readonly PerspexProperty AcceptsTabProperty = PerspexProperty.Register("AcceptsTab"); + public static readonly PerspexProperty CaretIndexProperty = + PerspexProperty.Register("CaretIndex", coerce: CoerceCaretIndex); + + public static readonly PerspexProperty SelectionStartProperty = + PerspexProperty.Register("SelectionStart", coerce: CoerceCaretIndex); + + public static readonly PerspexProperty SelectionEndProperty = + PerspexProperty.Register("SelectionEnd", coerce: CoerceCaretIndex); + public static readonly PerspexProperty TextProperty = TextBlock.TextProperty.AddOwner(); - private int caretIndex; - private TextBoxView textBoxView; static TextBox() @@ -54,23 +61,20 @@ namespace Perspex.Controls public int CaretIndex { - get - { - return this.caretIndex; - } - - set - { - var text = this.Text ?? string.Empty; + get { return this.GetValue(CaretIndexProperty); } + set { this.SetValue(CaretIndexProperty, value); } + } - value = Math.Min(Math.Max(value, 0), text.Length); + public int SelectionStart + { + get { return this.GetValue(SelectionStartProperty); } + set { this.SetValue(SelectionStartProperty, value); } + } - if (this.caretIndex != value) - { - this.caretIndex = value; - this.textBoxView.CaretMoved(); - } - } + public int SelectionEnd + { + get { return this.GetValue(SelectionEndProperty); } + set { this.SetValue(SelectionEndProperty, value); } } public string Text @@ -95,33 +99,54 @@ namespace Perspex.Controls textContainer.Content = this.textBoxView = new TextBoxView(this); } + private static int CoerceCaretIndex(PerspexObject o, int value) + { + var text = o.GetValue(TextProperty); + var length = (text != null) ? text.Length : 0; + return Math.Max(0, Math.Min(length, value)); + } + + private void MoveHorizontal(int count, ModifierKeys modifiers) + { + if (modifiers == ModifierKeys.None) + { + this.CaretIndex += count; + this.SelectionStart = this.SelectionEnd = this.CaretIndex; + } + else if ((modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) + { + this.SelectionEnd = (this.CaretIndex += count); + } + } + private void OnKeyDown(object sender, KeyEventArgs e) { string text = this.Text ?? string.Empty; + var caretIndex = this.CaretIndex; switch (e.Key) { case Key.Left: - --this.CaretIndex; + this.MoveHorizontal(-1, e.Device.Modifiers); break; case Key.Right: - ++this.CaretIndex; + this.MoveHorizontal(1, e.Device.Modifiers); break; case Key.Back: - if (this.caretIndex > 0) + if (this.CaretIndex > 0) { - this.Text = text.Substring(0, this.caretIndex - 1) + text.Substring(this.caretIndex); + this.Text = text.Substring(0, caretIndex - 1) + text.Substring(caretIndex); --this.CaretIndex; } break; case Key.Delete: - if (this.caretIndex < text.Length) + if (caretIndex < text.Length) { - this.Text = text.Substring(0, this.caretIndex) + text.Substring(this.caretIndex + 1); + this.Text = text.Substring(0, caretIndex) + text.Substring(caretIndex + 1); } break; @@ -145,7 +170,7 @@ namespace Perspex.Controls default: if (!string.IsNullOrEmpty(e.Text)) { - this.Text = text.Substring(0, this.caretIndex) + e.Text + text.Substring(this.caretIndex); + this.Text = text.Substring(0, caretIndex) + e.Text + text.Substring(caretIndex); ++this.CaretIndex; } diff --git a/Perspex.Controls/TextBoxView.cs b/Perspex.Controls/TextBoxView.cs index ddc3ab9112..bd554c797f 100644 --- a/Perspex.Controls/TextBoxView.cs +++ b/Perspex.Controls/TextBoxView.cs @@ -7,6 +7,7 @@ namespace Perspex.Controls { using System; + using System.Reactive.Linq; using Perspex.Media; using Perspex.Threading; @@ -25,6 +26,13 @@ namespace Perspex.Controls this.caretTimer.Tick += this.CaretTimerTick; this.parent = parent; this[!TextProperty] = parent[!TextProperty]; + + Observable.Merge( + this.parent.GetObservable(TextBox.SelectionStartProperty), + this.parent.GetObservable(TextBox.SelectionEndProperty)) + .Subscribe(_ => this.InvalidateVisual()); + + parent.GetObservable(TextBox.CaretIndexProperty).Subscribe(_ => this.CaretMoved()); } public int GetCaretIndex(Point point) @@ -47,6 +55,23 @@ namespace Perspex.Controls public override void Render(IDrawingContext context) { + var selectionStart = this.parent.SelectionStart; + var selectionEnd = this.parent.SelectionEnd; + + if (selectionStart != selectionEnd) + { + var start = Math.Min(selectionStart, selectionEnd); + var length = Math.Max(selectionStart, selectionEnd) - start; + var rects = this.FormattedText.HitTestTextRange(start, length); + + var brush = new SolidColorBrush(0xff086f9e); + + foreach (var rect in rects) + { + context.FillRectange(brush, rect); + } + } + base.Render(context); if (this.parent.IsFocused) diff --git a/Perspex.SceneGraph/Media/FormattedText.cs b/Perspex.SceneGraph/Media/FormattedText.cs index d6dc09665e..114a480551 100644 --- a/Perspex.SceneGraph/Media/FormattedText.cs +++ b/Perspex.SceneGraph/Media/FormattedText.cs @@ -79,7 +79,7 @@ namespace Perspex.Media return this.PlatformImpl.HitTestTextPosition(index); } - public IEnumerable HitTestTextRange(int index, int length, Point origin) + public IEnumerable HitTestTextRange(int index, int length, Point origin = default(Point)) { return this.PlatformImpl.HitTestTextRange(index, length, origin); } @@ -89,4 +89,4 @@ namespace Perspex.Media return this.PlatformImpl.Measure(); } } -} +} diff --git a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs index c5bee8c6a8..998ad78ec2 100644 --- a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs +++ b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs @@ -28,26 +28,22 @@ namespace Perspex.Win32.Input { ModifierKeys result = 0; - if (this.GetKeyStates(Key.LeftAlt) == KeyStates.Down || - this.GetKeyStates(Key.RightAlt) == KeyStates.Down) + if (this.IsDown(Key.LeftAlt) || this.IsDown(Key.RightAlt)) { result |= ModifierKeys.Alt; } - if (this.GetKeyStates(Key.LeftCtrl) == KeyStates.Down || - this.GetKeyStates(Key.RightCtrl) == KeyStates.Down) + if (this.IsDown(Key.LeftCtrl) || this.IsDown(Key.RightCtrl)) { result |= ModifierKeys.Control; } - if (this.GetKeyStates(Key.LeftShift) == KeyStates.Down || - this.GetKeyStates(Key.RightShift) == KeyStates.Down) + if (this.IsDown(Key.LeftShift) || this.IsDown(Key.RightShift)) { result |= ModifierKeys.Shift; } - if (this.GetKeyStates(Key.LWin) == KeyStates.Down || - this.GetKeyStates(Key.RWin) == KeyStates.Down) + if (this.IsDown(Key.LWin) || this.IsDown(Key.RWin)) { result |= ModifierKeys.Windows; } @@ -79,6 +75,11 @@ namespace Perspex.Win32.Input UnmanagedMethods.GetKeyboardState(this.keyStates); } + private bool IsDown(Key key) + { + return (this.GetKeyStates(key) & KeyStates.Down) != 0; + } + private KeyStates GetKeyStates(Key key) { int vk = KeyInterop.VirtualKeyFromKey(key);