Console MessageBox, admin mode
This commit is contained in:
@@ -4,6 +4,7 @@ public class ConsoleApplicationConfiguration
|
|||||||
{
|
{
|
||||||
public string? ConsoleDriver { get; set; }
|
public string? ConsoleDriver { get; set; }
|
||||||
public bool DisableUtf8 { get; set; }
|
public bool DisableUtf8 { get; set; }
|
||||||
|
public string? AdminModeIcon { get; set; }
|
||||||
public string? ClipboardSingleIcon { get; set; }
|
public string? ClipboardSingleIcon { get; set; }
|
||||||
public string? ClipboardMultipleIcon { get; set; }
|
public string? ClipboardMultipleIcon { get; set; }
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
|
||||||
<ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" />
|
<ProjectReference Include="..\..\AppCommon\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\..\Library\TerminalUI\TerminalUI.csproj" />
|
<ProjectReference Include="..\..\Library\TerminalUI\TerminalUI.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Providers\FileTime.Providers.LocalAdmin.Abstractions\FileTime.Providers.LocalAdmin.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using FileTime.App.FrequencyNavigation.ViewModels;
|
|||||||
using FileTime.ConsoleUI.App.Services;
|
using FileTime.ConsoleUI.App.Services;
|
||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Providers.LocalAdmin;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
@@ -23,5 +24,6 @@ public interface IRootViewModel
|
|||||||
IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
||||||
IItemPreviewService ItemPreviewService { get; }
|
IItemPreviewService ItemPreviewService { get; }
|
||||||
IClipboardService ClipboardService { get; }
|
IClipboardService ClipboardService { get; }
|
||||||
|
IAdminElevationManager AdminElevationManager { get; }
|
||||||
event Action<IInputElement>? FocusReadInputElement;
|
event Action<IInputElement>? FocusReadInputElement;
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using TerminalUI;
|
using TerminalUI;
|
||||||
using TerminalUI.ConsoleDrivers;
|
using TerminalUI.ConsoleDrivers;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
@@ -138,6 +139,14 @@ public class App : IApplication
|
|||||||
|
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_consoleDriver.ExitRestrictedMode();
|
||||||
|
_consoleDriver.Clear();
|
||||||
|
var size = _consoleDriver.GetWindowSize();
|
||||||
|
var shutdownText = "Shutting down...";
|
||||||
|
|
||||||
|
_consoleDriver.SetCursorPosition(new Position(size.Width / 2 - shutdownText.Length / 2, size.Height / 2));
|
||||||
|
_consoleDriver.Write(shutdownText);
|
||||||
|
|
||||||
Task.Run(async () => await _lifecycleService.ExitAsync()).Wait();
|
Task.Run(async () => await _lifecycleService.ExitAsync()).Wait();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,35 +51,96 @@ public class Dialogs
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateReadInputsFocus()
|
public IView<IRootViewModel> View()
|
||||||
{
|
{
|
||||||
foreach (var readInputsChild in _readInputs.Children)
|
var root = new Grid<IRootViewModel>()
|
||||||
{
|
{
|
||||||
if (readInputsChild.DataContext == _inputElementToFocus)
|
Margin = 5,
|
||||||
|
ChildInitializer =
|
||||||
{
|
{
|
||||||
if (FindFocusable(readInputsChild) is { } focusable)
|
ReadInputs(),
|
||||||
{
|
MessageBox()
|
||||||
focusable.Focus();
|
|
||||||
_inputElementToFocus = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
IFocusable? FindFocusable(IView view)
|
return root;
|
||||||
{
|
|
||||||
if (view is IFocusable focusable) return focusable;
|
|
||||||
foreach (var viewVisualChild in view.VisualChildren)
|
|
||||||
{
|
|
||||||
if (FindFocusable(viewVisualChild) is { } focusableChild)
|
|
||||||
return focusableChild;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IView<IRootViewModel> View()
|
private IView<IRootViewModel> MessageBox()
|
||||||
|
{
|
||||||
|
var okButton = new Button<IRootViewModel>
|
||||||
|
{
|
||||||
|
Margin = "0 0 5 0",
|
||||||
|
Content = new TextBlock<IRootViewModel>()
|
||||||
|
.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.DialogService.LastMessageBox.Value.OkText,
|
||||||
|
t => t.Text)),
|
||||||
|
}.WithClickHandler(b => b.DataContext?.DialogService.LastMessageBox.Value?.Ok());
|
||||||
|
|
||||||
|
var cancelButton =
|
||||||
|
new Button<IRootViewModel>
|
||||||
|
{
|
||||||
|
Margin = "0 0 5 0",
|
||||||
|
Content = new TextBlock<IRootViewModel>()
|
||||||
|
.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.DialogService.LastMessageBox.Value.CancelText,
|
||||||
|
t => t.Text)),
|
||||||
|
}
|
||||||
|
.Setup(b => b.Bind(
|
||||||
|
b,
|
||||||
|
dc => dc.DialogService.LastMessageBox.Value.ShowCancel,
|
||||||
|
b => b.IsVisible))
|
||||||
|
.WithClickHandler(b => b.DataContext?.DialogService.LastMessageBox.Value?.Cancel());
|
||||||
|
|
||||||
|
var root = new Border<IRootViewModel>
|
||||||
|
{
|
||||||
|
Margin = 5,
|
||||||
|
BorderThickness = 1,
|
||||||
|
Background = SpecialColor.None,
|
||||||
|
Content = new Grid<IRootViewModel>
|
||||||
|
{
|
||||||
|
RowDefinitionsObject = "Auto Auto",
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
new TextBlock<IRootViewModel>()
|
||||||
|
.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.DialogService.LastMessageBox.Value.Text,
|
||||||
|
t => t.Text)),
|
||||||
|
new StackPanel<IRootViewModel>
|
||||||
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
Extensions = {new GridPositionExtension(0, 1)},
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
okButton,
|
||||||
|
cancelButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.Bind(
|
||||||
|
root,
|
||||||
|
d => d.DialogService.LastMessageBox.Value != null,
|
||||||
|
v => v.IsVisible,
|
||||||
|
fallbackValue: false);
|
||||||
|
|
||||||
|
((INotifyPropertyChanged) root).PropertyChanged += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(IView.IsVisible))
|
||||||
|
{
|
||||||
|
okButton.Focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IView<IRootViewModel> ReadInputs()
|
||||||
{
|
{
|
||||||
var root = new Border<IRootViewModel>
|
var root = new Border<IRootViewModel>
|
||||||
{
|
{
|
||||||
@@ -88,9 +149,20 @@ public class Dialogs
|
|||||||
Background = SpecialColor.None,
|
Background = SpecialColor.None,
|
||||||
Content = new Grid<IRootViewModel>
|
Content = new Grid<IRootViewModel>
|
||||||
{
|
{
|
||||||
|
RowDefinitionsObject = "Auto Auto",
|
||||||
ChildInitializer =
|
ChildInitializer =
|
||||||
{
|
{
|
||||||
ReadInputs()
|
ReadInputsList(),
|
||||||
|
new ItemsControl<IRootViewModel, IPreviewElement>
|
||||||
|
{
|
||||||
|
ItemTemplate = ReadInputPreviewItemTemplate
|
||||||
|
}
|
||||||
|
.Setup(i => i.Bind(
|
||||||
|
i,
|
||||||
|
dc => dc.DialogService.ReadInput.Value.Previews,
|
||||||
|
c => c.ItemsSource
|
||||||
|
))
|
||||||
|
.WithExtension(new GridPositionExtension(0, 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -100,58 +172,9 @@ public class Dialogs
|
|||||||
d => d.DialogService.ReadInput.Value != null,
|
d => d.DialogService.ReadInput.Value != null,
|
||||||
v => v.IsVisible);
|
v => v.IsVisible);
|
||||||
|
|
||||||
((INotifyPropertyChanged) _readInputs).PropertyChanged += (_, e) =>
|
|
||||||
{
|
|
||||||
if (e.PropertyName == nameof(ItemsControl<object, object>.Children))
|
|
||||||
{
|
|
||||||
_readInputChildHandlerUnSubscriber?.Invoke();
|
|
||||||
|
|
||||||
if (_readInputs.Children.Count > 0)
|
|
||||||
{
|
|
||||||
UpdateReadInputsFocus();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_inputElementToFocus = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_readInputs.Children is INotifyCollectionChanged notifyCollectionChanged)
|
|
||||||
{
|
|
||||||
notifyCollectionChanged.CollectionChanged += NotifyCollectionChangedEventHandler;
|
|
||||||
_readInputChildHandlerUnSubscriber = () => { notifyCollectionChanged.CollectionChanged -= NotifyCollectionChangedEventHandler; };
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyCollectionChangedEventHandler(
|
|
||||||
object? sender,
|
|
||||||
NotifyCollectionChangedEventArgs e)
|
|
||||||
{
|
|
||||||
UpdateReadInputsFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IView<IRootViewModel> ReadInputs()
|
|
||||||
=> new Grid<IRootViewModel>
|
|
||||||
{
|
|
||||||
RowDefinitionsObject = "Auto Auto",
|
|
||||||
ChildInitializer =
|
|
||||||
{
|
|
||||||
ReadInputsList(),
|
|
||||||
new ItemsControl<IRootViewModel, IPreviewElement>
|
|
||||||
{
|
|
||||||
ItemTemplate = ReadInputPreviewItemTemplate
|
|
||||||
}
|
|
||||||
.Setup(i => i.Bind(
|
|
||||||
i,
|
|
||||||
dc => dc.DialogService.ReadInput.Value.Previews,
|
|
||||||
c => c.ItemsSource
|
|
||||||
))
|
|
||||||
.WithExtension(new GridPositionExtension(0, 1))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private IView<IPreviewElement> ReadInputPreviewItemTemplate()
|
private IView<IPreviewElement> ReadInputPreviewItemTemplate()
|
||||||
{
|
{
|
||||||
var grid = new Grid<IPreviewElement>
|
var grid = new Grid<IPreviewElement>
|
||||||
@@ -367,6 +390,64 @@ public class Dialogs
|
|||||||
|
|
||||||
_readInputs = readInputs;
|
_readInputs = readInputs;
|
||||||
|
|
||||||
|
((INotifyPropertyChanged) _readInputs).PropertyChanged += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(ItemsControl<object, object>.Children))
|
||||||
|
{
|
||||||
|
_readInputChildHandlerUnSubscriber?.Invoke();
|
||||||
|
|
||||||
|
if (_readInputs.Children.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateReadInputsFocus();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_inputElementToFocus = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_readInputs.Children is INotifyCollectionChanged notifyCollectionChanged)
|
||||||
|
{
|
||||||
|
notifyCollectionChanged.CollectionChanged += NotifyCollectionChangedEventHandler;
|
||||||
|
_readInputChildHandlerUnSubscriber = () => { notifyCollectionChanged.CollectionChanged -= NotifyCollectionChangedEventHandler; };
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyCollectionChangedEventHandler(
|
||||||
|
object? sender,
|
||||||
|
NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
UpdateReadInputsFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return readInputs;
|
return readInputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateReadInputsFocus()
|
||||||
|
{
|
||||||
|
foreach (var readInputsChild in _readInputs.Children)
|
||||||
|
{
|
||||||
|
if (readInputsChild.DataContext == _inputElementToFocus)
|
||||||
|
{
|
||||||
|
if (FindFocusable(readInputsChild) is { } focusable)
|
||||||
|
{
|
||||||
|
focusable.Focus();
|
||||||
|
_inputElementToFocus = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IFocusable? FindFocusable(IView view)
|
||||||
|
{
|
||||||
|
if (view is IFocusable focusable) return focusable;
|
||||||
|
foreach (var viewVisualChild in view.VisualChildren)
|
||||||
|
{
|
||||||
|
if (FindFocusable(viewVisualChild) is { } focusableChild)
|
||||||
|
return focusableChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -138,12 +138,22 @@ public class MainWindow
|
|||||||
{
|
{
|
||||||
Margin = "2 0 0 0",
|
Margin = "2 0 0 0",
|
||||||
Extensions = {new GridPositionExtension(2, 0)},
|
Extensions = {new GridPositionExtension(2, 0)},
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
ChildInitializer =
|
ChildInitializer =
|
||||||
{
|
{
|
||||||
new TextBlock<IRootViewModel>
|
new TextBlock<IRootViewModel>
|
||||||
|
{
|
||||||
|
Text = _consoleApplicationConfiguration.Value.AdminModeIcon ??
|
||||||
|
(_consoleApplicationConfiguration.Value.DisableUtf8 ? "A+ " : "\ud83d\udd11"),
|
||||||
|
AsciiOnly = false
|
||||||
|
}.Setup(t => t.Bind(
|
||||||
|
t,
|
||||||
|
dc => dc.AdminElevationManager.IsAdminInstanceRunning,
|
||||||
|
t => t.IsVisible)),
|
||||||
|
new TextBlock<IRootViewModel>
|
||||||
{
|
{
|
||||||
Text = _consoleApplicationConfiguration.Value.ClipboardSingleIcon ??
|
Text = _consoleApplicationConfiguration.Value.ClipboardSingleIcon ??
|
||||||
(_consoleApplicationConfiguration.Value.DisableUtf8 ? "C" : "\ud83d\udccb"),
|
(_consoleApplicationConfiguration.Value.DisableUtf8 ? "C " : "\ud83d\udccb"),
|
||||||
AsciiOnly = false
|
AsciiOnly = false
|
||||||
}.Setup(t => t.Bind(
|
}.Setup(t => t.Bind(
|
||||||
t,
|
t,
|
||||||
@@ -152,7 +162,7 @@ public class MainWindow
|
|||||||
new TextBlock<IRootViewModel>
|
new TextBlock<IRootViewModel>
|
||||||
{
|
{
|
||||||
Text = _consoleApplicationConfiguration.Value.ClipboardMultipleIcon ??
|
Text = _consoleApplicationConfiguration.Value.ClipboardMultipleIcon ??
|
||||||
(_consoleApplicationConfiguration.Value.DisableUtf8 ? "CC" : "\ud83d\udccb+"),
|
(_consoleApplicationConfiguration.Value.DisableUtf8 ? "CC " : "\ud83d\udccb+"),
|
||||||
AsciiOnly = false
|
AsciiOnly = false
|
||||||
}.Setup(t => t.Bind(
|
}.Setup(t => t.Bind(
|
||||||
t,
|
t,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using FileTime.App.FrequencyNavigation.ViewModels;
|
|||||||
using FileTime.ConsoleUI.App.Services;
|
using FileTime.ConsoleUI.App.Services;
|
||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Providers.LocalAdmin;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ public partial class RootViewModel : IRootViewModel
|
|||||||
public IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
public IFrequencyNavigationViewModel FrequencyNavigation { get; }
|
||||||
public IItemPreviewService ItemPreviewService { get; }
|
public IItemPreviewService ItemPreviewService { get; }
|
||||||
public IClipboardService ClipboardService { get; }
|
public IClipboardService ClipboardService { get; }
|
||||||
|
public IAdminElevationManager AdminElevationManager { get; }
|
||||||
public IDialogService DialogService { get; }
|
public IDialogService DialogService { get; }
|
||||||
public ITimelineViewModel TimelineViewModel { get; }
|
public ITimelineViewModel TimelineViewModel { get; }
|
||||||
public IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get;}
|
public IDeclarativeProperty<VolumeSizeInfo?> VolumeSizeInfo { get;}
|
||||||
@@ -34,7 +36,8 @@ public partial class RootViewModel : IRootViewModel
|
|||||||
ITimelineViewModel timelineViewModel,
|
ITimelineViewModel timelineViewModel,
|
||||||
IFrequencyNavigationViewModel frequencyNavigation,
|
IFrequencyNavigationViewModel frequencyNavigation,
|
||||||
IItemPreviewService itemPreviewService,
|
IItemPreviewService itemPreviewService,
|
||||||
IClipboardService clipboardService)
|
IClipboardService clipboardService,
|
||||||
|
IAdminElevationManager adminElevationManager)
|
||||||
{
|
{
|
||||||
AppState = appState;
|
AppState = appState;
|
||||||
PossibleCommands = possibleCommands;
|
PossibleCommands = possibleCommands;
|
||||||
@@ -44,6 +47,7 @@ public partial class RootViewModel : IRootViewModel
|
|||||||
FrequencyNavigation = frequencyNavigation;
|
FrequencyNavigation = frequencyNavigation;
|
||||||
ItemPreviewService = itemPreviewService;
|
ItemPreviewService = itemPreviewService;
|
||||||
ClipboardService = clipboardService;
|
ClipboardService = clipboardService;
|
||||||
|
AdminElevationManager = adminElevationManager;
|
||||||
|
|
||||||
DialogService.ReadInput.PropertyChanged += (o, e) =>
|
DialogService.ReadInput.PropertyChanged += (o, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,21 +8,27 @@ namespace FileTime.ConsoleUI.App.Services;
|
|||||||
public class CustomLoggerSink : ILogEventSink
|
public class CustomLoggerSink : ILogEventSink
|
||||||
{
|
{
|
||||||
private readonly Lazy<IDialogService> _dialogService;
|
private readonly Lazy<IDialogService> _dialogService;
|
||||||
|
|
||||||
public CustomLoggerSink(IServiceProvider serviceProvider)
|
public CustomLoggerSink(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_dialogService = new Lazy<IDialogService>(() => serviceProvider.GetRequiredService<IDialogService>());
|
_dialogService = new Lazy<IDialogService>(() => serviceProvider.GetRequiredService<IDialogService>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Emit(LogEvent logEvent)
|
public void Emit(LogEvent logEvent)
|
||||||
{
|
{
|
||||||
if (logEvent.Level >= LogEventLevel.Error)
|
if (logEvent.Level >= LogEventLevel.Error
|
||||||
|
&& logEvent.Properties.TryGetValue("SourceContext", out var sourceContext))
|
||||||
{
|
{
|
||||||
var message = logEvent.RenderMessage();
|
var s = sourceContext.ToString();
|
||||||
if (logEvent.Exception is not null)
|
|
||||||
message += $" {logEvent.Exception.Message}";
|
if (s != "\"Microsoft.AspNetCore.SignalR.Client.HubConnection\"")
|
||||||
Debug.WriteLine(message);
|
{
|
||||||
_dialogService.Value.ShowToastMessage(message);
|
var message = logEvent.RenderMessage();
|
||||||
|
if (logEvent.Exception is not null)
|
||||||
|
message += $" {logEvent.Exception.Message}";
|
||||||
|
Debug.WriteLine(message);
|
||||||
|
_dialogService.Value.ShowToastMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using FileTime.App.Core;
|
using FileTime.App.Core;
|
||||||
using FileTime.App.Core.Configuration;
|
using FileTime.App.Core.Configuration;
|
||||||
using FileTime.ConsoleUI;
|
using FileTime.ConsoleUI;
|
||||||
@@ -19,6 +21,7 @@ Console.OutputEncoding = System.Text.Encoding.UTF8;
|
|||||||
IConsoleDriver? driver = null;
|
IConsoleDriver? driver = null;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
(AppDataRoot, EnvironmentName) = Init.InitDevelopment();
|
(AppDataRoot, EnvironmentName) = Init.InitDevelopment();
|
||||||
#endif
|
#endif
|
||||||
if (AppDataRoot is null)
|
if (AppDataRoot is null)
|
||||||
@@ -49,6 +52,7 @@ try
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
driver?.Clear();
|
||||||
driver?.SetCursorVisible(true);
|
driver?.SetCursorVisible(true);
|
||||||
driver?.Dispose();
|
driver?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"AdminModeIcon": "\uf0e7",
|
||||||
"ClipboardSingleIcon": "\udb80\udd4c",
|
"ClipboardSingleIcon": "\udb80\udd4c",
|
||||||
"ClipboardMultipleIcon": "\udb84\ude68"
|
"ClipboardMultipleIcon": "\udb84\ude68"
|
||||||
}
|
}
|
||||||
@@ -57,9 +57,9 @@ public static class Program
|
|||||||
|
|
||||||
Log.Logger.Information("Early app starting...");
|
Log.Logger.Information("Early app starting...");
|
||||||
|
|
||||||
AppDomain.CurrentDomain.FirstChanceException -= OnFirstChanceException;
|
AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
|
||||||
AppDomain.CurrentDomain.UnhandledException -= OnAppDomainUnhandledException;
|
AppDomain.CurrentDomain.UnhandledException += OnAppDomainUnhandledException;
|
||||||
TaskScheduler.UnobservedTaskException -= OnTaskSchedulerUnobservedTaskException;
|
TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
BuildAvaloniaApp()
|
BuildAvaloniaApp()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public class DotnetDriver : IConsoleDriver
|
|||||||
public int ThreadId { get; set; }
|
public int ThreadId { get; set; }
|
||||||
|
|
||||||
public void EnterRestrictedMode() => CheckThreadId = true;
|
public void EnterRestrictedMode() => CheckThreadId = true;
|
||||||
|
public void ExitRestrictedMode() => CheckThreadId = false;
|
||||||
|
|
||||||
public virtual bool Init()
|
public virtual bool Init()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,4 +24,5 @@ public interface IConsoleDriver
|
|||||||
Size GetWindowSize();
|
Size GetWindowSize();
|
||||||
void Clear();
|
void Clear();
|
||||||
void EnterRestrictedMode();
|
void EnterRestrictedMode();
|
||||||
|
void ExitRestrictedMode();
|
||||||
}
|
}
|
||||||
94
src/Library/TerminalUI/Controls/Button.cs
Normal file
94
src/Library/TerminalUI/Controls/Button.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using GeneralInputKey;
|
||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using TerminalUI.Color;
|
||||||
|
using TerminalUI.ConsoleDrivers;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
using TerminalUI.Traits;
|
||||||
|
|
||||||
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
|
public partial class Button<T> : ContentView<Button<T>, T>, IFocusable
|
||||||
|
{
|
||||||
|
private record RenderState(IColor? Foreground, IColor? Background);
|
||||||
|
|
||||||
|
private Position? _cursorRenderPosition;
|
||||||
|
private RenderState? _lastRenderState;
|
||||||
|
|
||||||
|
[Notify] private Position? _cursorPosition;
|
||||||
|
private List<Action<Button<T>>> _clickHandlers = new();
|
||||||
|
|
||||||
|
public Button()
|
||||||
|
{
|
||||||
|
RerenderProperties.Add(nameof(CursorPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Focus()
|
||||||
|
=> ApplicationContext?.FocusManager.SetFocus(this);
|
||||||
|
|
||||||
|
public void UnFocus()
|
||||||
|
=> ApplicationContext?.FocusManager.UnFocus(this);
|
||||||
|
|
||||||
|
public void SetCursorPosition(IConsoleDriver consoleDriver)
|
||||||
|
{
|
||||||
|
if (_cursorRenderPosition is null) return;
|
||||||
|
consoleDriver.SetCursorPosition(_cursorRenderPosition.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Size CalculateSize()
|
||||||
|
{
|
||||||
|
if (Content is null || !Content.IsVisible) return new Size(0, 0);
|
||||||
|
|
||||||
|
return Content.GetRequestedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size)
|
||||||
|
{
|
||||||
|
if (ContentRendererMethod is null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException(
|
||||||
|
nameof(ContentRendererMethod)
|
||||||
|
+ " is null, cannot render content of "
|
||||||
|
+ Content?.GetType().Name
|
||||||
|
+ " with DataContext of "
|
||||||
|
+ DataContext?.GetType().Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundColor = Background ?? renderContext.Background;
|
||||||
|
var foregroundColor = Foreground ?? renderContext.Foreground;
|
||||||
|
|
||||||
|
var renderState = new RenderState(foregroundColor, backgroundColor);
|
||||||
|
|
||||||
|
var forceRerender = !renderContext.ForceRerender && !NeedsRerender(renderState);
|
||||||
|
_lastRenderState = renderState;
|
||||||
|
|
||||||
|
var childRenderContext = renderContext with
|
||||||
|
{
|
||||||
|
Background = backgroundColor,
|
||||||
|
Foreground = foregroundColor,
|
||||||
|
ForceRerender = forceRerender
|
||||||
|
};
|
||||||
|
|
||||||
|
_cursorRenderPosition = position;
|
||||||
|
|
||||||
|
return ContentRendererMethod(childRenderContext, position, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool NeedsRerender(RenderState renderState) => renderState != _lastRenderState;
|
||||||
|
|
||||||
|
public override void HandleKeyInput(GeneralKeyEventArgs keyEventArgs)
|
||||||
|
{
|
||||||
|
if (keyEventArgs.Key != Keys.Enter) return;
|
||||||
|
|
||||||
|
keyEventArgs.Handled = true;
|
||||||
|
foreach (var clickHandler in _clickHandlers)
|
||||||
|
{
|
||||||
|
clickHandler(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Button<T> WithClickHandler(Action<Button<T>> handler)
|
||||||
|
{
|
||||||
|
_clickHandlers.Add(handler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,8 +125,7 @@ public sealed partial class ItemsControl<TDataContext, TItem>
|
|||||||
{
|
{
|
||||||
if (!child.IsVisible) continue;
|
if (!child.IsVisible) continue;
|
||||||
|
|
||||||
if (!_requestedSizes.TryGetValue(child, out var childSize)) throw new Exception("Child size not found");
|
if (!_requestedSizes.TryGetValue(child, out var childSize)) continue;
|
||||||
|
|
||||||
|
|
||||||
var childPosition = Orientation == Orientation.Vertical
|
var childPosition = Orientation == Orientation.Vertical
|
||||||
? position with {Y = position.Y + delta}
|
? position with {Y = position.Y + delta}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class EventLoop : IEventLoop
|
|||||||
private readonly ILogger<EventLoop> _logger;
|
private readonly ILogger<EventLoop> _logger;
|
||||||
private readonly List<Action> _initializers = new();
|
private readonly List<Action> _initializers = new();
|
||||||
private readonly List<Action> _permanentQueue = new();
|
private readonly List<Action> _permanentQueue = new();
|
||||||
|
private readonly List<Action> _finalizers = new();
|
||||||
|
|
||||||
public int ThreadId { get; set; } = -1;
|
public int ThreadId { get; set; } = -1;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ public class EventLoop : IEventLoop
|
|||||||
|
|
||||||
public void AddToPermanentQueue(Action action) => _permanentQueue.Add(action);
|
public void AddToPermanentQueue(Action action) => _permanentQueue.Add(action);
|
||||||
public void AddInitializer(Action action) => _initializers.Add(action);
|
public void AddInitializer(Action action) => _initializers.Add(action);
|
||||||
|
public void AddFinalizer(Action action) => _finalizers.Add(action);
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
@@ -35,6 +37,10 @@ public class EventLoop : IEventLoop
|
|||||||
ProcessQueues();
|
ProcessQueues();
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
|
foreach (var finalizer in _finalizers)
|
||||||
|
{
|
||||||
|
finalizer();
|
||||||
|
}
|
||||||
ThreadId = -1;
|
ThreadId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ public interface IEventLoop
|
|||||||
void AddToPermanentQueue(Action action);
|
void AddToPermanentQueue(Action action);
|
||||||
void AddInitializer(Action action);
|
void AddInitializer(Action action);
|
||||||
int ThreadId { get; set; }
|
int ThreadId { get; set; }
|
||||||
|
void AddFinalizer(Action action);
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,10 @@ public class RenderEngine : IRenderEngine
|
|||||||
_applicationContext.ConsoleDriver.ThreadId = _eventLoop.ThreadId;
|
_applicationContext.ConsoleDriver.ThreadId = _eventLoop.ThreadId;
|
||||||
_applicationContext.ConsoleDriver.EnterRestrictedMode();
|
_applicationContext.ConsoleDriver.EnterRestrictedMode();
|
||||||
});
|
});
|
||||||
|
_eventLoop.AddFinalizer(() =>
|
||||||
|
{
|
||||||
|
_applicationContext.ConsoleDriver.ExitRestrictedMode();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestRerender(IView view) => RequestRerender();
|
public void RequestRerender(IView view) => RequestRerender();
|
||||||
|
|||||||
Reference in New Issue
Block a user