Files
Dashboard/Dashboard.Common/Layout/ContainerLayoutInfo.cs
T

118 lines
4.2 KiB
C#

using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Dashboard.Layout
{
public class ContainerLayoutInfo : INotifyPropertyChanged
{
public FlowDirection FlowDirection
{
get;
set => SetField(ref field, value);
} = FlowDirection.Row;
public Extents Padding
{
get;
set => SetField(ref field, value);
}
public event PropertyChangedEventHandler? PropertyChanged;
public virtual void ValidateLayout(ILayoutContainer container)
{
if (container.Count == 0)
return;
ItemSolution[] items = [.. container.Select(x => new ItemSolution(x))];
// TODO: is this the actual size budget available at draw time?
Vector2 budget = container.Layout.ComputedBox.Size - Padding.ExtentSize;
IEnumerable<ItemSolution> growItems = items.Where(x => x.DisplayMode == DisplayMode.Grow);
Vector2 denominator = growItems.Any()
? growItems.Select(x => x.Layout.Size + x.Layout.Margin.ExtentSize)
.Aggregate((a, b) => a + b)
: Vector2.One;
// TODO: The layout engine assumes row layout atm. Implement other layouts.
for (int i = 0; i < items.Length; i++)
items[i].Measure(budget, budget/denominator);
float fixedWidth = items
.Where(x => x.DisplayMode != DisplayMode.Grow)
.Sum(x => x.DesiredSize.X);
float remaining = budget.X - fixedWidth;
float star = remaining / denominator.X;
Vector2 pen = container.Layout.ComputedBox.Min + new Vector2(Padding.Left, Padding.Top);
for (int i = 0; i < items.Length; i++)
{
items[i].Measure(budget, new Vector2(star, budget.Y));
items[i].Position = pen;
pen.X += items[i].DesiredSize.X;
items[i].Finish();
}
}
private struct ItemSolution(ILayoutItem item)
{
public ILayoutItem Item { get; } = item;
public Vector2 InstrinsicSize { get; } = item.CalculateIntrinsicSize();
public Vector2 DesiredSize { get; private set; }
public readonly LayoutInfo Layout => Item.Layout;
public readonly DisplayMode DisplayMode => Item.Layout.DisplayMode;
public Vector2 Position { get; set; }
public void Measure(Vector2 budget, Vector2 coefficient)
{
switch (DisplayMode)
{
case DisplayMode.None:
DesiredSize = Vector2.Zero;
break;
case DisplayMode.Fit:
DesiredSize = InstrinsicSize;
break;
case DisplayMode.Fixed:
DesiredSize = Layout.Size;
break;
case DisplayMode.Grow:
DesiredSize = Layout.Size * coefficient;
break;
case DisplayMode.Relative:
DesiredSize = Layout.Size * budget;
break;
}
DesiredSize = Vector2.Clamp(DesiredSize, Layout.MinimumSize, Layout.MaximumSize);
}
public void Finish()
{
Layout.ComputedBox = new Box2d(Position, Position + DesiredSize);
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
}