feat(TextArea): implement dynamic height adjustment with min/max constraints

This commit is contained in:
chinboy
2026-05-10 12:54:35 +08:00
parent 4104b74473
commit bfcd593ea9
4 changed files with 51 additions and 41 deletions
@@ -348,20 +348,20 @@
Title="With character counting"
Description="Show character counting.">
<StackPanel Orientation="Vertical" Spacing="10" Margin="0, 0, 20, 0">
<atom:LineEdit IsShowCount="True" MaxLength="20" />
<!-- <atom:LineEdit IsShowCount="True" MaxLength="20" /> -->
<atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="can resize" IsResizable="True" />
<atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="disable resize" Height="120"
IsResizable="True" />
</StackPanel>
</gallery:ShowCaseItem>
<gallery:ShowCaseItem
Title="Status"
Description="Add status to TextArea with status, which could be error or warning.">
<StackPanel Orientation="Vertical" Spacing="10" Margin="0, 0, 20, 0">
<atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="Error" IsResizable="True" Status="Error" />
<atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="Warning" IsResizable="True" Status="Warning"/>
<!-- <atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="disable resize" Height="120" -->
<!-- IsResizable="False" /> -->
</StackPanel>
</gallery:ShowCaseItem>
<!-- -->
<!-- <gallery:ShowCaseItem -->
<!-- Title="Status" -->
<!-- Description="Add status to TextArea with status, which could be error or warning."> -->
<!-- <StackPanel Orientation="Vertical" Spacing="10" Margin="0, 0, 20, 0"> -->
<!-- <atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="Error" IsResizable="True" Status="Error" /> -->
<!-- <atom:TextArea IsShowCount="True" MaxLength="100" PlaceholderText="Warning" IsResizable="True" Status="Warning"/> -->
<!-- </StackPanel> -->
<!-- </gallery:ShowCaseItem> -->
</gallery:ShowCasePanel>
</UserControl>
@@ -16,8 +16,9 @@ internal class ResizeHandle : TemplatedControl
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
e.Handled = true;
_lastPoint = e.GetPosition(TopLevel.GetTopLevel(this));
e.Handled = true;
_lastPoint = e.GetPosition(TopLevel.GetTopLevel(this));
e.Pointer.Capture(this);
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
@@ -26,9 +27,10 @@ internal class ResizeHandle : TemplatedControl
if (_lastPoint.HasValue)
{
Owner?.NotifyResizeCompleted();
e.Handled = true;
_lastPoint = null;
_dragging = false;
e.Handled = true;
_lastPoint = null;
_dragging = false;
e.Pointer.Capture(null);
}
}
+31 -24
View File
@@ -185,6 +185,8 @@ public class TextArea : AvaloniaTextBox,
private ResizeHandle? _resizeHandle;
private CompositeDisposable? _contentRightAddOnBindings;
private double? _originHeight; // 拖动改变高度的初始值
private double _minResizeHeight; // 拖动改变高度时允许的最小 TextArea.Height
private double _maxResizeHeight; // 拖动改变高度时允许的最大 TextArea.Height
static TextArea()
{
@@ -323,14 +325,8 @@ public class TextArea : AvaloniaTextBox,
{
if (Lines > 0 && double.IsNaN(Height))
{
var height = double.NaN;
Lines = Math.Max(Lines, MinLines);
var fontSize = FontSize;
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
var paragraphProperties = TextLayoutReflectionExtensions.CreateTextParagraphProperties(typeface, fontSize, null, default, default, null, default, LineHeight, default, FontFeatures);
var textLayout = new TextLayout(new LineTextSource(Lines), paragraphProperties);
var verticalSpace = this.GetVerticalSpaceBetweenScrollViewerAndPresenter();
height = Math.Ceiling(textLayout.Height + verticalSpace);
var height = CalculateScrollViewerHeight(Lines);
_scrollViewer.SetCurrentValue(MinHeightProperty, height);
_scrollViewer.SetCurrentValue(MaxHeightProperty, height);
}
@@ -338,6 +334,16 @@ public class TextArea : AvaloniaTextBox,
}
return size;
}
private double CalculateScrollViewerHeight(int lines)
{
var fontSize = FontSize;
var typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
var paragraphProperties = TextLayoutReflectionExtensions.CreateTextParagraphProperties(typeface, fontSize, null, default, default, null, default, LineHeight, default, FontFeatures);
var textLayout = new TextLayout(new LineTextSource(lines), paragraphProperties);
var verticalSpace = this.GetVerticalSpaceBetweenScrollViewerAndPresenter();
return Math.Ceiling(textLayout.Height + verticalSpace);
}
private void ConfigureEffectiveShowClearButton()
{
@@ -384,32 +390,33 @@ public class TextArea : AvaloniaTextBox,
internal void NotifyAboutToResize()
{
_originHeight = Bounds.Height;
SetCurrentValue(HeightProperty, Bounds.Height);
if (_scrollViewer != null)
{
if (!double.IsNaN(Height))
{
_scrollViewer.SetCurrentValue(HeightProperty, _scrollViewer.Bounds.Height);
}
var minHeight = _scrollViewer.MinHeight;
var height = _scrollViewer.Height;
if (double.IsNaN(height))
{
height = minHeight;
}
_originHeight = height;
SetCurrentValue(HeightProperty, double.NaN);
var overhead = Math.Max(0, Bounds.Height - _scrollViewer.Bounds.Height);
var minScrollHeight = MinLines > 0 ? CalculateScrollViewerHeight(MinLines) : 0;
_minResizeHeight = overhead + minScrollHeight;
_maxResizeHeight = MaxLines > 0
? overhead + CalculateScrollViewerHeight(MaxLines)
: double.PositiveInfinity;
_scrollViewer.ClearValue(MinHeightProperty);
_scrollViewer.ClearValue(MaxHeightProperty);
}
else
{
_minResizeHeight = 0;
_maxResizeHeight = double.PositiveInfinity;
}
}
internal void NotifyResizing(Point delta)
{
if (_scrollViewer != null && _originHeight != null)
if (_originHeight != null)
{
var minHeight = _scrollViewer.MinHeight;
var maxHeight = _scrollViewer.MaxHeight;
var height = _originHeight.Value + delta.Y;
height = Math.Max(minHeight, Math.Min(height, maxHeight));
_scrollViewer.SetCurrentValue(HeightProperty, height);
height = Math.Max(_minResizeHeight, Math.Min(height, _maxResizeHeight));
SetCurrentValue(HeightProperty, height);
}
}
@@ -103,6 +103,7 @@
Value="{atom:SharedTokenResource ColorTextPlaceholder}" />
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="MinLines" Value="1" />
<Style Selector="^ /template/ Panel#ContentLayout">
<Setter Property="Background" Value="Transparent" />