Force rerender on visibility change
This commit is contained in:
@@ -7,7 +7,6 @@ namespace TerminalUI.Controls;
|
||||
public abstract class ChildContainerView<T> : View<T>, IChildContainer<T>
|
||||
{
|
||||
private readonly ObservableCollection<IView> _children = new();
|
||||
private readonly Dictionary<IView, bool> _visibilities = new();
|
||||
public ReadOnlyObservableCollection<IView> Children { get; }
|
||||
public ChildInitializer<T> ChildInitializer { get; }
|
||||
|
||||
@@ -27,7 +26,7 @@ public abstract class ChildContainerView<T> : View<T>, IChildContainer<T>
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
ApplicationContext?.RenderEngine.RequestRerender(this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,18 +42,6 @@ public abstract class ChildContainerView<T> : View<T>, IChildContainer<T>
|
||||
};
|
||||
}
|
||||
|
||||
protected void SaveVisibilities()
|
||||
{
|
||||
_visibilities.Clear();
|
||||
foreach (var child in _children)
|
||||
{
|
||||
_visibilities[child] = child.IsVisible;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool? GetLastVisibility(IView view)
|
||||
=> _visibilities.TryGetValue(view, out var visibility) ? visibility : null;
|
||||
|
||||
public override TChild AddChild<TChild>(TChild child)
|
||||
{
|
||||
child = base.AddChild(child);
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TerminalUI.Extensions;
|
||||
using TerminalUI.Models;
|
||||
using TerminalUI.Traits;
|
||||
using TerminalUI.ViewExtensions;
|
||||
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
public class Grid<T> : ChildContainerView<T>
|
||||
public class Grid<T> : ChildContainerView<T>, IVisibilityChangeHandler
|
||||
{
|
||||
private List<RowDefinition> _rowDefinitions = new() {RowDefinition.Star(1)};
|
||||
private List<ColumnDefinition> _columnDefinitions = new() {ColumnDefinition.Star(1)};
|
||||
private List<IView> _forceRerenderChildren = new();
|
||||
private readonly object _forceRerenderChildrenLock = new();
|
||||
private ILogger<Grid<T>>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger<Grid<T>>();
|
||||
|
||||
private delegate void WithSizes(RenderContext renderContext, ReadOnlySpan<int> widths, ReadOnlySpan<int> heights);
|
||||
@@ -150,6 +153,13 @@ public class Grid<T> : ChildContainerView<T>
|
||||
new Option<Size>(size, true),
|
||||
(context, columnWidths, rowHeights) =>
|
||||
{
|
||||
IReadOnlyList<IView> forceRerenderChildren;
|
||||
lock (_forceRerenderChildrenLock)
|
||||
{
|
||||
forceRerenderChildren = _forceRerenderChildren;
|
||||
_forceRerenderChildren.Clear();
|
||||
}
|
||||
|
||||
context = new RenderContext(
|
||||
context.ConsoleDriver,
|
||||
context.ForceRerender,
|
||||
@@ -169,7 +179,8 @@ public class Grid<T> : ChildContainerView<T>
|
||||
rowHeights,
|
||||
viewsByPosition,
|
||||
column,
|
||||
row
|
||||
row,
|
||||
forceRerenderChildren
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -177,29 +188,17 @@ public class Grid<T> : ChildContainerView<T>
|
||||
return true;
|
||||
});
|
||||
|
||||
private void RenderViewsByPosition(
|
||||
RenderContext context,
|
||||
private void RenderViewsByPosition(RenderContext context,
|
||||
Position gridPosition,
|
||||
ReadOnlySpan<int> columnWidths,
|
||||
ReadOnlySpan<int> rowHeights,
|
||||
IReadOnlyDictionary<(int, int), List<IView>> viewsByPosition,
|
||||
int column,
|
||||
int row)
|
||||
int row,
|
||||
IReadOnlyList<IView> forceRerenderChildren)
|
||||
{
|
||||
if (!viewsByPosition.TryGetValue((column, row), out var children)) return;
|
||||
|
||||
var anyChangedVisibility = false;
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
var lastVisibility = GetLastVisibility(child);
|
||||
if (lastVisibility is { } b && b != child.IsVisible)
|
||||
{
|
||||
anyChangedVisibility = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var width = columnWidths[column];
|
||||
var height = rowHeights[row];
|
||||
var renderSize = new Size(width, height);
|
||||
@@ -212,7 +211,7 @@ public class Grid<T> : ChildContainerView<T>
|
||||
row
|
||||
);
|
||||
|
||||
var needsRerender = anyChangedVisibility;
|
||||
var needsRerender = children.Any(forceRerenderChildren.Contains);
|
||||
if (needsRerender)
|
||||
{
|
||||
context = new RenderContext(
|
||||
@@ -232,7 +231,7 @@ public class Grid<T> : ChildContainerView<T>
|
||||
{
|
||||
needsRerender = true;
|
||||
context = new RenderContext(
|
||||
context.ConsoleDriver,
|
||||
context.ConsoleDriver,
|
||||
true,
|
||||
context.Foreground,
|
||||
context.Background
|
||||
@@ -483,4 +482,20 @@ public class Grid<T> : ChildContainerView<T>
|
||||
|
||||
ColumnDefinitions = columnDefinitions;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ public interface IView : INotifyPropertyChanged, IDisposableCollection
|
||||
IApplicationContext? ApplicationContext { get; set; }
|
||||
List<object> Extensions { get; }
|
||||
RenderMethod RenderMethod { get; set; }
|
||||
IView? VisualParent { get; set; }
|
||||
event Action<IView> Disposed;
|
||||
|
||||
Size GetRequestedSize();
|
||||
|
||||
@@ -102,14 +102,14 @@ public partial class ListView<TDataContext, TItem> : View<TDataContext>
|
||||
if (_itemsSource is ObservableCollection<TItem> observableDeclarative)
|
||||
{
|
||||
((INotifyCollectionChanged) observableDeclarative).CollectionChanged +=
|
||||
(_, _) => ApplicationContext?.EventLoop.RequestRerender();
|
||||
(_, _) => ApplicationContext?.RenderEngine.RequestRerender(this);
|
||||
|
||||
_getItems = () => observableDeclarative;
|
||||
}
|
||||
else if (_itemsSource is ReadOnlyObservableCollection<TItem> readOnlyObservableDeclarative)
|
||||
{
|
||||
((INotifyCollectionChanged) readOnlyObservableDeclarative).CollectionChanged +=
|
||||
(_, _) => ApplicationContext?.EventLoop.RequestRerender();
|
||||
(_, _) => ApplicationContext?.RenderEngine.RequestRerender(this);
|
||||
|
||||
_getItems = () => readOnlyObservableDeclarative;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ public partial class TextBox<T> : View<T>, IFocusable
|
||||
HandleKeyInputInternal(keyEventArgs);
|
||||
if (keyEventArgs.Handled)
|
||||
{
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
ApplicationContext?.RenderEngine.RequestRerender(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ public abstract partial class View<T> : IView<T>
|
||||
[Notify] private string? _name;
|
||||
[Notify] private IApplicationContext? _applicationContext;
|
||||
[Notify] private bool _attached;
|
||||
|
||||
[Notify] private IView? _visualParent;
|
||||
|
||||
protected ObservableCollection<IView> VisualChildren { get; } = new();
|
||||
|
||||
public List<object> Extensions { get; } = new();
|
||||
@@ -90,7 +91,7 @@ public abstract partial class View<T> : IView<T>
|
||||
)
|
||||
)
|
||||
{
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
ApplicationContext?.RenderEngine.RequestRerender(this);
|
||||
}
|
||||
|
||||
if (e.PropertyName == nameof(Attached))
|
||||
@@ -100,13 +101,17 @@ public abstract partial class View<T> : IView<T>
|
||||
visualChild.Attached = Attached;
|
||||
}
|
||||
}
|
||||
else if(e.PropertyName == nameof(ApplicationContext))
|
||||
else if (e.PropertyName == nameof(ApplicationContext))
|
||||
{
|
||||
foreach (var visualChild in VisualChildren)
|
||||
{
|
||||
visualChild.ApplicationContext = ApplicationContext;
|
||||
}
|
||||
}
|
||||
else if (e.PropertyName == nameof(IsVisible))
|
||||
{
|
||||
ApplicationContext?.RenderEngine.VisibilityChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool DefaultRenderer(RenderContext renderContext, Position position, Size size);
|
||||
@@ -221,7 +226,7 @@ public abstract partial class View<T> : IView<T>
|
||||
Size size)
|
||||
{
|
||||
var contentString = new string(content, size.Width);
|
||||
|
||||
|
||||
for (var i = 0; i < size.Height; i++)
|
||||
{
|
||||
var currentPosition = position with {Y = position.Y + i};
|
||||
@@ -234,7 +239,7 @@ public abstract partial class View<T> : IView<T>
|
||||
protected void SetColorsForDriver(RenderContext renderContext)
|
||||
{
|
||||
var driver = renderContext.ConsoleDriver;
|
||||
|
||||
|
||||
var foreground = Foreground ?? renderContext.Foreground;
|
||||
var background = Background ?? renderContext.Background;
|
||||
if (foreground is not null)
|
||||
@@ -264,12 +269,8 @@ public abstract partial class View<T> : IView<T>
|
||||
public virtual TChild AddChild<TChild>(TChild child) where TChild : IView<T>
|
||||
{
|
||||
child.DataContext = DataContext;
|
||||
CopyCommonPropertiesToNewChild(child);
|
||||
VisualChildren.Add(child);
|
||||
|
||||
var mapper = new DataContextMapper<T, T>(this, child, d => d);
|
||||
AddDisposable(mapper);
|
||||
child.AddDisposable(mapper);
|
||||
SetupNewChild(child, mapper);
|
||||
|
||||
return child;
|
||||
}
|
||||
@@ -278,20 +279,21 @@ public abstract partial class View<T> : IView<T>
|
||||
where TChild : IView<TDataContext>
|
||||
{
|
||||
child.DataContext = dataContextMapper(DataContext);
|
||||
CopyCommonPropertiesToNewChild(child);
|
||||
VisualChildren.Add(child);
|
||||
|
||||
var mapper = new DataContextMapper<T, TDataContext>(this, child, dataContextMapper);
|
||||
AddDisposable(mapper);
|
||||
child.AddDisposable(mapper);
|
||||
SetupNewChild(child, mapper);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
private void CopyCommonPropertiesToNewChild(IView child)
|
||||
private void SetupNewChild(IView child, IDisposable dataContextmapper)
|
||||
{
|
||||
child.ApplicationContext = ApplicationContext;
|
||||
child.Attached = Attached;
|
||||
child.VisualParent = this;
|
||||
VisualChildren.Add(child);
|
||||
|
||||
AddDisposable(dataContextmapper);
|
||||
child.AddDisposable(dataContextmapper);
|
||||
}
|
||||
|
||||
public virtual void RemoveChild<TDataContext>(IView<TDataContext> child)
|
||||
|
||||
Reference in New Issue
Block a user