Browse Source

Adjust TextInputMethodClient

pull/11848/head
Benedikt Stebner 3 years ago
parent
commit
037bbd3ca0
  1. 1
      samples/MobileSandbox.Android/MainActivity.cs
  2. 70
      src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs
  3. 216
      src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

1
samples/MobileSandbox.Android/MainActivity.cs

@ -1,6 +1,7 @@
using System;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Avalonia;
using Avalonia.Android;

70
src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs

@ -4,10 +4,6 @@ namespace Avalonia.Input.TextInput
{
public abstract class TextInputMethodClient
{
private Rect _cursorRectangle;
private string _surroundingText = "";
private TextSelection _selection;
/// <summary>
/// Fires when the text view visual has changed
/// </summary>
@ -46,87 +42,39 @@ namespace Avalonia.Input.TextInput
/// <summary>
/// Returns the text around the cursor, usually the current paragraph
/// </summary>
public string SurroundingText
{
get => _surroundingText;
set
{
var oldValue = _surroundingText;
if (oldValue == value)
{
return;
}
_surroundingText = value;
OnSurroundingTextChanged(oldValue, value);
}
}
public abstract string SurroundingText { get; }
/// <summary>
/// Gets the cursor rectangle relative to the TextViewVisual
/// </summary>
public Rect CursorRectangle
{
get => _cursorRectangle;
protected set
{
var oldvalue = _cursorRectangle;
if (oldvalue == value)
{
return;
}
_cursorRectangle = value;
OnCursorRectangleChanged(oldvalue, value);
}
}
public abstract Rect CursorRectangle { get; }
/// <summary>
/// Gets or sets the curent selection range within current surrounding text.
/// </summary>
public TextSelection Selection
{
get => _selection;
set
{
var oldValue = _selection;
if (oldValue == value)
{
return;
}
_selection = value;
OnSelectionChanged(oldValue, value);
}
}
public abstract TextSelection Selection { get; set; }
/// <summary>
/// Sets the non-committed input string
/// </summary>
public virtual void SetPreeditText(string? preeditText) { }
protected virtual void OnCursorRectangleChanged(Rect oldValue, Rect newValue)
protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue)
{
CursorRectangleChanged?.Invoke(this, EventArgs.Empty);
TextViewVisualChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnTextViewVisualChanged(Visual? oldValue, Visual? newValue)
protected virtual void OnCursorRectangleChanged()
{
TextViewVisualChanged?.Invoke(this, EventArgs.Empty);
CursorRectangleChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnSurroundingTextChanged(string? oldValue, string? newValue)
protected virtual void OnSurroundingTextChanged()
{
SurroundingTextChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnSelectionChanged(TextSelection oldValue, TextSelection newValue)
protected virtual void OnSelectionChanged()
{
SelectionChanged?.Invoke(this, EventArgs.Empty);
}

216
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@ -10,10 +10,104 @@ namespace Avalonia.Controls
{
private TextBox? _parent;
private TextPresenter? _presenter;
private bool _isPropertyChange;
public override Visual TextViewVisual => _presenter!;
public override string SurroundingText
{
get
{
if (_presenter is null || _parent is null)
{
return "";
}
#if DEBUG
if (_parent.CaretIndex != _presenter.CaretIndex)
{
throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
}
if (_parent.Text != _presenter.Text)
{
throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
}
#endif
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineText = GetTextLineText(textLine);
return lineText;
}
}
public override Rect CursorRectangle
{
get
{
if (_parent == null || _presenter == null)
{
return default;
}
var transform = _presenter.TransformToVisual(_parent);
if (transform == null)
{
return default;
}
return _presenter.GetCursorRectangle().TransformToAABB(transform.Value);
}
}
public override TextSelection Selection
{
get
{
if (_presenter is null || _parent is null)
{
return default;
}
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineStart = textLine.FirstTextSourceIndex;
var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart);
var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart);
return new TextSelection(selectionStart, selectionEnd);
}
set
{
if (_parent is null || _presenter is null)
{
return;
}
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineStart = textLine.FirstTextSourceIndex;
var selectionStart = lineStart + value.Start;
var selectionEnd = lineStart + value.End;
_parent.SelectionStart = selectionStart;
_parent.SelectionEnd = selectionEnd;
OnSelectionChanged();
}
}
public override bool SupportsPreedit => true;
public override bool SupportsSurroundingText => true;
@ -38,19 +132,19 @@ namespace Avalonia.Controls
{
oldPresenter.ClearValue(TextPresenter.PreeditTextProperty);
oldPresenter.CaretBoundsChanged -= OnPresenterCursorRectangleChanged;
oldPresenter.CaretBoundsChanged -= (s,e) => OnCursorRectangleChanged();
}
_presenter = presenter;
if (_presenter != null)
{
_presenter.CaretBoundsChanged += OnPresenterCursorRectangleChanged;
_presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged();
}
OnTextViewVisualChanged(oldPresenter, presenter);
OnPresenterCursorRectangleChanged(this, EventArgs.Empty);
OnCursorRectangleChanged();
}
public override void SetPreeditText(string? preeditText)
@ -63,21 +157,6 @@ namespace Avalonia.Controls
_presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText);
}
protected override void OnSelectionChanged(TextSelection oldValue, TextSelection newValue)
{
base.OnSelectionChanged(oldValue, newValue);
if (_isPropertyChange)
{
return;
}
if (oldValue != newValue)
{
SetParentSelection(newValue);
}
}
private static string GetTextLineText(TextLine textLine)
{
var builder = StringBuilderCache.Acquire(textLine.Length);
@ -101,112 +180,17 @@ namespace Avalonia.Controls
return lineText;
}
private void OnParentTextChanged()
{
if (_presenter is null || _parent is null)
{
SurroundingText = "";
return;
}
#if DEBUG
if (_parent.CaretIndex != _presenter.CaretIndex)
{
throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
}
if (_parent.Text != _presenter.Text)
{
throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
}
#endif
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineText = GetTextLineText(textLine);
SurroundingText = lineText;
}
private void OnPresenterCursorRectangleChanged(object? sender, EventArgs e)
{
if (_parent == null || _presenter == null)
{
CursorRectangle = default;
return;
}
var transform = _presenter.TransformToVisual(_parent);
if (transform == null)
{
CursorRectangle = default;
return;
}
CursorRectangle = _presenter.GetCursorRectangle().TransformToAABB(transform.Value);
}
private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
_isPropertyChange = true;
if (e.Property == TextBox.TextProperty)
{
OnParentTextChanged();
OnSurroundingTextChanged();
}
if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty)
{
Selection = GetParentSelection();
}
_isPropertyChange = false;
}
private TextSelection GetParentSelection()
{
if (_presenter is null || _parent is null)
{
return default;
}
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineStart = textLine.FirstTextSourceIndex;
var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart);
var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart);
return new TextSelection(selectionStart, selectionEnd);
}
private void SetParentSelection(TextSelection selection)
{
if (_parent is null || _presenter is null)
{
return;
OnSelectionChanged();
}
var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
var textLine = _presenter.TextLayout.TextLines[lineIndex];
var lineStart = textLine.FirstTextSourceIndex;
var selectionStart = lineStart + selection.Start;
var selectionEnd = lineStart + selection.End;
_parent.SelectionStart = selectionStart;
_parent.SelectionEnd = selectionEnd;
}
}
}

Loading…
Cancel
Save