From 9ee819cee3fba70f583c053dd137295f396e662c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Mon, 26 Feb 2024 18:13:23 +0100 Subject: [PATCH] Console bugfixes --- src/Library/TerminalUI/Controls/Grid.cs | 128 ++++++++++++------ src/Library/TerminalUI/Controls/View.cs | 9 +- .../TerminalUI/Extensions/SpanExtensions.cs | 4 +- src/Library/TerminalUI/RenderEngine.cs | 25 ++-- 4 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/Library/TerminalUI/Controls/Grid.cs b/src/Library/TerminalUI/Controls/Grid.cs index 622b38d..f50bb6d 100644 --- a/src/Library/TerminalUI/Controls/Grid.cs +++ b/src/Library/TerminalUI/Controls/Grid.cs @@ -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 : ChildCollectionView, 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 _forceRerenderChildren = new(); private readonly object _forceRerenderChildrenLock = new(); - private List _rowDefinitions = new() {RowDefinition.Star(1)}; - private List _columnDefinitions = new() {ColumnDefinition.Star(1)}; + private List _rowDefinitions = new() { RowDefinition.Star(1) }; + private List _columnDefinitions = new() { ColumnDefinition.Star(1) }; private ILogger>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger>(); private delegate void WithSizes(in RenderContext renderContext, ReadOnlySpan widths, ReadOnlySpan heights); @@ -30,7 +37,7 @@ public sealed class Grid : ChildCollectionView, T>, IVisibilityChange var nextValue = value; if (value.Count == 0) { - nextValue = new List {RowDefinition.Star(1)}; + nextValue = new List { RowDefinition.Star(1) }; } var needUpdate = nextValue.Count != _rowDefinitions.Count; @@ -62,7 +69,7 @@ public sealed class Grid : ChildCollectionView, T>, IVisibilityChange var nextValue = value; if (value.Count == 0) { - nextValue = new List {ColumnDefinition.Star(1)}; + nextValue = new List { ColumnDefinition.Star(1) }; } var needUpdate = nextValue.Count != _columnDefinitions.Count; @@ -132,7 +139,7 @@ public sealed class Grid : ChildCollectionView, T>, IVisibilityChange { return new Size(Width.Value, Height.Value); } - + var size = WithCalculatedSize( RenderContext.Empty, new Option(new Size(0, 0), false), @@ -163,7 +170,7 @@ public sealed class Grid : ChildCollectionView, T>, IVisibilityChange { var width = Width ?? size.Width; var height = Height ?? size.Height; - + if (width == 0 || height == 0) return false; return WithCalculatedSize( @@ -238,12 +245,12 @@ public sealed class Grid : ChildCollectionView, 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 : ChildCollectionView, 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 : ChildCollectionView, 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 : ChildCollectionView, T>, IVisibilityChange } else { - viewsByPosition[(x, y)] = new List {child}; + viewsByPosition[(x, y)] = new List { child }; } } @@ -349,9 +356,64 @@ public sealed class Grid : ChildCollectionView, 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 columnWidths = stackalloc int[columns]; + Span 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 columnWidths, + Span rowHeights, + Option size) + { Span allWidth = stackalloc int[columns * rows]; Span 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 allWidth, Span 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 : ChildCollectionView, T>, IVisibilityChange allHeight.SetToMatrix(childSize.Height, x, y, columns); } } + } - //Calculate the width and height for each column and row - Span columnWidths = stackalloc int[columns]; - Span rowHeights = stackalloc int[rows]; - + private (int usedWidth, int widthStars) CalculateColumnWidth(Span columnWidths, Option size, int columns, int rows, Span allWidth) + { var usedWidth = 0; var widthStars = 0; for (var i = 0; i < columnWidths.Length; i++) @@ -404,6 +465,11 @@ public sealed class Grid : ChildCollectionView, T>, IVisibilityChange usedWidth += columnWidths[i]; } + return (usedWidth, widthStars); + } + + private (int usedHeight, int heightStars) CalculateRowHeight(Span rowHeights, Option size, int columns, Span allHeight) + { var usedHeight = 0; var heightStars = 0; for (var i = 0; i < rowHeights.Length; i++) @@ -432,35 +498,7 @@ public sealed class Grid : ChildCollectionView, 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) diff --git a/src/Library/TerminalUI/Controls/View.cs b/src/Library/TerminalUI/Controls/View.cs index 4eb58f7..e921dee 100644 --- a/src/Library/TerminalUI/Controls/View.cs +++ b/src/Library/TerminalUI/Controls/View.cs @@ -215,8 +215,13 @@ public abstract partial class View : IView where TConcrete : Vi driver.ResetStyle(); } - Span placeHolder = stackalloc char[size.Width]; - placeHolder.Fill(ApplicationContext!.EmptyCharacter); + /*Span 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}); diff --git a/src/Library/TerminalUI/Extensions/SpanExtensions.cs b/src/Library/TerminalUI/Extensions/SpanExtensions.cs index f777c89..1dd6695 100644 --- a/src/Library/TerminalUI/Extensions/SpanExtensions.cs +++ b/src/Library/TerminalUI/Extensions/SpanExtensions.cs @@ -2,6 +2,6 @@ public static class SpanExtensions { - public static T GetFromMatrix(this Span span, int x, int y, int width) => span[y * width + x]; - public static void SetToMatrix(this Span span, T value, int x, int y, int width) => span[y * width + x] = value; + public static T GetFromMatrix(this in Span span, int x, int y, int width) => span[y * width + x]; + public static void SetToMatrix(this in Span span, T value, int x, int y, int width) => span[y * width + x] = value; } \ No newline at end of file diff --git a/src/Library/TerminalUI/RenderEngine.cs b/src/Library/TerminalUI/RenderEngine.cs index d770397..548270c 100644 --- a/src/Library/TerminalUI/RenderEngine.cs +++ b/src/Library/TerminalUI/RenderEngine.cs @@ -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 _logger; private readonly object _lock = new(); private readonly List _permanentViewsToRender = new(); private readonly List _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 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"); + } } }