diff --git a/samples/Sandbox/MainWindow.axaml b/samples/Sandbox/MainWindow.axaml index 6929f192c7..20d7a53a11 100644 --- a/samples/Sandbox/MainWindow.axaml +++ b/samples/Sandbox/MainWindow.axaml @@ -1,4 +1,9 @@ + + + + + diff --git a/samples/Sandbox/MainWindow.axaml.cs b/samples/Sandbox/MainWindow.axaml.cs index 3d54036d29..23d45edf6a 100644 --- a/samples/Sandbox/MainWindow.axaml.cs +++ b/samples/Sandbox/MainWindow.axaml.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Presenters; using Avalonia.Markup.Xaml; using Avalonia.Win32.WinRT.Composition; diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index e540f58195..3f4d881735 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -54,6 +54,15 @@ namespace Avalonia.Controls.Presenters o => o.Text, (o, v) => o.Text = v); + /// + /// Defines the property. + /// + public static readonly DirectProperty PreeditTextProperty = + AvaloniaProperty.RegisterDirect( + nameof(PreeditText), + o => o.PreeditText, + (o, v) => o.PreeditText = v); + /// /// Defines the property. /// @@ -90,6 +99,7 @@ namespace Avalonia.Controls.Presenters private CharacterHit _lastCharacterHit; private Rect _caretBounds; private Point _navigationPosition; + private string? _preeditText; static TextPresenter() { @@ -124,6 +134,12 @@ namespace Avalonia.Controls.Presenters set => SetAndRaise(TextProperty, ref _text, value); } + public string? PreeditText + { + get => _preeditText; + set => SetAndRaise(PreeditTextProperty, ref _preeditText, value); + } + /// /// Gets or sets the font family. /// @@ -479,6 +495,18 @@ namespace Avalonia.Controls.Presenters } } + private string? GetText() + { + if (!string.IsNullOrEmpty(_preeditText)) + { + var text = _text?.Substring(0, _caretIndex) + _preeditText + _text?.Substring(_caretIndex); + + return text; + } + + return _text; + } + /// /// Creates the used to render the text. /// @@ -487,7 +515,7 @@ namespace Avalonia.Controls.Presenters { TextLayout result; - var text = Text; + var text = GetText(); var typeface = new Typeface(FontFamily, FontStyle, FontWeight); @@ -496,11 +524,11 @@ namespace Avalonia.Controls.Presenters var start = Math.Min(selectionStart, selectionEnd); var length = Math.Max(selectionStart, selectionEnd) - start; - IReadOnlyList>? textStyleOverrides = null; + List>? textStyleOverrides = null; if (length > 0 && SelectionForegroundBrush != null) { - textStyleOverrides = new[] + textStyleOverrides = new List> { new ValueSpan(start, length, new GenericTextRunProperties(typeface, FontSize, @@ -508,6 +536,28 @@ namespace Avalonia.Controls.Presenters }; } + var foreground = Foreground; + + if (!string.IsNullOrEmpty(_preeditText)) + { + var preeditHighlight = new ValueSpan(_caretIndex, _preeditText.Length, + new GenericTextRunProperties(typeface, FontSize, + foregroundBrush: foreground, + textDecorations: TextDecorations.Underline)); + + if (textStyleOverrides == null) + { + textStyleOverrides = new List> + { + preeditHighlight + }; + } + else + { + textStyleOverrides.Add(preeditHighlight); + } + } + if (PasswordChar != default(char) && !RevealPassword) { result = CreateTextLayoutInternal(_constraint, new string(PasswordChar, text?.Length ?? 0), typeface, @@ -814,6 +864,7 @@ namespace Avalonia.Controls.Presenters case nameof (FontStretch): case nameof (Text): + case nameof (PreeditText): case nameof (TextAlignment): case nameof (TextWrapping): diff --git a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs index 4d2ed03440..f9eb90611d 100644 --- a/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs +++ b/src/Avalonia.Controls/TextBoxTextInputMethodClient.cs @@ -9,7 +9,7 @@ namespace Avalonia.Controls { internal class TextBoxTextInputMethodClient : ITextInputMethodClient { - private InputElement? _parent; + private TextBox? _parent; private TextPresenter? _presenter; public Rect CursorRectangle @@ -36,19 +36,29 @@ namespace Avalonia.Controls public event EventHandler? CursorRectangleChanged; public IVisual TextViewVisual => _presenter!; public event EventHandler? TextViewVisualChanged; - public bool SupportsPreedit => false; - public void SetPreeditText(string text) => throw new NotSupportedException(); + public bool SupportsPreedit => true; + public void SetPreeditText(string? text) + { + if(_presenter == null) + { + return; + } + + _presenter.PreeditText = text; + } public bool SupportsSurroundingText => false; - public TextInputMethodSurroundingText SurroundingText => throw new NotSupportedException(); + + public event EventHandler? SurroundingTextChanged { add { } remove { } } + public TextInputMethodSurroundingText SurroundingText => throw new NotSupportedException(); public string? TextBeforeCursor => null; public string? TextAfterCursor => null; private void OnCaretBoundsChanged(object? sender, EventArgs e) => CursorRectangleChanged?.Invoke(this, EventArgs.Empty); - public void SetPresenter(TextPresenter? presenter, InputElement? parent) + public void SetPresenter(TextPresenter? presenter, TextBox? parent) { _parent = parent; @@ -63,6 +73,11 @@ namespace Avalonia.Controls { _presenter.CaretBoundsChanged += OnCaretBoundsChanged; } + + if(presenter == null) + { + SetPreeditText(null); + } TextViewVisualChanged?.Invoke(this, EventArgs.Empty); CursorRectangleChanged?.Invoke(this, EventArgs.Empty);