New controls, main view

This commit is contained in:
2023-08-09 20:40:54 +02:00
parent d549733b71
commit 7dcca6363b
41 changed files with 668 additions and 234 deletions

View File

@@ -5,14 +5,15 @@ using TerminalUI.ViewExtensions;
namespace TerminalUI.Controls;
public class Grid<T> : View<T>
public class Grid<T> : ChildContainerView<T>
{
private delegate void WithSizes(Span<int> widths, Span<int> heights);
private delegate TResult WithSizes<TResult>(Span<int> widths, Span<int> heights);
private const int ToBeCalculated = -1;
private readonly ObservableCollection<IView> _children = new();
public ReadOnlyObservableCollection<IView> Children { get; }
public GridChildInitializer<T> ChildInitializer { get; }
public ObservableCollection<RowDefinition> RowDefinitions { get; } = new();
public ObservableCollection<ColumnDefinition> ColumnDefinitions { get; } = new();
public ObservableCollection<RowDefinition> RowDefinitions { get; } = new() {RowDefinition.Star(1)};
public ObservableCollection<ColumnDefinition> ColumnDefinitions { get; } = new() {ColumnDefinition.Star(1)};
public object? ColumnDefinitionsObject
{
@@ -62,35 +63,76 @@ public class Grid<T> : View<T>
}
}
public Grid()
{
ChildInitializer = new GridChildInitializer<T>(this);
Children = new ReadOnlyObservableCollection<IView>(_children);
_children.CollectionChanged += (o, e) =>
public override Size GetRequestedSize()
=> WithCalculatedSize((columnWidths, rowHeights) =>
{
if (Attached)
var width = 0;
var height = 0;
for (var i = 0; i < columnWidths.Length; i++)
{
if (e.NewItems?.OfType<IView>() is { } newItems)
{
foreach (var newItem in newItems)
{
newItem.Attached = true;
}
}
ApplicationContext?.EventLoop.RequestRerender();
width += columnWidths[i];
}
};
}
public override Size GetRequestedSize() => throw new NotImplementedException();
for (var i = 0; i < rowHeights.Length; i++)
{
height += rowHeights[i];
}
return new Size(width, height);
}, new Option<Size>(new Size(0, 0), false));
protected override void DefaultRenderer(Position position, Size size)
=> WithCalculatedSize((columnWidths, rowHeights) =>
{
foreach (var child in Children)
{
var positionExtension = child.GetExtension<GridPositionExtension>();
var x = positionExtension?.Column ?? 0;
var y = positionExtension?.Row ?? 0;
var width = columnWidths[x];
var height = rowHeights[y];
var left = 0;
var top = 0;
for (var i = 0; i < x; i++)
{
left += columnWidths[i];
}
for (var i = 0; i < y; i++)
{
top += rowHeights[i];
}
child.Render(new Position(position.X + left, position.Y + top), new Size(width, height));
}
}, new Option<Size>(size, true));
private void WithCalculatedSize(WithSizes actionWithSizes, Option<Size> size)
{
//TODO: Optimize it, dont calculate all of these only if there is Auto value(s)
WithCalculatedSize(Helper, size);
object? Helper(Span<int> widths, Span<int> heights)
{
actionWithSizes(widths, heights);
return null;
}
}
private TResult WithCalculatedSize<TResult>(WithSizes<TResult> actionWithSizes, Option<Size> size)
{
//TODO: Optimize it, dont calculate all of these, only if there is Auto value(s)
var columns = ColumnDefinitions.Count;
Span<int> allWidth = stackalloc int[columns * RowDefinitions.Count];
Span<int> allHeight = stackalloc int[columns * RowDefinitions.Count];
var rows = RowDefinitions.Count;
if (columns < 1) columns = 1;
if (rows < 1) rows = 1;
Span<int> allWidth = stackalloc int[columns * rows];
Span<int> allHeight = stackalloc int[columns * rows];
foreach (var child in Children)
{
@@ -104,38 +146,47 @@ public class Grid<T> : View<T>
}
Span<int> columnWidths = stackalloc int[columns];
Span<int> rowHeights = stackalloc int[RowDefinitions.Count];
Span<int> rowHeights = stackalloc int[rows];
var usedWidth = 0;
var widthStars = 0;
for (var i = 0; i < columnWidths.Length; i++)
{
if (ColumnDefinitions[i].Type == GridUnitType.Pixel)
{
columnWidths[i] = ColumnDefinitions[i].Value;
}
else if (ColumnDefinitions[i].Type == GridUnitType.Star)
else if (size.IsSome && ColumnDefinitions[i].Type == GridUnitType.Star)
{
widthStars += ColumnDefinitions[i].Value;
columnWidths[i] = ToBeCalculated;
}
else
{
var max = 0;
for (var j = 0; j < RowDefinitions.Count; j++)
for (var j = 0; j < rows; j++)
{
max = Math.Max(max, allWidth.GetFromMatrix(i, j, columns));
}
columnWidths[i] = max;
}
if (columnWidths[i] != ToBeCalculated)
usedWidth += columnWidths[i];
}
var usedHeight = 0;
var heightStars = 0;
for (var i = 0; i < rowHeights.Length; i++)
{
if (RowDefinitions[i].Type == GridUnitType.Pixel)
{
rowHeights[i] = RowDefinitions[i].Value;
}
else if (RowDefinitions[i].Type == GridUnitType.Star)
else if (size.IsSome && RowDefinitions[i].Type == GridUnitType.Star)
{
heightStars += RowDefinitions[i].Value;
rowHeights[i] = ToBeCalculated;
}
else
@@ -148,33 +199,39 @@ public class Grid<T> : View<T>
rowHeights[i] = max;
}
if (rowHeights[i] != ToBeCalculated)
usedHeight += rowHeights[i];
}
foreach (var child in Children)
if (size.IsSome)
{
var childSize = child.GetRequestedSize();
var positionExtension = child.GetExtension<GridPositionExtension>();
var x = positionExtension?.Column ?? 0;
var y = positionExtension?.Row ?? 0;
var widthLeft = size.Value.Width - usedWidth;
var heightLeft = size.Value.Height - usedHeight;
var width = columnWidths[x];
var height = rowHeights[y];
var widthPerStart = (int) Math.Floor((double) widthLeft / widthStars);
var heightPerStart = (int) Math.Floor((double) heightLeft / heightStars);
var left = 0;
var top = 0;
for (var i = 0; i < x; i++)
for (var i = 0; i < columnWidths.Length; i++)
{
left += columnWidths[i];
var column = ColumnDefinitions[i];
if (column.Type == GridUnitType.Star)
{
columnWidths[i] = widthPerStart * column.Value;
}
}
for (var i = 0; i < y; i++)
for (var i = 0; i < rowHeights.Length; i++)
{
top += rowHeights[i];
var row = RowDefinitions[i];
if (row.Type == GridUnitType.Star)
{
rowHeights[i] = heightPerStart * row.Value;
}
}
child.Render(new Position(left, top), new Size(width, height));
}
return actionWithSizes(columnWidths, rowHeights);
}
public void SetRowDefinitions(string value)
@@ -190,7 +247,7 @@ public class Grid<T> : View<T>
}
else if (v.EndsWith("*"))
{
var starValue = int.Parse(v[0..^1]);
var starValue = v.Length == 1 ? 1 : int.Parse(v[..^1]);
RowDefinitions.Add(RowDefinition.Star(starValue));
}
else if (int.TryParse(v, out var pixelValue))
@@ -217,7 +274,7 @@ public class Grid<T> : View<T>
}
else if (v.EndsWith("*"))
{
var starValue = int.Parse(v[0..^1]);
var starValue = v.Length == 1 ? 1 : int.Parse(v[..^1]);
ColumnDefinitions.Add(ColumnDefinition.Star(starValue));
}
else if (int.TryParse(v, out var pixelValue))
@@ -230,19 +287,4 @@ public class Grid<T> : View<T>
}
}
}
public override TChild AddChild<TChild>(TChild child)
{
child = base.AddChild(child);
_children.Add(child);
return child;
}
public override TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
where TDataContext : default
{
child = base.AddChild(child, dataContextMapper);
_children.Add(child);
return child;
}
}