Implement ItemSpacing and LineSpacing in ElasticWrapPanel (#843)
* Initial plan * Implement ItemSpacing and LineSpacing support for ElasticWrapPanel Co-authored-by: rabbitism <14807942+rabbitism@users.noreply.github.com> * Add demo tab showcasing ItemSpacing and LineSpacing Co-authored-by: rabbitism <14807942+rabbitism@users.noreply.github.com> * Add additional test for ItemSpacing affecting line wrapping Co-authored-by: rabbitism <14807942+rabbitism@users.noreply.github.com> * feat: add bindings for IsFillHorizontal and IsFillVertical in ElasticWrapPanelDemo * Fix measure/arrange mismatch by tracking spacing in UVCollection Co-authored-by: rabbitism <14807942+rabbitism@users.noreply.github.com> * feat: add ItemSpacing & LineSpacing FormItems. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rabbitism <14807942+rabbitism@users.noreply.github.com> Co-authored-by: Dong Bin <popmessiah@hotmail.com> Co-authored-by: Zhang Dian <54255897+zdpcdt@users.noreply.github.com>
This commit is contained in:
@@ -64,6 +64,22 @@
|
|||||||
Minimum="0"
|
Minimum="0"
|
||||||
Value="{Binding ItemHeight}" />
|
Value="{Binding ItemHeight}" />
|
||||||
</u:FormItem>
|
</u:FormItem>
|
||||||
|
<u:FormItem Label="ItemSpacing">
|
||||||
|
<u:NumericDoubleUpDown
|
||||||
|
AllowDrag="True"
|
||||||
|
Step="1"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="-10"
|
||||||
|
Value="{Binding ItemSpacing}" />
|
||||||
|
</u:FormItem>
|
||||||
|
<u:FormItem Label="LineSpacing">
|
||||||
|
<u:NumericDoubleUpDown
|
||||||
|
AllowDrag="True"
|
||||||
|
Step="1"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="-10"
|
||||||
|
Value="{Binding LineSpacing}" />
|
||||||
|
</u:FormItem>
|
||||||
</u:FormGroup>
|
</u:FormGroup>
|
||||||
<u:FormGroup Header="FixToRB Extension" IsEnabled="{Binding #FixToRBTabItem.IsSelected}">
|
<u:FormGroup Header="FixToRB Extension" IsEnabled="{Binding #FixToRBTabItem.IsSelected}">
|
||||||
<u:FormItem>
|
<u:FormItem>
|
||||||
@@ -108,6 +124,8 @@
|
|||||||
IsFillVertical="{Binding IsFillVertical}"
|
IsFillVertical="{Binding IsFillVertical}"
|
||||||
ItemHeight="{Binding ItemHeight}"
|
ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -134,6 +152,8 @@
|
|||||||
IsFillVertical="{Binding IsFillVertical}"
|
IsFillVertical="{Binding IsFillVertical}"
|
||||||
ItemHeight="{Binding ItemHeight}"
|
ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -165,6 +185,8 @@
|
|||||||
IsFillVertical="{Binding IsFillVertical}"
|
IsFillVertical="{Binding IsFillVertical}"
|
||||||
ItemHeight="{Binding ItemHeight}"
|
ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -200,6 +222,8 @@
|
|||||||
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
||||||
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -223,6 +247,8 @@
|
|||||||
IsFillVertical="{Binding IsFillVertical}"
|
IsFillVertical="{Binding IsFillVertical}"
|
||||||
ItemHeight="{Binding ItemHeight}"
|
ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -249,6 +275,8 @@
|
|||||||
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
<u:Divider HorizontalContentAlignment="Left" Content="WrapPanel" />
|
||||||
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
<WrapPanel ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
@@ -285,6 +313,8 @@
|
|||||||
IsFillVertical="{Binding IsFillVertical}"
|
IsFillVertical="{Binding IsFillVertical}"
|
||||||
ItemHeight="{Binding ItemHeight}"
|
ItemHeight="{Binding ItemHeight}"
|
||||||
ItemWidth="{Binding ItemWidth}"
|
ItemWidth="{Binding ItemWidth}"
|
||||||
|
ItemSpacing="{Binding ItemSpacing}"
|
||||||
|
LineSpacing="{Binding LineSpacing}"
|
||||||
Orientation="{Binding SelectedOrientation}">
|
Orientation="{Binding SelectedOrientation}">
|
||||||
<Border Background="{DynamicResource SemiRed5Color}" />
|
<Border Background="{DynamicResource SemiRed5Color}" />
|
||||||
<Border Background="{DynamicResource SemiPink5Color}" />
|
<Border Background="{DynamicResource SemiPink5Color}" />
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ public partial class ElasticWrapPanelDemoViewModel : ObservableObject
|
|||||||
[ObservableProperty] private bool _isFillVertical;
|
[ObservableProperty] private bool _isFillVertical;
|
||||||
[ObservableProperty] private double _itemWidth = 40d;
|
[ObservableProperty] private double _itemWidth = 40d;
|
||||||
[ObservableProperty] private double _itemHeight = 40d;
|
[ObservableProperty] private double _itemHeight = 40d;
|
||||||
|
[ObservableProperty] private double _itemSpacing;
|
||||||
|
[ObservableProperty] private double _lineSpacing;
|
||||||
|
|
||||||
[ObservableProperty] private bool _autoWidth = true;
|
[ObservableProperty] private bool _autoWidth = true;
|
||||||
[ObservableProperty] private bool _autoHeight = true;
|
[ObservableProperty] private bool _autoHeight = true;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
{
|
{
|
||||||
static ElasticWrapPanel()
|
static ElasticWrapPanel()
|
||||||
{
|
{
|
||||||
AffectsMeasure<ElasticWrapPanel>(IsFillHorizontalProperty, IsFillVerticalProperty);
|
AffectsMeasure<ElasticWrapPanel>(IsFillHorizontalProperty, IsFillVerticalProperty, ItemSpacingProperty, LineSpacingProperty);
|
||||||
AffectsArrange<ElasticWrapPanel>(IsFillHorizontalProperty, IsFillVerticalProperty);
|
AffectsArrange<ElasticWrapPanel>(IsFillHorizontalProperty, IsFillVerticalProperty, ItemSpacingProperty, LineSpacingProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region AttachedProperty
|
#region AttachedProperty
|
||||||
@@ -78,6 +78,8 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
double itemHeight = ItemHeight;
|
double itemHeight = ItemHeight;
|
||||||
var orientation = Orientation;
|
var orientation = Orientation;
|
||||||
var children = Children;
|
var children = Children;
|
||||||
|
double itemSpacing = ItemSpacing;
|
||||||
|
double lineSpacing = LineSpacing;
|
||||||
|
|
||||||
// Determine the space required for items in the same row/column based on horizontal/vertical arrangement
|
// Determine the space required for items in the same row/column based on horizontal/vertical arrangement
|
||||||
var curLineSize = new UVSize(orientation);
|
var curLineSize = new UVSize(orientation);
|
||||||
@@ -111,6 +113,8 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
itemWidthSet ? itemWidth : 0,
|
itemWidthSet ? itemWidth : 0,
|
||||||
itemHeightSet ? itemHeight : 0);
|
itemHeightSet ? itemHeight : 0);
|
||||||
|
|
||||||
|
bool isFirstItemInLine = true;
|
||||||
|
|
||||||
foreach (var child in children)
|
foreach (var child in children)
|
||||||
{
|
{
|
||||||
UVSize sz;
|
UVSize sz;
|
||||||
@@ -138,21 +142,38 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
sz.V = itemSetSize.V;
|
sz.V = itemSetSize.V;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MathHelpers.GreaterThan(curLineSize.U + sz.U, uvConstraint.U))
|
double itemUWithSpacing = isFirstItemInLine ? sz.U : sz.U + itemSpacing;
|
||||||
|
|
||||||
|
if (MathHelpers.GreaterThan(curLineSize.U + itemUWithSpacing, uvConstraint.U))
|
||||||
{
|
{
|
||||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||||
|
if (panelSize.V > 0)
|
||||||
|
{
|
||||||
|
panelSize.V += lineSpacing;
|
||||||
|
}
|
||||||
panelSize.V += curLineSize.V;
|
panelSize.V += curLineSize.V;
|
||||||
curLineSize = sz;
|
curLineSize = sz;
|
||||||
|
isFirstItemInLine = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (!isFirstItemInLine)
|
||||||
|
{
|
||||||
|
curLineSize.U += itemSpacing;
|
||||||
|
}
|
||||||
curLineSize.U += sz.U;
|
curLineSize.U += sz.U;
|
||||||
curLineSize.V = Max(sz.V, curLineSize.V);
|
curLineSize.V = Max(sz.V, curLineSize.V);
|
||||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||||
|
if (panelSize.V > 0)
|
||||||
|
{
|
||||||
|
panelSize.V += lineSpacing;
|
||||||
|
}
|
||||||
panelSize.V += curLineSize.V;
|
panelSize.V += curLineSize.V;
|
||||||
|
isFirstItemInLine = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
curLineSize = new UVSize(orientation);
|
curLineSize = new UVSize(orientation);
|
||||||
|
isFirstItemInLine = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,23 +185,40 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
itemWidthSet ? itemWidth : child.DesiredSize.Width,
|
itemWidthSet ? itemWidth : child.DesiredSize.Width,
|
||||||
itemHeightSet ? itemHeight : child.DesiredSize.Height);
|
itemHeightSet ? itemHeight : child.DesiredSize.Height);
|
||||||
|
|
||||||
if (MathHelpers.GreaterThan(curLineSize.U + sz.U, uvConstraint.U)) // Need to switch to another line
|
double itemUWithSpacing = isFirstItemInLine ? sz.U : sz.U + itemSpacing;
|
||||||
|
|
||||||
|
if (MathHelpers.GreaterThan(curLineSize.U + itemUWithSpacing, uvConstraint.U)) // Need to switch to another line
|
||||||
{
|
{
|
||||||
panelSize.U = Max(curLineSize.U, panelSize.U);
|
panelSize.U = Max(curLineSize.U, panelSize.U);
|
||||||
|
if (panelSize.V > 0)
|
||||||
|
{
|
||||||
|
panelSize.V += lineSpacing;
|
||||||
|
}
|
||||||
panelSize.V += curLineSize.V;
|
panelSize.V += curLineSize.V;
|
||||||
curLineSize = sz;
|
curLineSize = sz;
|
||||||
|
isFirstItemInLine = false;
|
||||||
|
|
||||||
if (MathHelpers.GreaterThan(sz.U, uvConstraint.U)) // The element is wider than the constraint - give it a separate line
|
if (MathHelpers.GreaterThan(sz.U, uvConstraint.U)) // The element is wider than the constraint - give it a separate line
|
||||||
{
|
{
|
||||||
panelSize.U = Max(sz.U, panelSize.U);
|
panelSize.U = Max(sz.U, panelSize.U);
|
||||||
|
if (panelSize.V > 0)
|
||||||
|
{
|
||||||
|
panelSize.V += lineSpacing;
|
||||||
|
}
|
||||||
panelSize.V += sz.V;
|
panelSize.V += sz.V;
|
||||||
curLineSize = new UVSize(orientation);
|
curLineSize = new UVSize(orientation);
|
||||||
|
isFirstItemInLine = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Continue to accumulate a line
|
else // Continue to accumulate a line
|
||||||
{
|
{
|
||||||
|
if (!isFirstItemInLine)
|
||||||
|
{
|
||||||
|
curLineSize.U += itemSpacing;
|
||||||
|
}
|
||||||
curLineSize.U += sz.U;
|
curLineSize.U += sz.U;
|
||||||
curLineSize.V = Max(sz.V, curLineSize.V);
|
curLineSize.V = Max(sz.V, curLineSize.V);
|
||||||
|
isFirstItemInLine = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +235,8 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
{
|
{
|
||||||
bool itemWidthSet = !double.IsNaN(ItemWidth);
|
bool itemWidthSet = !double.IsNaN(ItemWidth);
|
||||||
bool itemHeightSet = !double.IsNaN(ItemHeight);
|
bool itemHeightSet = !double.IsNaN(ItemHeight);
|
||||||
|
double itemSpacing = ItemSpacing;
|
||||||
|
double lineSpacing = LineSpacing;
|
||||||
|
|
||||||
// This is the size for non-space measurement
|
// This is the size for non-space measurement
|
||||||
UVSize itemSetSize = new UVSize(Orientation,
|
UVSize itemSetSize = new UVSize(Orientation,
|
||||||
@@ -212,7 +252,7 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
#region Get the collection of elements in the same direction
|
#region Get the collection of elements in the same direction
|
||||||
|
|
||||||
// Current collection of elements in a row/column
|
// Current collection of elements in a row/column
|
||||||
UVCollection curLineUIs = new UVCollection(Orientation, itemSetSize);
|
UVCollection curLineUIs = new UVCollection(Orientation, itemSetSize, itemSpacing);
|
||||||
|
|
||||||
// Iterate over the child elements
|
// Iterate over the child elements
|
||||||
var children = Children;
|
var children = Children;
|
||||||
@@ -242,14 +282,16 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
sz.V = itemSetSize.V;
|
sz.V = itemSetSize.V;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MathHelpers.GreaterThan(curLineUIs.TotalU + sz.U, uvFinalSize.U))
|
// Account for spacing before this item if it's not the first
|
||||||
|
double spacingBeforeItem = curLineUIs.Count > 0 ? itemSpacing : 0;
|
||||||
|
if (MathHelpers.GreaterThan(curLineUIs.TotalU + spacingBeforeItem + sz.U, uvFinalSize.U))
|
||||||
{
|
{
|
||||||
if (curLineUIs.Count > 0)
|
if (curLineUIs.Count > 0)
|
||||||
{
|
{
|
||||||
lineUVCollection.Add(curLineUIs);
|
lineUVCollection.Add(curLineUIs);
|
||||||
}
|
}
|
||||||
|
|
||||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
curLineUIs = new UVCollection(Orientation, itemSetSize, itemSpacing);
|
||||||
curLineUIs.Add(child, sz, Convert.ToInt32(lengthCount));
|
curLineUIs.Add(child, sz, Convert.ToInt32(lengthCount));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -258,7 +300,7 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineUVCollection.Add(curLineUIs);
|
lineUVCollection.Add(curLineUIs);
|
||||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
curLineUIs = new UVCollection(Orientation, itemSetSize, itemSpacing);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -266,19 +308,21 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
itemWidthSet ? ItemWidth : child.DesiredSize.Width,
|
itemWidthSet ? ItemWidth : child.DesiredSize.Width,
|
||||||
itemHeightSet ? ItemHeight : child.DesiredSize.Height);
|
itemHeightSet ? ItemHeight : child.DesiredSize.Height);
|
||||||
|
|
||||||
if (MathHelpers.GreaterThan(curLineUIs.TotalU + sz.U, uvFinalSize.U)) // Need to switch to another line
|
// Account for spacing before this item if it's not the first
|
||||||
|
double spacingBeforeItem = curLineUIs.Count > 0 ? itemSpacing : 0;
|
||||||
|
if (MathHelpers.GreaterThan(curLineUIs.TotalU + spacingBeforeItem + sz.U, uvFinalSize.U)) // Need to switch to another line
|
||||||
{
|
{
|
||||||
if (curLineUIs.Count > 0)
|
if (curLineUIs.Count > 0)
|
||||||
{
|
{
|
||||||
lineUVCollection.Add(curLineUIs);
|
lineUVCollection.Add(curLineUIs);
|
||||||
}
|
}
|
||||||
|
|
||||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
curLineUIs = new UVCollection(Orientation, itemSetSize, itemSpacing);
|
||||||
curLineUIs.Add(child, sz);
|
curLineUIs.Add(child, sz);
|
||||||
if (MathHelpers.GreaterThan(sz.U, uvFinalSize.U))
|
if (MathHelpers.GreaterThan(sz.U, uvFinalSize.U))
|
||||||
{
|
{
|
||||||
lineUVCollection.Add(curLineUIs);
|
lineUVCollection.Add(curLineUIs);
|
||||||
curLineUIs = new UVCollection(Orientation, itemSetSize);
|
curLineUIs = new UVCollection(Orientation, itemSetSize, itemSpacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -332,7 +376,8 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
}
|
}
|
||||||
maxElementCount = Max(sum, maxElementCount);
|
maxElementCount = Max(sum, maxElementCount);
|
||||||
}
|
}
|
||||||
adaptULength = (uvFinalSize.U - maxElementCount * itemSetSize.U) / maxElementCount;
|
double totalItemSpacing = maxElementCount > 1 ? (maxElementCount - 1) * itemSpacing : 0;
|
||||||
|
adaptULength = (uvFinalSize.U - maxElementCount * itemSetSize.U - totalItemSpacing) / maxElementCount;
|
||||||
adaptULength = Max(adaptULength, 0);
|
adaptULength = Max(adaptULength, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,16 +387,19 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
if (itemSetSize.V > 0)
|
if (itemSetSize.V > 0)
|
||||||
{
|
{
|
||||||
isAdaptV = true;
|
isAdaptV = true;
|
||||||
adaptVLength = uvFinalSize.V / lineUVCollection.Count;
|
double totalLineSpacing = lineUVCollection.Count > 1 ? (lineUVCollection.Count - 1) * lineSpacing : 0;
|
||||||
|
adaptVLength = (uvFinalSize.V - totalLineSpacing) / lineUVCollection.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isHorizontal = Orientation == Orientation.Horizontal;
|
bool isHorizontal = Orientation == Orientation.Horizontal;
|
||||||
|
int lineIndex = 0;
|
||||||
foreach (var uvCollection in lineUVCollection)
|
foreach (var uvCollection in lineUVCollection)
|
||||||
{
|
{
|
||||||
double u = 0;
|
double u = 0;
|
||||||
var lineUIEles = uvCollection.UICollection.Keys.ToList();
|
var lineUIEles = uvCollection.UICollection.Keys.ToList();
|
||||||
double linevV = isAdaptV ? adaptVLength : uvCollection.LineV;
|
double linevV = isAdaptV ? adaptVLength : uvCollection.LineV;
|
||||||
|
int itemIndex = 0;
|
||||||
foreach (var child in lineUIEles)
|
foreach (var child in lineUIEles)
|
||||||
{
|
{
|
||||||
UVLengthSize childSize = uvCollection.UICollection[child];
|
UVLengthSize childSize = uvCollection.UICollection[child];
|
||||||
@@ -384,9 +432,19 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
}
|
}
|
||||||
|
|
||||||
u += layoutSlotU;
|
u += layoutSlotU;
|
||||||
|
if (itemIndex < lineUIEles.Count - 1)
|
||||||
|
{
|
||||||
|
u += itemSpacing;
|
||||||
|
}
|
||||||
|
itemIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
accumulatedV += linevV;
|
accumulatedV += linevV;
|
||||||
|
if (lineIndex < lineUVCollection.Count - 1)
|
||||||
|
{
|
||||||
|
accumulatedV += lineSpacing;
|
||||||
|
}
|
||||||
|
lineIndex++;
|
||||||
lineUIEles.Clear();
|
lineUIEles.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,14 +521,25 @@ public class ElasticWrapPanel : WrapPanel
|
|||||||
|
|
||||||
private UVSize ItemSetSize;
|
private UVSize ItemSetSize;
|
||||||
|
|
||||||
public UVCollection(Orientation orientation, UVSize itemSetSize)
|
private double ItemSpacing;
|
||||||
|
|
||||||
|
public UVCollection(Orientation orientation, UVSize itemSetSize, double itemSpacing = 0)
|
||||||
{
|
{
|
||||||
UICollection = new Dictionary<Control, UVLengthSize>();
|
UICollection = new Dictionary<Control, UVLengthSize>();
|
||||||
LineDesireUVSize = new UVSize(orientation);
|
LineDesireUVSize = new UVSize(orientation);
|
||||||
ItemSetSize = itemSetSize;
|
ItemSetSize = itemSetSize;
|
||||||
|
ItemSpacing = itemSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double TotalU => LineDesireUVSize.U;
|
public double TotalU
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// TotalU includes spacing between items (not before first or after last)
|
||||||
|
double spacingTotal = UICollection.Count > 1 ? (UICollection.Count - 1) * ItemSpacing : 0;
|
||||||
|
return LineDesireUVSize.U + spacingTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public double LineV => LineDesireUVSize.V;
|
public double LineV => LineDesireUVSize.V;
|
||||||
|
|
||||||
|
|||||||
@@ -69,4 +69,151 @@ public class Tests
|
|||||||
window.Show();
|
window.Show();
|
||||||
Assert.Equal(2, panel.LineCount);
|
Assert.Equal(2, panel.LineCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void ItemSpacing_Applied_Correctly()
|
||||||
|
{
|
||||||
|
var window = new Window() { };
|
||||||
|
var panel = new ElasticWrapPanel
|
||||||
|
{
|
||||||
|
Width = 400,
|
||||||
|
Height = 200,
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
ItemSpacing = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add 3 rectangles of 100x100 with 10px spacing
|
||||||
|
// Total width should be: 100 + 10 + 100 + 10 + 100 = 320
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = 100,
|
||||||
|
Height = 100,
|
||||||
|
};
|
||||||
|
panel.Children.Add(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Content = panel;
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
// All 3 should fit on one line
|
||||||
|
Assert.Equal(1, panel.LineCount);
|
||||||
|
|
||||||
|
// Check that the desired size accounts for spacing
|
||||||
|
Assert.True(panel.DesiredSize.Width >= 320);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void LineSpacing_Applied_Correctly()
|
||||||
|
{
|
||||||
|
var window = new Window() { };
|
||||||
|
var panel = new ElasticWrapPanel
|
||||||
|
{
|
||||||
|
Width = 200,
|
||||||
|
Height = 400,
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
LineSpacing = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add 4 rectangles of 100x100, should wrap to 2 lines
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = 100,
|
||||||
|
Height = 100,
|
||||||
|
};
|
||||||
|
panel.Children.Add(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Content = panel;
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
// Should have 2 lines
|
||||||
|
Assert.Equal(2, panel.LineCount);
|
||||||
|
|
||||||
|
// Total height should be: 100 + 20 + 100 = 220
|
||||||
|
Assert.True(panel.DesiredSize.Height >= 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void ItemSpacing_And_LineSpacing_Together()
|
||||||
|
{
|
||||||
|
var window = new Window() { };
|
||||||
|
var panel = new ElasticWrapPanel
|
||||||
|
{
|
||||||
|
Width = 250,
|
||||||
|
Height = 400,
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
ItemSpacing = 10,
|
||||||
|
LineSpacing = 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add 4 rectangles of 100x100
|
||||||
|
// With ItemSpacing=10, two items per line need 100 + 10 + 100 = 210 width
|
||||||
|
// With LineSpacing=20, two lines need 100 + 20 + 100 = 220 height
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = 100,
|
||||||
|
Height = 100,
|
||||||
|
};
|
||||||
|
panel.Children.Add(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Content = panel;
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
// Should have 2 lines
|
||||||
|
Assert.Equal(2, panel.LineCount);
|
||||||
|
|
||||||
|
// Check dimensions include spacing
|
||||||
|
Assert.True(panel.DesiredSize.Width >= 210);
|
||||||
|
Assert.True(panel.DesiredSize.Height >= 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void ItemSpacing_Does_Not_Affect_LineCount()
|
||||||
|
{
|
||||||
|
var window = new Window() { };
|
||||||
|
|
||||||
|
// Panel without spacing - should fit 2 items per line (200 width / 100 per item = 2)
|
||||||
|
var panel1 = new ElasticWrapPanel
|
||||||
|
{
|
||||||
|
Width = 200,
|
||||||
|
Height = 400,
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
ItemSpacing = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
panel1.Children.Add(new Rectangle { Width = 100, Height = 100 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel with spacing - items with spacing should fit fewer per line
|
||||||
|
// 100 + 10 + 100 = 210 > 200, so only 1 item per line
|
||||||
|
var panel2 = new ElasticWrapPanel
|
||||||
|
{
|
||||||
|
Width = 200,
|
||||||
|
Height = 400,
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
ItemSpacing = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
panel2.Children.Add(new Rectangle { Width = 100, Height = 100 });
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Content = panel1;
|
||||||
|
window.Show();
|
||||||
|
Assert.Equal(2, panel1.LineCount); // 2 items per line, 4 items total = 2 lines
|
||||||
|
|
||||||
|
window.Content = panel2;
|
||||||
|
window.UpdateLayout();
|
||||||
|
Assert.Equal(4, panel2.LineCount); // 1 item per line, 4 items total = 4 lines
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user