Browse Source

Rendering part of PreeditText

feature/ImePreeditText
Benedikt Stebner 4 years ago
parent
commit
75870f751e
  1. 5
      samples/Sandbox/MainWindow.axaml
  2. 1
      samples/Sandbox/MainWindow.axaml.cs
  3. 57
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  4. 25
      src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

5
samples/Sandbox/MainWindow.axaml

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

1
samples/Sandbox/MainWindow.axaml.cs

@ -1,5 +1,6 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Win32.WinRT.Composition; using Avalonia.Win32.WinRT.Composition;

57
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -54,6 +54,15 @@ namespace Avalonia.Controls.Presenters
o => o.Text, o => o.Text,
(o, v) => o.Text = v); (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> /// <summary>
/// Defines the <see cref="TextAlignment"/> property. /// Defines the <see cref="TextAlignment"/> property.
/// </summary> /// </summary>
@ -90,6 +99,7 @@ namespace Avalonia.Controls.Presenters
private CharacterHit _lastCharacterHit; private CharacterHit _lastCharacterHit;
private Rect _caretBounds; private Rect _caretBounds;
private Point _navigationPosition; private Point _navigationPosition;
private string? _preeditText;
static TextPresenter() static TextPresenter()
{ {
@ -124,6 +134,12 @@ namespace Avalonia.Controls.Presenters
set => SetAndRaise(TextProperty, ref _text, value); set => SetAndRaise(TextProperty, ref _text, value);
} }
public string? PreeditText
{
get => _preeditText;
set => SetAndRaise(PreeditTextProperty, ref _preeditText, value);
}
/// <summary> /// <summary>
/// Gets or sets the font family. /// Gets or sets the font family.
/// </summary> /// </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> /// <summary>
/// Creates the <see cref="TextLayout"/> used to render the text. /// Creates the <see cref="TextLayout"/> used to render the text.
/// </summary> /// </summary>
@ -487,7 +515,7 @@ namespace Avalonia.Controls.Presenters
{ {
TextLayout result; TextLayout result;
var text = Text; var text = GetText();
var typeface = new Typeface(FontFamily, FontStyle, FontWeight); var typeface = new Typeface(FontFamily, FontStyle, FontWeight);
@ -496,11 +524,11 @@ namespace Avalonia.Controls.Presenters
var start = Math.Min(selectionStart, selectionEnd); var start = Math.Min(selectionStart, selectionEnd);
var length = Math.Max(selectionStart, selectionEnd) - start; var length = Math.Max(selectionStart, selectionEnd) - start;
IReadOnlyList<ValueSpan<TextRunProperties>>? textStyleOverrides = null; List<ValueSpan<TextRunProperties>>? textStyleOverrides = null;
if (length > 0 && SelectionForegroundBrush != null) if (length > 0 && SelectionForegroundBrush != null)
{ {
textStyleOverrides = new[] textStyleOverrides = new List<ValueSpan<TextRunProperties>>
{ {
new ValueSpan<TextRunProperties>(start, length, new ValueSpan<TextRunProperties>(start, length,
new GenericTextRunProperties(typeface, FontSize, 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) if (PasswordChar != default(char) && !RevealPassword)
{ {
result = CreateTextLayoutInternal(_constraint, new string(PasswordChar, text?.Length ?? 0), typeface, result = CreateTextLayoutInternal(_constraint, new string(PasswordChar, text?.Length ?? 0), typeface,
@ -814,6 +864,7 @@ namespace Avalonia.Controls.Presenters
case nameof (FontStretch): case nameof (FontStretch):
case nameof (Text): case nameof (Text):
case nameof (PreeditText):
case nameof (TextAlignment): case nameof (TextAlignment):
case nameof (TextWrapping): case nameof (TextWrapping):

25
src/Avalonia.Controls/TextBoxTextInputMethodClient.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
{ {
internal class TextBoxTextInputMethodClient : ITextInputMethodClient internal class TextBoxTextInputMethodClient : ITextInputMethodClient
{ {
private InputElement? _parent; private TextBox? _parent;
private TextPresenter? _presenter; private TextPresenter? _presenter;
public Rect CursorRectangle public Rect CursorRectangle
@ -36,19 +36,29 @@ namespace Avalonia.Controls
public event EventHandler? CursorRectangleChanged; public event EventHandler? CursorRectangleChanged;
public IVisual TextViewVisual => _presenter!; public IVisual TextViewVisual => _presenter!;
public event EventHandler? TextViewVisualChanged; public event EventHandler? TextViewVisualChanged;
public bool SupportsPreedit => false; public bool SupportsPreedit => true;
public void SetPreeditText(string text) => throw new NotSupportedException(); public void SetPreeditText(string? text)
{
if(_presenter == null)
{
return;
}
_presenter.PreeditText = text;
}
public bool SupportsSurroundingText => false; public bool SupportsSurroundingText => false;
public TextInputMethodSurroundingText SurroundingText => throw new NotSupportedException();
public event EventHandler? SurroundingTextChanged { add { } remove { } } public event EventHandler? SurroundingTextChanged { add { } remove { } }
public TextInputMethodSurroundingText SurroundingText => throw new NotSupportedException();
public string? TextBeforeCursor => null; public string? TextBeforeCursor => null;
public string? TextAfterCursor => null; public string? TextAfterCursor => null;
private void OnCaretBoundsChanged(object? sender, EventArgs e) => CursorRectangleChanged?.Invoke(this, EventArgs.Empty); 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; _parent = parent;
@ -63,6 +73,11 @@ namespace Avalonia.Controls
{ {
_presenter.CaretBoundsChanged += OnCaretBoundsChanged; _presenter.CaretBoundsChanged += OnCaretBoundsChanged;
} }
if(presenter == null)
{
SetPreeditText(null);
}
TextViewVisualChanged?.Invoke(this, EventArgs.Empty); TextViewVisualChanged?.Invoke(this, EventArgs.Empty);
CursorRectangleChanged?.Invoke(this, EventArgs.Empty); CursorRectangleChanged?.Invoke(this, EventArgs.Empty);

Loading…
Cancel
Save