|
|
|
@ -18,9 +18,6 @@ using Avalonia.Media.TextFormatting; |
|
|
|
using Avalonia.Media.TextFormatting.Unicode; |
|
|
|
using Avalonia.Automation.Peers; |
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.Platform; |
|
|
|
using System.Reflection; |
|
|
|
using static System.Net.Mime.MediaTypeNames; |
|
|
|
|
|
|
|
namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
@ -28,6 +25,7 @@ namespace Avalonia.Controls |
|
|
|
/// Represents a control that can be used to display or edit unformatted text.
|
|
|
|
/// </summary>
|
|
|
|
[TemplatePart("PART_TextPresenter", typeof(TextPresenter))] |
|
|
|
[TemplatePart("PART_ScrollViewer", typeof(ScrollViewer))] |
|
|
|
[PseudoClasses(":empty")] |
|
|
|
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost |
|
|
|
{ |
|
|
|
@ -158,7 +156,7 @@ namespace Avalonia.Controls |
|
|
|
/// Defines see <see cref="TextPresenter.LineHeight"/> property.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly StyledProperty<double> LineHeightProperty = |
|
|
|
TextBlock.LineHeightProperty.AddOwner<TextBox>(); |
|
|
|
TextBlock.LineHeightProperty.AddOwner<TextBox>(new(defaultValue: double.NaN)); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines see <see cref="TextBlock.LetterSpacing"/> property.
|
|
|
|
@ -310,6 +308,7 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
|
|
|
|
private TextPresenter? _presenter; |
|
|
|
private ScrollViewer? _scrollViewer; |
|
|
|
private readonly TextBoxTextInputMethodClient _imClient = new(); |
|
|
|
private readonly UndoRedoHelper<UndoRedoState> _undoRedoHelper; |
|
|
|
private bool _isUndoingRedoing; |
|
|
|
@ -490,7 +489,7 @@ namespace Avalonia.Controls |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the maximum character length of the TextBox
|
|
|
|
/// Gets or sets the maximum number of visible lines.
|
|
|
|
/// </summary>
|
|
|
|
public int MaxLength |
|
|
|
{ |
|
|
|
@ -803,6 +802,8 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
_presenter = e.NameScope.Get<TextPresenter>("PART_TextPresenter"); |
|
|
|
|
|
|
|
_scrollViewer = e.NameScope.Find<ScrollViewer>("PART_ScrollViewer"); |
|
|
|
|
|
|
|
_imClient.SetPresenter(_presenter, this); |
|
|
|
|
|
|
|
if (IsFocused) |
|
|
|
@ -855,6 +856,10 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
OnSelectionEndChanged(change); |
|
|
|
} |
|
|
|
else if (change.Property == MaxLinesProperty) |
|
|
|
{ |
|
|
|
InvalidateMeasure(); |
|
|
|
} |
|
|
|
else if (change.Property == UndoLimitProperty) |
|
|
|
{ |
|
|
|
OnUndoLimitChanged(change.GetNewValue<int>()); |
|
|
|
@ -942,40 +947,10 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
_selectedTextChangesMadeSinceLastUndoSnapshot++; |
|
|
|
SnapshotUndoRedo(ignoreChangeCount: false); |
|
|
|
|
|
|
|
if (_presenter != null && MaxLines > 0) |
|
|
|
{ |
|
|
|
var lineCount = _presenter.TextLayout.TextLines.Count; |
|
|
|
|
|
|
|
var length = 0; |
|
|
|
|
|
|
|
var graphemeEnumerator = new GraphemeEnumerator(input.AsSpan()); |
|
|
|
|
|
|
|
while (graphemeEnumerator.MoveNext(out var grapheme)) |
|
|
|
{ |
|
|
|
if (grapheme.FirstCodepoint.IsBreakChar) |
|
|
|
{ |
|
|
|
if (lineCount + 1 > MaxLines) |
|
|
|
{ |
|
|
|
break; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
lineCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
length += grapheme.Length; |
|
|
|
} |
|
|
|
|
|
|
|
if (length < input.Length) |
|
|
|
{ |
|
|
|
input = input.Remove(Math.Max(0, length)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var currentText = Text ?? string.Empty; |
|
|
|
var selectionLength = Math.Abs(SelectionStart - SelectionEnd); |
|
|
|
var newLength = input.Length + currentText.Length - selectionLength; |
|
|
|
@ -1518,7 +1493,7 @@ namespace Avalonia.Controls |
|
|
|
_presenter.MoveCaretToPoint(point); |
|
|
|
|
|
|
|
var caretIndex = _presenter.CaretIndex; |
|
|
|
|
|
|
|
|
|
|
|
var selectionStart = SelectionStart; |
|
|
|
var selectionEnd = SelectionEnd; |
|
|
|
|
|
|
|
@ -1976,5 +1951,47 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
CanRedo = _undoRedoHelper.CanRedo; |
|
|
|
} |
|
|
|
|
|
|
|
protected override Size MeasureOverride(Size availableSize) |
|
|
|
{ |
|
|
|
if(_scrollViewer != null) |
|
|
|
{ |
|
|
|
var maxHeight = double.PositiveInfinity; |
|
|
|
|
|
|
|
if (MaxLines > 0 && double.IsNaN(Height)) |
|
|
|
{ |
|
|
|
var fontSize = FontSize; |
|
|
|
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); |
|
|
|
var paragraphProperties = TextLayout.CreateTextParagraphProperties(typeface, fontSize, null, default, default, null, default, LineHeight, default); |
|
|
|
var textLayout = new TextLayout(new MaxLinesTextSource(MaxLines), paragraphProperties); |
|
|
|
|
|
|
|
maxHeight = Math.Ceiling(textLayout.Height); |
|
|
|
} |
|
|
|
|
|
|
|
_scrollViewer.SetCurrentValue(MaxHeightProperty, maxHeight); |
|
|
|
} |
|
|
|
|
|
|
|
return base.MeasureOverride(availableSize); |
|
|
|
} |
|
|
|
|
|
|
|
private class MaxLinesTextSource : ITextSource |
|
|
|
{ |
|
|
|
private readonly int _maxLines; |
|
|
|
|
|
|
|
public MaxLinesTextSource(int maxLines) |
|
|
|
{ |
|
|
|
_maxLines = maxLines; |
|
|
|
} |
|
|
|
|
|
|
|
public TextRun? GetTextRun(int textSourceIndex) |
|
|
|
{ |
|
|
|
if (textSourceIndex >= _maxLines) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
return new TextEndOfLine(1); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|