"In" modifier for ref readonly struct, StackPanel force rerender fix

This commit is contained in:
2023-08-12 23:42:30 +02:00
parent 55a4dd733b
commit 09f44e9db2
13 changed files with 118 additions and 97 deletions

View File

@@ -1,4 +1,4 @@
using PropertyChanged.SourceGenerator;
using PropertyChanged.SourceGenerator;
using TerminalUI.Models;
namespace TerminalUI.Controls;
@@ -38,7 +38,7 @@ public partial class Border<T> : ContentView<T>
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<T> : ContentView<T>
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<T> : ContentView<T>
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<T> : ContentView<T>
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<T> : ContentView<T>
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;

View File

@@ -40,7 +40,7 @@ public abstract partial class ContentView<T> : View<T>, IContentRenderer<T>
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)
{

View File

@@ -15,9 +15,9 @@ public class Grid<T> : ChildContainerView<T>, IVisibilityChangeHandler
private List<ColumnDefinition> _columnDefinitions = new() {ColumnDefinition.Star(1)};
private ILogger<Grid<T>>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger<Grid<T>>();
private delegate void WithSizes(RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
private delegate void WithSizes(in RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
private delegate TResult WithSizes<TResult>(RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
private delegate TResult WithSizes<out TResult>(in RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
private const int ToBeCalculated = -1;
@@ -126,67 +126,77 @@ public class Grid<T> : ChildContainerView<T>, IVisibilityChangeHandler
}
protected override Size CalculateSize()
=> WithCalculatedSize(
{
return WithCalculatedSize(
RenderContext.Empty,
new Option<Size>(new Size(0, 0), false),
(_, columnWidths, rowHeights) =>
CalculateSizeInternal);
Size CalculateSizeInternal(in RenderContext _, ReadOnlySpan<int> columnWidths, ReadOnlySpan<int> 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>(size, true),
(context, columnWidths, rowHeights) =>
DefaultRendererInternal
);
bool DefaultRendererInternal(in RenderContext context, ReadOnlySpan<int> columnWidths, ReadOnlySpan<int> rowHeights)
{
IReadOnlyList<IView> forceRerenderChildren;
lock (_forceRerenderChildrenLock)
{
IReadOnlyList<IView> 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<T> : ChildContainerView<T>, IVisibilityChangeHandler
context.ConsoleDriver,
true,
context.Foreground,
context.Background
context.Background,
context.Statistics
);
RenderEmpty(context, renderPosition, renderSize);
}
@@ -234,7 +245,8 @@ public class Grid<T> : ChildContainerView<T>, IVisibilityChangeHandler
context.ConsoleDriver,
true,
context.Foreground,
context.Background
context.Background,
context.Statistics
);
}
}
@@ -304,18 +316,18 @@ public class Grid<T> : ChildContainerView<T>, IVisibilityChangeHandler
return (x, y);
}
private void WithCalculatedSize(RenderContext renderContext, Option<Size> size, WithSizes actionWithSizes)
private void WithCalculatedSize(in RenderContext renderContext, Option<Size> size, WithSizes actionWithSizes)
{
WithCalculatedSize(renderContext, size, Helper);
object? Helper(RenderContext renderContext1, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights)
object? Helper(in RenderContext renderContext1, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights)
{
actionWithSizes(renderContext1, widths, heights);
return null;
}
}
private TResult WithCalculatedSize<TResult>(RenderContext renderContext, Option<Size> size, WithSizes<TResult> actionWithSizes)
private TResult WithCalculatedSize<TResult>(in RenderContext renderContext, Option<Size> size, WithSizes<TResult> actionWithSizes)
{
//TODO: Optimize it, dont calculate all of these, only if there is Auto value(s)
var columns = ColumnDefinitions.Count;

View File

@@ -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<IView> Disposed;
Size GetRequestedSize();
bool Render(RenderContext renderContext, Position position, Size size);
bool Render(in RenderContext renderContext, Position position, Size size);
}
public interface IView<T> : IView

View File

@@ -162,12 +162,12 @@ public partial class ListView<TDataContext, TItem> : View<TDataContext>
}
}
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<TDataContext, TItem> : View<TDataContext>
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;

View File

@@ -21,7 +21,7 @@ public partial class ListViewItem<T, TParentDataContext> : ContentView<T>
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)
{

View File

@@ -16,7 +16,7 @@ public partial class Rectangle<T> : View<T>
[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;

View File

@@ -39,7 +39,7 @@ public partial class StackPanel<T> : ChildContainerView<T>, 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<T> : ChildContainerView<T>, 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<T> : ChildContainerView<T>, 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

View File

@@ -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<T> : View<T>
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;

View File

@@ -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<T> : View<T>, 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;

View File

@@ -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<T> : IView<T>
}
}
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<T> : IView<T>
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<T> : IView<T>
}
}
protected void SetColorsForDriver(RenderContext renderContext)
protected void SetColorsForDriver(in RenderContext renderContext)
{
var driver = renderContext.ConsoleDriver;
@@ -316,7 +316,7 @@ public abstract partial class View<T> : IView<T>
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()

View File

@@ -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<IView> views, RenderContext renderContext, Position position, Size size)
private void RenderViews(List<IView> views, in RenderContext renderContext, Position position, Size size)
{
foreach (var view in views)
{

View File

@@ -12,6 +12,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="PropertyChanged.SourceGenerator" Version="1.0.8">
<PrivateAssets>all</PrivateAssets>