Browse Source

Rendering part of PreeditText

Benedikt Stebner 3 years ago
parent
commit
75870f751e

+ 5 - 0
samples/Sandbox/MainWindow.axaml

@@ -1,4 +1,9 @@
 <Window xmlns="https://github.com/avaloniaui"
         xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
         x:Class="Sandbox.MainWindow">
+  <StackPanel>
+    <TextBox x:Name="txtBox" />
+    <TextPresenter Text="Hello World" PreeditText="{Binding #txtBox.Text}" CaretIndex="11"/>
+  </StackPanel>
+ 
 </Window>

+ 1 - 0
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;
 

+ 54 - 3
src/Avalonia.Controls/Presenters/TextPresenter.cs

@@ -54,6 +54,15 @@ namespace Avalonia.Controls.Presenters
                 o => o.Text,
                 (o, v) => o.Text = v);
 
+        /// <summary>
+        /// Defines the <see cref="PreeditText"/> property.
+        /// </summary>
+        public static readonly DirectProperty<TextPresenter, string?> PreeditTextProperty =
+            AvaloniaProperty.RegisterDirect<TextPresenter, string?>(
+                nameof(PreeditText),
+                o => o.PreeditText,
+                 (o, v) => o.PreeditText = v);
+
         /// <summary>
         /// Defines the <see cref="TextAlignment"/> property.
         /// </summary>
@@ -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);
+        }
+
         /// <summary>
         /// Gets or sets the font family.
         /// </summary>
@@ -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;
+        }
+
         /// <summary>
         /// Creates the <see cref="TextLayout"/> used to render the text.
         /// </summary>
@@ -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<ValueSpan<TextRunProperties>>? textStyleOverrides = null;
+            List<ValueSpan<TextRunProperties>>? textStyleOverrides = null;
 
             if (length > 0 && SelectionForegroundBrush != null)
             {
-                textStyleOverrides = new[]
+                textStyleOverrides = new List<ValueSpan<TextRunProperties>>
                 {
                     new ValueSpan<TextRunProperties>(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<TextRunProperties>(_caretIndex, _preeditText.Length,
+                        new GenericTextRunProperties(typeface, FontSize,
+                        foregroundBrush: foreground,
+                        textDecorations: TextDecorations.Underline));
+
+                if (textStyleOverrides == null)
+                {
+                    textStyleOverrides = new List<ValueSpan<TextRunProperties>>
+                    {
+                        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):
 

+ 20 - 5
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);