Preview refactor, Console rename form

This commit is contained in:
2023-08-14 16:42:22 +02:00
parent 2a595b2548
commit 8aa8d83598
25 changed files with 610 additions and 348 deletions

View File

@@ -4,7 +4,7 @@ public class GeneralKeyEventArgs
{
private readonly Action<bool>? _handledChanged;
private bool _handled;
public required Keys Key { get; init; }
public required Keys? Key { get; init; }
public required char KeyChar { get; init; }
public required SpecialKeysStatus SpecialKeysStatus { get; init; }

View File

@@ -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()

View File

@@ -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)
{

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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)
{