Add cursor position and selection support to TextEntry

* add properties for cursor position and selection bounds
* add SelectedText property (which is writeble to support direct selection changes)
* add SelectionChanged event
* includes ComboBoxTextEntry support

WPF and GTK support is included. Mac has to be added.
This commit is contained in:
Vsevolod Kukol 2014-05-15 12:53:17 +02:00
Родитель 6f9bfacdf7
Коммит e515a12d83
6 изменённых файлов: 333 добавлений и 2 удалений

Просмотреть файл

@ -158,6 +158,75 @@ namespace Xwt.GtkBackend
}
}
public int CursorPosition {
get {
return Widget.Position;
}
set {
Widget.Position = value;
}
}
public int SelectionStart {
get {
int start, end;
Widget.GetSelectionBounds (out start, out end);
return start;
}
set {
int cacheStart = SelectionStart;
int cacheLength = SelectionLength;
Widget.GrabFocus ();
if (String.IsNullOrEmpty (Text))
return;
Widget.SelectRegion (value, value + cacheLength);
if (cacheStart != value)
HandleSelectionChanged ();
}
}
public int SelectionLength {
get {
int start, end;
Widget.GetSelectionBounds (out start, out end);
return end - start;
}
set {
int cacheStart = SelectionStart;
int cacheLength = SelectionLength;
Widget.GrabFocus ();
if (String.IsNullOrEmpty (Text))
return;
Widget.SelectRegion (cacheStart, cacheStart + value);
if (cacheLength != value)
HandleSelectionChanged ();
}
}
public string SelectedText {
get {
int start = SelectionStart;
int end = start + SelectionLength;
if (start == end) return String.Empty;
try {
return Text.Substring (start, end - start);
} catch {
return String.Empty;
}
}
set {
int cacheSelStart = SelectionStart;
int pos = cacheSelStart;
if (SelectionLength > 0) {
Widget.DeleteSelection ();
}
Widget.InsertText (value, ref pos);
Widget.GrabFocus ();
Widget.SelectRegion (cacheSelStart, pos);
HandleSelectionChanged ();
}
}
public bool MultiLine {
get; set;
}
@ -169,6 +238,13 @@ namespace Xwt.GtkBackend
switch ((TextEntryEvent)eventId) {
case TextEntryEvent.Changed: Widget.Changed += HandleChanged; break;
case TextEntryEvent.Activated: Widget.Activated += HandleActivated; break;
case TextEntryEvent.SelectionChanged:
enableSelectionChangedEvent = true;
Widget.MoveCursor += HandleMoveCursor;
Widget.ButtonPressEvent += HandleButtonPressEvent;
Widget.ButtonReleaseEvent += HandleButtonReleaseEvent;
Widget.MotionNotifyEvent += HandleMotionNotifyEvent;
break;
}
}
}
@ -180,6 +256,13 @@ namespace Xwt.GtkBackend
switch ((TextEntryEvent)eventId) {
case TextEntryEvent.Changed: Widget.Changed -= HandleChanged; break;
case TextEntryEvent.Activated: Widget.Activated -= HandleActivated; break;
case TextEntryEvent.SelectionChanged:
enableSelectionChangedEvent = false;
Widget.MoveCursor -= HandleMoveCursor;
Widget.ButtonPressEvent -= HandleButtonPressEvent;
Widget.ButtonReleaseEvent -= HandleButtonReleaseEvent;
Widget.MotionNotifyEvent -= HandleMotionNotifyEvent;
break;
}
}
}
@ -188,6 +271,7 @@ namespace Xwt.GtkBackend
{
ApplicationContext.InvokeUserCode (delegate {
EventSink.OnChanged ();
EventSink.OnSelectionChanged ();
});
}
@ -198,6 +282,53 @@ namespace Xwt.GtkBackend
});
}
bool enableSelectionChangedEvent;
void HandleSelectionChanged ()
{
if (enableSelectionChangedEvent)
ApplicationContext.InvokeUserCode (delegate {
EventSink.OnSelectionChanged ();
});
}
void HandleMoveCursor (object sender, EventArgs e)
{
HandleSelectionChanged ();
}
int cacheSelectionStart, cacheSelectionLength;
bool isMouseSelection;
[GLib.ConnectBefore]
void HandleButtonPressEvent (object o, Gtk.ButtonPressEventArgs args)
{
if (args.Event.Button == 1) {
HandleSelectionChanged ();
cacheSelectionStart = SelectionStart;
cacheSelectionLength = SelectionLength;
isMouseSelection = true;
}
}
[GLib.ConnectBefore]
void HandleMotionNotifyEvent (object o, Gtk.MotionNotifyEventArgs args)
{
if (isMouseSelection)
if (cacheSelectionStart != SelectionStart || cacheSelectionLength != SelectionLength)
HandleSelectionChanged ();
cacheSelectionStart = SelectionStart;
cacheSelectionLength = SelectionLength;
}
[GLib.ConnectBefore]
void HandleButtonReleaseEvent (object o, Gtk.ButtonReleaseEventArgs args)
{
if (args.Event.Button == 1) {
isMouseSelection = false;
HandleSelectionChanged ();
}
}
protected override void Dispose (bool disposing)
{
if (disposing) {

Просмотреть файл

@ -140,6 +140,14 @@ namespace Xwt.Mac
}
}
public int CursorPosition { get; set; }
public int SelectionStart { get; set; }
public int SelectionLength { get; set; }
public string SelectedText { get; set; }
public override void SetFocus ()
{
Widget.BecomeFirstResponder ();

Просмотреть файл

@ -95,6 +95,78 @@ namespace Xwt.WPFBackend
}
}
protected TextBox TextBox {
get { return combobox.Template.FindName ("PART_EditableTextBox", combobox) as TextBox; }
}
public int CursorPosition {
get {
if (ReadOnly)
return 0;
else
return TextBox.SelectionStart;
}
set {
if (!ReadOnly) {
TextBox.Focus();
TextBox.SelectionStart = value;
}
}
}
public int SelectionStart {
get {
if (ReadOnly)
return 0;
else
return TextBox.SelectionStart;
}
set {
if (!ReadOnly) {
int cacheLength = SelectionLength;
TextBox.Focus ();
TextBox.SelectionStart = value;
TextBox.SelectionLength = cacheLength;
}
}
}
public int SelectionLength {
get {
if (ReadOnly)
return this.SelectedText.Length;
else
return TextBox.SelectionLength;
}
set {
if (!ReadOnly) {
int cacheStart = SelectionStart;
TextBox.Focus ();
TextBox.SelectionLength = value;
TextBox.SelectionStart = cacheStart;
}
}
}
public string SelectedText {
get {
if (ReadOnly)
return this.SelectedText;
else
return TextBox.SelectedText;
}
set {
if (!ReadOnly) {
int cacheStart = SelectionStart;
int cacheLength = SelectionLength;
TextBox.Focus ();
TextBox.SelectionStart = cacheStart;
TextBox.SelectionLength = cacheLength;
TextBox.SelectedText = value;
}
}
}
public bool MultiLine { get; set; }
public override void EnableEvent (object eventId)
@ -105,10 +177,19 @@ namespace Xwt.WPFBackend
case TextEntryEvent.Changed:
this.combobox.TextChanged += OnTextChanged;
break;
case TextEntryEvent.SelectionChanged:
combobox.Loaded += HandleLoaded;
break;
}
}
}
void HandleLoaded (object sender, RoutedEventArgs e)
{
if (TextBox != null)
TextBox.SelectionChanged += OnSelectionChanged;
}
public override void DisableEvent (object eventId)
{
base.DisableEvent (eventId);
@ -117,6 +198,10 @@ namespace Xwt.WPFBackend
case TextEntryEvent.Changed:
this.combobox.TextChanged -= OnTextChanged;
break;
case TextEntryEvent.SelectionChanged:
if (TextBox != null)
TextBox.SelectionChanged -= OnSelectionChanged;
break;
}
}
}
@ -133,6 +218,11 @@ namespace Xwt.WPFBackend
Context.InvokeUserCode (TextEntryEventSink.OnChanged);
}
private void OnSelectionChanged (object s, EventArgs e)
{
Context.InvokeUserCode (TextEntryEventSink.OnSelectionChanged);
}
private void UpdatePlaceholder (string newPlaceholder, bool focused)
{
if (Text == this.placeholderText)

Просмотреть файл

@ -90,6 +90,44 @@ namespace Xwt.WPFBackend
set { TextBox.ShowFrame = value; }
}
public int CursorPosition {
get {
return TextBox.SelectionStart;
}
set {
TextBox.SelectionStart = value;
}
}
public int SelectionStart {
get {
return TextBox.SelectionStart;
}
set {
TextBox.Focus();
TextBox.Select(value, SelectionLength);
}
}
public int SelectionLength {
get {
return TextBox.SelectionLength;
}
set {
TextBox.Focus();
TextBox.Select(SelectionStart, value);
}
}
public string SelectedText {
get {
return TextBox.SelectedText;
}
set {
TextBox.SelectedText = value;
}
}
public bool MultiLine {
get { return multiline; }
set {
@ -123,6 +161,9 @@ namespace Xwt.WPFBackend
case TextEntryEvent.Activated:
TextBox.KeyDown += OnActivated;
break;
case TextEntryEvent.SelectionChanged:
TextBox.SelectionChanged += OnSelectionChanged;
break;
}
}
}
@ -141,6 +182,9 @@ namespace Xwt.WPFBackend
case TextEntryEvent.Activated:
TextBox.KeyDown -= OnActivated;
break;
case TextEntryEvent.SelectionChanged:
TextBox.SelectionChanged -= OnSelectionChanged;
break;
}
}
}
@ -164,5 +208,10 @@ namespace Xwt.WPFBackend
{
Context.InvokeUserCode (EventSink.OnChanged);
}
private void OnSelectionChanged (object s, EventArgs e)
{
Context.InvokeUserCode (EventSink.OnSelectionChanged);
}
}
}

Просмотреть файл

@ -35,18 +35,24 @@ namespace Xwt.Backends
bool ReadOnly { get; set; }
bool ShowFrame { get; set; }
bool MultiLine { get; set; }
int CursorPosition { get; set; }
int SelectionStart { get; set; }
int SelectionLength { get; set; }
string SelectedText { get; set; }
}
public interface ITextEntryEventSink: IWidgetEventSink
{
void OnChanged ();
void OnActivated ();
void OnSelectionChanged ();
}
public enum TextEntryEvent
{
Changed,
Activated
Activated,
SelectionChanged
}
}

Просмотреть файл

@ -32,12 +32,13 @@ namespace Xwt
[BackendType (typeof(ITextEntryBackend))]
public class TextEntry: Widget
{
EventHandler changed, activated;
EventHandler changed, activated, selectionChanged;
static TextEntry ()
{
MapEvent (TextEntryEvent.Changed, typeof(TextEntry), "OnChanged");
MapEvent (TextEntryEvent.Activated, typeof(TextEntry), "OnActivated");
MapEvent (TextEntryEvent.SelectionChanged, typeof(TextEntry), "OnSelectionChanged");
}
protected new class WidgetBackendHost: Widget.WidgetBackendHost, ITextEntryEventSink
@ -51,6 +52,11 @@ namespace Xwt
{
((TextEntry)Parent).OnActivated (EventArgs.Empty);
}
public void OnSelectionChanged ()
{
((TextEntry)Parent).OnSelectionChanged (EventArgs.Empty);
}
public override Size GetDefaultNaturalSize ()
{
@ -99,6 +105,30 @@ namespace Xwt
get { return Backend.ShowFrame; }
set { Backend.ShowFrame = value; }
}
[DefaultValue (0)]
public int CursorPosition {
get { return Backend.CursorPosition; }
set { Backend.CursorPosition = value; }
}
[DefaultValue (0)]
public int SelectionStart {
get { return Backend.SelectionStart; }
set { Backend.SelectionStart = value; }
}
[DefaultValue (0)]
public int SelectionLength {
get { return Backend.SelectionLength; }
set { Backend.SelectionLength = value; }
}
[DefaultValue ("")]
public string SelectedText {
get { return Backend.SelectedText; }
set { Backend.SelectedText = value; }
}
[DefaultValue (true)]
public bool MultiLine {
@ -123,6 +153,23 @@ namespace Xwt
}
}
protected virtual void OnSelectionChanged (EventArgs e)
{
if (selectionChanged != null)
selectionChanged (this, e);
}
public event EventHandler SelectionChanged {
add {
BackendHost.OnBeforeEventAdd (TextEntryEvent.SelectionChanged, selectionChanged);
selectionChanged += value;
}
remove {
selectionChanged -= value;
BackendHost.OnAfterEventRemove (TextEntryEvent.SelectionChanged, selectionChanged);
}
}
protected virtual void OnActivated (EventArgs e)
{
if (activated != null)