diff --git a/src/Library/TerminalUI/Controls/Border.cs b/src/Library/TerminalUI/Controls/Border.cs index 1ce97ae..07f17b8 100644 --- a/src/Library/TerminalUI/Controls/Border.cs +++ b/src/Library/TerminalUI/Controls/Border.cs @@ -1,4 +1,4 @@ -using PropertyChanged.SourceGenerator; +using PropertyChanged.SourceGenerator; using TerminalUI.Models; namespace TerminalUI.Controls; @@ -38,7 +38,7 @@ public partial class Border : ContentView return new Size(contentSize.Width + size.Width, contentSize.Height + size.Height); } - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { if (ContentRendererMethod is null) { @@ -89,35 +89,35 @@ public partial class Border : ContentView return contentRendered; } - private void RenderTopBorder(RenderContext renderContext, Position position, Size size) + private void RenderTopBorder(in RenderContext renderContext, Position position, Size size) { position = position with {X = position.X + _borderThickness.Left}; size = new Size(Width: size.Width - _borderThickness.Left - _borderThickness.Right, Height: _borderThickness.Top); RenderText(_topChar, renderContext.ConsoleDriver, position, size); } - private void RenderBottomBorder(RenderContext renderContext, Position position, Size size) + private void RenderBottomBorder(in RenderContext renderContext, Position position, Size size) { position = new Position(X: position.X + _borderThickness.Left, Y: position.Y + size.Height - _borderThickness.Bottom); size = new Size(Width: size.Width - _borderThickness.Left - _borderThickness.Right, Height: _borderThickness.Bottom); RenderText(_bottomChar, renderContext.ConsoleDriver, position, size); } - private void RenderLeftBorder(RenderContext renderContext, Position position, Size size) + private void RenderLeftBorder(in RenderContext renderContext, Position position, Size size) { position = position with {Y = position.Y + _borderThickness.Top}; size = new Size(Width: _borderThickness.Left, Height: size.Height - _borderThickness.Top - _borderThickness.Bottom); RenderText(_leftChar, renderContext.ConsoleDriver, position, size); } - private void RenderRightBorder(RenderContext renderContext, Position position, Size size) + private void RenderRightBorder(in RenderContext renderContext, Position position, Size size) { position = new Position(X: position.X + size.Width - _borderThickness.Right, Y: position.Y + _borderThickness.Top); size = new Size(Width: _borderThickness.Right, Height: size.Height - _borderThickness.Top - _borderThickness.Bottom); RenderText(_rightChar, renderContext.ConsoleDriver, position, size); } - private void RenderTopLeftCorner(RenderContext renderContext, Position position) + private void RenderTopLeftCorner(in RenderContext renderContext, Position position) { if (_borderThickness.Left == 0 || _borderThickness.Top == 0) return; @@ -125,7 +125,7 @@ public partial class Border : ContentView RenderText(_topLeftChar, renderContext.ConsoleDriver, position, size); } - private void RenderTopRightCorner(RenderContext renderContext, Position position, Size size) + private void RenderTopRightCorner(in RenderContext renderContext, Position position, Size size) { if (_borderThickness.Right == 0 || _borderThickness.Top == 0) return; @@ -134,7 +134,7 @@ public partial class Border : ContentView RenderText(_topRightChar, renderContext.ConsoleDriver, position, size); } - private void RenderBottomLeftCorner(RenderContext renderContext, Position position, Size size) + private void RenderBottomLeftCorner(in RenderContext renderContext, Position position, Size size) { if (_borderThickness.Left == 0 || _borderThickness.Bottom == 0) return; @@ -143,7 +143,7 @@ public partial class Border : ContentView RenderText(_bottomLeftChar, renderContext.ConsoleDriver, position, size); } - private void RenderBottomRightCorner(RenderContext renderContext, Position position, Size size) + private void RenderBottomRightCorner(in RenderContext renderContext, Position position, Size size) { if (_borderThickness.Right == 0 || _borderThickness.Bottom == 0) return; diff --git a/src/Library/TerminalUI/Controls/ContentView.cs b/src/Library/TerminalUI/Controls/ContentView.cs index 709bef3..d99616c 100644 --- a/src/Library/TerminalUI/Controls/ContentView.cs +++ b/src/Library/TerminalUI/Controls/ContentView.cs @@ -40,7 +40,7 @@ public abstract partial class ContentView : View, IContentRenderer RerenderProperties.Add(nameof(ContentRendererMethod)); } - private bool DefaultContentRender(RenderContext renderContext, Position position, Size size) + private bool DefaultContentRender(in RenderContext renderContext, Position position, Size size) { if (Content is null || !Content.IsVisible) { diff --git a/src/Library/TerminalUI/Controls/Grid.cs b/src/Library/TerminalUI/Controls/Grid.cs index 6987cdd..449c59b 100644 --- a/src/Library/TerminalUI/Controls/Grid.cs +++ b/src/Library/TerminalUI/Controls/Grid.cs @@ -15,9 +15,9 @@ public class Grid : ChildContainerView, IVisibilityChangeHandler private List _columnDefinitions = new() {ColumnDefinition.Star(1)}; private ILogger>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger>(); - private delegate void WithSizes(RenderContext renderContext, ReadOnlySpan widths, ReadOnlySpan heights); + private delegate void WithSizes(in RenderContext renderContext, ReadOnlySpan widths, ReadOnlySpan heights); - private delegate TResult WithSizes(RenderContext renderContext, ReadOnlySpan widths, ReadOnlySpan heights); + private delegate TResult WithSizes(in RenderContext renderContext, ReadOnlySpan widths, ReadOnlySpan heights); private const int ToBeCalculated = -1; @@ -126,67 +126,77 @@ public class Grid : ChildContainerView, IVisibilityChangeHandler } protected override Size CalculateSize() - => WithCalculatedSize( + { + return WithCalculatedSize( RenderContext.Empty, new Option(new Size(0, 0), false), - (_, columnWidths, rowHeights) => + CalculateSizeInternal); + + Size CalculateSizeInternal(in RenderContext _, ReadOnlySpan columnWidths, ReadOnlySpan rowHeights) + { + var width = 0; + var height = 0; + + foreach (var t in columnWidths) { - var width = 0; - var height = 0; + width += t; + } - foreach (var t in columnWidths) - { - width += t; - } + foreach (var t in rowHeights) + { + height += t; + } - foreach (var t in rowHeights) - { - height += t; - } + return new Size(width, height); + } + } - return new Size(width, height); - }); - - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) - => WithCalculatedSize( + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) + { + return WithCalculatedSize( renderContext, new Option(size, true), - (context, columnWidths, rowHeights) => + DefaultRendererInternal + ); + + bool DefaultRendererInternal(in RenderContext context, ReadOnlySpan columnWidths, ReadOnlySpan rowHeights) + { + IReadOnlyList forceRerenderChildren; + lock (_forceRerenderChildrenLock) { - IReadOnlyList forceRerenderChildren; - lock (_forceRerenderChildrenLock) + forceRerenderChildren = _forceRerenderChildren.ToList(); + _forceRerenderChildren.Clear(); + } + + var childContext = new RenderContext( + context.ConsoleDriver, + context.ForceRerender, + Foreground ?? context.Foreground, + Background ?? context.Background, + context.Statistics + ); + var viewsByPosition = GroupViewsByPosition(columnWidths.Length, rowHeights.Length); + + for (var column = 0; column < columnWidths.Length; column++) + { + for (var row = 0; row < rowHeights.Length; row++) { - forceRerenderChildren = _forceRerenderChildren.ToList(); - _forceRerenderChildren.Clear(); + RenderViewsByPosition( + childContext, + position, + columnWidths, + rowHeights, + viewsByPosition, + column, + row, + forceRerenderChildren + ); } + } - context = new RenderContext( - context.ConsoleDriver, - context.ForceRerender, - Foreground ?? context.Foreground, - Background ?? context.Background - ); - var viewsByPosition = GroupViewsByPosition(columnWidths.Length, rowHeights.Length); - - for (var column = 0; column < columnWidths.Length; column++) - { - for (var row = 0; row < rowHeights.Length; row++) - { - RenderViewsByPosition( - context, - position, - columnWidths, - rowHeights, - viewsByPosition, - column, - row, - forceRerenderChildren - ); - } - } - - return true; - }); + return true; + } + } private void RenderViewsByPosition(RenderContext context, Position gridPosition, @@ -218,7 +228,8 @@ public class Grid : ChildContainerView, IVisibilityChangeHandler context.ConsoleDriver, true, context.Foreground, - context.Background + context.Background, + context.Statistics ); RenderEmpty(context, renderPosition, renderSize); } @@ -234,7 +245,8 @@ public class Grid : ChildContainerView, IVisibilityChangeHandler context.ConsoleDriver, true, context.Foreground, - context.Background + context.Background, + context.Statistics ); } } @@ -304,18 +316,18 @@ public class Grid : ChildContainerView, IVisibilityChangeHandler return (x, y); } - private void WithCalculatedSize(RenderContext renderContext, Option size, WithSizes actionWithSizes) + private void WithCalculatedSize(in RenderContext renderContext, Option size, WithSizes actionWithSizes) { WithCalculatedSize(renderContext, size, Helper); - object? Helper(RenderContext renderContext1, ReadOnlySpan widths, ReadOnlySpan heights) + object? Helper(in RenderContext renderContext1, ReadOnlySpan widths, ReadOnlySpan heights) { actionWithSizes(renderContext1, widths, heights); return null; } } - private TResult WithCalculatedSize(RenderContext renderContext, Option size, WithSizes actionWithSizes) + private TResult WithCalculatedSize(in RenderContext renderContext, Option size, WithSizes actionWithSizes) { //TODO: Optimize it, dont calculate all of these, only if there is Auto value(s) var columns = ColumnDefinitions.Count; diff --git a/src/Library/TerminalUI/Controls/IView.cs b/src/Library/TerminalUI/Controls/IView.cs index dc877fb..808f566 100644 --- a/src/Library/TerminalUI/Controls/IView.cs +++ b/src/Library/TerminalUI/Controls/IView.cs @@ -5,7 +5,7 @@ using TerminalUI.Traits; namespace TerminalUI.Controls; -public delegate bool RenderMethod(RenderContext renderContext, Position position, Size size); +public delegate bool RenderMethod(in RenderContext renderContext, Position position, Size size); public interface IView : INotifyPropertyChanged, IDisposableCollection { @@ -31,7 +31,7 @@ public interface IView : INotifyPropertyChanged, IDisposableCollection event Action Disposed; Size GetRequestedSize(); - bool Render(RenderContext renderContext, Position position, Size size); + bool Render(in RenderContext renderContext, Position position, Size size); } public interface IView : IView diff --git a/src/Library/TerminalUI/Controls/ListView.cs b/src/Library/TerminalUI/Controls/ListView.cs index dbe5f71..650835e 100644 --- a/src/Library/TerminalUI/Controls/ListView.cs +++ b/src/Library/TerminalUI/Controls/ListView.cs @@ -162,12 +162,12 @@ public partial class ListView : View } } - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) => Orientation == Orientation.Vertical ? RenderVertical(renderContext, position, size) : RenderHorizontal(renderContext, position, size); - private bool RenderHorizontal(RenderContext renderContext, Position position, Size size) + private bool RenderHorizontal(in RenderContext renderContext, Position position, Size size) { //Note: no support for same width elements var listViewItems = InstantiateItemViews(); @@ -236,7 +236,7 @@ public partial class ListView : View return true; } - private bool RenderVertical(RenderContext renderContext, Position position, Size size) + private bool RenderVertical(in RenderContext renderContext, Position position, Size size) { //Note: only same height is supported var requestedItemSize = _requestedItemSize; diff --git a/src/Library/TerminalUI/Controls/ListViewItem.cs b/src/Library/TerminalUI/Controls/ListViewItem.cs index e0270e2..afbff22 100644 --- a/src/Library/TerminalUI/Controls/ListViewItem.cs +++ b/src/Library/TerminalUI/Controls/ListViewItem.cs @@ -21,7 +21,7 @@ public partial class ListViewItem : ContentView return Content.GetRequestedSize(); } - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { if (ContentRendererMethod is null) { diff --git a/src/Library/TerminalUI/Controls/Rectangle.cs b/src/Library/TerminalUI/Controls/Rectangle.cs index c8ff4bf..caecbc0 100644 --- a/src/Library/TerminalUI/Controls/Rectangle.cs +++ b/src/Library/TerminalUI/Controls/Rectangle.cs @@ -16,7 +16,7 @@ public partial class Rectangle : View [Notify] private IColor? _fill; protected override Size CalculateSize() => new(Width ?? 0, Height ?? 0); - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { var renderState = new RenderState(position, size, Fill); if ((!renderContext.ForceRerender && !NeedsRerender(renderState)) || Fill is null) return false; diff --git a/src/Library/TerminalUI/Controls/StackPanel.cs b/src/Library/TerminalUI/Controls/StackPanel.cs index cb8b9e1..9d23f98 100644 --- a/src/Library/TerminalUI/Controls/StackPanel.cs +++ b/src/Library/TerminalUI/Controls/StackPanel.cs @@ -39,7 +39,7 @@ public partial class StackPanel : ChildContainerView, IVisibilityChangeHan return new Size(width, height); } - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { var delta = 0; var neededRerender = false; @@ -56,15 +56,6 @@ public partial class StackPanel : ChildContainerView, IVisibilityChangeHan if (!_requestedSizes.TryGetValue(child, out var childSize)) throw new Exception("Child size not found"); - if (forceRerenderChildren.Contains(child)) - { - renderContext = new RenderContext( - renderContext.ConsoleDriver, - true, - renderContext.Foreground, - renderContext.Background - ); - } var childPosition = Orientation == Orientation.Vertical ? position with {Y = position.Y + delta} @@ -84,7 +75,21 @@ public partial class StackPanel : ChildContainerView, IVisibilityChangeHan childSize = childSize with {Height = endY - childPosition.Y}; } - neededRerender = child.Render(renderContext, childPosition, childSize) || neededRerender; + if (forceRerenderChildren.Contains(child)) + { + var rerenderContext = new RenderContext( + renderContext.ConsoleDriver, + true, + renderContext.Foreground, + renderContext.Background, + renderContext.Statistics + ); + neededRerender = child.Render(rerenderContext, childPosition, childSize) || neededRerender; + } + else + { + neededRerender = child.Render(renderContext, childPosition, childSize) || neededRerender; + } delta += Orientation == Orientation.Vertical ? childSize.Height diff --git a/src/Library/TerminalUI/Controls/TextBlock.cs b/src/Library/TerminalUI/Controls/TextBlock.cs index 6c7730d..62904cd 100644 --- a/src/Library/TerminalUI/Controls/TextBlock.cs +++ b/src/Library/TerminalUI/Controls/TextBlock.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using System.Diagnostics; using PropertyChanged.SourceGenerator; using TerminalUI.Color; @@ -46,7 +46,7 @@ public partial class TextBlock : View protected override Size CalculateSize() => new(_textLines?.Max(l => l.Length) ?? 0, _textLines?.Length ?? 0); - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { if (size.Width == 0 || size.Height == 0) return false; diff --git a/src/Library/TerminalUI/Controls/TextBox.cs b/src/Library/TerminalUI/Controls/TextBox.cs index 79ee749..a361640 100644 --- a/src/Library/TerminalUI/Controls/TextBox.cs +++ b/src/Library/TerminalUI/Controls/TextBox.cs @@ -1,4 +1,4 @@ -using GeneralInputKey; +using GeneralInputKey; using PropertyChanged.SourceGenerator; using TerminalUI.Color; using TerminalUI.ConsoleDrivers; @@ -71,7 +71,7 @@ public partial class TextBox : View, IFocusable protected override Size CalculateSize() => new(Width ?? 10, Height ?? 1); - protected override bool DefaultRenderer(RenderContext renderContext, Position position, Size size) + protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { var foreground = Foreground ?? renderContext.Foreground; var background = Background ?? renderContext.Background; diff --git a/src/Library/TerminalUI/Controls/View.cs b/src/Library/TerminalUI/Controls/View.cs index ea7b40e..372c7d8 100644 --- a/src/Library/TerminalUI/Controls/View.cs +++ b/src/Library/TerminalUI/Controls/View.cs @@ -1,4 +1,4 @@ -using System.Buffers; +using System.Buffers; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; @@ -114,9 +114,9 @@ public abstract partial class View : IView } } - protected abstract bool DefaultRenderer(RenderContext renderContext, Position position, Size size); + protected abstract bool DefaultRenderer(in RenderContext renderContext, Position position, Size size); - public bool Render(RenderContext renderContext, Position position, Size size) + public bool Render(in RenderContext renderContext, Position position, Size size) { if (!Attached) throw new InvalidOperationException("Cannot render unattached view"); @@ -152,7 +152,7 @@ public abstract partial class View : IView return RenderMethod(renderContext, position, size); } - protected void RenderEmpty(RenderContext renderContext, Position position, Size size) + protected void RenderEmpty(in RenderContext renderContext, Position position, Size size) { var driver = renderContext.ConsoleDriver; driver.ResetColor(); @@ -236,7 +236,7 @@ public abstract partial class View : IView } } - protected void SetColorsForDriver(RenderContext renderContext) + protected void SetColorsForDriver(in RenderContext renderContext) { var driver = renderContext.ConsoleDriver; @@ -316,7 +316,7 @@ public abstract partial class View : IView public event PropertyChangedEventHandler? PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) + protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); public void Dispose() diff --git a/src/Library/TerminalUI/RenderEngine.cs b/src/Library/TerminalUI/RenderEngine.cs index aaf2c71..b33d61b 100644 --- a/src/Library/TerminalUI/RenderEngine.cs +++ b/src/Library/TerminalUI/RenderEngine.cs @@ -1,4 +1,4 @@ -using TerminalUI.Controls; +using TerminalUI.Controls; using TerminalUI.Models; using TerminalUI.Traits; @@ -114,7 +114,7 @@ public class RenderEngine : IRenderEngine } } - private void RenderViews(List views, RenderContext renderContext, Position position, Size size) + private void RenderViews(List views, in RenderContext renderContext, Position position, Size size) { foreach (var view in views) { diff --git a/src/Library/TerminalUI/TerminalUI.csproj b/src/Library/TerminalUI/TerminalUI.csproj index b23bfd2..21f8f4b 100644 --- a/src/Library/TerminalUI/TerminalUI.csproj +++ b/src/Library/TerminalUI/TerminalUI.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all