|
|
@@ -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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|