117 lines
3.7 KiB
C#
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);
|
|
}
|
|
}
|
|
} |