Browse Source

Added selection to TextBox.

pull/10/head
Steven Kirk 12 years ago
parent
commit
2e0e225e26
  1. 73
      Perspex.Controls/TextBox.cs
  2. 25
      Perspex.Controls/TextBoxView.cs
  3. 4
      Perspex.SceneGraph/Media/FormattedText.cs
  4. 17
      Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs

73
Perspex.Controls/TextBox.cs

@ -20,11 +20,18 @@ namespace Perspex.Controls
public static readonly PerspexProperty<bool> AcceptsTabProperty = public static readonly PerspexProperty<bool> AcceptsTabProperty =
PerspexProperty.Register<TextBox, bool>("AcceptsTab"); PerspexProperty.Register<TextBox, bool>("AcceptsTab");
public static readonly PerspexProperty<int> CaretIndexProperty =
PerspexProperty.Register<TextBox, int>("CaretIndex", coerce: CoerceCaretIndex);
public static readonly PerspexProperty<int> SelectionStartProperty =
PerspexProperty.Register<TextBox, int>("SelectionStart", coerce: CoerceCaretIndex);
public static readonly PerspexProperty<int> SelectionEndProperty =
PerspexProperty.Register<TextBox, int>("SelectionEnd", coerce: CoerceCaretIndex);
public static readonly PerspexProperty<string> TextProperty = public static readonly PerspexProperty<string> TextProperty =
TextBlock.TextProperty.AddOwner<TextBox>(); TextBlock.TextProperty.AddOwner<TextBox>();
private int caretIndex;
private TextBoxView textBoxView; private TextBoxView textBoxView;
static TextBox() static TextBox()
@ -54,23 +61,20 @@ namespace Perspex.Controls
public int CaretIndex public int CaretIndex
{ {
get get { return this.GetValue(CaretIndexProperty); }
{ set { this.SetValue(CaretIndexProperty, value); }
return this.caretIndex; }
}
set
{
var text = this.Text ?? string.Empty;
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) public int SelectionEnd
{ {
this.caretIndex = value; get { return this.GetValue(SelectionEndProperty); }
this.textBoxView.CaretMoved(); set { this.SetValue(SelectionEndProperty, value); }
}
}
} }
public string Text public string Text
@ -95,33 +99,54 @@ namespace Perspex.Controls
textContainer.Content = this.textBoxView = new TextBoxView(this); 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) private void OnKeyDown(object sender, KeyEventArgs e)
{ {
string text = this.Text ?? string.Empty; string text = this.Text ?? string.Empty;
var caretIndex = this.CaretIndex;
switch (e.Key) switch (e.Key)
{ {
case Key.Left: case Key.Left:
--this.CaretIndex; this.MoveHorizontal(-1, e.Device.Modifiers);
break; break;
case Key.Right: case Key.Right:
++this.CaretIndex; this.MoveHorizontal(1, e.Device.Modifiers);
break; break;
case Key.Back: 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; --this.CaretIndex;
} }
break; break;
case Key.Delete: 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; break;
@ -145,7 +170,7 @@ namespace Perspex.Controls
default: default:
if (!string.IsNullOrEmpty(e.Text)) 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; ++this.CaretIndex;
} }

25
Perspex.Controls/TextBoxView.cs

@ -7,6 +7,7 @@
namespace Perspex.Controls namespace Perspex.Controls
{ {
using System; using System;
using System.Reactive.Linq;
using Perspex.Media; using Perspex.Media;
using Perspex.Threading; using Perspex.Threading;
@ -25,6 +26,13 @@ namespace Perspex.Controls
this.caretTimer.Tick += this.CaretTimerTick; this.caretTimer.Tick += this.CaretTimerTick;
this.parent = parent; this.parent = parent;
this[!TextProperty] = parent[!TextProperty]; 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) public int GetCaretIndex(Point point)
@ -47,6 +55,23 @@ namespace Perspex.Controls
public override void Render(IDrawingContext context) 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); base.Render(context);
if (this.parent.IsFocused) if (this.parent.IsFocused)

4
Perspex.SceneGraph/Media/FormattedText.cs

@ -79,7 +79,7 @@ namespace Perspex.Media
return this.PlatformImpl.HitTestTextPosition(index); return this.PlatformImpl.HitTestTextPosition(index);
} }
public IEnumerable<Rect> HitTestTextRange(int index, int length, Point origin) public IEnumerable<Rect> HitTestTextRange(int index, int length, Point origin = default(Point))
{ {
return this.PlatformImpl.HitTestTextRange(index, length, origin); return this.PlatformImpl.HitTestTextRange(index, length, origin);
} }
@ -89,4 +89,4 @@ namespace Perspex.Media
return this.PlatformImpl.Measure(); return this.PlatformImpl.Measure();
} }
} }
} }

17
Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs

@ -28,26 +28,22 @@ namespace Perspex.Win32.Input
{ {
ModifierKeys result = 0; ModifierKeys result = 0;
if (this.GetKeyStates(Key.LeftAlt) == KeyStates.Down || if (this.IsDown(Key.LeftAlt) || this.IsDown(Key.RightAlt))
this.GetKeyStates(Key.RightAlt) == KeyStates.Down)
{ {
result |= ModifierKeys.Alt; result |= ModifierKeys.Alt;
} }
if (this.GetKeyStates(Key.LeftCtrl) == KeyStates.Down || if (this.IsDown(Key.LeftCtrl) || this.IsDown(Key.RightCtrl))
this.GetKeyStates(Key.RightCtrl) == KeyStates.Down)
{ {
result |= ModifierKeys.Control; result |= ModifierKeys.Control;
} }
if (this.GetKeyStates(Key.LeftShift) == KeyStates.Down || if (this.IsDown(Key.LeftShift) || this.IsDown(Key.RightShift))
this.GetKeyStates(Key.RightShift) == KeyStates.Down)
{ {
result |= ModifierKeys.Shift; result |= ModifierKeys.Shift;
} }
if (this.GetKeyStates(Key.LWin) == KeyStates.Down || if (this.IsDown(Key.LWin) || this.IsDown(Key.RWin))
this.GetKeyStates(Key.RWin) == KeyStates.Down)
{ {
result |= ModifierKeys.Windows; result |= ModifierKeys.Windows;
} }
@ -79,6 +75,11 @@ namespace Perspex.Win32.Input
UnmanagedMethods.GetKeyboardState(this.keyStates); UnmanagedMethods.GetKeyboardState(this.keyStates);
} }
private bool IsDown(Key key)
{
return (this.GetKeyStates(key) & KeyStates.Down) != 0;
}
private KeyStates GetKeyStates(Key key) private KeyStates GetKeyStates(Key key)
{ {
int vk = KeyInterop.VirtualKeyFromKey(key); int vk = KeyInterop.VirtualKeyFromKey(key);

Loading…
Cancel
Save