wip
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Toolkit.UI.Controls.Avalonia
|
||||
{
|
||||
public class ResponsiveGrid : Grid
|
||||
{
|
||||
public static readonly AvaloniaProperty<int> ActualColumnProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualColumn", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> ActualRowProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ActualRow", 0);
|
||||
|
||||
public static readonly StyledProperty<SizeThresholds> BreakPointsProperty =
|
||||
AvaloniaProperty.Register<ResponsiveGrid, SizeThresholds>(nameof(Thresholds));
|
||||
|
||||
public static readonly AvaloniaProperty<int> LargeOffsetProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargeOffset", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> LargePullProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePull", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> LargePushProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("LargePush", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> LargeProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Large", 0);
|
||||
|
||||
public static readonly StyledProperty<int> MaxDivisionProperty =
|
||||
AvaloniaProperty.Register<ResponsiveGrid, int>(nameof(MaxDivision), 12);
|
||||
|
||||
public static readonly AvaloniaProperty<int> MediumOffsetProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumOffset", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> MediumPullProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPull", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> MediumPushProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("MediumPush", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> MediumProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Medium", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> SmallOffsetProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallOffset", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> SmallPullProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPull", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> SmallPushProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("SmallPush", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> SmallProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("Small", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> ExtraSmallOffsetProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallOffset", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> ExtraSmallPullProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPull", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> ExtraSmallPushProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmallPush", 0);
|
||||
|
||||
public static readonly AvaloniaProperty<int> ExtraSmallProperty =
|
||||
AvaloniaProperty.RegisterAttached<ResponsiveGrid, Control, int>("ExtraSmall", 0);
|
||||
|
||||
static ResponsiveGrid()
|
||||
{
|
||||
AffectsMeasure<ResponsiveGrid>(
|
||||
MaxDivisionProperty,
|
||||
BreakPointsProperty,
|
||||
LargeProperty,
|
||||
MediumProperty,
|
||||
SmallProperty,
|
||||
ExtraSmallProperty,
|
||||
LargeOffsetProperty,
|
||||
LargePullProperty,
|
||||
LargePushProperty,
|
||||
MediumOffsetProperty,
|
||||
MediumPullProperty,
|
||||
MediumPushProperty,
|
||||
SmallOffsetProperty,
|
||||
SmallPullProperty,
|
||||
SmallPushProperty,
|
||||
ExtraSmallOffsetProperty,
|
||||
ExtraSmallPullProperty,
|
||||
ExtraSmallPushProperty
|
||||
);
|
||||
}
|
||||
|
||||
public ResponsiveGrid()
|
||||
{
|
||||
MaxDivision = 12;
|
||||
Thresholds = new SizeThresholds();
|
||||
}
|
||||
|
||||
public int MaxDivision
|
||||
{
|
||||
get => GetValue(MaxDivisionProperty);
|
||||
set => SetValue(MaxDivisionProperty, value);
|
||||
}
|
||||
|
||||
public SizeThresholds Thresholds
|
||||
{
|
||||
get => GetValue(BreakPointsProperty);
|
||||
set => SetValue(BreakPointsProperty, value);
|
||||
}
|
||||
|
||||
public static int GetActualColumn(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ActualColumnProperty);
|
||||
|
||||
public static int GetActualRow(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ActualRowProperty);
|
||||
|
||||
public static int GetLarge(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(LargeProperty);
|
||||
|
||||
public static int GetLargeOffset(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(LargeOffsetProperty);
|
||||
|
||||
public static int GetLargePull(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(LargePullProperty);
|
||||
|
||||
public static int GetLargePush(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(LargePushProperty);
|
||||
|
||||
public static int GetMedium(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(MediumProperty);
|
||||
|
||||
public static int GetMediumOffset(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(MediumOffsetProperty);
|
||||
|
||||
public static int GetMediumPull(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(MediumPullProperty);
|
||||
|
||||
public static int GetMediumPush(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(MediumPushProperty);
|
||||
|
||||
public static int GetSmall(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(SmallProperty);
|
||||
|
||||
public static int GetSmallOffset(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(SmallOffsetProperty);
|
||||
|
||||
public static int GetSmallPull(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(SmallPullProperty);
|
||||
|
||||
public static int GetSmallPush(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(SmallPushProperty);
|
||||
|
||||
public static int GetExtraSmall(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ExtraSmallProperty);
|
||||
|
||||
public static int GetExtraSmallOffset(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ExtraSmallOffsetProperty);
|
||||
|
||||
public static int GetExtraSmallPull(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ExtraSmallPullProperty);
|
||||
|
||||
public static int GetExtraSmallPush(AvaloniaObject avaloniaObject) =>
|
||||
avaloniaObject.GetValue<int>(ExtraSmallPushProperty);
|
||||
|
||||
public static void SetLarge(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(LargeProperty, value);
|
||||
|
||||
public static void SetLargeOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(LargeOffsetProperty, value);
|
||||
|
||||
public static void SetLargePull(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(LargePullProperty, value);
|
||||
|
||||
public static void SetLargePush(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(LargePushProperty, value);
|
||||
|
||||
public static void SetMedium(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(MediumProperty, value);
|
||||
|
||||
public static void SetMediumOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(MediumOffsetProperty, value);
|
||||
|
||||
public static void SetMediumPull(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(MediumPullProperty, value);
|
||||
|
||||
public static void SetMediumPush(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(MediumPushProperty, value);
|
||||
|
||||
public static void SetSmall(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(SmallProperty, value);
|
||||
|
||||
public static void SetSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(SmallOffsetProperty, value);
|
||||
|
||||
public static void SetSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(SmallPullProperty, value);
|
||||
|
||||
public static void SetSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(SmallPushProperty, value);
|
||||
|
||||
public static void SetExtraSmall(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ExtraSmallProperty, value);
|
||||
|
||||
public static void SetExtraSmallOffset(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ExtraSmallOffsetProperty, value);
|
||||
|
||||
public static void SetExtraSmallPull(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ExtraSmallPullProperty, value);
|
||||
|
||||
public static void SetExtraSmallPush(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ExtraSmallPushProperty, value);
|
||||
|
||||
protected static void SetActualColumn(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ActualColumnProperty, value);
|
||||
|
||||
protected static void SetActualRow(AvaloniaObject avaloniaObject, int value) =>
|
||||
avaloniaObject.SetValue(ActualRowProperty, value);
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
double columnWidth = finalSize.Width / MaxDivision;
|
||||
|
||||
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
||||
|
||||
double yOffset = 0;
|
||||
foreach (IGrouping<int, Control> row in groupedRows)
|
||||
{
|
||||
double maxRowHeight = row.Max(control => control.DesiredSize.Height);
|
||||
|
||||
foreach (Control element in row)
|
||||
{
|
||||
int column = GetActualColumn(element);
|
||||
int span = GetSpan(element, finalSize.Width);
|
||||
|
||||
Rect rect = new(column * columnWidth, yOffset, span * columnWidth, maxRowHeight);
|
||||
element.Arrange(rect);
|
||||
}
|
||||
|
||||
yOffset += maxRowHeight;
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
protected int GetOffset(Control control, double width)
|
||||
{
|
||||
int GetXS() => Math.Max(0, GetExtraSmallOffset(control));
|
||||
int GetSM() => Math.Max(GetXS(), GetSmallOffset(control));
|
||||
int GetMD() => Math.Max(GetSM(), GetMediumOffset(control));
|
||||
int GetLG() => Math.Max(GetMD(), GetLargeOffset(control));
|
||||
|
||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG();
|
||||
return Math.Min(span, MaxDivision);
|
||||
}
|
||||
|
||||
protected int GetPull(Control control, double width)
|
||||
{
|
||||
int GetXS() => Math.Max(0, GetExtraSmallPull(control));
|
||||
int GetSM() => Math.Max(GetXS(), GetSmallPull(control));
|
||||
int GetMD() => Math.Max(GetSM(), GetMediumPull(control));
|
||||
int GetLG() => Math.Max(GetMD(), GetLargePull(control));
|
||||
|
||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG();
|
||||
return Math.Min(span, MaxDivision);
|
||||
}
|
||||
|
||||
protected int GetPush(Control control, double width)
|
||||
{
|
||||
int GetXS() => Math.Max(0, GetExtraSmallPush(control));
|
||||
int GetSM() => Math.Max(GetXS(), GetSmallPush(control));
|
||||
int GetMD() => Math.Max(GetSM(), GetMediumPush(control));
|
||||
int GetLG() => Math.Max(GetMD(), GetLargePush(control));
|
||||
|
||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG();
|
||||
return Math.Min(span, MaxDivision);
|
||||
}
|
||||
|
||||
protected int GetSpan(Control control, double width)
|
||||
{
|
||||
int GetXS() => Math.Max(0, GetExtraSmall(control));
|
||||
int GetSM() => Math.Max(GetXS(), GetSmall(control));
|
||||
int GetMD() => Math.Max(GetSM(), GetMedium(control));
|
||||
int GetLG() => Math.Max(GetMD(), GetLarge(control));
|
||||
|
||||
int span = width < Thresholds.ExtraSmallToSmall ? GetXS() : width < Thresholds.SmallToMedium ? GetSM() : width < Thresholds.MediumToLarge ? GetMD() : GetLG();
|
||||
return Math.Min(span, MaxDivision);
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
int count = 0;
|
||||
int currentRow = 0;
|
||||
|
||||
double availableWidth = double.IsPositiveInfinity(availableSize.Width)
|
||||
? double.PositiveInfinity
|
||||
: availableSize.Width / MaxDivision;
|
||||
|
||||
foreach (Control control in Children.OfType<Control>())
|
||||
{
|
||||
if (control.IsVisible)
|
||||
{
|
||||
int span = GetSpan(control, availableSize.Width);
|
||||
int offset = GetOffset(control, availableSize.Width);
|
||||
int push = GetPush(control, availableSize.Width);
|
||||
int pull = GetPull(control, availableSize.Width);
|
||||
|
||||
if (count + span + offset > MaxDivision)
|
||||
{
|
||||
currentRow++;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
SetActualColumn(control, count + offset + push - pull);
|
||||
SetActualRow(control, currentRow);
|
||||
|
||||
count += span + offset;
|
||||
|
||||
Size size = new(availableWidth * span, double.PositiveInfinity);
|
||||
control.Measure(size);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<IGrouping<int, Control>> groupedRows = Children.OfType<Control>().GroupBy(GetActualRow);
|
||||
|
||||
Size totalSize = new(groupedRows.Max(rows => rows.Sum(control => control.DesiredSize.Width)),
|
||||
groupedRows.Sum(rows => rows.Max(control => control.DesiredSize.Height)));
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Avalonia;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Toolkit.UI.Controls.Avalonia
|
||||
{
|
||||
[TypeConverter(typeof(SizeThresholdsTypeConverter))]
|
||||
public class SizeThresholds : AvaloniaObject
|
||||
{
|
||||
public static readonly StyledProperty<double> MediumToLargeProperty =
|
||||
AvaloniaProperty.Register<SizeThresholds, double>("MediumToLarge", 1200.0);
|
||||
|
||||
public static readonly StyledProperty<double> SmallToMediumProperty =
|
||||
AvaloniaProperty.Register<SizeThresholds, double>("SmallToMedium", 992.0);
|
||||
|
||||
public static readonly StyledProperty<double> ExtraSmallToSmallProperty =
|
||||
AvaloniaProperty.Register<SizeThresholds, double>("ExtraSmallToSmall", 768.0);
|
||||
|
||||
public double MediumToLarge
|
||||
{
|
||||
get => (double)GetValue(MediumToLargeProperty);
|
||||
set => SetValue(MediumToLargeProperty, value);
|
||||
}
|
||||
|
||||
public double SmallToMedium
|
||||
{
|
||||
get => (double)GetValue(SmallToMediumProperty);
|
||||
set => SetValue(SmallToMediumProperty, value);
|
||||
}
|
||||
|
||||
public double ExtraSmallToSmall
|
||||
{
|
||||
get => (double)GetValue(ExtraSmallToSmallProperty);
|
||||
set => SetValue(ExtraSmallToSmallProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Toolkit.UI.Controls.Avalonia
|
||||
{
|
||||
public class SizeThresholdsTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext? context,
|
||||
CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is not string text)
|
||||
{
|
||||
return new SizeThresholds();
|
||||
}
|
||||
|
||||
List<int> values = text.Split(',')
|
||||
.Select(o => o.Trim())
|
||||
.Select(o => int.TryParse(o, out var result) ? result : 0)
|
||||
.ToList();
|
||||
|
||||
if (values.Count != 3)
|
||||
{
|
||||
return new SizeThresholds
|
||||
{
|
||||
ExtraSmallToSmall = 768,
|
||||
SmallToMedium = 992,
|
||||
MediumToLarge = 1200
|
||||
};
|
||||
}
|
||||
|
||||
return new SizeThresholds
|
||||
{
|
||||
ExtraSmallToSmall = values[0],
|
||||
SmallToMedium = values[1],
|
||||
MediumToLarge = values[2]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user