Console bugfixes

This commit is contained in:
2024-02-26 18:13:23 +01:00
parent 5b4bda55ed
commit 9ee819cee3
4 changed files with 108 additions and 58 deletions

View File

@@ -1,5 +1,4 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using TerminalUI.Extensions;
using TerminalUI.Models;
@@ -10,10 +9,18 @@ namespace TerminalUI.Controls;
public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChangeHandler
{
private readonly struct ColumnRowSizeCalculationResult(int usedWidth, int widthStars, int usedHeight, int heightStars)
{
public readonly int UsedWidth = usedWidth;
public readonly int WidthStars = widthStars;
public readonly int UsedHeight = usedHeight;
public readonly int HeightStars = heightStars;
}
private readonly List<IView> _forceRerenderChildren = new();
private readonly object _forceRerenderChildrenLock = new();
private List<RowDefinition> _rowDefinitions = new() {RowDefinition.Star(1)};
private List<ColumnDefinition> _columnDefinitions = new() {ColumnDefinition.Star(1)};
private List<RowDefinition> _rowDefinitions = new() { RowDefinition.Star(1) };
private List<ColumnDefinition> _columnDefinitions = new() { ColumnDefinition.Star(1) };
private ILogger<Grid<T>>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger<Grid<T>>();
private delegate void WithSizes(in RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
@@ -30,7 +37,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
var nextValue = value;
if (value.Count == 0)
{
nextValue = new List<RowDefinition> {RowDefinition.Star(1)};
nextValue = new List<RowDefinition> { RowDefinition.Star(1) };
}
var needUpdate = nextValue.Count != _rowDefinitions.Count;
@@ -62,7 +69,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
var nextValue = value;
if (value.Count == 0)
{
nextValue = new List<ColumnDefinition> {ColumnDefinition.Star(1)};
nextValue = new List<ColumnDefinition> { ColumnDefinition.Star(1) };
}
var needUpdate = nextValue.Count != _columnDefinitions.Count;
@@ -238,12 +245,12 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
if (renderPosition.X + width > gridPosition.X + gridSize.Width)
{
renderSize = renderSize with {Width = gridPosition.X + gridSize.Width - renderPosition.X};
renderSize = renderSize with { Width = gridPosition.X + gridSize.Width - renderPosition.X };
}
if (renderPosition.Y + height > gridPosition.Y + gridSize.Height)
{
renderSize = renderSize with {Height = gridPosition.Y + gridSize.Height - renderPosition.Y};
renderSize = renderSize with { Height = gridPosition.Y + gridSize.Height - renderPosition.Y };
}
if (renderSize.Width == 0 || renderSize.Height == 0) return false;
@@ -257,7 +264,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
var updatedContext = context;
if (needsRerender)
{
updatedContext = context with {ForceRerender = true};
updatedContext = context with { ForceRerender = true };
}
//This implies that children further back in the list will be rendered on top of children placed before in the list.
@@ -267,7 +274,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
if (rendered && !needsRerender)
{
needsRerender = true;
updatedContext = context with {ForceRerender = true};
updatedContext = context with { ForceRerender = true };
}
}
@@ -310,7 +317,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
}
else
{
viewsByPosition[(x, y)] = new List<IView> {child};
viewsByPosition[(x, y)] = new List<IView> { child };
}
}
@@ -349,9 +356,64 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
Debug.Assert(columns > 0, "Columns must contain at least one element");
Debug.Assert(rows > 0, "Rows must contain at least one element");
//Calculate the width and height for each column and row
Span<int> columnWidths = stackalloc int[columns];
Span<int> rowHeights = stackalloc int[rows];
var calculationResult = CalculateColumnAndRowSize(columns, rows, columnWidths, rowHeights, size);
//Calculate the width and height for each column and row with star value if size of the current grid is given
if (size.IsSome)
{
var widthLeft = size.Value.Width - calculationResult.UsedWidth;
var heightLeft = size.Value.Height - calculationResult.UsedHeight;
var widthPerStart = (int) Math.Floor((double) widthLeft / calculationResult.WidthStars);
var heightPerStart = (int) Math.Floor((double) heightLeft / calculationResult.HeightStars);
for (var i = 0; i < columnWidths.Length; i++)
{
var column = ColumnDefinitions[i];
if (column.Type == GridUnitType.Star)
{
columnWidths[i] = widthPerStart * column.Value;
}
}
for (var i = 0; i < rowHeights.Length; i++)
{
var row = RowDefinitions[i];
if (row.Type == GridUnitType.Star)
{
rowHeights[i] = heightPerStart * row.Value;
}
}
}
return actionWithSizes(renderContext, columnWidths, rowHeights);
}
private ColumnRowSizeCalculationResult CalculateColumnAndRowSize(
int columns,
int rows,
Span<int> columnWidths,
Span<int> rowHeights,
Option<Size> size)
{
Span<int> allWidth = stackalloc int[columns * rows];
Span<int> allHeight = stackalloc int[columns * rows];
CalculateAllWidthAndHeight(allWidth, allHeight, columns, rows);
var (usedWidth, widthStars) = CalculateColumnWidth(columnWidths, size, columns, rows, allWidth);
var (usedHeight, heightStars) = CalculateRowHeight(rowHeights, size, columns, allHeight);
return new ColumnRowSizeCalculationResult(usedWidth, widthStars, usedHeight, heightStars);
}
private void CalculateAllWidthAndHeight(Span<int> allWidth, Span<int> allHeight, int columns, int rows)
{
//Store the largest width and height for a cell
foreach (var child in Children)
{
@@ -371,11 +433,10 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
allHeight.SetToMatrix(childSize.Height, x, y, columns);
}
}
}
//Calculate the width and height for each column and row
Span<int> columnWidths = stackalloc int[columns];
Span<int> rowHeights = stackalloc int[rows];
private (int usedWidth, int widthStars) CalculateColumnWidth(Span<int> columnWidths, Option<Size> size, int columns, int rows, Span<int> allWidth)
{
var usedWidth = 0;
var widthStars = 0;
for (var i = 0; i < columnWidths.Length; i++)
@@ -404,6 +465,11 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
usedWidth += columnWidths[i];
}
return (usedWidth, widthStars);
}
private (int usedHeight, int heightStars) CalculateRowHeight(Span<int> rowHeights, Option<Size> size, int columns, Span<int> allHeight)
{
var usedHeight = 0;
var heightStars = 0;
for (var i = 0; i < rowHeights.Length; i++)
@@ -432,35 +498,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
usedHeight += rowHeights[i];
}
//Calculate the width and height for each column and row with star value if size of the current grid is given
if (size.IsSome)
{
var widthLeft = size.Value.Width - usedWidth;
var heightLeft = size.Value.Height - usedHeight;
var widthPerStart = (int) Math.Floor((double) widthLeft / widthStars);
var heightPerStart = (int) Math.Floor((double) heightLeft / heightStars);
for (var i = 0; i < columnWidths.Length; i++)
{
var column = ColumnDefinitions[i];
if (column.Type == GridUnitType.Star)
{
columnWidths[i] = widthPerStart * column.Value;
}
}
for (var i = 0; i < rowHeights.Length; i++)
{
var row = RowDefinitions[i];
if (row.Type == GridUnitType.Star)
{
rowHeights[i] = heightPerStart * row.Value;
}
}
}
return actionWithSizes(renderContext, columnWidths, rowHeights);
return (usedHeight, heightStars);
}
public void SetRowDefinitions(string value)

View File

@@ -215,8 +215,13 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
driver.ResetStyle();
}
Span<char> placeHolder = stackalloc char[size.Width];
placeHolder.Fill(ApplicationContext!.EmptyCharacter);
/*Span<char> placeHolder = stackalloc char[size.Width];
placeHolder.Fill(ApplicationContext!.EmptyCharacter);*/
var placeHolder = new char[size.Width];
for (var i = 0; i < size.Width; i++)
{
placeHolder[i] = ApplicationContext!.EmptyCharacter;
}
for (var i = 0; i < size.Height; i++)
{
driver.SetCursorPosition(position with {Y = position.Y + i});

View File

@@ -2,6 +2,6 @@
public static class SpanExtensions
{
public static T GetFromMatrix<T>(this Span<T> span, int x, int y, int width) => span[y * width + x];
public static void SetToMatrix<T>(this Span<T> span, T value, int x, int y, int width) => span[y * width + x] = value;
public static T GetFromMatrix<T>(this in Span<T> span, int x, int y, int width) => span[y * width + x];
public static void SetToMatrix<T>(this in Span<T> span, T value, int x, int y, int width) => span[y * width + x] = value;
}

View File

@@ -1,4 +1,5 @@
using TerminalUI.Controls;
using Microsoft.Extensions.Logging;
using TerminalUI.Controls;
using TerminalUI.Models;
using TerminalUI.Styling;
using TerminalUI.TextFormat;
@@ -10,6 +11,7 @@ public class RenderEngine : IRenderEngine
{
private readonly IApplicationContext _applicationContext;
private readonly IEventLoop _eventLoop;
private readonly ILogger<RenderEngine> _logger;
private readonly object _lock = new();
private readonly List<IView> _permanentViewsToRender = new();
private readonly List<IView> _forcedTemporaryViewsToRender = new();
@@ -21,10 +23,11 @@ public class RenderEngine : IRenderEngine
private bool[,]? _lastFilledCells;
private ITheme? _lastTheme;
public RenderEngine(IApplicationContext applicationContext, IEventLoop eventLoop)
public RenderEngine(IApplicationContext applicationContext, IEventLoop eventLoop, ILogger<RenderEngine> logger)
{
_applicationContext = applicationContext;
_eventLoop = eventLoop;
_logger = logger;
_eventLoop.AddToPermanentQueue(Render);
_eventLoop.AddInitializer(() =>
@@ -32,10 +35,7 @@ public class RenderEngine : IRenderEngine
_applicationContext.ConsoleDriver.ThreadId = _eventLoop.ThreadId;
_applicationContext.ConsoleDriver.EnterRestrictedMode();
});
_eventLoop.AddFinalizer(() =>
{
_applicationContext.ConsoleDriver.ExitRestrictedMode();
});
_eventLoop.AddFinalizer(() => { _applicationContext.ConsoleDriver.ExitRestrictedMode(); });
}
public void RequestRerender(IView view) => RequestRerender();
@@ -205,9 +205,16 @@ public class RenderEngine : IRenderEngine
{
foreach (var view in views)
{
view.Attached = true;
view.GetRequestedSize();
view.Render(renderContext, position, size);
try
{
view.Attached = true;
view.GetRequestedSize();
view.Render(renderContext, position, size);
}
catch (Exception e)
{
_logger.LogError(e, "Error while rendering view");
}
}
}