using System.Numerics; namespace Dashboard.Layout { public record struct LayoutItemSolution(ILayoutItem Item, ComputedBox Solution); public class LayoutSolution { public ILayoutContainer Container { get; } public IReadOnlyList Items { get; } private LayoutSolution(ILayoutContainer container, IEnumerable itemSolutions) { Container = container; Items = itemSolutions.ToList().AsReadOnly(); } public static LayoutSolution CalculateLayout(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 container, Vector2 limits, int iterations, float absTol, float relTol) where T1 : ILayoutContainer { throw new NotImplementedException(); } private static LayoutSolution SolveForFlex(T1 container, Vector2 limits,int iterations, float absTol, float relTol) where T1 : ILayoutContainer { throw new NotImplementedException(); } private static LayoutSolution SolveForBasicLayout(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(float dpi, float rel, float star, T1 item) where T1 : ILayoutItem; private TrackSolution[] SolveForGridTracks( float limit, T1 tracks, T2 container, int iterations, float absTol, float relTol, Func getItemTrack, GetItemLength getItemLength) where T1 : IList 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(Span cols, Span rows, T1 parent, T2 items) // where T1 : ILayoutItem // where T2 : IEnumerable // { // 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 parent, T2 items) // where T1 : ILayoutItem // where T2 : IEnumerable // { // // Copy layout details. // Span cols = stackalloc float[parent.Layout.Columns.Count]; // Span 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(cols); // float height = parent.Layout.Margin.Y + parent.Layout.Margin.W + parent.Layout.Padding.Y + parent.Layout.Padding.W + SumSpan(rows); // // return new Vector2(width, height); // } // // public static GridResult Layout(T1 parent, T2 items, Vector2 limits, int iterations = 3, float abstol = 0.0001f, float reltol = 0.01f) // where T1 : ILayoutItem // where T2 : IEnumerable // { // 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 cols = stackalloc Track[parent.Layout.Columns.Count]; // Span 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 tracks) // { // int i = 0; // foreach (Track track in tracks) // { // if (!track.IsFrozen) // i++; // } // // return i; // } // } // // private static void CopyToSpan(Span span, T2 items) // where T2 : IEnumerable // { // using IEnumerator iterator = items.GetEnumerator(); // for (int i = 0; i < span.Length; i++) // { // if (!iterator.MoveNext()) // break; // // span[i] = iterator.Current; // } // } // // private static T1 SumSpan(ReadOnlySpan span) // where T1 : struct, INumber // { // T1 value = default; // // foreach (T1 item in span) // { // value += item; // } // // return value; // } // // private static bool WithinTolerance(T value, T limit, T abstol, T reltol) // where T : INumber // { // 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; // } // } } }