From 037bbd3ca012b3289f00be03b37df1cb58f8ff1c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 23 Jun 2023 10:55:23 +0200 Subject: [PATCH] Adjust TextInputMethodClient --- samples/MobileSandbox.Android/MainActivity.cs | 1 + .../Input/TextInput/TextInputMethodClient.cs | 70 +----- .../TextBoxTextInputMethodClient.cs | 216 ++++++++---------- 3 files changed, 110 insertions(+), 177 deletions(-) diff --git a/samples/MobileSandbox.Android/MainActivity.cs b/samples/MobileSandbox.Android/MainActivity.cs index bc1be42f12..7a0de74757 100644 --- a/samples/MobileSandbox.Android/MainActivity.cs +++ b/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; diff --git a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs index 87f8d7ba97..4abd20f911 100644 --- a/src/Avalonia.Base/Input/TextInput/TextInputMethodClient.cs +++ b/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; - /// /// Fires when the text view visual has changed /// @@ -46,87 +42,39 @@ namespace Avalonia.Input.TextInput /// /// Returns the text around the cursor, usually the current paragraph /// - public string SurroundingText - { - get => _surroundingText; - set - { - var oldValue = _surroundingText; - - if (oldValue == value) - { - return; - } - - _surroundingText = value; - - OnSurroundingTextChanged(oldValue, value); - } - } + public abstract string SurroundingText { get; } /// /// Gets the cursor rectangle relative to the TextViewVisual /// - public Rect CursorRectangle - { - get => _cursorRectangle; - protected set - { - var oldvalue = _cursorRectangle; - - if (oldvalue == value) - { - return; - } - - _cursorRectangle = value; - - OnCursorRectangleChanged(oldvalue, value); - } - } + public abstract Rect CursorRectangle { get; } /// /// Gets or sets the curent selection range within current surrounding text. /// - public TextSelection Selection - { - get => _selection; - set - { - var oldValue = _selection; - - if (oldValue == value) - { - return; - } - - _selection = value; - - OnSelectionChanged(oldValue, value); - } - } + public abstract TextSelection Selection { get; set; } /// /// Sets the non-committed input string /// 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); } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 33a0482c68..de4a3c5cf9 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/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; } } }