From 040cccf61984bec9b7db451c18e59044c6bd0a0d Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 7 Nov 2022 18:08:07 +0100 Subject: [PATCH 1/4] Attempts to fix Android IME --- Avalonia.sln | 1 + samples/MobileSandbox/MainView.xaml | 1 + .../Avalonia.Android/AndroidInputMethod.cs | 37 +++---- .../Platform/SkiaPlatform/TopLevelImpl.cs | 96 ++++++++++++++----- .../Helpers/AndroidKeyboardEventsHelper.cs | 2 +- .../Input/TextInput/ITextInputMethodClient.cs | 24 +++++ .../TextFormatting/FormattedTextSource.cs | 2 +- .../Media/TextFormatting/TextCharacters.cs | 11 +-- .../Presenters/TextPresenter.cs | 33 +++---- src/Avalonia.Controls/TextBox.cs | 2 +- .../TextBoxTextInputMethodClient.cs | 15 +++ 11 files changed, 147 insertions(+), 77 deletions(-) diff --git a/Avalonia.sln b/Avalonia.sln index 461de8530b..e2f04ddc35 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -522,6 +522,7 @@ Global {3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.Build.0 = Release|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C90FE60B-B01E-4F35-91D6-379D6966030F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.ActiveCfg = Release|Any CPU {C90FE60B-B01E-4F35-91D6-379D6966030F}.Release|Any CPU.Build.0 = Release|Any CPU {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU diff --git a/samples/MobileSandbox/MainView.xaml b/samples/MobileSandbox/MainView.xaml index 9c46f3eae9..5f9f41f3a9 100644 --- a/samples/MobileSandbox/MainView.xaml +++ b/samples/MobileSandbox/MainView.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 040043e618..22d2f812bc 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -1,6 +1,7 @@ using System; using Android.Content; using Android.Runtime; +using Android.Text; using Android.Views; using Android.Views.InputMethods; using Avalonia.Android.Platform.SkiaPlatform; @@ -62,22 +63,21 @@ namespace Avalonia.Android public void Reset() { - _imm.RestartInput(_host); + } public void SetClient(ITextInputMethodClient client) { - if(client is null) - { - _inputConnection?.SetComposingText("", 0); - } - if (_client != null) { _client.SurroundingTextChanged -= SurroundingTextChanged; } - Reset(); + if(_inputConnection != null) + { + _inputConnection.ComposingText = null; + _inputConnection.ComposingRegion = default; + } _client = client; @@ -87,23 +87,31 @@ namespace Avalonia.Android _host.RequestFocus(); + _imm.RestartInput(View); + _imm.ShowSoftInput(_host, ShowFlags.Implicit); - } - else - { - _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None); + + var surroundingText = Client.SurroundingText; + + _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset); } } private void SurroundingTextChanged(object sender, EventArgs e) { - if (IsActive) + if (IsActive && _inputConnection != null) { var surroundingText = Client.SurroundingText; _inputConnection.SurroundingText = surroundingText; _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset); + + if (_inputConnection.ComposingText != null && !_inputConnection.IsCommiting && surroundingText.AnchorOffset == surroundingText.CursorOffset) + { + _inputConnection.CommitText(_inputConnection.ComposingText, 0); + _inputConnection.SetSelection(surroundingText.AnchorOffset, surroundingText.CursorOffset); + } } } @@ -153,10 +161,5 @@ namespace Avalonia.Android return _inputConnection; }); } - - private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e) - { - _imm.ShowSoftInput(_host, ShowFlags.Implicit); - } } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 984eb775b5..e69780b1b3 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using Android.App; using Android.Content; using Android.Graphics; +using Android.OS; using Android.Runtime; +using Android.Text; using Android.Views; using Android.Views.InputMethods; using Avalonia.Android.OpenGL; @@ -23,6 +25,7 @@ using Avalonia.Platform.Storage; using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Java.Lang; +using Math = System.Math; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -254,8 +257,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { throw new NotImplementedException(); } - - } internal class AvaloniaInputConnection : BaseInputConnection @@ -267,15 +268,18 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _topLevel = topLevel; _inputMethod = inputMethod; + + _inputMethod.Client?.SetComposingRegion(null); } public TextInputMethodSurroundingText SurroundingText { get; set; } - public string ComposingText { get; private set; } + public string ComposingText { get; internal set; } - public ComposingRegion ComposingRegion { get; private set; } + public ComposingRegion? ComposingRegion { get; internal set; } - public bool IsComposing { get; private set; } + public bool IsComposing => !string.IsNullOrEmpty(ComposingText); + public bool IsCommiting { get; private set; } public override bool SetComposingRegion(int start, int end) { @@ -283,6 +287,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform ComposingRegion = new ComposingRegion(start, end); + _inputMethod.Client?.SetComposingRegion(ComposingRegion); + return base.SetComposingRegion(start, end); } @@ -292,8 +298,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform ComposingText = composingText; - IsComposing = true; - _inputMethod.Client?.SetPreeditText(ComposingText); return base.SetComposingText(text, newCursorPosition); @@ -301,20 +305,25 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override bool FinishComposingText() { - IsComposing = false; - - ComposingRegion = new ComposingRegion(SurroundingText.CursorOffset, SurroundingText.CursorOffset); + if (!string.IsNullOrEmpty(ComposingText)) + { + CommitText(ComposingText, ComposingText.Length); + } + else + { + ComposingRegion = new ComposingRegion(SurroundingText.CursorOffset, SurroundingText.CursorOffset); + } return base.FinishComposingText(); } public override ICharSequence GetTextBeforeCursorFormatted(int length, [GeneratedEnum] GetTextFlags flags) { - if (!string.IsNullOrEmpty(SurroundingText.Text)) + if (!string.IsNullOrEmpty(SurroundingText.Text) && length > 0) { var start = System.Math.Max(SurroundingText.CursorOffset - length, 0); - var end = System.Math.Min(start + length, SurroundingText.CursorOffset); + var end = System.Math.Min(start + length - 1, SurroundingText.CursorOffset); var text = SurroundingText.Text.Substring(start, end - start); @@ -346,11 +355,31 @@ namespace Avalonia.Android.Platform.SkiaPlatform public override bool CommitText(ICharSequence text, int newCursorPosition) { + IsCommiting = true; var committedText = text.ToString(); _inputMethod.Client.SetPreeditText(null); - _inputMethod.Client.SelectInSurroundingText(ComposingRegion.Start, ComposingRegion.End); + int? start, end; + + if(SurroundingText.CursorOffset != SurroundingText.AnchorOffset) + { + start = Math.Min(SurroundingText.CursorOffset, SurroundingText.AnchorOffset); + end = Math.Max(SurroundingText.CursorOffset, SurroundingText.AnchorOffset); + } + else if (ComposingRegion != null) + { + start = ComposingRegion?.Start; + end = ComposingRegion?.End; + + ComposingRegion = null; + } + else + { + start = end = _inputMethod.Client.SurroundingText.CursorOffset; + } + + _inputMethod.Client.SelectInSurroundingText((int)start, (int)end); var time = DateTime.Now.TimeOfDay; @@ -358,15 +387,29 @@ namespace Avalonia.Android.Platform.SkiaPlatform _topLevel.Input(rawTextEvent); + ComposingText = null; + + ComposingRegion = new ComposingRegion(newCursorPosition, newCursorPosition); + return base.CommitText(text, newCursorPosition); } public override bool DeleteSurroundingText(int beforeLength, int afterLength) { - _inputMethod.Client.SelectInSurroundingText(beforeLength, afterLength); + var surroundingText = _inputMethod.Client.SurroundingText; + + var selectionStart = surroundingText.CursorOffset; + + _inputMethod.Client.SelectInSurroundingText(selectionStart - beforeLength, selectionStart + afterLength); _inputMethod.View.DispatchKeyEvent(new KeyEvent(KeyEventActions.Down, Keycode.ForwardDel)); + surroundingText = _inputMethod.Client.SurroundingText; + + selectionStart = surroundingText.CursorOffset; + + ComposingRegion = new ComposingRegion(selectionStart, selectionStart); + return base.DeleteSurroundingText(beforeLength, afterLength); } @@ -374,22 +417,23 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _inputMethod.Client.SelectInSurroundingText(start, end); + ComposingRegion = new ComposingRegion(start, end); + return base.SetSelection(start, end); } - } - - public readonly struct ComposingRegion - { - private readonly int _start = -1; - private readonly int _end = -1; - public ComposingRegion(int start, int end) + public override bool PerformEditorAction([GeneratedEnum] ImeAction actionCode) { - _start = start; - _end = end; - } + switch (actionCode) + { + case ImeAction.Done: + { + _inputMethod.IMM.HideSoftInputFromWindow(_inputMethod.View.WindowToken, HideSoftInputFlags.ImplicitOnly); + break; + } + } - public int Start => _start; - public int End => _end; + return base.PerformEditorAction(actionCode); + } } } diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs index 4cae700c0a..57f6469a42 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs @@ -63,7 +63,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers { var rawTextEvent = new RawTextInputEventArgs( AndroidKeyboardDevice.Instance, - Convert.ToUInt32(e.EventTime), + Convert.ToUInt64(e.EventTime), _view.InputRoot, unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString() ); diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs index 325b6dd0ef..0ff9299873 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs @@ -29,6 +29,8 @@ namespace Avalonia.Input.TextInput /// Sets the non-committed input string /// void SetPreeditText(string? text); + + void SetComposingRegion(ComposingRegion? region); /// /// Indicates if text input client is capable of providing the text around the cursor /// @@ -51,4 +53,26 @@ namespace Avalonia.Input.TextInput public int CursorOffset { get; set; } public int AnchorOffset { get; set; } } + + public readonly struct ComposingRegion + { + private readonly int _start = -1; + private readonly int _end = -1; + + public ComposingRegion(int start, int end) + { + _start = start; + _end = end; + } + + public int Start => _start; + public int End => _end; + + public bool Intersects(ComposingRegion region) + { + return _start <= region.Start && _end >= region.Start || + _end >= region.End && _start <= region.End || + _start >= region.Start && _end <= region.End; + } + } } diff --git a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs index 7ab67ea34d..fb8e699d8e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs @@ -140,7 +140,7 @@ namespace Avalonia.Media.TextFormatting } } - return length; + return Math.Min(length, text.Length); } } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index bcfa35ae30..efe6a94856 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -74,15 +74,6 @@ namespace Avalonia.Media.TextFormatting if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { - if (script == Script.Common && previousTypeface is not null) - { - if (TryGetShapeableLength(text, previousTypeface.Value, null, out var fallbackCount, out _)) - { - return new ShapeableTextCharacters(text.Take(fallbackCount), - defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); - } - } - return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -182,7 +173,7 @@ namespace Avalonia.Media.TextFormatting var currentScript = currentGrapheme.FirstCodepoint.Script; - if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + if (defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index adf0569551..230c57d34b 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -9,6 +9,7 @@ using Avalonia.VisualTree; using Avalonia.Layout; using Avalonia.Media.Immutable; using Avalonia.Controls.Documents; +using Avalonia.Input.TextInput; namespace Avalonia.Controls.Presenters { @@ -331,6 +332,8 @@ namespace Avalonia.Controls.Presenters protected override bool BypassFlowDirectionPolicies => true; + public ComposingRegion? ComposingRegion { get; internal set; } + /// /// Creates the used to render the text. /// @@ -514,7 +517,12 @@ namespace Avalonia.Controls.Presenters { if (!string.IsNullOrEmpty(_preeditText)) { - var text = _text?.Substring(0, _caretIndex) + _preeditText + _text?.Substring(_caretIndex); + if (string.IsNullOrEmpty(_text) || _caretIndex > _text.Length) + { + return _preeditText; + } + + var text = _text.Substring(0, _caretIndex) + _preeditText + _text.Substring(_caretIndex); return text; } @@ -545,7 +553,7 @@ namespace Avalonia.Controls.Presenters if (!string.IsNullOrEmpty(_preeditText)) { - var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, + var preeditHighlight = new ValueSpan(ComposingRegion.HasValue ? ComposingRegion.Value.Start : _caretIndex, _preeditText.Length, new GenericTextRunProperties(typeface, FontSize, foregroundBrush: foreground, textDecorations: TextDecorations.Underline)); @@ -868,28 +876,11 @@ namespace Avalonia.Controls.Presenters if (string.IsNullOrEmpty(newValue)) { - if (!string.IsNullOrEmpty(oldValue)) - { - var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0; - - var characterHit = GetCharacterHitFromTextPosition(textPosition); - - UpdateCaret(characterHit, true); - } - - _compositionStartHit = new CharacterHit(-1); + UpdateCaret(_lastCharacterHit); } else { - if (_compositionStartHit.FirstCharacterIndex == -1) - { - _compositionStartHit = _lastCharacterHit; - } - } - - if (_compositionStartHit.FirstCharacterIndex != -1) - { - var textPosition = _compositionStartHit.FirstCharacterIndex + _compositionStartHit.TrailingLength + newValue?.Length ?? 0; + var textPosition = (ComposingRegion.HasValue? ComposingRegion.Value.Start : _caretIndex) + newValue?.Length ?? 0; var characterHit = GetCharacterHitFromTextPosition(textPosition); diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 85c1c9a9d1..d5b45398e7 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1212,7 +1212,7 @@ namespace Avalonia.Controls protected override void OnPointerPressed(PointerPressedEventArgs e) { - if (_presenter == null || !string.IsNullOrEmpty(_presenter.PreeditText)) + if (_presenter == null ) { return; } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 5d5ffcc381..61ac1d3c1f 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -108,6 +108,11 @@ namespace Avalonia.Controls } _presenter.PreeditText = text; + + if(text == null) + { + _presenter.ComposingRegion = null; + } } public void SelectInSurroundingText(int start, int end) @@ -182,5 +187,15 @@ namespace Avalonia.Controls }, DispatcherPriority.Input); } + + public void SetComposingRegion(ComposingRegion? region) + { + if(_presenter == null) + { + return; + } + + _presenter.ComposingRegion = region; + } } } From a589d2e43b762a1d51c8be4142c85a57bb3e5fc2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 7 Nov 2022 18:17:09 +0100 Subject: [PATCH 2/4] Relocate CompositionRegion --- .../Avalonia.Android/AndroidInputMethod.cs | 15 ++++++++++++ .../Platform/SkiaPlatform/TopLevelImpl.cs | 2 -- .../Input/TextInput/ITextInputMethodClient.cs | 23 ------------------- .../TextBoxTextInputMethodClient.cs | 10 -------- 4 files changed, 15 insertions(+), 35 deletions(-) diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index 22d2f812bc..b6adbde738 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -162,4 +162,19 @@ namespace Avalonia.Android }); } } + + public readonly struct ComposingRegion + { + private readonly int _start = -1; + private readonly int _end = -1; + + public ComposingRegion(int start, int end) + { + _start = start; + _end = end; + } + + public int Start => _start; + public int End => _end; + } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index e69780b1b3..932dc185ff 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -287,8 +287,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform ComposingRegion = new ComposingRegion(start, end); - _inputMethod.Client?.SetComposingRegion(ComposingRegion); - return base.SetComposingRegion(start, end); } diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs index 0ff9299873..325d745782 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs @@ -30,7 +30,6 @@ namespace Avalonia.Input.TextInput /// void SetPreeditText(string? text); - void SetComposingRegion(ComposingRegion? region); /// /// Indicates if text input client is capable of providing the text around the cursor /// @@ -53,26 +52,4 @@ namespace Avalonia.Input.TextInput public int CursorOffset { get; set; } public int AnchorOffset { get; set; } } - - public readonly struct ComposingRegion - { - private readonly int _start = -1; - private readonly int _end = -1; - - public ComposingRegion(int start, int end) - { - _start = start; - _end = end; - } - - public int Start => _start; - public int End => _end; - - public bool Intersects(ComposingRegion region) - { - return _start <= region.Start && _end >= region.Start || - _end >= region.End && _start <= region.End || - _start >= region.Start && _end <= region.End; - } - } } diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 61ac1d3c1f..79a16281cd 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -187,15 +187,5 @@ namespace Avalonia.Controls }, DispatcherPriority.Input); } - - public void SetComposingRegion(ComposingRegion? region) - { - if(_presenter == null) - { - return; - } - - _presenter.ComposingRegion = region; - } } } From 7e3024ede20490eadb0511a50a7059f8bd436ab2 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 7 Nov 2022 19:46:02 +0100 Subject: [PATCH 3/4] Fix fallback handling --- .../Media/TextFormatting/TextCharacters.cs | 11 ++++++++++- src/Avalonia.Controls/Presenters/TextPresenter.cs | 6 ++---- src/Avalonia.Controls/TextBoxTextInputMethodClient.cs | 5 ----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs index efe6a94856..bcfa35ae30 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs @@ -74,6 +74,15 @@ namespace Avalonia.Media.TextFormatting if (TryGetShapeableLength(text, currentTypeface, null, out var count, out var script)) { + if (script == Script.Common && previousTypeface is not null) + { + if (TryGetShapeableLength(text, previousTypeface.Value, null, out var fallbackCount, out _)) + { + return new ShapeableTextCharacters(text.Take(fallbackCount), + defaultProperties.WithTypeface(previousTypeface.Value), biDiLevel); + } + } + return new ShapeableTextCharacters(text.Take(count), defaultProperties.WithTypeface(currentTypeface), biDiLevel); } @@ -173,7 +182,7 @@ namespace Avalonia.Media.TextFormatting var currentScript = currentGrapheme.FirstCodepoint.Script; - if (defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) + if (!currentGrapheme.FirstCodepoint.IsWhiteSpace && defaultFont != null && defaultFont.TryGetGlyph(currentGrapheme.FirstCodepoint, out _)) { break; } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 230c57d34b..cc1fa6c513 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -332,8 +332,6 @@ namespace Avalonia.Controls.Presenters protected override bool BypassFlowDirectionPolicies => true; - public ComposingRegion? ComposingRegion { get; internal set; } - /// /// Creates the used to render the text. /// @@ -553,7 +551,7 @@ namespace Avalonia.Controls.Presenters if (!string.IsNullOrEmpty(_preeditText)) { - var preeditHighlight = new ValueSpan(ComposingRegion.HasValue ? ComposingRegion.Value.Start : _caretIndex, _preeditText.Length, + var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, new GenericTextRunProperties(typeface, FontSize, foregroundBrush: foreground, textDecorations: TextDecorations.Underline)); @@ -880,7 +878,7 @@ namespace Avalonia.Controls.Presenters } else { - var textPosition = (ComposingRegion.HasValue? ComposingRegion.Value.Start : _caretIndex) + newValue?.Length ?? 0; + var textPosition = _caretIndex + newValue?.Length ?? 0; var characterHit = GetCharacterHitFromTextPosition(textPosition); diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 79a16281cd..5d5ffcc381 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -108,11 +108,6 @@ namespace Avalonia.Controls } _presenter.PreeditText = text; - - if(text == null) - { - _presenter.ComposingRegion = null; - } } public void SelectInSurroundingText(int start, int end) From f6f89f4e33b86f8a9ae0f6ccb5ade9ac8d69e20e Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 8 Nov 2022 06:59:16 +0100 Subject: [PATCH 4/4] Remove redundant method call --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 932dc185ff..24ee418480 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -268,8 +268,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform { _topLevel = topLevel; _inputMethod = inputMethod; - - _inputMethod.Client?.SetComposingRegion(null); } public TextInputMethodSurroundingText SurroundingText { get; set; }