feat: add TrailingWrapWidth.

This commit is contained in:
Dong Bin
2025-09-03 21:18:00 +08:00
parent 9ee89b7ce5
commit 8147a05b5c
2 changed files with 47 additions and 45 deletions

View File

@@ -6,18 +6,15 @@ using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls; namespace Ursa.Controls;
public class WrapPanelWithTrailingItem: Panel public class WrapPanelWithTrailingItem : Panel
{ {
public static readonly StyledProperty<Layoutable?> TrailingItemProperty = AvaloniaProperty.Register<WrapPanelWithTrailingItem, Layoutable?>( public static readonly StyledProperty<Layoutable?> TrailingItemProperty =
nameof(TrailingItem)); AvaloniaProperty.Register<WrapPanelWithTrailingItem, Layoutable?>(
nameof(TrailingItem));
public Layoutable? TrailingItem public static readonly StyledProperty<double> TrailingWrapWidthProperty =
{ AvaloniaProperty.Register<WrapPanelWithTrailingItem, double>(
get => GetValue(TrailingItemProperty); nameof(TrailingWrapWidth));
set => SetValue(TrailingItemProperty, value);
}
static WrapPanelWithTrailingItem() static WrapPanelWithTrailingItem()
{ {
@@ -25,52 +22,55 @@ public class WrapPanelWithTrailingItem: Panel
AffectsArrange<WrapPanelWithTrailingItem>(TrailingItemProperty); AffectsArrange<WrapPanelWithTrailingItem>(TrailingItemProperty);
} }
public Layoutable? TrailingItem
{
get => GetValue(TrailingItemProperty);
set => SetValue(TrailingItemProperty, value);
}
public double TrailingWrapWidth
{
get => GetValue(TrailingWrapWidthProperty);
set => SetValue(TrailingWrapWidthProperty, value);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == TrailingItemProperty) if (change.Property == TrailingItemProperty)
{ {
if(change.GetOldValue<Layoutable?>() is { } oldValue) if (change.GetOldValue<Layoutable?>() is { } oldValue)
{ {
VisualChildren.Remove(oldValue); VisualChildren.Remove(oldValue);
if (!IsItemsHost) if (!IsItemsHost) LogicalChildren.Remove(oldValue);
{
LogicalChildren.Remove(oldValue);
}
} }
if(change.GetNewValue<Layoutable?>() is {} newValue)
if (change.GetNewValue<Layoutable?>() is { } newValue)
{ {
VisualChildren.Add(newValue); VisualChildren.Add(newValue);
if (!IsItemsHost) if (!IsItemsHost) LogicalChildren.Add(newValue);
{
LogicalChildren.Add(newValue);
}
} }
} }
WrapPanel p = new WrapPanel(); WrapPanel p = new WrapPanel();
} }
protected override void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
base.ChildrenChanged(sender, e);
}
protected override Size MeasureOverride(Size availableSize) protected override Size MeasureOverride(Size availableSize)
{ {
double currentLineX = 0; double currentLineX = 0;
double currentLineHeight = 0; double currentLineHeight = 0;
double totalHeight = 0; double totalHeight = 0;
var children = Children; var children = Children;
foreach (var child in children) foreach (var child in children)
{ {
child.Measure(availableSize); child.Measure(availableSize);
double deltaX = availableSize.Width - currentLineX; var deltaX = availableSize.Width - currentLineX;
// Width is enough to place next child // Width is enough to place next child
if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width)) if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width))
{ {
currentLineX+=child.DesiredSize.Width; currentLineX += child.DesiredSize.Width;
currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height); currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height);
} }
// Width is not enough to place next child // Width is not enough to place next child
@@ -88,11 +88,11 @@ public class WrapPanelWithTrailingItem: Panel
var last = TrailingItem; var last = TrailingItem;
if (last is null) return new Size(availableSize.Width, totalHeight); if (last is null) return new Size(availableSize.Width, totalHeight);
last.Measure(availableSize); last.Measure(availableSize);
double lastDeltaX = availableSize.Width - currentLineX; var lastDeltaX = availableSize.Width - currentLineX;
// If width is not enough, add a new line, and recalculate total height // If width is not enough, add a new line, and recalculate total height
if (lastDeltaX < 30) if (lastDeltaX < TrailingWrapWidth)
{ {
totalHeight+=currentLineHeight; totalHeight += currentLineHeight;
totalHeight += last.DesiredSize.Height; totalHeight += last.DesiredSize.Height;
} }
else else
@@ -103,23 +103,24 @@ public class WrapPanelWithTrailingItem: Panel
return new Size(availableSize.Width, totalHeight); return new Size(availableSize.Width, totalHeight);
} }
protected override Size ArrangeOverride(Size finalSize) protected override Size ArrangeOverride(Size finalSize)
{ {
double currentLineX = 0; double currentLineX = 0;
double currentLineHeight = 0; double currentLineHeight = 0;
double totalHeight = 0; double totalHeight = 0;
var children = Children; var children = Children;
for (int i = 0; i < children.Count; i++)
foreach (var child in children)
{ {
var child = children[i];
double deltaX = finalSize.Width - currentLineX; double deltaX = finalSize.Width - currentLineX;
// Width is enough to place next child // Width is enough to place next child
if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width)) if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width))
{ {
child.Arrange(new Rect(currentLineX, totalHeight, child.DesiredSize.Width, Math.Max(child.DesiredSize.Height, currentLineHeight))); child.Arrange(new Rect(currentLineX, totalHeight, child.DesiredSize.Width,
currentLineX += child.DesiredSize.Width; Math.Max(child.DesiredSize.Height, currentLineHeight)));
currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height); currentLineX += child.Bounds.Width;
currentLineHeight = Math.Max(currentLineHeight, child.Bounds.Height);
} }
// Width is not enough to place next child // Width is not enough to place next child
// reset currentLineX and currentLineHeight // reset currentLineX and currentLineHeight
@@ -128,17 +129,18 @@ public class WrapPanelWithTrailingItem: Panel
else else
{ {
totalHeight += currentLineHeight; totalHeight += currentLineHeight;
child.Arrange(new Rect(0, totalHeight, Math.Min(child.DesiredSize.Width, finalSize.Width), child.DesiredSize.Height)); child.Arrange(new Rect(0, totalHeight, Math.Min(child.DesiredSize.Width, finalSize.Width),
currentLineX = child.DesiredSize.Width; child.DesiredSize.Height));
currentLineHeight = child.DesiredSize.Height; currentLineX = child.Bounds.Width;
currentLineHeight = child.Bounds.Height;
} }
} }
var last = TrailingItem; var last = TrailingItem;
if (last is null) return new Size(finalSize.Width, totalHeight); if (last is null) return new Size(finalSize.Width, totalHeight);
double lastDeltaX = finalSize.Width - currentLineX; var lastDeltaX = finalSize.Width - currentLineX;
// If width is not enough, add a new line, and recalculate total height // If width is not enough, add a new line, and recalculate total height
if (lastDeltaX < 30) if (lastDeltaX < TrailingWrapWidth)
{ {
totalHeight += currentLineHeight; totalHeight += currentLineHeight;
last.Arrange(new Rect(0, totalHeight, finalSize.Width, last.DesiredSize.Height)); last.Arrange(new Rect(0, totalHeight, finalSize.Width, last.DesiredSize.Height));

View File

@@ -10,7 +10,7 @@ namespace HeadlessTest.Ursa.Controls.PanelTests;
public class WrapPanelWithTrailingItemTests public class WrapPanelWithTrailingItemTests
{ {
[Fact] [AvaloniaFact]
public void Visual_Children_Correct() public void Visual_Children_Correct()
{ {
var panel = new WrapPanelWithTrailingItem(); var panel = new WrapPanelWithTrailingItem();
@@ -57,7 +57,7 @@ public class WrapPanelWithTrailingItemTests
Assert.Equal(trailing2, visualChildren[2]); Assert.Equal(trailing2, visualChildren[2]);
} }
[Fact] [AvaloniaFact]
// Items Appears in Logical Children because IsItemsHost is false for individual Panels // Items Appears in Logical Children because IsItemsHost is false for individual Panels
public void Logical_Children_Correct() public void Logical_Children_Correct()
{ {