From 8147a05b5cd319458aad0cb1e8b61b0556a4d8f6 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Wed, 3 Sep 2025 21:18:00 +0800 Subject: [PATCH] feat: add TrailingWrapWidth. --- .../Panels/WrapPanelWithTrailingItem.cs | 88 ++++++++++--------- .../WrapPanelWithTrailingItemTests.cs | 4 +- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/Ursa/Controls/Panels/WrapPanelWithTrailingItem.cs b/src/Ursa/Controls/Panels/WrapPanelWithTrailingItem.cs index 7bdf531..f26d193 100644 --- a/src/Ursa/Controls/Panels/WrapPanelWithTrailingItem.cs +++ b/src/Ursa/Controls/Panels/WrapPanelWithTrailingItem.cs @@ -6,18 +6,15 @@ using Irihi.Avalonia.Shared.Helpers; namespace Ursa.Controls; -public class WrapPanelWithTrailingItem: Panel +public class WrapPanelWithTrailingItem : Panel { - public static readonly StyledProperty TrailingItemProperty = AvaloniaProperty.Register( - nameof(TrailingItem)); + public static readonly StyledProperty TrailingItemProperty = + AvaloniaProperty.Register( + nameof(TrailingItem)); - public Layoutable? TrailingItem - { - get => GetValue(TrailingItemProperty); - set => SetValue(TrailingItemProperty, value); - } - - + public static readonly StyledProperty TrailingWrapWidthProperty = + AvaloniaProperty.Register( + nameof(TrailingWrapWidth)); static WrapPanelWithTrailingItem() { @@ -25,52 +22,55 @@ public class WrapPanelWithTrailingItem: Panel AffectsArrange(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) { base.OnPropertyChanged(change); if (change.Property == TrailingItemProperty) { - if(change.GetOldValue() is { } oldValue) + if (change.GetOldValue() is { } oldValue) { VisualChildren.Remove(oldValue); - if (!IsItemsHost) - { - LogicalChildren.Remove(oldValue); - } + if (!IsItemsHost) LogicalChildren.Remove(oldValue); } - if(change.GetNewValue() is {} newValue) + + if (change.GetNewValue() is { } newValue) { VisualChildren.Add(newValue); - if (!IsItemsHost) - { - LogicalChildren.Add(newValue); - } + if (!IsItemsHost) LogicalChildren.Add(newValue); } } WrapPanel p = new WrapPanel(); } - protected override void ChildrenChanged(object? sender, NotifyCollectionChangedEventArgs e) - { - base.ChildrenChanged(sender, e); - } - protected override Size MeasureOverride(Size availableSize) { double currentLineX = 0; double currentLineHeight = 0; double totalHeight = 0; - + var children = Children; foreach (var child in children) { child.Measure(availableSize); - double deltaX = availableSize.Width - currentLineX; + var deltaX = availableSize.Width - currentLineX; // Width is enough to place next child if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width)) { - currentLineX+=child.DesiredSize.Width; + currentLineX += child.DesiredSize.Width; currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height); } // Width is not enough to place next child @@ -88,11 +88,11 @@ public class WrapPanelWithTrailingItem: Panel var last = TrailingItem; if (last is null) return new Size(availableSize.Width, totalHeight); 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 (lastDeltaX < 30) + if (lastDeltaX < TrailingWrapWidth) { - totalHeight+=currentLineHeight; + totalHeight += currentLineHeight; totalHeight += last.DesiredSize.Height; } else @@ -103,23 +103,24 @@ public class WrapPanelWithTrailingItem: Panel return new Size(availableSize.Width, totalHeight); } - + protected override Size ArrangeOverride(Size finalSize) { double currentLineX = 0; double currentLineHeight = 0; double totalHeight = 0; 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; // Width is enough to place next child if (MathHelpers.GreaterThan(deltaX, child.DesiredSize.Width)) { - child.Arrange(new Rect(currentLineX, totalHeight, child.DesiredSize.Width, Math.Max(child.DesiredSize.Height, currentLineHeight))); - currentLineX += child.DesiredSize.Width; - currentLineHeight = Math.Max(currentLineHeight, child.DesiredSize.Height); + child.Arrange(new Rect(currentLineX, totalHeight, child.DesiredSize.Width, + Math.Max(child.DesiredSize.Height, currentLineHeight))); + currentLineX += child.Bounds.Width; + currentLineHeight = Math.Max(currentLineHeight, child.Bounds.Height); } // Width is not enough to place next child // reset currentLineX and currentLineHeight @@ -128,17 +129,18 @@ public class WrapPanelWithTrailingItem: Panel else { totalHeight += currentLineHeight; - child.Arrange(new Rect(0, totalHeight, Math.Min(child.DesiredSize.Width, finalSize.Width), child.DesiredSize.Height)); - currentLineX = child.DesiredSize.Width; - currentLineHeight = child.DesiredSize.Height; + child.Arrange(new Rect(0, totalHeight, Math.Min(child.DesiredSize.Width, finalSize.Width), + child.DesiredSize.Height)); + currentLineX = child.Bounds.Width; + currentLineHeight = child.Bounds.Height; } } - + var last = TrailingItem; 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 (lastDeltaX < 30) + if (lastDeltaX < TrailingWrapWidth) { totalHeight += currentLineHeight; last.Arrange(new Rect(0, totalHeight, finalSize.Width, last.DesiredSize.Height)); diff --git a/tests/HeadlessTest.Ursa/Controls/PanelTests/WrapPanelWithTrailingItemTests.cs b/tests/HeadlessTest.Ursa/Controls/PanelTests/WrapPanelWithTrailingItemTests.cs index 76d5224..c1387a1 100644 --- a/tests/HeadlessTest.Ursa/Controls/PanelTests/WrapPanelWithTrailingItemTests.cs +++ b/tests/HeadlessTest.Ursa/Controls/PanelTests/WrapPanelWithTrailingItemTests.cs @@ -10,7 +10,7 @@ namespace HeadlessTest.Ursa.Controls.PanelTests; public class WrapPanelWithTrailingItemTests { - [Fact] + [AvaloniaFact] public void Visual_Children_Correct() { var panel = new WrapPanelWithTrailingItem(); @@ -57,7 +57,7 @@ public class WrapPanelWithTrailingItemTests Assert.Equal(trailing2, visualChildren[2]); } - [Fact] + [AvaloniaFact] // Items Appears in Logical Children because IsItemsHost is false for individual Panels public void Logical_Children_Correct() {