Files
Common-VS2019/SacsMobileMaui/Maui/Layouts/SamWrapLayout.cs
T
2025-11-20 17:17:40 +00:00

244 lines
7.8 KiB
C#

using Microsoft.Maui.Controls.Compatibility;
namespace Sarand.Net.Mobile.Maui.Layouts
{
//=======================================================================
/// <summary>
/// Wrapping columnar layout - to achieve resposive behaviour.
/// <para>Layout which performs wrapping on the boundaries.</para>
/// <para>Base Code -> https://forums.xamarin.com/discussion/comment/57486/#Comment_57486</para>
/// <para> http://bit.ly/xf-custompanel</para>
/// </summary>
//=======================================================================
public class SamWrapLayout : Layout<View>
{
//-------------------------------------------------------------------
/// <summary>Horizontal spacing between elements.</summary>
public double SpacingX
{
get { return (double)GetValue(SpacingXProperty); }
set { SetValue(SpacingXProperty, value); }
}
public static BindableProperty SpacingXProperty =
BindableProperty.Create(
nameof(SpacingX),
typeof(double),
typeof(SamWrapLayout),
default(double),
defaultBindingMode: BindingMode.OneWay,
propertyChanged: (bindable, oldvalue, newvalue) => ((SamWrapLayout)bindable).InvalidateMeasure()
);
/// <summary>Vertical spacing between elements.</summary>
public double SpacingY
{
get { return (double)GetValue(SpacingYProperty); }
set { SetValue(SpacingYProperty, value); }
}
public static BindableProperty SpacingYProperty =
BindableProperty.Create(
nameof(SpacingY),
typeof(double),
typeof(SamWrapLayout),
default(double),
defaultBindingMode: BindingMode.OneWay,
propertyChanged: (bindable, oldvalue, newvalue) => ((SamWrapLayout)bindable).InvalidateMeasure()
);
//-------------------------------------------------------------------
/// <summary>Number of uniform columns in each row.</summary>
public int UniformColumns
{
get { return (int)GetValue(UniformColumnsProperty); }
set { SetValue(UniformColumnsProperty, value); }
}
public static BindableProperty UniformColumnsProperty =
BindableProperty.Create(
nameof(UniformColumns),
typeof(int),
typeof(SamWrapLayout),
default(int),
defaultBindingMode: BindingMode.OneWay,
propertyChanged: (bindable, oldvalue, newvalue) => ((SamWrapLayout)bindable).InvalidateMeasure()
);
//-------------------------------------------------------------------
/// <summary>Item height equals item width when UniformColums > 0</summary>
public bool IsSquare
{
get { return (bool)GetValue(IsSquareProperty); }
set { SetValue(IsSquareProperty, value); }
}
public static BindableProperty IsSquareProperty =
BindableProperty.Create(
nameof(IsSquare),
typeof(bool),
typeof(SamWrapLayout),
false,
propertyChanged: (bindable, oldvalue, newvalue) => ((SamWrapLayout)bindable).InvalidateMeasure()
);
//-------------------------------------------------------------------
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (WidthRequest > 0)
widthConstraint = Math.Min(widthConstraint, WidthRequest);
if (HeightRequest > 0)
heightConstraint = Math.Min(heightConstraint, HeightRequest);
double internalWidth = double.IsPositiveInfinity(widthConstraint) ? double.PositiveInfinity : Math.Max(0, widthConstraint);
double internalHeight = double.IsPositiveInfinity(heightConstraint) ? double.PositiveInfinity : Math.Max(0, heightConstraint);
if (double.IsPositiveInfinity(widthConstraint) && double.IsPositiveInfinity(heightConstraint))
{
return new SizeRequest(Microsoft.Maui.Graphics.Size.Zero, Microsoft.Maui.Graphics.Size.Zero);
}
return UniformColumns > 0 ? UniformMeasureAndLayout(internalWidth, internalHeight) :
VariableMeasureAndLayout(internalWidth, internalHeight);
}
//-------------------------------------------------------------------
protected override void LayoutChildren(double x, double y, double width, double height)
{
if (UniformColumns > 0)
UniformMeasureAndLayout(width, height, true, x, y);
else
VariableMeasureAndLayout(width, height, true, x, y);
}
//-------------------------------------------------------------------
private SizeRequest UniformMeasureAndLayout
(double widthConstraint, double heightConstraint, bool doLayout = false, double x = 0, double y = 0)
{
double totalWidth = 0;
double totalHeight = 0;
double rowHeight = 0;
double minWidth = 0;
double minHeight = 0;
double xPos = x;
double yPos = y;
totalWidth = widthConstraint;
var exceptedWidth = widthConstraint - (UniformColumns - 1) * SpacingX; //excepted spacing width
var columnsSize = exceptedWidth / UniformColumns;
if (columnsSize < 1)
{
columnsSize = 1;
}
foreach (var child in Children.Where(c => c.IsVisible))
{
var size = child.Measure(widthConstraint, heightConstraint);
var itemWidth = (double)columnsSize;
var itemHeight = size.Height;
if (IsSquare)
{
itemHeight = columnsSize;
}
rowHeight = Math.Max(rowHeight, itemHeight + SpacingY);
minHeight = Math.Max(minHeight, itemHeight);
minWidth = Math.Max(minWidth, itemWidth);
if (doLayout)
{
var region = new Rect(xPos, yPos, itemWidth, itemHeight);
LayoutChildIntoBoundingRegion(child, region);
}
xPos += itemWidth + SpacingX;
if (xPos + columnsSize - x > widthConstraint)
{
xPos = x;
yPos += rowHeight;
totalHeight += rowHeight;
rowHeight = 0;
}
}
totalHeight = Math.Max(totalHeight + rowHeight - SpacingY, 0);
return new SizeRequest(new Microsoft.Maui.Graphics.Size(totalWidth, totalHeight), new Microsoft.Maui.Graphics.Size(minWidth, minHeight));
}
//-------------------------------------------------------------------
private SizeRequest VariableMeasureAndLayout
(double widthConstraint, double heightConstraint, bool doLayout = false, double x = 0, double y = 0)
{
double totalWidth = 0;
double totalHeight = 0;
double rowHeight = 0;
double rowWidth = 0;
double minWidth = 0;
double minHeight = 0;
double xPos = x;
double yPos = y;
var visibleChildren = Children.Where(c => c.IsVisible).Select(c => new
{
child = c,
size = c.Measure(widthConstraint, heightConstraint)
});
var nextChildren = visibleChildren.Skip(1).ToList();
nextChildren.Add(null); //make element count same
var zipChildren = visibleChildren.Zip(nextChildren, (c, n) => new { current = c, next = n });
foreach (var childBlock in zipChildren)
{
var child = childBlock.current.child;
var size = childBlock.current.size;
var itemWidth = size.Width;
var itemHeight = size.Height;
rowHeight = Math.Max(rowHeight, itemHeight + SpacingY);
rowWidth += itemWidth + SpacingX;
minHeight = Math.Max(minHeight, itemHeight);
minWidth = Math.Max(minWidth, itemWidth);
if (doLayout)
{
var region = new Rect(xPos, yPos, itemWidth, itemHeight);
LayoutChildIntoBoundingRegion(child, region);
}
if (childBlock.next == null)
{
totalHeight += rowHeight;
totalWidth = Math.Max(totalWidth, rowWidth);
break;
}
xPos += itemWidth + SpacingX;
var nextWitdh = childBlock.next.size.Width;
if (xPos + nextWitdh - x > widthConstraint)
{
xPos = x;
yPos += rowHeight;
totalHeight += rowHeight;
totalWidth = Math.Max(totalWidth, rowWidth);
rowHeight = 0;
rowWidth = 0;
}
}
totalWidth = Math.Max(totalWidth - SpacingX, 0);
totalHeight = Math.Max(totalHeight - SpacingY, 0);
return new SizeRequest(new Microsoft.Maui.Graphics.Size(totalWidth, totalHeight), new Microsoft.Maui.Graphics.Size(minWidth, minHeight));
}
//-------------------------------------------------------------------
}
//=======================================================================
}