diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs index 9ef2214cd8..14d57334d1 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs @@ -49,16 +49,7 @@ namespace Avalonia.Input.TextInput /// The length after current selection. void DeleteSurroundingText(int beforeLength, int afterLength); - /// - /// Returns the text before the cursor. Must return a non-empty string if cursor is not at the beginning of the text entry - /// - string? TextBeforeCursor { get; } - /// - /// Returns the text before the cursor. Must return a non-empty string if cursor is not at the end of the text entry - /// - string? TextAfterCursor { get; } - - public void SelectInSurroundingText(int start, int end); + void SelectInSurroundingText(int start, int end); } public struct TextInputMethodSurroundingText diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index c17d32ec17..425273d779 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -1,9 +1,6 @@ using System; -using System.Diagnostics; using Avalonia.Controls.Presenters; -using Avalonia.Input; using Avalonia.Input.TextInput; -using Avalonia.Media; using Avalonia.Threading; using Avalonia.VisualTree; @@ -14,6 +11,12 @@ namespace Avalonia.Controls private TextBox? _parent; private TextPresenter? _presenter; + public IVisual TextViewVisual => _presenter!; + + public bool SupportsPreedit => true; + + public bool SupportsSurroundingText => true; + public Rect CursorRectangle { get @@ -36,10 +39,41 @@ namespace Avalonia.Controls } } - public event EventHandler? CursorRectangleChanged; - public IVisual TextViewVisual => _presenter!; + public TextInputMethodSurroundingText SurroundingText + { + get + { + if(_presenter is null) + { + return default; + } + + var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false); + + var textLine = _presenter.TextLayout.TextLines[lineIndex]; + + var lineStart = textLine.FirstTextSourceIndex; + + var lineText = _presenter.Text?.Substring(lineStart, textLine.Length); + + var anchorOffset = Math.Max(0, _presenter.SelectionStart - lineStart); + + var cursorOffset = Math.Max(0, _presenter.SelectionEnd - lineStart); + + return new TextInputMethodSurroundingText + { + Text = lineText ?? "", + AnchorOffset = anchorOffset, + CursorOffset = cursorOffset + }; + } + } + public event EventHandler? TextViewVisualChanged; - public bool SupportsPreedit => true; + + public event EventHandler? CursorRectangleChanged; + + public event EventHandler? SurroundingTextChanged; public void SetPreeditText(string? text) { @@ -51,58 +85,27 @@ namespace Avalonia.Controls _presenter.PreeditText = text; } - public bool SupportsSurroundingText => true; - - public event EventHandler? SurroundingTextChanged; - - public TextInputMethodSurroundingText SurroundingText => new() - { - Text = _presenter?.Text ?? "", - CursorOffset = _presenter?.CaretIndex ?? 0, - AnchorOffset = _presenter?.SelectionStart ?? 0 - }; - - public string? TextBeforeCursor => null; - - public string? TextAfterCursor => null; public void SelectInSurroundingText(int start, int end) { if(_parent == null) - return; - // TODO: Account for the offset - _parent.SelectionStart = start; - _parent.SelectionEnd = end; - } - - private void OnCaretBoundsChanged(object? sender, EventArgs e) => - Dispatcher.UIThread.Post(() => CursorRectangleChanged?.Invoke(this, EventArgs.Empty), DispatcherPriority.Input); - - private void OnTextBoxPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) - { - if (e.Property == TextBox.TextProperty || e.Property == TextBox.SelectionStartProperty || - e.Property == TextBox.SelectionEndProperty) { - if (string.IsNullOrEmpty(_presenter?.PreeditText)) - { - SurroundingTextChanged?.Invoke(this, EventArgs.Empty); - } + return; } - } + //start and end are relative to surroundingText + var surroundingText = SurroundingText; + + var selectionStart = surroundingText.AnchorOffset + start; + var selectionEnd = surroundingText.AnchorOffset + end; + + _parent.SelectionStart = selectionStart; + _parent.SelectionEnd = selectionEnd; + } + public void SetPresenter(TextPresenter? presenter, TextBox? parent) { - if (_parent != null) - { - _parent.PropertyChanged -= OnTextBoxPropertyChanged; - } - _parent = parent; - if (_parent != null) - { - _parent.PropertyChanged += OnTextBoxPropertyChanged; - } - if (_presenter != null) { _presenter.PreeditText = null; @@ -118,7 +121,8 @@ namespace Avalonia.Controls } TextViewVisualChanged?.Invoke(this, EventArgs.Empty); - CursorRectangleChanged?.Invoke(this, EventArgs.Empty); + + OnCaretBoundsChanged(this, EventArgs.Empty); } public void DeleteSurroundingText(int beforeLength, int afterLength) @@ -133,5 +137,19 @@ namespace Avalonia.Controls _parent.DeleteSelection(true); } } + + private void OnCaretBoundsChanged(object? sender, EventArgs e) + { + Dispatcher.UIThread.Post(() => + { + if (SupportsSurroundingText) + { + SurroundingTextChanged?.Invoke(sender, e); + } + + CursorRectangleChanged?.Invoke(sender, e); + + }, DispatcherPriority.Input); + } } } diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs index edd9c3e6e3..5b5951e800 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs +++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs @@ -349,12 +349,12 @@ namespace Avalonia.Web.Blazor IsComposing = true; break; case WebCompositionEventArgs.WebCompositionEventType.Update: - _client?.SetPreeditText(e.Data); + _client.SetPreeditText(e.Data); break; case WebCompositionEventArgs.WebCompositionEventType.End: IsComposing = false; - _client?.SetPreeditText(null); - _topLevelImpl.RawTextEvent(e.Data); + _client.SetPreeditText(null); + _topLevelImpl.RawTextEvent(e.Data); break; } } @@ -471,7 +471,7 @@ namespace Avalonia.Web.Blazor private void SurroundingTextChanged(object? sender, EventArgs e) { - if(_client != null && IsComposing) + if(_client != null) { var surroundingText = _client.SurroundingText; @@ -484,15 +484,8 @@ namespace Avalonia.Web.Blazor _inputHelper?.Focus(); var bounds = new PixelRect((int)rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height); - if (_client != null) - { - var surroundingText = _client.SurroundingText; - - _inputHelper?.SetSurroundingText(surroundingText.Text, surroundingText.AnchorOffset, - surroundingText.CursorOffset); - - _inputHelper?.SetBounds(bounds, surroundingText.CursorOffset); - } + _inputHelper?.SetBounds(bounds, _client?.SurroundingText.CursorOffset ?? 0); + _inputHelper?.Focus(); } public void SetOptions(TextInputOptions options)