244 lines
7.8 KiB
C#
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));
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|