Files
FileTime2/src/Library/TerminalUI/Controls/StackPanel.cs

117 lines
3.7 KiB
C#

using PropertyChanged.SourceGenerator;
using TerminalUI.Models;
using TerminalUI.Traits;
namespace TerminalUI.Controls;
public sealed partial class StackPanel<T> : ChildCollectionView<StackPanel<T>, T>, IVisibilityChangeHandler
{
private readonly List<IView> _forceRerenderChildren = new();
private readonly object _forceRerenderChildrenLock = new();
private readonly Dictionary<IView, Size> _requestedSizes = new();
[Notify] private Orientation _orientation = Orientation.Vertical;
protected override Size CalculateSize()
{
_requestedSizes.Clear();
var width = 0;
var height = 0;
foreach (var child in Children)
{
if (!child.IsVisible) continue;
var childSize = child.GetRequestedSize();
_requestedSizes.Add(child, childSize);
if (Orientation == Orientation.Vertical)
{
width = Math.Max(width, childSize.Width);
height += childSize.Height;
}
else
{
width += childSize.Width;
height = Math.Max(height, childSize.Height);
}
}
return new Size(width, height);
}
protected override bool DefaultRenderer(
in RenderContext renderContext,
Position position,
Size size)
{
var neededRerender = false;
IReadOnlyList<IView> forceRerenderChildren;
lock (_forceRerenderChildrenLock)
{
forceRerenderChildren = _forceRerenderChildren.ToList();
_forceRerenderChildren.Clear();
}
var delta = 0;
foreach (var child in Children)
{
if (!child.IsVisible) continue;
if (!_requestedSizes.TryGetValue(child, out var childSize)) throw new Exception("Child size not found");
var childPosition = Orientation == Orientation.Vertical
? position with {Y = position.Y + delta}
: position with {X = position.X + delta};
childSize = Orientation == Orientation.Vertical
? childSize with {Width = size.Width}
: childSize with {Height = size.Height};
var endX = position.X + size.Width;
var endY = position.Y + size.Height;
if (childPosition.X > endX || childPosition.Y > endY) break;
if (childPosition.X + childSize.Width > endX)
{
childSize = childSize with {Width = endX - childPosition.X};
}
if (childPosition.Y + childSize.Height > endY)
{
childSize = childSize with {Height = endY - childPosition.Y};
}
if (forceRerenderChildren.Contains(child))
{
var rerenderContext = renderContext with {ForceRerender = true};
neededRerender = child.Render(rerenderContext, childPosition, childSize) || neededRerender;
}
else
{
neededRerender = child.Render(renderContext, childPosition, childSize) || neededRerender;
}
delta += Orientation == Orientation.Vertical
? childSize.Height
: childSize.Width;
}
return neededRerender;
}
public void ChildVisibilityChanged(IView child)
{
var viewToForceRerender = child;
while (viewToForceRerender.VisualParent != null && viewToForceRerender.VisualParent != this)
{
viewToForceRerender = viewToForceRerender.VisualParent;
}
if (viewToForceRerender.VisualParent != this) return;
lock (_forceRerenderChildrenLock)
{
_forceRerenderChildren.Add(viewToForceRerender);
}
}
}