|
|
@@ -10,10 +10,104 @@ namespace Avalonia.Controls
|
|
|
{
|
|
|
private TextBox? _parent;
|
|
|
private TextPresenter? _presenter;
|
|
|
- private bool _isPropertyChange;
|
|
|
|
|
|
public override Visual TextViewVisual => _presenter!;
|
|
|
|
|
|
+ public override string SurroundingText
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (_presenter is null || _parent is null)
|
|
|
+ {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+#if DEBUG
|
|
|
+ if (_parent.CaretIndex != _presenter.CaretIndex)
|
|
|
+ {
|
|
|
+ throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_parent.Text != _presenter.Text)
|
|
|
+ {
|
|
|
+ throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false);
|
|
|
+
|
|
|
+ var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
+
|
|
|
+ var lineText = GetTextLineText(textLine);
|
|
|
+
|
|
|
+ return lineText;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public override Rect CursorRectangle
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (_parent == null || _presenter == null)
|
|
|
+ {
|
|
|
+ return default;
|
|
|
+ }
|
|
|
+
|
|
|
+ var transform = _presenter.TransformToVisual(_parent);
|
|
|
+
|
|
|
+ if (transform == null)
|
|
|
+ {
|
|
|
+ return default;
|
|
|
+ }
|
|
|
+
|
|
|
+ return _presenter.GetCursorRectangle().TransformToAABB(transform.Value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public override TextSelection Selection
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ if (_presenter is null || _parent is null)
|
|
|
+ {
|
|
|
+ return default;
|
|
|
+ }
|
|
|
+
|
|
|
+ var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
|
|
|
+
|
|
|
+ var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
+
|
|
|
+ var lineStart = textLine.FirstTextSourceIndex;
|
|
|
+
|
|
|
+ var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart);
|
|
|
+
|
|
|
+ var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart);
|
|
|
+
|
|
|
+ return new TextSelection(selectionStart, selectionEnd);
|
|
|
+ }
|
|
|
+ set
|
|
|
+ {
|
|
|
+ if (_parent is null || _presenter is null)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
|
|
|
+
|
|
|
+ var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
+
|
|
|
+ var lineStart = textLine.FirstTextSourceIndex;
|
|
|
+
|
|
|
+ var selectionStart = lineStart + value.Start;
|
|
|
+ var selectionEnd = lineStart + value.End;
|
|
|
+
|
|
|
+ _parent.SelectionStart = selectionStart;
|
|
|
+ _parent.SelectionEnd = selectionEnd;
|
|
|
+
|
|
|
+ OnSelectionChanged();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public override bool SupportsPreedit => true;
|
|
|
|
|
|
public override bool SupportsSurroundingText => true;
|
|
|
@@ -38,19 +132,19 @@ namespace Avalonia.Controls
|
|
|
{
|
|
|
oldPresenter.ClearValue(TextPresenter.PreeditTextProperty);
|
|
|
|
|
|
- oldPresenter.CaretBoundsChanged -= OnPresenterCursorRectangleChanged;
|
|
|
+ oldPresenter.CaretBoundsChanged -= (s,e) => OnCursorRectangleChanged();
|
|
|
}
|
|
|
|
|
|
_presenter = presenter;
|
|
|
|
|
|
if (_presenter != null)
|
|
|
{
|
|
|
- _presenter.CaretBoundsChanged += OnPresenterCursorRectangleChanged;
|
|
|
+ _presenter.CaretBoundsChanged += (s, e) => OnCursorRectangleChanged();
|
|
|
}
|
|
|
|
|
|
OnTextViewVisualChanged(oldPresenter, presenter);
|
|
|
|
|
|
- OnPresenterCursorRectangleChanged(this, EventArgs.Empty);
|
|
|
+ OnCursorRectangleChanged();
|
|
|
}
|
|
|
|
|
|
public override void SetPreeditText(string? preeditText)
|
|
|
@@ -63,21 +157,6 @@ namespace Avalonia.Controls
|
|
|
_presenter.SetCurrentValue(TextPresenter.PreeditTextProperty, preeditText);
|
|
|
}
|
|
|
|
|
|
- protected override void OnSelectionChanged(TextSelection oldValue, TextSelection newValue)
|
|
|
- {
|
|
|
- base.OnSelectionChanged(oldValue, newValue);
|
|
|
-
|
|
|
- if (_isPropertyChange)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (oldValue != newValue)
|
|
|
- {
|
|
|
- SetParentSelection(newValue);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private static string GetTextLineText(TextLine textLine)
|
|
|
{
|
|
|
var builder = StringBuilderCache.Acquire(textLine.Length);
|
|
|
@@ -101,112 +180,17 @@ namespace Avalonia.Controls
|
|
|
return lineText;
|
|
|
}
|
|
|
|
|
|
- private void OnParentTextChanged()
|
|
|
- {
|
|
|
- if (_presenter is null || _parent is null)
|
|
|
- {
|
|
|
- SurroundingText = "";
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
-#if DEBUG
|
|
|
- if (_parent.CaretIndex != _presenter.CaretIndex)
|
|
|
- {
|
|
|
- throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
|
|
|
- }
|
|
|
-
|
|
|
- if (_parent.Text != _presenter.Text)
|
|
|
- {
|
|
|
- throw new InvalidOperationException("TextBox and TextPresenter are out of sync");
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_presenter.CaretIndex, false);
|
|
|
-
|
|
|
- var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
-
|
|
|
- var lineText = GetTextLineText(textLine);
|
|
|
-
|
|
|
- SurroundingText = lineText;
|
|
|
- }
|
|
|
-
|
|
|
- private void OnPresenterCursorRectangleChanged(object? sender, EventArgs e)
|
|
|
- {
|
|
|
- if (_parent == null || _presenter == null)
|
|
|
- {
|
|
|
- CursorRectangle = default;
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- var transform = _presenter.TransformToVisual(_parent);
|
|
|
-
|
|
|
- if (transform == null)
|
|
|
- {
|
|
|
- CursorRectangle = default;
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- CursorRectangle = _presenter.GetCursorRectangle().TransformToAABB(transform.Value);
|
|
|
- }
|
|
|
-
|
|
|
private void OnParentPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
|
|
{
|
|
|
- _isPropertyChange = true;
|
|
|
-
|
|
|
if (e.Property == TextBox.TextProperty)
|
|
|
{
|
|
|
- OnParentTextChanged();
|
|
|
+ OnSurroundingTextChanged();
|
|
|
}
|
|
|
|
|
|
if (e.Property == TextBox.SelectionStartProperty || e.Property == TextBox.SelectionEndProperty)
|
|
|
{
|
|
|
- Selection = GetParentSelection();
|
|
|
- }
|
|
|
-
|
|
|
- _isPropertyChange = false;
|
|
|
- }
|
|
|
-
|
|
|
- private TextSelection GetParentSelection()
|
|
|
- {
|
|
|
- if (_presenter is null || _parent is null)
|
|
|
- {
|
|
|
- return default;
|
|
|
- }
|
|
|
-
|
|
|
- var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
|
|
|
-
|
|
|
- var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
-
|
|
|
- var lineStart = textLine.FirstTextSourceIndex;
|
|
|
-
|
|
|
- var selectionStart = Math.Max(0, _parent.SelectionStart - lineStart);
|
|
|
-
|
|
|
- var selectionEnd = Math.Max(0, _parent.SelectionEnd - lineStart);
|
|
|
-
|
|
|
- return new TextSelection(selectionStart, selectionEnd);
|
|
|
- }
|
|
|
-
|
|
|
- private void SetParentSelection(TextSelection selection)
|
|
|
- {
|
|
|
- if (_parent is null || _presenter is null)
|
|
|
- {
|
|
|
- return;
|
|
|
+ OnSelectionChanged();
|
|
|
}
|
|
|
-
|
|
|
- var lineIndex = _presenter.TextLayout.GetLineIndexFromCharacterIndex(_parent.CaretIndex, false);
|
|
|
-
|
|
|
- var textLine = _presenter.TextLayout.TextLines[lineIndex];
|
|
|
-
|
|
|
- var lineStart = textLine.FirstTextSourceIndex;
|
|
|
-
|
|
|
- var selectionStart = lineStart + selection.Start;
|
|
|
- var selectionEnd = lineStart + selection.End;
|
|
|
-
|
|
|
- _parent.SelectionStart = selectionStart;
|
|
|
- _parent.SelectionEnd = selectionEnd;
|
|
|
}
|
|
|
}
|
|
|
}
|