From 7e33d7a2353a31d53a4adf9325c5a028029a42ff Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Mon, 13 Feb 2023 15:41:21 +0000 Subject: [PATCH] Add Composition Start and End properties to TextEditable --- src/Android/Avalonia.Android/InputEditable.cs | 12 +++++-- .../Platform/SkiaPlatform/TopLevelImpl.cs | 33 +++++++------------ .../Input/TextInput/ITextEditable.cs | 3 ++ .../TextBoxTextInputMethodClient.cs | 10 ++++++ 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/Android/Avalonia.Android/InputEditable.cs b/src/Android/Avalonia.Android/InputEditable.cs index 2463280769..d594b25d7e 100644 --- a/src/Android/Avalonia.Android/InputEditable.cs +++ b/src/Android/Avalonia.Android/InputEditable.cs @@ -26,6 +26,7 @@ namespace Avalonia.Android public event EventHandler TextChanged; public event EventHandler SelectionChanged; + public event EventHandler CompositionChanged; public InputEditable(TopLevelImpl topLevel, IAndroidInputMethod inputMethod, AvaloniaInputConnection avaloniaInputConnection) { @@ -54,8 +55,6 @@ namespace Avalonia.Android { } - public bool IsInBatchEdit => _currentBatchLevel > 0; - public TextPresenter Presenter { get => _presenter; } @@ -90,6 +89,10 @@ namespace Avalonia.Android } } + public int CompositionStart => BaseInputConnection.GetComposingSpanStart(this); + + public int CompositionEnd => BaseInputConnection.GetComposingSpanEnd(this); + public void BeginBatchEdit() { _currentBatchLevel++; @@ -120,6 +123,11 @@ namespace Avalonia.Android _currentBatchLevel--; } + public void RaiseCompositionChanged() + { + CompositionChanged?.Invoke(this, EventArgs.Empty); + } + void IDisposable.Dispose() { _presenter = null; diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index df35b069dc..e511ed9a8b 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -28,6 +28,7 @@ using Math = System.Math; using AndroidRect = Android.Graphics.Rect; using Window = Android.Views.Window; using Android.Graphics.Drawables; +using Java.Util; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -411,8 +412,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform private readonly TopLevelImpl _topLevel; private readonly IAndroidInputMethod _inputMethod; private readonly InputEditable _editable; - private bool _hasComposingRegion; - private int _compositionStart; public AvaloniaInputConnection(TopLevelImpl topLevel, IAndroidInputMethod inputMethod) : base(inputMethod.View, true) { @@ -427,12 +426,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override bool SetComposingRegion(int start, int end) { - _inputMethod.Client.SetComposingRegion(new Media.TextFormatting.TextRange(start, end)); + var ret = base.SetComposingRegion(start, end); - _hasComposingRegion = true; - _compositionStart = start; + InputEditable.RaiseCompositionChanged(); - return base.SetComposingRegion(start, end); + return ret; } public override bool SetComposingText(ICharSequence text, int newCursorPosition) @@ -447,13 +445,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform { var ret = base.SetComposingText(text, newCursorPosition); - if (!_hasComposingRegion) - { - _compositionStart = _editable.SelectionEnd - composingText.Length; - _hasComposingRegion = true; - } - - _inputMethod.Client.SetComposingRegion(new Media.TextFormatting.TextRange(_compositionStart, _compositionStart + composingText.Length)); + InputEditable.RaiseCompositionChanged(); return ret; } @@ -476,19 +468,16 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override bool FinishComposingText() { - _inputMethod.Client?.SetComposingRegion(null); - _hasComposingRegion = false; - _compositionStart = -1; - return base.FinishComposingText(); + var ret = base.FinishComposingText(); + InputEditable.RaiseCompositionChanged(); + return ret; } public override bool CommitText(ICharSequence text, int newCursorPosition) { - _inputMethod.Client?.SetComposingRegion(null); - _hasComposingRegion = false; - _compositionStart = -1; - - return base.CommitText(text, newCursorPosition); + var ret = base.CommitText(text, newCursorPosition); + InputEditable.RaiseCompositionChanged(); + return ret; } public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) diff --git a/src/Avalonia.Base/Input/TextInput/ITextEditable.cs b/src/Avalonia.Base/Input/TextInput/ITextEditable.cs index 4d86ef8d5a..f9f5ec8d40 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextEditable.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextEditable.cs @@ -12,8 +12,11 @@ namespace Avalonia.Input.TextInput { event EventHandler TextChanged; event EventHandler SelectionChanged; + event EventHandler CompositionChanged; int SelectionStart { get; set; } int SelectionEnd { get; set; } + int CompositionStart { get; } + int CompositionEnd { get; } string? Text { get; set; } } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 6713fed244..a5ff707e1e 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -81,6 +81,7 @@ namespace Avalonia.Controls { _textEditable.TextChanged -= TextEditable_TextChanged; _textEditable.SelectionChanged -= TextEditable_SelectionChanged; + _textEditable.CompositionChanged -= TextEditable_CompositionChanged; } _textEditable = value; @@ -89,6 +90,7 @@ namespace Avalonia.Controls { _textEditable.TextChanged += TextEditable_TextChanged; _textEditable.SelectionChanged += TextEditable_SelectionChanged; + _textEditable.CompositionChanged += TextEditable_CompositionChanged; if (_presenter != null) { @@ -100,6 +102,14 @@ namespace Avalonia.Controls } } + private void TextEditable_CompositionChanged(object? sender, EventArgs e) + { + if (_presenter != null && _textEditable != null) + { + _presenter.CompositionRegion = new TextRange(_textEditable.CompositionStart, _textEditable.CompositionEnd); + } + } + private void TextEditable_SelectionChanged(object? sender, EventArgs e) { if(_parent != null && _textEditable != null)