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;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TerminalUI.Extensions; using TerminalUI.Extensions;
using TerminalUI.Models; using TerminalUI.Models;
@@ -10,10 +9,18 @@ namespace TerminalUI.Controls;
public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChangeHandler 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 List<IView> _forceRerenderChildren = new();
private readonly object _forceRerenderChildrenLock = new(); private readonly object _forceRerenderChildrenLock = new();
private List<RowDefinition> _rowDefinitions = new() {RowDefinition.Star(1)}; private List<RowDefinition> _rowDefinitions = new() { RowDefinition.Star(1) };
private List<ColumnDefinition> _columnDefinitions = new() {ColumnDefinition.Star(1)}; private List<ColumnDefinition> _columnDefinitions = new() { ColumnDefinition.Star(1) };
private ILogger<Grid<T>>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger<Grid<T>>(); private ILogger<Grid<T>>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger<Grid<T>>();
private delegate void WithSizes(in RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights); 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; var nextValue = value;
if (value.Count == 0) if (value.Count == 0)
{ {
nextValue = new List<RowDefinition> {RowDefinition.Star(1)}; nextValue = new List<RowDefinition> { RowDefinition.Star(1) };
} }
var needUpdate = nextValue.Count != _rowDefinitions.Count; var needUpdate = nextValue.Count != _rowDefinitions.Count;
@@ -62,7 +69,7 @@ public sealed class Grid<T> : ChildCollectionView<Grid<T>, T>, IVisibilityChange
var nextValue = value; var nextValue = value;
if (value.Count == 0) if (value.Count == 0)
{ {
nextValue = new List<ColumnDefinition> {ColumnDefinition.Star(1)}; nextValue = new List<ColumnDefinition> { ColumnDefinition.Star(1) };
} }
var needUpdate = nextValue.Count != _columnDefinitions.Count; 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) 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) 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; 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; var updatedContext = context;
if (needsRerender) 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. //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) if (rendered && !needsRerender)
{ {
needsRerender = true; 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 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(columns > 0, "Columns must contain at least one element");
Debug.Assert(rows > 0, "Rows 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> allWidth = stackalloc int[columns * rows];
Span<int> allHeight = 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 //Store the largest width and height for a cell
foreach (var child in Children) 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); allHeight.SetToMatrix(childSize.Height, x, y, columns);
} }
} }
}
//Calculate the width and height for each column and row private (int usedWidth, int widthStars) CalculateColumnWidth(Span<int> columnWidths, Option<Size> size, int columns, int rows, Span<int> allWidth)
Span<int> columnWidths = stackalloc int[columns]; {
Span<int> rowHeights = stackalloc int[rows];
var usedWidth = 0; var usedWidth = 0;
var widthStars = 0; var widthStars = 0;
for (var i = 0; i < columnWidths.Length; i++) 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]; 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 usedHeight = 0;
var heightStars = 0; var heightStars = 0;
for (var i = 0; i < rowHeights.Length; i++) 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]; usedHeight += rowHeights[i];
} }
//Calculate the width and height for each column and row with star value if size of the current grid is given return (usedHeight, heightStars);
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);
} }
public void SetRowDefinitions(string value) 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(); driver.ResetStyle();
} }
Span<char> placeHolder = stackalloc char[size.Width]; /*Span<char> placeHolder = stackalloc char[size.Width];
placeHolder.Fill(ApplicationContext!.EmptyCharacter); 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++) for (var i = 0; i < size.Height; i++)
{ {
driver.SetCursorPosition(position with {Y = position.Y + i}); driver.SetCursorPosition(position with {Y = position.Y + i});

View File

@@ -2,6 +2,6 @@
public static class SpanExtensions 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 T GetFromMatrix<T>(this in 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 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.Models;
using TerminalUI.Styling; using TerminalUI.Styling;
using TerminalUI.TextFormat; using TerminalUI.TextFormat;
@@ -10,6 +11,7 @@ public class RenderEngine : IRenderEngine
{ {
private readonly IApplicationContext _applicationContext; private readonly IApplicationContext _applicationContext;
private readonly IEventLoop _eventLoop; private readonly IEventLoop _eventLoop;
private readonly ILogger<RenderEngine> _logger;
private readonly object _lock = new(); private readonly object _lock = new();
private readonly List<IView> _permanentViewsToRender = new(); private readonly List<IView> _permanentViewsToRender = new();
private readonly List<IView> _forcedTemporaryViewsToRender = new(); private readonly List<IView> _forcedTemporaryViewsToRender = new();
@@ -21,10 +23,11 @@ public class RenderEngine : IRenderEngine
private bool[,]? _lastFilledCells; private bool[,]? _lastFilledCells;
private ITheme? _lastTheme; private ITheme? _lastTheme;
public RenderEngine(IApplicationContext applicationContext, IEventLoop eventLoop) public RenderEngine(IApplicationContext applicationContext, IEventLoop eventLoop, ILogger<RenderEngine> logger)
{ {
_applicationContext = applicationContext; _applicationContext = applicationContext;
_eventLoop = eventLoop; _eventLoop = eventLoop;
_logger = logger;
_eventLoop.AddToPermanentQueue(Render); _eventLoop.AddToPermanentQueue(Render);
_eventLoop.AddInitializer(() => _eventLoop.AddInitializer(() =>
@@ -32,10 +35,7 @@ public class RenderEngine : IRenderEngine
_applicationContext.ConsoleDriver.ThreadId = _eventLoop.ThreadId; _applicationContext.ConsoleDriver.ThreadId = _eventLoop.ThreadId;
_applicationContext.ConsoleDriver.EnterRestrictedMode(); _applicationContext.ConsoleDriver.EnterRestrictedMode();
}); });
_eventLoop.AddFinalizer(() => _eventLoop.AddFinalizer(() => { _applicationContext.ConsoleDriver.ExitRestrictedMode(); });
{
_applicationContext.ConsoleDriver.ExitRestrictedMode();
});
} }
public void RequestRerender(IView view) => RequestRerender(); public void RequestRerender(IView view) => RequestRerender();
@@ -204,11 +204,18 @@ public class RenderEngine : IRenderEngine
private void RenderViews(List<IView> views, in RenderContext renderContext, Position position, Size size) private void RenderViews(List<IView> views, in RenderContext renderContext, Position position, Size size)
{ {
foreach (var view in views) foreach (var view in views)
{
try
{ {
view.Attached = true; view.Attached = true;
view.GetRequestedSize(); view.GetRequestedSize();
view.Render(renderContext, position, size); view.Render(renderContext, position, size);
} }
catch (Exception e)
{
_logger.LogError(e, "Error while rendering view");
}
}
} }
private void ClearArray2D<T>(T[,] array, T defaultValue = default!) private void ClearArray2D<T>(T[,] array, T defaultValue = default!)