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 System;
using Android.App; using Android.App;
using Android.Content.PM; using Android.Content.PM;
using Android.OS;
using Avalonia; using Avalonia;
using Avalonia.Android; using Avalonia.Android;

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

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

216
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@ -10,10 +10,104 @@ namespace Avalonia.Controls
{ {
private TextBox? _parent; private TextBox? _parent;
private TextPresenter? _presenter; private TextPresenter? _presenter;
private bool _isPropertyChange;
public override Visual TextViewVisual => _presenter!; 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 SupportsPreedit => true;
public override bool SupportsSurroundingText => true; public override bool SupportsSurroundingText => true;
@ -38,19 +132,19 @@ namespace Avalonia.Controls
{ {
oldPresenter.ClearValue(TextPresenter.PreeditTextProperty); oldPresenter.ClearValue(TextPresenter.PreeditTextProperty);
oldPresenter.CaretBoundsChanged -= OnPresenterCursorRectangleChanged; oldPresenter.CaretBoundsChanged -= (s,e) => OnCursorRectangleChanged();
} }
_presenter = presenter; _presenter = presenter;
if (_presenter != null) if (_presenter != null)
{ {
_presenter.CaretBoundsChanged += OnPresenterCursorRectangleChanged; _presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged();
} }
OnTextViewVisualChanged(oldPresenter, presenter); OnTextViewVisualChanged(oldPresenter, presenter);
OnPresenterCursorRectangleChanged(this, EventArgs.Empty); OnCursorRectangleChanged();
} }
public override void SetPreeditText(string? preeditText) public override void SetPreeditText(string? preeditText)
@ -63,21 +157,6 @@ namespace Avalonia.Controls
_presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText); _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) private static string GetTextLineText(TextLine textLine)
{ {
var builder = StringBuilderCache.Acquire(textLine.Length); var builder = StringBuilderCache.Acquire(textLine.Length);
@ -101,112 +180,17 @@ namespace Avalonia.Controls
return lineText; 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) private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{ {
_isPropertyChange = true;
if (e.Property == TextBox.TextProperty) if (e.Property == TextBox.TextProperty)
{ {
OnParentTextChanged(); OnSurroundingTextChanged();
} }
if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty) if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty)
{ {
Selection = GetParentSelection(); OnSelectionChanged();
}
_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;
} }
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