118 lines
4.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|