Preview refactor, Console rename form
This commit is contained in:
@@ -78,7 +78,13 @@ public sealed class Binding<TDataContext, TExpressionResult, TResult> : Property
|
||||
value = _fallbackValue;
|
||||
}
|
||||
|
||||
_targetProperty.SetValue(_propertySource, value);
|
||||
try
|
||||
{
|
||||
_targetProperty.SetValue(_propertySource, value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
||||
@@ -6,7 +6,7 @@ using TerminalUI.Traits;
|
||||
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
: View<ItemsControl<TDataContext, TItem>, TDataContext>, IVisibilityChangeHandler
|
||||
{
|
||||
private readonly List<IView> _forceRerenderChildren = new();
|
||||
@@ -17,7 +17,7 @@ public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
private object? _itemsSource;
|
||||
[Notify] private Orientation _orientation = Orientation.Vertical;
|
||||
|
||||
public Func<IView<TItem>?> ItemTemplate { get; set; } = DefaultItemTemplate;
|
||||
public Func<IView<TItem>> ItemTemplate { get; set; } = DefaultItemTemplate;
|
||||
|
||||
public IReadOnlyList<IView<TItem>> Children => _children.AsReadOnly();
|
||||
|
||||
@@ -41,7 +41,6 @@ public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
var consumer = new OcConsumer();
|
||||
_children = observableDeclarative
|
||||
.Selecting(i => CreateItem(i))
|
||||
.OfTypeComputing<IView<TItem>>()
|
||||
.For(consumer);
|
||||
_itemsDisposables.Add(consumer);
|
||||
}
|
||||
@@ -50,16 +49,15 @@ public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
var consumer = new OcConsumer();
|
||||
_children = readOnlyObservableDeclarative
|
||||
.Selecting(i => CreateItem(i))
|
||||
.OfTypeComputing<IView<TItem>>()
|
||||
.For(consumer);
|
||||
_itemsDisposables.Add(consumer);
|
||||
}
|
||||
else if (_itemsSource is ICollection<TItem> collection)
|
||||
_children = collection.Select(CreateItem).OfType<IView<TItem>>().ToList();
|
||||
_children = collection.Select(CreateItem).ToList();
|
||||
else if (_itemsSource is TItem[] array)
|
||||
_children = array.Select(CreateItem).OfType<IView<TItem>>().ToList();
|
||||
_children = array.Select(CreateItem).ToList();
|
||||
else if (_itemsSource is IEnumerable<TItem> enumerable)
|
||||
_children = enumerable.Select(CreateItem).OfType<IView<TItem>>().ToList();
|
||||
_children = enumerable.Select(CreateItem).ToList();
|
||||
else if (value is null)
|
||||
{
|
||||
_children = new List<IView<TItem>>();
|
||||
@@ -173,14 +171,14 @@ public sealed partial class ItemsControl<TDataContext, TItem>
|
||||
return neededRerender;
|
||||
}
|
||||
|
||||
private IView<TItem>? CreateItem(TItem dataContext)
|
||||
private IView<TItem> CreateItem(TItem dataContext)
|
||||
{
|
||||
var newItem = ItemTemplate();
|
||||
AddChild(newItem, _ => dataContext);
|
||||
return newItem;
|
||||
}
|
||||
|
||||
private static IView<TItem>? DefaultItemTemplate() => null;
|
||||
private static IView<TItem> DefaultItemTemplate() => new TextBlock<TItem> {Text = typeof(TItem).ToString()};
|
||||
|
||||
public void ChildVisibilityChanged(IView child)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GeneralInputKey;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
@@ -321,6 +322,7 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
||||
|
||||
public virtual TChild AddChild<TChild>(TChild child) where TChild : IView<T>
|
||||
{
|
||||
Debug.Assert(child != null);
|
||||
child.DataContext = DataContext;
|
||||
var mapper = new DataContextMapper<T, T>(this, child, d => d);
|
||||
SetupNewChild(child, mapper);
|
||||
@@ -331,6 +333,7 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
||||
public virtual TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>
|
||||
{
|
||||
Debug.Assert(child != null);
|
||||
child.DataContext = dataContextMapper(DataContext);
|
||||
var mapper = new DataContextMapper<T, TDataContext>(this, child, dataContextMapper);
|
||||
SetupNewChild(child, mapper);
|
||||
@@ -395,7 +398,7 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
||||
|
||||
_disposables.Clear();
|
||||
KeyHandlers.Clear();
|
||||
|
||||
|
||||
Disposed?.Invoke(this);
|
||||
}
|
||||
}
|
||||
@@ -411,7 +414,7 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
||||
{
|
||||
foreach (var keyHandler in KeyHandlers)
|
||||
{
|
||||
keyHandler((TConcrete)this, keyEventArgs);
|
||||
keyHandler((TConcrete) this, keyEventArgs);
|
||||
if (keyEventArgs.Handled) return;
|
||||
}
|
||||
}
|
||||
@@ -427,6 +430,6 @@ public abstract partial class View<TConcrete, T> : IView<T> where TConcrete : Vi
|
||||
public TConcrete WithKeyHandler(Action<TConcrete, GeneralKeyEventArgs> keyHandler)
|
||||
{
|
||||
KeyHandlers.Add(keyHandler);
|
||||
return (TConcrete)this;
|
||||
return (TConcrete) this;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
namespace TerminalUI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace TerminalUI;
|
||||
|
||||
public class EventLoop : IEventLoop
|
||||
{
|
||||
private readonly IApplicationContext _applicationContext;
|
||||
private readonly ILogger<EventLoop> _logger;
|
||||
private readonly List<Action> _permanentQueue = new();
|
||||
|
||||
public EventLoop(IApplicationContext applicationContext)
|
||||
public EventLoop(
|
||||
IApplicationContext applicationContext,
|
||||
ILogger<EventLoop> logger)
|
||||
{
|
||||
_applicationContext = applicationContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void AddToPermanentQueue(Action action) => _permanentQueue.Add(action);
|
||||
@@ -26,7 +32,14 @@ public class EventLoop : IEventLoop
|
||||
{
|
||||
foreach (var action in _permanentQueue)
|
||||
{
|
||||
action();
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error while processing action in permanent queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public class FocusManager : IFocusManager
|
||||
{
|
||||
private readonly IRenderEngine _renderEngine;
|
||||
private IFocusable? _focused;
|
||||
private DateTime _focusLostCandidateTime = DateTime.MinValue;
|
||||
|
||||
public IFocusable? Focused
|
||||
{
|
||||
@@ -18,15 +19,30 @@ public class FocusManager : IFocusManager
|
||||
{
|
||||
var visible = _focused.IsVisible;
|
||||
var parent = _focused.VisualParent;
|
||||
while (parent != null)
|
||||
while (parent != null && visible)
|
||||
{
|
||||
visible &= parent.IsVisible;
|
||||
visible = parent.IsVisible && visible;
|
||||
parent = parent.VisualParent;
|
||||
}
|
||||
|
||||
if (!visible)
|
||||
{
|
||||
_focused = null;
|
||||
if (_focusLostCandidateTime != DateTime.MinValue)
|
||||
{
|
||||
if (DateTime.Now - _focusLostCandidateTime > TimeSpan.FromMilliseconds(10))
|
||||
{
|
||||
_focused = null;
|
||||
_focusLostCandidateTime = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_focusLostCandidateTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_focusLostCandidateTime = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,52 +69,66 @@ public class FocusManager : IFocusManager
|
||||
{
|
||||
if (keyEventArgs.Handled || Focused is null) return;
|
||||
|
||||
if (keyEventArgs.Key == Keys.Tab && keyEventArgs.SpecialKeysStatus.IsShiftPressed)
|
||||
if (keyEventArgs is {Key: Keys.Tab, SpecialKeysStatus.IsShiftPressed: true})
|
||||
{
|
||||
FocusElement(
|
||||
(views, from) => views.TakeWhile(x => x != from).Reverse(),
|
||||
c => c.Reverse()
|
||||
);
|
||||
FocusLastElement(Focused);
|
||||
keyEventArgs.Handled = true;
|
||||
}
|
||||
else if (keyEventArgs.Key == Keys.Tab)
|
||||
{
|
||||
FocusElement(
|
||||
(views, from) => views.SkipWhile(x => x != from).Skip(1),
|
||||
c => c
|
||||
);
|
||||
FocusFirstElement(Focused);
|
||||
keyEventArgs.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void FocusFirstElement(IView view, IView? from = null) =>
|
||||
FocusElement(
|
||||
view,
|
||||
(views, fromView) => views.SkipWhile(x => x != fromView).Skip(1),
|
||||
c => c.Reverse(),
|
||||
from
|
||||
);
|
||||
|
||||
public void FocusLastElement(IView view, IView? from = null) =>
|
||||
FocusElement(
|
||||
view,
|
||||
(views, fromView) => views.TakeWhile(x => x != fromView).Reverse(),
|
||||
c => c,
|
||||
from
|
||||
);
|
||||
|
||||
|
||||
private void FocusElement(
|
||||
IView view,
|
||||
Func<IEnumerable<IView>, IView, IEnumerable<IView>> fromChildSelector,
|
||||
Func<IEnumerable<IView>, IEnumerable<IView>> childSelector
|
||||
Func<IEnumerable<IView>, IEnumerable<IView>> childSelector,
|
||||
IView? from = null
|
||||
)
|
||||
{
|
||||
if (Focused is null) return;
|
||||
|
||||
var element = FindElement(Focused,
|
||||
Focused,
|
||||
fromChildSelector
|
||||
var element = FindElement(view,
|
||||
view,
|
||||
fromChildSelector,
|
||||
from: from
|
||||
);
|
||||
|
||||
if (element is null)
|
||||
{
|
||||
var topParent = FindLastFocusParent(Focused);
|
||||
var topParent = FindLastFocusParent(view);
|
||||
element = FindElement(
|
||||
topParent,
|
||||
Focused,
|
||||
view,
|
||||
fromChildSelector,
|
||||
childSelector
|
||||
childSelector,
|
||||
from
|
||||
);
|
||||
}
|
||||
|
||||
if (element is null) return;
|
||||
|
||||
_renderEngine.RequestRerender(element);
|
||||
_renderEngine.RequestRerender(Focused);
|
||||
_renderEngine.RequestRerender(view);
|
||||
Focused = element;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using GeneralInputKey;
|
||||
using TerminalUI.Controls;
|
||||
using TerminalUI.Traits;
|
||||
|
||||
namespace TerminalUI;
|
||||
@@ -9,4 +10,6 @@ public interface IFocusManager
|
||||
void UnFocus(IFocusable focusable);
|
||||
IFocusable? Focused { get; }
|
||||
void HandleKeyInput(GeneralKeyEventArgs keyEventArgs);
|
||||
void FocusFirstElement(IView view, IView? from = null);
|
||||
void FocusLastElement(IView view, IView? from = null);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public abstract class PropertyTrackerBase<TSource, TExpressionResult> : IDisposa
|
||||
}
|
||||
else if (expression is UnaryExpression unaryExpression)
|
||||
{
|
||||
SavePropertyPath(FindReactiveProperties(unaryExpression.Operand, properties));
|
||||
return FindReactiveProperties(unaryExpression.Operand, properties);
|
||||
}
|
||||
else if (expression is ParameterExpression parameterExpression)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user