365 lines
13 KiB
C#
365 lines
13 KiB
C#
using System.Numerics;
|
|
|
|
namespace Dashboard.Layout
|
|
{
|
|
public record struct LayoutItemSolution(ILayoutItem Item, ComputedBox Solution);
|
|
|
|
public class LayoutSolution
|
|
{
|
|
public ILayoutContainer Container { get; }
|
|
public IReadOnlyList<LayoutItemSolution> Items { get; }
|
|
|
|
private LayoutSolution(ILayoutContainer container, IEnumerable<LayoutItemSolution> itemSolutions)
|
|
{
|
|
Container = container;
|
|
Items = itemSolutions.ToList().AsReadOnly();
|
|
}
|
|
|
|
public static LayoutSolution CalculateLayout<T1>(T1 container, Vector2 limits, int iterations = 3, float absTol = 0.001f, float relTol = 0.01f)
|
|
where T1 : ILayoutContainer
|
|
{
|
|
switch (container.ContainerLayout.ContainerMode)
|
|
{
|
|
default:
|
|
case ContainerMode.Basic:
|
|
return SolveForBasicLayout(container, limits, iterations, absTol, relTol);
|
|
case ContainerMode.Flex:
|
|
return SolveForFlex(container, limits, iterations, absTol, relTol);
|
|
case ContainerMode.Grid:
|
|
return SolveForGrid(container, limits, iterations, absTol, relTol);
|
|
}
|
|
}
|
|
|
|
private static LayoutSolution SolveForGrid<T1>(T1 container, Vector2 limits, int iterations, float absTol, float relTol) where T1 : ILayoutContainer
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private static LayoutSolution SolveForFlex<T1>(T1 container, Vector2 limits,int iterations, float absTol, float relTol) where T1 : ILayoutContainer
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private static LayoutSolution SolveForBasicLayout<T1>(T1 container, Vector2 limits, int iterations, float absTol, float relTol) where T1 : ILayoutContainer
|
|
{
|
|
int count = container.Count();
|
|
LayoutItemSolution[] items = new LayoutItemSolution[count];
|
|
|
|
int i = 0;
|
|
foreach (ILayoutItem item in container)
|
|
{
|
|
items[i++] = new LayoutItemSolution(item, default);
|
|
}
|
|
|
|
bool limitX = limits.X > 0;
|
|
bool limitY = limits.Y > 0;
|
|
|
|
while (iterations-- > 0)
|
|
{
|
|
Vector2 pen = Vector2.Zero;
|
|
|
|
i = 0;
|
|
foreach (ILayoutItem item in container)
|
|
{
|
|
Vector2 size = item.CalculateIntrinsicSize();
|
|
|
|
}
|
|
}
|
|
|
|
return new LayoutSolution(container, items);
|
|
}
|
|
|
|
private record TrackSolution(TrackInfo Track)
|
|
{
|
|
public bool Auto => Track.Unit == LayoutUnit.Auto;
|
|
|
|
public bool Absolute => Track.Unit.IsAbsolute();
|
|
|
|
public float Requested { get; private set; }
|
|
public float Value { get; private set; }
|
|
|
|
public float Result { get; set; } = 0.0f;
|
|
public bool IsFrozen { get; set; } = false;
|
|
|
|
public void CalculateRequested(float dpi, float rel, float star)
|
|
{
|
|
Requested = new Metric(Track.Unit, Track.Width).Compute(dpi, rel, star);
|
|
}
|
|
|
|
public void Freeze()
|
|
{
|
|
if (IsFrozen)
|
|
return;
|
|
|
|
IsFrozen = true;
|
|
Result = Value;
|
|
}
|
|
}
|
|
|
|
private delegate float GetItemLength<in T1>(float dpi, float rel, float star, T1 item)
|
|
where T1 : ILayoutItem;
|
|
|
|
private TrackSolution[] SolveForGridTracks<T1, T2, T3>(
|
|
float limit,
|
|
T1 tracks,
|
|
T2 container,
|
|
int iterations,
|
|
float absTol,
|
|
float relTol,
|
|
Func<int, T3> getItemTrack,
|
|
GetItemLength<T3> getItemLength)
|
|
where T1 : IList<TrackInfo>
|
|
where T2 : ILayoutContainer
|
|
where T3 : ILayoutItem
|
|
{
|
|
int itemCount = container.Count();
|
|
bool auto = limit < 0;
|
|
TrackSolution[] solution = new TrackSolution[tracks.Count];
|
|
|
|
foreach (TrackSolution track in solution)
|
|
{
|
|
if (track.Absolute) {
|
|
// FIXME: pass DPI here.
|
|
track.CalculateRequested(96f, limit, 0);
|
|
track.Freeze();
|
|
}
|
|
}
|
|
|
|
while (iterations-- > 0)
|
|
{
|
|
}
|
|
|
|
for (int i = 0; i < tracks.Count; i++)
|
|
{
|
|
solution[i].Freeze();
|
|
}
|
|
|
|
return solution;
|
|
}
|
|
|
|
// private static void GetIntrinsicGridSizes<T1, T2>(Span<float> cols, Span<float> rows, T1 parent, T2 items)
|
|
// where T1 : ILayoutItem
|
|
// where T2 : IEnumerable<ILayoutItem>
|
|
// {
|
|
// CopyToSpan(rows, parent.Layout.Rows);
|
|
// CopyToSpan(cols, parent.Layout.Columns);
|
|
//
|
|
// foreach (ILayoutItem item in items)
|
|
// {
|
|
// int col = Math.Clamp(item.Layout.Column, 0, cols.Length - 1);
|
|
// int row = Math.Clamp(item.Layout.Row, 0, rows.Length - 1);
|
|
//
|
|
// bool autoCols = parent.Layout.Columns[col] < 0;
|
|
// bool autoRows = parent.Layout.Rows[row] < 0;
|
|
//
|
|
// if (!autoRows && !autoCols)
|
|
// continue;
|
|
//
|
|
// Vector2 size = item.CalculateIntrinsicSize();
|
|
// cols[col] = autoCols ? Math.Max(size.X, cols[col]) : cols[col];
|
|
// rows[row] = autoRows ? Math.Max(size.Y, rows[row]) : rows[row];
|
|
// }
|
|
// }
|
|
//
|
|
// public static Vector2 CalculateIntrinsicSize<T1, T2>(T1 parent, T2 items)
|
|
// where T1 : ILayoutItem
|
|
// where T2 : IEnumerable<ILayoutItem>
|
|
// {
|
|
// // Copy layout details.
|
|
// Span<float> cols = stackalloc float[parent.Layout.Columns.Count];
|
|
// Span<float> rows = stackalloc float[parent.Layout.Rows.Count];
|
|
//
|
|
// GetIntrinsicGridSizes(cols, rows, parent, items);
|
|
//
|
|
// float width = parent.Layout.Margin.X + parent.Layout.Margin.Z + parent.Layout.Padding.X + parent.Layout.Padding.Z + SumSpan<float>(cols);
|
|
// float height = parent.Layout.Margin.Y + parent.Layout.Margin.W + parent.Layout.Padding.Y + parent.Layout.Padding.W + SumSpan<float>(rows);
|
|
//
|
|
// return new Vector2(width, height);
|
|
// }
|
|
//
|
|
// public static GridResult Layout<T1, T2>(T1 parent, T2 items, Vector2 limits, int iterations = 3, float abstol = 0.0001f, float reltol = 0.01f)
|
|
// where T1 : ILayoutItem
|
|
// where T2 : IEnumerable<ILayoutItem>
|
|
// {
|
|
// Vector4 contentSpace = parent.Layout.Margin + parent.Layout.Padding;
|
|
// Vector2 contentLimits = new Vector2(
|
|
// limits.X > 0 ? limits.X - contentSpace.X - contentSpace.Z : -1,
|
|
// limits.Y > 0 ? limits.Y - contentSpace.Y - contentSpace.W : -1);
|
|
//
|
|
// // Get rows and columns for now.
|
|
// Span<Track> cols = stackalloc Track[parent.Layout.Columns.Count];
|
|
// Span<Track> rows = stackalloc Track[parent.Layout.Rows.Count];
|
|
//
|
|
// for (int i = 0; i < cols.Length; i++)
|
|
// {
|
|
// cols[i] = new Track(parent.Layout.Columns[i]);
|
|
//
|
|
// if (!cols[i].Auto)
|
|
// {
|
|
// cols[i].Freeze();
|
|
// }
|
|
// }
|
|
//
|
|
// for (int i = 0; i < rows.Length; i++)
|
|
// {
|
|
// rows[i] = new Track(parent.Layout.Rows[i]);
|
|
//
|
|
// if (!rows[i].Auto)
|
|
// {
|
|
// rows[i].Freeze();
|
|
// }
|
|
// }
|
|
//
|
|
// int freeRows = 0;
|
|
// int freeCols = 0;
|
|
// while (iterations-- > 0 && ((freeRows = CountFree(rows)) > 0 || (freeCols = CountFree(cols)) > 0))
|
|
// {
|
|
// // Calculate the remaining size.
|
|
// Vector2 remaining = contentLimits;
|
|
//
|
|
// for (int i = 0; contentLimits.X > 0 && i < cols.Length; i++)
|
|
// {
|
|
// if (cols[i].IsFrozen)
|
|
// remaining.X -= cols[i].Value;
|
|
// }
|
|
//
|
|
// for (int i = 0; contentLimits.Y > 0 && i < rows.Length; i++)
|
|
// {
|
|
// if (rows[i].IsFrozen)
|
|
// remaining.Y -= rows[i].Value;
|
|
// }
|
|
//
|
|
// Vector2 childLimits = remaining / new Vector2(Math.Max(freeCols, 1), Math.Max(freeRows, 1));
|
|
//
|
|
//
|
|
// // Calculate the size of each free track.
|
|
// foreach (ILayoutItem child in items)
|
|
// {
|
|
// int c = Math.Clamp(child.Layout.Column, 0, cols.Length - 1);
|
|
// int r = Math.Clamp(child.Layout.Row, 0, rows.Length - 1);
|
|
//
|
|
// bool autoRow = rows[r].Auto;
|
|
// bool autoCol = cols[c].Auto;
|
|
//
|
|
// if (!autoRow && !autoCol)
|
|
// continue;
|
|
//
|
|
// Vector2 childSize = child.CalculateSize(childLimits);
|
|
//
|
|
// if (autoCol)
|
|
// cols[c].Value = Math.Max(childLimits.X, childSize.X);
|
|
//
|
|
// if (autoRow)
|
|
// rows[r].Value = Math.Max(childLimits.Y, childSize.Y);
|
|
// }
|
|
//
|
|
// // Calculate for errors and decide to freeze them.
|
|
//
|
|
// for (int i = 0; limits.X > 0 && i < cols.Length; i++)
|
|
// {
|
|
// if (WithinTolerance(cols[i].Value, childLimits.X, abstol, reltol))
|
|
// {
|
|
// cols[i].Freeze();
|
|
// }
|
|
// }
|
|
//
|
|
// for (int i = 0; limits.Y > 0 && i < rows.Length; i++)
|
|
// {
|
|
// if (WithinTolerance(rows[i].Value, childLimits.Y, abstol, reltol))
|
|
// {
|
|
// rows[i].Freeze();
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// Vector2 size = new Vector2(
|
|
// parent.Layout.Margin.X + parent.Layout.Margin.Z + parent.Layout.Padding.X + parent.Layout.Padding.Z,
|
|
// parent.Layout.Margin.Y + parent.Layout.Margin.W + parent.Layout.Padding.Y + parent.Layout.Padding.W);
|
|
//
|
|
// foreach (ref Track col in cols)
|
|
// {
|
|
// col.Freeze();
|
|
// size.X += col.Result;
|
|
// }
|
|
//
|
|
// foreach (ref Track row in rows)
|
|
// {
|
|
// row.Freeze();
|
|
// size.Y += row.Result;
|
|
// }
|
|
//
|
|
// if (limits.X > 0) size.X = Math.Max(size.X, limits.X);
|
|
// if (limits.Y > 0) size.Y = Math.Max(size.Y, limits.Y);
|
|
//
|
|
// // Temporary solution
|
|
// return new GridResult(size, cols.ToArray().Select(x => x.Result).ToArray(), rows.ToArray().Select(x => x.Result).ToArray());
|
|
//
|
|
// static int CountFree(Span<Track> tracks)
|
|
// {
|
|
// int i = 0;
|
|
// foreach (Track track in tracks)
|
|
// {
|
|
// if (!track.IsFrozen)
|
|
// i++;
|
|
// }
|
|
//
|
|
// return i;
|
|
// }
|
|
// }
|
|
//
|
|
// private static void CopyToSpan<T1, T2>(Span<T1> span, T2 items)
|
|
// where T2 : IEnumerable<T1>
|
|
// {
|
|
// using IEnumerator<T1> iterator = items.GetEnumerator();
|
|
// for (int i = 0; i < span.Length; i++)
|
|
// {
|
|
// if (!iterator.MoveNext())
|
|
// break;
|
|
//
|
|
// span[i] = iterator.Current;
|
|
// }
|
|
// }
|
|
//
|
|
// private static T1 SumSpan<T1>(ReadOnlySpan<T1> span)
|
|
// where T1 : struct, INumber<T1>
|
|
// {
|
|
// T1 value = default;
|
|
//
|
|
// foreach (T1 item in span)
|
|
// {
|
|
// value += item;
|
|
// }
|
|
//
|
|
// return value;
|
|
// }
|
|
//
|
|
// private static bool WithinTolerance<T>(T value, T limit, T abstol, T reltol)
|
|
// where T : INumber<T>
|
|
// {
|
|
// T tol = T.Max(abstol, value * reltol);
|
|
// return T.Abs(value - limit) < tol;
|
|
// }
|
|
//
|
|
// public record GridResult(Vector2 Size, float[] Columns, float[] Rows)
|
|
// {
|
|
//
|
|
// }
|
|
//
|
|
// private record struct Track(float Request)
|
|
// {
|
|
// public bool Auto => Request < 0;
|
|
//
|
|
// public bool IsFrozen { get; private set; } = false;
|
|
// public float Value { get; set; } = Request;
|
|
//
|
|
// public float Result { get; private set; }
|
|
//
|
|
// public void Freeze()
|
|
// {
|
|
// Result = Value;
|
|
// IsFrozen = true;
|
|
// }
|
|
// }
|
|
}
|
|
}
|