Controls, Startup&Driver improvements
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
namespace FileTime.App.Core.Configuration;
|
||||||
|
|
||||||
|
public record ApplicationConfiguration(bool AllowCloseLastTab);
|
||||||
@@ -4,4 +4,5 @@ public static class SectionNames
|
|||||||
{
|
{
|
||||||
public const string KeybindingSectionName = "KeyBindings";
|
public const string KeybindingSectionName = "KeyBindings";
|
||||||
public const string ProgramsSectionName = "Programs";
|
public const string ProgramsSectionName = "Programs";
|
||||||
|
public const string ApplicationSectionName = "Application";
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using FileTime.App.CommandPalette.Services;
|
using FileTime.App.CommandPalette.Services;
|
||||||
|
using FileTime.App.Core.Configuration;
|
||||||
using FileTime.App.Core.Extensions;
|
using FileTime.App.Core.Extensions;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.UserCommand;
|
using FileTime.App.Core.UserCommand;
|
||||||
@@ -14,6 +15,7 @@ using FileTime.Core.Timeline;
|
|||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
using InitableService;
|
using InitableService;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace FileTime.App.Core.Services.UserCommandHandler;
|
namespace FileTime.App.Core.Services.UserCommandHandler;
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
||||||
private readonly ICommandPaletteService _commandPaletteService;
|
private readonly ICommandPaletteService _commandPaletteService;
|
||||||
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
private readonly ILogger<NavigationUserCommandHandlerService> _logger;
|
||||||
|
private readonly ApplicationConfiguration _applicationConfiguration;
|
||||||
private ITabViewModel? _selectedTab;
|
private ITabViewModel? _selectedTab;
|
||||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||||
@@ -44,7 +47,8 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
IUserCommunicationService userCommunicationService,
|
IUserCommunicationService userCommunicationService,
|
||||||
IFrequencyNavigationService frequencyNavigationService,
|
IFrequencyNavigationService frequencyNavigationService,
|
||||||
ICommandPaletteService commandPaletteService,
|
ICommandPaletteService commandPaletteService,
|
||||||
ILogger<NavigationUserCommandHandlerService> logger) : base(appState)
|
ILogger<NavigationUserCommandHandlerService> logger,
|
||||||
|
ApplicationConfiguration applicationConfiguration) : base(appState)
|
||||||
{
|
{
|
||||||
_appState = appState;
|
_appState = appState;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
@@ -55,6 +59,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
_frequencyNavigationService = frequencyNavigationService;
|
_frequencyNavigationService = frequencyNavigationService;
|
||||||
_commandPaletteService = commandPaletteService;
|
_commandPaletteService = commandPaletteService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_applicationConfiguration = applicationConfiguration;
|
||||||
|
|
||||||
SaveSelectedTab(t => _selectedTab = t);
|
SaveSelectedTab(t => _selectedTab = t);
|
||||||
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
|
||||||
@@ -387,7 +392,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
|
|
||||||
private Task CloseTab()
|
private Task CloseTab()
|
||||||
{
|
{
|
||||||
if (_appState.Tabs.Count < 2 || _selectedTab == null) return Task.CompletedTask;
|
if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null) return Task.CompletedTask;
|
||||||
|
|
||||||
var tabToRemove = _selectedTab;
|
var tabToRemove = _selectedTab;
|
||||||
_appState.RemoveTab(tabToRemove!);
|
_appState.RemoveTab(tabToRemove!);
|
||||||
|
|||||||
@@ -41,20 +41,16 @@ public static class Startup
|
|||||||
.AddSingleton<IExitHandler, ContainerRefreshHandler>();
|
.AddSingleton<IExitHandler, ContainerRefreshHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IServiceCollection AddCommandHandlers(this IServiceCollection serviceCollection)
|
private static IServiceCollection AddCommandHandlers(this IServiceCollection serviceCollection) =>
|
||||||
{
|
serviceCollection
|
||||||
return serviceCollection
|
|
||||||
.AddSingleton<IUserCommandHandler, NavigationUserCommandHandlerService>()
|
.AddSingleton<IUserCommandHandler, NavigationUserCommandHandlerService>()
|
||||||
.AddSingleton<IUserCommandHandler, ItemManipulationUserCommandHandlerService>()
|
.AddSingleton<IUserCommandHandler, ItemManipulationUserCommandHandlerService>()
|
||||||
.AddSingleton<IUserCommandHandler, ToolUserCommandHandlerService>()
|
.AddSingleton<IUserCommandHandler, ToolUserCommandHandlerService>()
|
||||||
.AddSingleton<IUserCommandHandler, CommandSchedulerUserCommandHandlerService>();
|
.AddSingleton<IUserCommandHandler, CommandSchedulerUserCommandHandlerService>();
|
||||||
}
|
|
||||||
|
|
||||||
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration)
|
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfigurationRoot configuration) =>
|
||||||
{
|
serviceCollection
|
||||||
return serviceCollection
|
|
||||||
.Configure<ProgramsConfiguration>(configuration.GetSection(SectionNames.ProgramsSectionName))
|
.Configure<ProgramsConfiguration>(configuration.GetSection(SectionNames.ProgramsSectionName))
|
||||||
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
|
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
|
||||||
.AddSingleton<IConfiguration>(configuration);
|
.AddSingleton<IConfiguration>(configuration);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace FileTime.ConsoleUI.App.Configuration;
|
||||||
|
|
||||||
|
public class ConsoleApplicationConfiguration
|
||||||
|
{
|
||||||
|
public string? ConsoleDriver { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using TerminalUI.Models;
|
using TerminalUI.Color;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using FileTime.App.Core.Models;
|
using System.Collections.Specialized;
|
||||||
|
using FileTime.App.Core.Models;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.ConsoleUI.App.KeyInputHandling;
|
using FileTime.ConsoleUI.App.KeyInputHandling;
|
||||||
using TerminalUI;
|
using TerminalUI;
|
||||||
using TerminalUI.ConsoleDrivers;
|
using TerminalUI.ConsoleDrivers;
|
||||||
@@ -16,6 +18,7 @@ public class App : IApplication
|
|||||||
private readonly MainWindow _mainWindow;
|
private readonly MainWindow _mainWindow;
|
||||||
private readonly IApplicationContext _applicationContext;
|
private readonly IApplicationContext _applicationContext;
|
||||||
private readonly IConsoleDriver _consoleDriver;
|
private readonly IConsoleDriver _consoleDriver;
|
||||||
|
private readonly IAppState _appState;
|
||||||
private readonly IKeyInputHandlerService _keyInputHandlerService;
|
private readonly IKeyInputHandlerService _keyInputHandlerService;
|
||||||
private readonly Thread _renderThread;
|
private readonly Thread _renderThread;
|
||||||
|
|
||||||
@@ -26,7 +29,8 @@ public class App : IApplication
|
|||||||
IAppKeyService<ConsoleKey> appKeyService,
|
IAppKeyService<ConsoleKey> appKeyService,
|
||||||
MainWindow mainWindow,
|
MainWindow mainWindow,
|
||||||
IApplicationContext applicationContext,
|
IApplicationContext applicationContext,
|
||||||
IConsoleDriver consoleDriver)
|
IConsoleDriver consoleDriver,
|
||||||
|
IAppState appState)
|
||||||
{
|
{
|
||||||
_lifecycleService = lifecycleService;
|
_lifecycleService = lifecycleService;
|
||||||
_keyInputHandlerService = keyInputHandlerService;
|
_keyInputHandlerService = keyInputHandlerService;
|
||||||
@@ -35,6 +39,7 @@ public class App : IApplication
|
|||||||
_mainWindow = mainWindow;
|
_mainWindow = mainWindow;
|
||||||
_applicationContext = applicationContext;
|
_applicationContext = applicationContext;
|
||||||
_consoleDriver = consoleDriver;
|
_consoleDriver = consoleDriver;
|
||||||
|
_appState = appState;
|
||||||
|
|
||||||
_renderThread = new Thread(Render);
|
_renderThread = new Thread(Render);
|
||||||
}
|
}
|
||||||
@@ -43,6 +48,12 @@ public class App : IApplication
|
|||||||
{
|
{
|
||||||
Task.Run(async () => await _lifecycleService.InitStartupHandlersAsync()).Wait();
|
Task.Run(async () => await _lifecycleService.InitStartupHandlersAsync()).Wait();
|
||||||
|
|
||||||
|
((INotifyCollectionChanged) _appState.Tabs).CollectionChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if(_appState.Tabs.Count == 0)
|
||||||
|
_applicationContext.IsRunning = false;
|
||||||
|
};
|
||||||
|
|
||||||
_mainWindow.Initialize();
|
_mainWindow.Initialize();
|
||||||
foreach (var rootView in _mainWindow.RootViews())
|
foreach (var rootView in _mainWindow.RootViews())
|
||||||
{
|
{
|
||||||
@@ -66,6 +77,7 @@ public class App : IApplication
|
|||||||
_keyInputHandlerService.HandleKeyInput(keyEventArgs);
|
_keyInputHandlerService.HandleKeyInput(keyEventArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.App.Core.ViewModels.Timeline;
|
using PropertyChanged.SourceGenerator;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
public class ConsoleAppState : AppStateBase, IConsoleAppState
|
public partial class ConsoleAppState : AppStateBase, IConsoleAppState
|
||||||
{
|
{
|
||||||
|
[Notify] private string? _errorText;
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="PropertyChanged.SourceGenerator" Version="1.0.8">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
|
public class MainConsoleConfiguration
|
||||||
|
{
|
||||||
|
public static Dictionary<string, string?> Configuration { get; }
|
||||||
|
static MainConsoleConfiguration()
|
||||||
|
{
|
||||||
|
Configuration = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,12 @@ using DeclarativeProperty;
|
|||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using TerminalUI;
|
using TerminalUI;
|
||||||
|
using TerminalUI.Color;
|
||||||
using TerminalUI.Controls;
|
using TerminalUI.Controls;
|
||||||
using TerminalUI.Extensions;
|
using TerminalUI.Extensions;
|
||||||
using TerminalUI.Models;
|
using TerminalUI.Models;
|
||||||
|
using TerminalUI.ViewExtensions;
|
||||||
|
using ConsoleColor = TerminalUI.Color.ConsoleColor;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.App;
|
namespace FileTime.ConsoleUI.App;
|
||||||
|
|
||||||
@@ -17,6 +20,8 @@ public class MainWindow
|
|||||||
private readonly ITheme _theme;
|
private readonly ITheme _theme;
|
||||||
private ListView<IAppState, IItemViewModel> _selectedItemsView;
|
private ListView<IAppState, IItemViewModel> _selectedItemsView;
|
||||||
|
|
||||||
|
private Grid<object> _grid;
|
||||||
|
|
||||||
public MainWindow(
|
public MainWindow(
|
||||||
IConsoleAppState consoleAppState,
|
IConsoleAppState consoleAppState,
|
||||||
IApplicationContext applicationContext,
|
IApplicationContext applicationContext,
|
||||||
@@ -56,9 +61,69 @@ public class MainWindow
|
|||||||
_selectedItemsView,
|
_selectedItemsView,
|
||||||
appState => appState == null ? null : appState.SelectedTab.Map(t => t == null ? null : t.CurrentItems).Switch(),
|
appState => appState == null ? null : appState.SelectedTab.Map(t => t == null ? null : t.CurrentItems).Switch(),
|
||||||
v => v.ItemsSource);
|
v => v.ItemsSource);
|
||||||
|
|
||||||
|
TestGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IView> RootViews() => new IView[] {_selectedItemsView};
|
private void TestGrid()
|
||||||
|
{
|
||||||
|
var grid = new Grid<object>
|
||||||
|
{
|
||||||
|
ApplicationContext = _applicationContext,
|
||||||
|
ColumnDefinitionsObject = "Auto Auto",
|
||||||
|
RowDefinitionsObject = "Auto Auto",
|
||||||
|
ChildInitializer =
|
||||||
|
{
|
||||||
|
new Rectangle<object>
|
||||||
|
{
|
||||||
|
Fill = new ConsoleColor(System.ConsoleColor.Blue, ColorType.Foreground),
|
||||||
|
Extensions =
|
||||||
|
{
|
||||||
|
new GridPositionExtension(0, 0)
|
||||||
|
},
|
||||||
|
Width = 2,
|
||||||
|
Height = 2,
|
||||||
|
},
|
||||||
|
new Rectangle<object>
|
||||||
|
{
|
||||||
|
Fill = new ConsoleColor(System.ConsoleColor.Red, ColorType.Foreground),
|
||||||
|
Extensions =
|
||||||
|
{
|
||||||
|
new GridPositionExtension(0, 1)
|
||||||
|
},
|
||||||
|
Width = 3,
|
||||||
|
Height = 3,
|
||||||
|
},
|
||||||
|
new Rectangle<object>
|
||||||
|
{
|
||||||
|
Fill = new ConsoleColor(System.ConsoleColor.Green, ColorType.Foreground),
|
||||||
|
Extensions =
|
||||||
|
{
|
||||||
|
new GridPositionExtension(1, 0)
|
||||||
|
},
|
||||||
|
Width = 4,
|
||||||
|
Height = 4,
|
||||||
|
},
|
||||||
|
new Rectangle<object>
|
||||||
|
{
|
||||||
|
Fill = new ConsoleColor(System.ConsoleColor.Yellow, ColorType.Foreground),
|
||||||
|
Extensions =
|
||||||
|
{
|
||||||
|
new GridPositionExtension(1, 1)
|
||||||
|
},
|
||||||
|
Width = 5,
|
||||||
|
Height = 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_grid = grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IView> RootViews() => new IView[]
|
||||||
|
{
|
||||||
|
_grid, _selectedItemsView
|
||||||
|
};
|
||||||
|
|
||||||
private IColor? ToForegroundColor(ItemViewMode viewMode)
|
private IColor? ToForegroundColor(ItemViewMode viewMode)
|
||||||
=> viewMode switch
|
=> viewMode switch
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Configuration;
|
||||||
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
|
using FileTime.ConsoleUI.App.Configuration;
|
||||||
using FileTime.ConsoleUI.App.KeyInputHandling;
|
using FileTime.ConsoleUI.App.KeyInputHandling;
|
||||||
using FileTime.ConsoleUI.App.Services;
|
using FileTime.ConsoleUI.App.Services;
|
||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using TerminalUI;
|
using TerminalUI;
|
||||||
@@ -12,7 +15,7 @@ namespace FileTime.ConsoleUI.App;
|
|||||||
|
|
||||||
public static class Startup
|
public static class Startup
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddConsoleServices(this IServiceCollection services)
|
public static IServiceCollection AddConsoleServices(this IServiceCollection services, IConfigurationRoot configuration)
|
||||||
{
|
{
|
||||||
services.TryAddSingleton<IApplication, App>();
|
services.TryAddSingleton<IApplication, App>();
|
||||||
services.TryAddSingleton<MainWindow>();
|
services.TryAddSingleton<MainWindow>();
|
||||||
@@ -23,6 +26,9 @@ public static class Startup
|
|||||||
services.TryAddSingleton<IAppKeyService<ConsoleKey>, ConsoleAppKeyService>();
|
services.TryAddSingleton<IAppKeyService<ConsoleKey>, ConsoleAppKeyService>();
|
||||||
services.TryAddSingleton<ISystemClipboardService, ConsoleSystemClipboardService>();
|
services.TryAddSingleton<ISystemClipboardService, ConsoleSystemClipboardService>();
|
||||||
services.AddSingleton<CustomLoggerSink>();
|
services.AddSingleton<CustomLoggerSink>();
|
||||||
|
services.TryAddSingleton(new ApplicationConfiguration(true));
|
||||||
|
|
||||||
|
services.Configure<ConsoleApplicationConfiguration>(configuration);
|
||||||
|
|
||||||
services.TryAddSingleton<IApplicationContext>(sp
|
services.TryAddSingleton<IApplicationContext>(sp
|
||||||
=> new ApplicationContext
|
=> new ApplicationContext
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using FileTime.ConsoleUI.App;
|
using FileTime.ConsoleUI.App;
|
||||||
|
using TerminalUI.Color;
|
||||||
using TerminalUI.Models;
|
using TerminalUI.Models;
|
||||||
using ConsoleColor = TerminalUI.Models.ConsoleColor;
|
using ConsoleColor = TerminalUI.Color.ConsoleColor;
|
||||||
|
|
||||||
namespace FileTime.ConsoleUI.Styles;
|
namespace FileTime.ConsoleUI.Styles;
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ public static class DI
|
|||||||
{
|
{
|
||||||
public static IServiceProvider ServiceProvider { get; private set; } = null!;
|
public static IServiceProvider ServiceProvider { get; private set; } = null!;
|
||||||
|
|
||||||
public static void Initialize(IConfigurationRoot configuration, IServiceCollection serviceCollection)
|
public static IServiceProvider Initialize(IConfigurationRoot configuration)
|
||||||
=> ServiceProvider = DependencyInjection
|
=> ServiceProvider = DependencyInjection
|
||||||
.RegisterDefaultServices(configuration: configuration, serviceCollection: serviceCollection)
|
.RegisterDefaultServices(configuration: configuration)
|
||||||
.AddConsoleServices()
|
.AddConsoleServices(configuration)
|
||||||
.AddLocalProviderServices()
|
.AddLocalProviderServices()
|
||||||
.AddServerCoreServices()
|
.AddServerCoreServices()
|
||||||
.AddFrequencyNavigation()
|
.AddFrequencyNavigation()
|
||||||
@@ -31,6 +31,8 @@ public static class DI
|
|||||||
.AddCompression()
|
.AddCompression()
|
||||||
.SetupLogging()
|
.SetupLogging()
|
||||||
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog())
|
.AddLogging(loggingBuilder => loggingBuilder.AddSerilog())
|
||||||
|
.AddConsoleDriver()
|
||||||
|
.AddTheme()
|
||||||
.BuildServiceProvider();
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
|
||||||
|
|||||||
26
src/ConsoleApp/FileTime.ConsoleUI/Help.cs
Normal file
26
src/ConsoleApp/FileTime.ConsoleUI/Help.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Text;
|
||||||
|
using FileTime.App.Core.Configuration;
|
||||||
|
using FileTime.ConsoleUI.App.Configuration;
|
||||||
|
|
||||||
|
namespace FileTime.ConsoleUI;
|
||||||
|
|
||||||
|
public static class Help
|
||||||
|
{
|
||||||
|
public static void PrintHelp()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
|
||||||
|
sb.AppendLine("Options:");
|
||||||
|
PrintDriverOption(sb);
|
||||||
|
Console.Write(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PrintDriverOption(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"--{SectionNames.ApplicationSectionName}.{nameof(ConsoleApplicationConfiguration.ConsoleDriver)}");
|
||||||
|
foreach (var driver in Startup.Drivers.Keys)
|
||||||
|
{
|
||||||
|
sb.AppendLine("\t" + driver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,75 @@
|
|||||||
using FileTime.App.Core;
|
using System.Diagnostics;
|
||||||
|
using FileTime.App.Core;
|
||||||
using FileTime.App.Core.Configuration;
|
using FileTime.App.Core.Configuration;
|
||||||
using FileTime.ConsoleUI;
|
using FileTime.ConsoleUI;
|
||||||
using FileTime.ConsoleUI.App;
|
using FileTime.ConsoleUI.App;
|
||||||
using FileTime.ConsoleUI.Styles;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Serilog;
|
||||||
|
using Serilog.Debugging;
|
||||||
using TerminalUI.ConsoleDrivers;
|
using TerminalUI.ConsoleDrivers;
|
||||||
|
|
||||||
IConsoleDriver driver = new WindowsDriver();
|
if(args.Contains("--help"))
|
||||||
driver.Init();
|
|
||||||
ITheme theme;
|
|
||||||
if (driver.GetCursorPosition() is not {PosX: 0, PosY: 0})
|
|
||||||
{
|
{
|
||||||
driver = new DotnetDriver();
|
Help.PrintHelp();
|
||||||
driver.Init();
|
return;
|
||||||
theme = DefaultThemes.ConsoleColorTheme;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
theme = DefaultThemes.Color256Theme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
driver.SetCursorVisible(false);
|
IConsoleDriver? driver = null;
|
||||||
|
|
||||||
|
(AppDataRoot, EnvironmentName) = Init.InitDevelopment();
|
||||||
|
InitLogging();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(AppDataRoot, EnvironmentName) = Init.InitDevelopment();
|
var configuration = CreateConfiguration(args);
|
||||||
var configuration = new ConfigurationBuilder()
|
|
||||||
.AddInMemoryCollection(MainConfiguration.Configuration)
|
|
||||||
#if DEBUG
|
|
||||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
|
||||||
#endif
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var serviceCollection = new ServiceCollection();
|
var serviceProvider = DI.Initialize(configuration);
|
||||||
serviceCollection.TryAddSingleton<IConsoleDriver>(driver);
|
|
||||||
serviceCollection.TryAddSingleton<ITheme>(theme);
|
|
||||||
|
|
||||||
DI.Initialize(configuration, serviceCollection);
|
driver = serviceProvider.GetRequiredService<IConsoleDriver>();
|
||||||
|
Log.Logger.Debug("Using driver {Driver}", driver.GetType().Name);
|
||||||
|
driver.SetCursorVisible(false);
|
||||||
|
|
||||||
var app = DI.ServiceProvider.GetRequiredService<IApplication>();
|
var app = serviceProvider.GetRequiredService<IApplication>();
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
driver.SetCursorVisible(true);
|
driver?.SetCursorVisible(true);
|
||||||
driver.Dispose();
|
driver?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitLogging()
|
||||||
|
{
|
||||||
|
SelfLog.Enable(l => Debug.WriteLine(l));
|
||||||
|
|
||||||
|
var logFolder = Path.Combine(AppDataRoot, "logs", "bootstrap");
|
||||||
|
|
||||||
|
if (!Directory.Exists(logFolder)) Directory.CreateDirectory(logFolder);
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
#if DEBUG || VERBOSE_LOGGING
|
||||||
|
.MinimumLevel.Verbose()
|
||||||
|
#endif
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.WriteTo.File(
|
||||||
|
Path.Combine(logFolder, "appLog.log"),
|
||||||
|
fileSizeLimitBytes: 10 * 1024 * 1024,
|
||||||
|
rollingInterval: RollingInterval.Day,
|
||||||
|
rollOnFileSizeLimit: true)
|
||||||
|
.CreateBootstrapLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
static IConfigurationRoot CreateConfiguration(string[] strings)
|
||||||
|
{
|
||||||
|
var configurationRoot = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(MainConfiguration.Configuration)
|
||||||
|
.AddInMemoryCollection(MainConsoleConfiguration.Configuration)
|
||||||
|
#if DEBUG
|
||||||
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
|
#endif
|
||||||
|
.AddCommandLine(strings)
|
||||||
|
.Build();
|
||||||
|
return configurationRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class Program
|
public partial class Program
|
||||||
|
|||||||
66
src/ConsoleApp/FileTime.ConsoleUI/Startup.cs
Normal file
66
src/ConsoleApp/FileTime.ConsoleUI/Startup.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using FileTime.ConsoleUI.App;
|
||||||
|
using FileTime.ConsoleUI.App.Configuration;
|
||||||
|
using FileTime.ConsoleUI.Styles;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using TerminalUI.ConsoleDrivers;
|
||||||
|
|
||||||
|
namespace FileTime.ConsoleUI;
|
||||||
|
|
||||||
|
public static class Startup
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<string, Func<IConsoleDriver>> Drivers = new()
|
||||||
|
{
|
||||||
|
["windows"] = () => new XTermDriver(),
|
||||||
|
["dotnet"] = () => new DotnetDriver()
|
||||||
|
};
|
||||||
|
public static IServiceCollection AddConsoleDriver(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
serviceCollection.TryAddSingleton<IConsoleDriver>(sp =>
|
||||||
|
{
|
||||||
|
var appConfig = sp.GetRequiredService<IOptions<ConsoleApplicationConfiguration>>();
|
||||||
|
|
||||||
|
IConsoleDriver? driver = null;
|
||||||
|
if (appConfig.Value.ConsoleDriver is { } consoleDriver
|
||||||
|
&& Drivers.TryGetValue(consoleDriver, out var driverFactory))
|
||||||
|
{
|
||||||
|
driver = driverFactory();
|
||||||
|
driver.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driver == null)
|
||||||
|
{
|
||||||
|
driver = new XTermDriver();
|
||||||
|
var asd = driver.GetCursorPosition();
|
||||||
|
driver.Init();
|
||||||
|
if (!driver.Init())
|
||||||
|
{
|
||||||
|
driver = new DotnetDriver();
|
||||||
|
driver.Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver;
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddTheme(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
serviceCollection.TryAddSingleton<ITheme>(sp =>
|
||||||
|
{
|
||||||
|
var driver = sp.GetRequiredService<IConsoleDriver>();
|
||||||
|
|
||||||
|
return driver switch
|
||||||
|
{
|
||||||
|
XTermDriver _ => DefaultThemes.Color256Theme,
|
||||||
|
DotnetDriver _ => DefaultThemes.ConsoleColorTheme,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(driver))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,6 +60,7 @@ public static class Startup
|
|||||||
serviceCollection.TryAddSingleton<ToastMessageSink>();
|
serviceCollection.TryAddSingleton<ToastMessageSink>();
|
||||||
serviceCollection.TryAddSingleton<IUserCommunicationService>(s => s.GetRequiredService<IDialogService>());
|
serviceCollection.TryAddSingleton<IUserCommunicationService>(s => s.GetRequiredService<IDialogService>());
|
||||||
serviceCollection.TryAddSingleton<IAppKeyService<Key>, GuiAppKeyService>();
|
serviceCollection.TryAddSingleton<IAppKeyService<Key>, GuiAppKeyService>();
|
||||||
|
serviceCollection.TryAddSingleton(new ApplicationConfiguration(false));
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public record struct Color256(byte Color, ColorType Type) : IColor
|
public record struct Color256(byte Color, ColorType Type) : IColor
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public record struct ColorRgb(byte R, byte G, byte B, ColorType Type) : IColor
|
public record struct ColorRgb(byte R, byte G, byte B, ColorType Type) : IColor
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public enum ColorType
|
public enum ColorType
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public static class Color256Colors
|
public static class Color256Colors
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public record ConsoleColor(System.ConsoleColor Color, ColorType Type) : IColor
|
public record ConsoleColor(System.ConsoleColor Color, ColorType Type) : IColor
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Color;
|
||||||
|
|
||||||
public interface IColor
|
public interface IColor
|
||||||
{
|
{
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
using TerminalUI.Models;
|
using TerminalUI.Color;
|
||||||
using ConsoleColor = TerminalUI.Models.ConsoleColor;
|
using TerminalUI.Models;
|
||||||
|
using ConsoleColor = TerminalUI.Color.ConsoleColor;
|
||||||
|
|
||||||
namespace TerminalUI.ConsoleDrivers;
|
namespace TerminalUI.ConsoleDrivers;
|
||||||
|
|
||||||
public class DotnetDriver : IConsoleDriver
|
public class DotnetDriver : IConsoleDriver
|
||||||
{
|
{
|
||||||
public virtual void Init() => Console.Clear();
|
public virtual bool Init()
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetCursorPosition(Position position) => Console.SetCursorPosition(position.PosX, position.PosY);
|
public void SetCursorPosition(Position position) => Console.SetCursorPosition(position.X, position.Y);
|
||||||
|
|
||||||
public void ResetColor() => Console.ResetColor();
|
public void ResetColor() => Console.ResetColor();
|
||||||
|
|
||||||
@@ -21,7 +26,7 @@ public class DotnetDriver : IConsoleDriver
|
|||||||
|
|
||||||
public void Write(char text) => Console.Write(text);
|
public void Write(char text) => Console.Write(text);
|
||||||
|
|
||||||
public virtual void Dispose() {}
|
public virtual void Dispose() => Console.Clear();
|
||||||
|
|
||||||
public bool CanRead() => Console.KeyAvailable;
|
public bool CanRead() => Console.KeyAvailable;
|
||||||
public ConsoleKeyInfo ReadKey() => Console.ReadKey(true);
|
public ConsoleKeyInfo ReadKey() => Console.ReadKey(true);
|
||||||
@@ -36,6 +41,9 @@ public class DotnetDriver : IConsoleDriver
|
|||||||
public virtual void SetBackgroundColor(IColor background)
|
public virtual void SetBackgroundColor(IColor background)
|
||||||
{
|
{
|
||||||
if (background is not ConsoleColor consoleColor) throw new NotSupportedException();
|
if (background is not ConsoleColor consoleColor) throw new NotSupportedException();
|
||||||
Console.ForegroundColor = consoleColor.Color;
|
Console.BackgroundColor = consoleColor.Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Size GetBufferSize() => new(Console.BufferWidth, Console.BufferHeight);
|
||||||
|
public void Clear() => Console.Clear();
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using TerminalUI.Models;
|
using TerminalUI.Color;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
|
||||||
namespace TerminalUI.ConsoleDrivers;
|
namespace TerminalUI.ConsoleDrivers;
|
||||||
|
|
||||||
public interface IConsoleDriver
|
public interface IConsoleDriver
|
||||||
{
|
{
|
||||||
void Init();
|
bool Init();
|
||||||
void Dispose();
|
void Dispose();
|
||||||
void SetCursorPosition(Position position);
|
void SetCursorPosition(Position position);
|
||||||
void ResetColor();
|
void ResetColor();
|
||||||
@@ -16,4 +17,6 @@ public interface IConsoleDriver
|
|||||||
void SetCursorVisible(bool cursorVisible);
|
void SetCursorVisible(bool cursorVisible);
|
||||||
void SetForegroundColor(IColor foreground);
|
void SetForegroundColor(IColor foreground);
|
||||||
void SetBackgroundColor(IColor background);
|
void SetBackgroundColor(IColor background);
|
||||||
|
Size GetBufferSize();
|
||||||
|
void Clear();
|
||||||
}
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using TerminalUI.Models;
|
|
||||||
|
|
||||||
namespace TerminalUI.ConsoleDrivers;
|
|
||||||
|
|
||||||
public sealed class WindowsDriver : DotnetDriver
|
|
||||||
{
|
|
||||||
public override void Init() => Console.Out.Write("\x1b[?1049h");
|
|
||||||
|
|
||||||
public override void Dispose() => Console.Out.Write("\x1b[?1049l");
|
|
||||||
|
|
||||||
public override void SetBackgroundColor(IColor background)
|
|
||||||
=> Write(background.ToConsoleColor());
|
|
||||||
|
|
||||||
public override void SetForegroundColor(IColor foreground)
|
|
||||||
=> Write(foreground.ToConsoleColor());
|
|
||||||
}
|
|
||||||
52
src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs
Normal file
52
src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using TerminalUI.Color;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
using ConsoleColor = TerminalUI.Color.ConsoleColor;
|
||||||
|
|
||||||
|
namespace TerminalUI.ConsoleDrivers;
|
||||||
|
|
||||||
|
public sealed class XTermDriver : DotnetDriver
|
||||||
|
{
|
||||||
|
private Position _initialCursorPosition;
|
||||||
|
public override bool Init()
|
||||||
|
{
|
||||||
|
_initialCursorPosition = GetCursorPosition();
|
||||||
|
Write("\x1b[?1047h");
|
||||||
|
var isInitSuccessful = _initialCursorPosition == GetCursorPosition();
|
||||||
|
if (isInitSuccessful)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInitSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
Write("\x1b[?1047l");
|
||||||
|
SetCursorPosition(_initialCursorPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetBackgroundColor(IColor background)
|
||||||
|
{
|
||||||
|
if (background is ConsoleColor consoleColor)
|
||||||
|
{
|
||||||
|
Console.BackgroundColor = consoleColor.Color;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(background.ToConsoleColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetForegroundColor(IColor foreground)
|
||||||
|
{
|
||||||
|
if (foreground is ConsoleColor consoleColor)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = consoleColor.Color;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(foreground.ToConsoleColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ public abstract class ContentView<T>: View<T>, IContentRenderer
|
|||||||
ContentRendererMethod = DefaultContentRender;
|
ContentRendererMethod = DefaultContentRender;
|
||||||
}
|
}
|
||||||
public IView? Content { get; set; }
|
public IView? Content { get; set; }
|
||||||
public Action<Position> ContentRendererMethod { get; set; }
|
public Action<Position, Size> ContentRendererMethod { get; set; }
|
||||||
|
|
||||||
private void DefaultContentRender(Position position) => Content?.Render(position);
|
private void DefaultContentRender(Position position, Size size) => Content?.Render(position, size);
|
||||||
}
|
}
|
||||||
248
src/Library/TerminalUI/Controls/Grid.cs
Normal file
248
src/Library/TerminalUI/Controls/Grid.cs
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using TerminalUI.Extensions;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
using TerminalUI.ViewExtensions;
|
||||||
|
|
||||||
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
|
public class Grid<T> : View<T>
|
||||||
|
{
|
||||||
|
private const int ToBeCalculated = -1;
|
||||||
|
private readonly ObservableCollection<IView> _children = new();
|
||||||
|
public ReadOnlyObservableCollection<IView> Children { get; }
|
||||||
|
public GridChildInitializer<T> ChildInitializer { get; }
|
||||||
|
public ObservableCollection<RowDefinition> RowDefinitions { get; } = new();
|
||||||
|
public ObservableCollection<ColumnDefinition> ColumnDefinitions { get; } = new();
|
||||||
|
|
||||||
|
public object? ColumnDefinitionsObject
|
||||||
|
{
|
||||||
|
get => ColumnDefinitions;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is IEnumerable<ColumnDefinition> columnDefinitions)
|
||||||
|
{
|
||||||
|
ColumnDefinitions.Clear();
|
||||||
|
foreach (var columnDefinition in columnDefinitions)
|
||||||
|
{
|
||||||
|
ColumnDefinitions.Add(columnDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (value is string s)
|
||||||
|
{
|
||||||
|
SetColumnDefinitions(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? RowDefinitionsObject
|
||||||
|
{
|
||||||
|
get => RowDefinitions;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is IEnumerable<RowDefinition> rowDefinitions)
|
||||||
|
{
|
||||||
|
RowDefinitions.Clear();
|
||||||
|
foreach (var rowDefinition in rowDefinitions)
|
||||||
|
{
|
||||||
|
RowDefinitions.Add(rowDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (value is string s)
|
||||||
|
{
|
||||||
|
SetRowDefinitions(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Grid()
|
||||||
|
{
|
||||||
|
ChildInitializer = new GridChildInitializer<T>(this);
|
||||||
|
Children = new ReadOnlyObservableCollection<IView>(_children);
|
||||||
|
_children.CollectionChanged += (o, e) =>
|
||||||
|
{
|
||||||
|
if (Attached)
|
||||||
|
{
|
||||||
|
if (e.NewItems?.OfType<IView>() is { } newItems)
|
||||||
|
{
|
||||||
|
foreach (var newItem in newItems)
|
||||||
|
{
|
||||||
|
newItem.Attached = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationContext?.EventLoop.RequestRerender();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Size GetRequestedSize() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
protected override void DefaultRenderer(Position position, Size size)
|
||||||
|
{
|
||||||
|
//TODO: Optimize it, dont calculate all of these only if there is Auto value(s)
|
||||||
|
var columns = ColumnDefinitions.Count;
|
||||||
|
Span<int> allWidth = stackalloc int[columns * RowDefinitions.Count];
|
||||||
|
Span<int> allHeight = stackalloc int[columns * RowDefinitions.Count];
|
||||||
|
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
var childSize = child.GetRequestedSize();
|
||||||
|
var positionExtension = child.GetExtension<GridPositionExtension>();
|
||||||
|
var x = positionExtension?.Column ?? 0;
|
||||||
|
var y = positionExtension?.Row ?? 0;
|
||||||
|
|
||||||
|
allWidth.SetToMatrix(childSize.Width, x, y, columns);
|
||||||
|
allHeight.SetToMatrix(childSize.Height, x, y, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<int> columnWidths = stackalloc int[columns];
|
||||||
|
Span<int> rowHeights = stackalloc int[RowDefinitions.Count];
|
||||||
|
|
||||||
|
for (var i = 0; i < columnWidths.Length; i++)
|
||||||
|
{
|
||||||
|
if (ColumnDefinitions[i].Type == GridUnitType.Pixel)
|
||||||
|
{
|
||||||
|
columnWidths[i] = ColumnDefinitions[i].Value;
|
||||||
|
}
|
||||||
|
else if (ColumnDefinitions[i].Type == GridUnitType.Star)
|
||||||
|
{
|
||||||
|
columnWidths[i] = ToBeCalculated;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var max = 0;
|
||||||
|
for (var j = 0; j < RowDefinitions.Count; j++)
|
||||||
|
{
|
||||||
|
max = Math.Max(max, allWidth.GetFromMatrix(i, j, columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
columnWidths[i] = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < rowHeights.Length; i++)
|
||||||
|
{
|
||||||
|
if (RowDefinitions[i].Type == GridUnitType.Pixel)
|
||||||
|
{
|
||||||
|
rowHeights[i] = RowDefinitions[i].Value;
|
||||||
|
}
|
||||||
|
else if (RowDefinitions[i].Type == GridUnitType.Star)
|
||||||
|
{
|
||||||
|
rowHeights[i] = ToBeCalculated;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var max = 0;
|
||||||
|
for (var j = 0; j < columns; j++)
|
||||||
|
{
|
||||||
|
max = Math.Max(max, allHeight.GetFromMatrix(j, i, columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
rowHeights[i] = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
var childSize = child.GetRequestedSize();
|
||||||
|
var positionExtension = child.GetExtension<GridPositionExtension>();
|
||||||
|
var x = positionExtension?.Column ?? 0;
|
||||||
|
var y = positionExtension?.Row ?? 0;
|
||||||
|
|
||||||
|
var width = columnWidths[x];
|
||||||
|
var height = rowHeights[y];
|
||||||
|
|
||||||
|
var left = 0;
|
||||||
|
var top = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < x; i++)
|
||||||
|
{
|
||||||
|
left += columnWidths[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < y; i++)
|
||||||
|
{
|
||||||
|
top += rowHeights[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
child.Render(new Position(left, top), new Size(width, height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRowDefinitions(string value)
|
||||||
|
{
|
||||||
|
var values = value.Split(' ');
|
||||||
|
RowDefinitions.Clear();
|
||||||
|
|
||||||
|
foreach (var v in values)
|
||||||
|
{
|
||||||
|
if (v == "Auto")
|
||||||
|
{
|
||||||
|
RowDefinitions.Add(RowDefinition.Auto);
|
||||||
|
}
|
||||||
|
else if (v.EndsWith("*"))
|
||||||
|
{
|
||||||
|
var starValue = int.Parse(v[0..^1]);
|
||||||
|
RowDefinitions.Add(RowDefinition.Star(starValue));
|
||||||
|
}
|
||||||
|
else if (int.TryParse(v, out var pixelValue))
|
||||||
|
{
|
||||||
|
RowDefinitions.Add(RowDefinition.Pixel(pixelValue));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid row definition: " + v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetColumnDefinitions(string value)
|
||||||
|
{
|
||||||
|
var values = value.Split(' ');
|
||||||
|
ColumnDefinitions.Clear();
|
||||||
|
|
||||||
|
foreach (var v in values)
|
||||||
|
{
|
||||||
|
if (v == "Auto")
|
||||||
|
{
|
||||||
|
ColumnDefinitions.Add(ColumnDefinition.Auto);
|
||||||
|
}
|
||||||
|
else if (v.EndsWith("*"))
|
||||||
|
{
|
||||||
|
var starValue = int.Parse(v[0..^1]);
|
||||||
|
ColumnDefinitions.Add(ColumnDefinition.Star(starValue));
|
||||||
|
}
|
||||||
|
else if (int.TryParse(v, out var pixelValue))
|
||||||
|
{
|
||||||
|
ColumnDefinitions.Add(ColumnDefinition.Pixel(pixelValue));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid column definition: " + v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TChild AddChild<TChild>(TChild child)
|
||||||
|
{
|
||||||
|
child = base.AddChild(child);
|
||||||
|
_children.Add(child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||||
|
where TDataContext : default
|
||||||
|
{
|
||||||
|
child = base.AddChild(child, dataContextMapper);
|
||||||
|
_children.Add(child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Library/TerminalUI/Controls/GridChildInitializer.cs
Normal file
24
src/Library/TerminalUI/Controls/GridChildInitializer.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
|
public record ChildWithDataContextMapper<TSourceDataContext, TTargetDataContext>(IView<TTargetDataContext> Child, Func<TSourceDataContext?, TTargetDataContext?> DataContextMapper);
|
||||||
|
|
||||||
|
public class GridChildInitializer<T> : IEnumerable<IView>
|
||||||
|
{
|
||||||
|
private readonly Grid<T> _grid;
|
||||||
|
|
||||||
|
public GridChildInitializer(Grid<T> grid)
|
||||||
|
{
|
||||||
|
_grid = grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(IView<T> item) => _grid.AddChild(item);
|
||||||
|
|
||||||
|
public void Add<TDataContext>(ChildWithDataContextMapper<T, TDataContext> item)
|
||||||
|
=> _grid.AddChild(item.Child, item.DataContextMapper);
|
||||||
|
|
||||||
|
public IEnumerator<IView> GetEnumerator() => _grid.Children.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
23
src/Library/TerminalUI/Controls/GridProperties.cs
Normal file
23
src/Library/TerminalUI/Controls/GridProperties.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
|
|
||||||
|
public enum GridUnitType
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
Pixel,
|
||||||
|
Star
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct RowDefinition(GridUnitType Type, int Value)
|
||||||
|
{
|
||||||
|
public static RowDefinition Auto => new(GridUnitType.Auto, 0);
|
||||||
|
public static RowDefinition Pixel(int value) => new(GridUnitType.Pixel, value);
|
||||||
|
public static RowDefinition Star(int value) => new(GridUnitType.Star, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct ColumnDefinition(GridUnitType Type, int Value)
|
||||||
|
{
|
||||||
|
public static ColumnDefinition Auto => new(GridUnitType.Auto, 0);
|
||||||
|
public static ColumnDefinition Pixel(int value) => new(GridUnitType.Pixel, value);
|
||||||
|
public static ColumnDefinition Star(int value) => new(GridUnitType.Star, value);
|
||||||
|
}
|
||||||
@@ -7,10 +7,20 @@ namespace TerminalUI.Controls;
|
|||||||
public interface IView : INotifyPropertyChanged, IDisposableCollection
|
public interface IView : INotifyPropertyChanged, IDisposableCollection
|
||||||
{
|
{
|
||||||
object? DataContext { get; set; }
|
object? DataContext { get; set; }
|
||||||
Action<Position> RenderMethod { get; set; }
|
int? MinWidth { get; set; }
|
||||||
IApplicationContext? ApplicationContext { get; init;}
|
int? MaxWidth { get; set; }
|
||||||
|
int? Width { get; set; }
|
||||||
|
int? MinHeight { get; set; }
|
||||||
|
int? MaxHeight { get; set; }
|
||||||
|
int? Height { get; set; }
|
||||||
|
bool Attached { get; set; }
|
||||||
|
Size GetRequestedSize();
|
||||||
|
IApplicationContext? ApplicationContext { get; set; }
|
||||||
|
List<object> Extensions { get; }
|
||||||
|
|
||||||
|
Action<Position, Size> RenderMethod { get; set; }
|
||||||
event Action<IView> Disposed;
|
event Action<IView> Disposed;
|
||||||
void Render(Position position);
|
void Render(Position position, Size size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IView<T> : IView
|
public interface IView<T> : IView
|
||||||
@@ -28,4 +38,9 @@ public interface IView<T> : IView
|
|||||||
|
|
||||||
TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||||
where TChild : IView<TDataContext>, new();
|
where TChild : IView<TDataContext>, new();
|
||||||
|
|
||||||
|
TChild AddChild<TChild>(TChild child) where TChild : IView<T>;
|
||||||
|
|
||||||
|
TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||||
|
where TChild : IView<TDataContext>;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
using TerminalUI.Models;
|
using TerminalUI.Models;
|
||||||
|
|
||||||
@@ -14,6 +15,23 @@ public class ListView<TDataContext, TItem> : View<TDataContext>
|
|||||||
private object? _itemsSource;
|
private object? _itemsSource;
|
||||||
private ListViewItem<TItem>[]? _listViewItems;
|
private ListViewItem<TItem>[]? _listViewItems;
|
||||||
private int _listViewItemLength;
|
private int _listViewItemLength;
|
||||||
|
private int _selectedIndex = 0;
|
||||||
|
private int _renderStartIndex = 0;
|
||||||
|
private Size _requestedItemSize = new(0, 0);
|
||||||
|
|
||||||
|
public int SelectedIndex
|
||||||
|
{
|
||||||
|
get => _selectedIndex;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_selectedIndex != value)
|
||||||
|
{
|
||||||
|
_selectedIndex = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
ApplicationContext?.EventLoop.RequestRerender();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public object? ItemsSource
|
public object? ItemsSource
|
||||||
{
|
{
|
||||||
@@ -64,13 +82,53 @@ public class ListView<TDataContext, TItem> : View<TDataContext>
|
|||||||
|
|
||||||
public Func<ListViewItem<TItem>, IView?> ItemTemplate { get; set; } = DefaultItemTemplate;
|
public Func<ListViewItem<TItem>, IView?> ItemTemplate { get; set; } = DefaultItemTemplate;
|
||||||
|
|
||||||
protected override void DefaultRenderer(Position position)
|
public override Size GetRequestedSize()
|
||||||
|
{
|
||||||
|
if (_listViewItems is null || _listViewItems.Length == 0)
|
||||||
|
return new Size(0, 0);
|
||||||
|
|
||||||
|
|
||||||
|
var itemSize = _listViewItems[0].GetRequestedSize();
|
||||||
|
_requestedItemSize = itemSize;
|
||||||
|
return itemSize with {Height = itemSize.Height * _listViewItems.Length};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DefaultRenderer(Position position, Size size)
|
||||||
{
|
{
|
||||||
var listViewItems = InstantiateItemViews();
|
var listViewItems = InstantiateItemViews();
|
||||||
var deltaY = 0;
|
if (listViewItems.Length == 0) return;
|
||||||
foreach (var item in listViewItems)
|
|
||||||
|
var requestedItemSize = _requestedItemSize;
|
||||||
|
|
||||||
|
var itemsToRender = listViewItems.Length;
|
||||||
|
var heightNeeded = requestedItemSize.Height * listViewItems.Length;
|
||||||
|
var renderStartIndex = _renderStartIndex;
|
||||||
|
if (heightNeeded < size.Height)
|
||||||
{
|
{
|
||||||
item.Render(position with {PosY = position.PosY + deltaY++});
|
var maxItemsToRender = (int) Math.Floor((double) size.Height / requestedItemSize.Height);
|
||||||
|
if (SelectedIndex < renderStartIndex)
|
||||||
|
{
|
||||||
|
renderStartIndex = SelectedIndex - 1;
|
||||||
|
}
|
||||||
|
else if (SelectedIndex > renderStartIndex + maxItemsToRender)
|
||||||
|
{
|
||||||
|
renderStartIndex = SelectedIndex - maxItemsToRender + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(renderStartIndex < 0)
|
||||||
|
renderStartIndex = 0;
|
||||||
|
else if (renderStartIndex + maxItemsToRender > listViewItems.Length)
|
||||||
|
renderStartIndex = listViewItems.Length - maxItemsToRender;
|
||||||
|
|
||||||
|
_renderStartIndex = renderStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
var deltaY = 0;
|
||||||
|
for (var i = renderStartIndex; i < itemsToRender && i < listViewItems.Length; i++)
|
||||||
|
{
|
||||||
|
var item = listViewItems[i];
|
||||||
|
item.Render(position with {Y = position.Y + deltaY}, requestedItemSize);
|
||||||
|
deltaY += requestedItemSize.Height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,13 @@ namespace TerminalUI.Controls;
|
|||||||
|
|
||||||
public class ListViewItem<T> : ContentView<T>
|
public class ListViewItem<T> : ContentView<T>
|
||||||
{
|
{
|
||||||
protected override void DefaultRenderer(Position position)
|
public override Size GetRequestedSize()
|
||||||
|
{
|
||||||
|
if (Content is null) return new Size(0, 0);
|
||||||
|
return Content.GetRequestedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DefaultRenderer(Position position, Size size)
|
||||||
{
|
{
|
||||||
if (ContentRendererMethod is null)
|
if (ContentRendererMethod is null)
|
||||||
{
|
{
|
||||||
@@ -16,6 +22,6 @@ public class ListViewItem<T> : ContentView<T>
|
|||||||
+ DataContext?.GetType().Name);
|
+ DataContext?.GetType().Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentRendererMethod(position);
|
ContentRendererMethod(position, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
src/Library/TerminalUI/Controls/Rectangle.cs
Normal file
24
src/Library/TerminalUI/Controls/Rectangle.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using TerminalUI.Color;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
|
||||||
|
namespace TerminalUI.Controls;
|
||||||
|
|
||||||
|
public partial class Rectangle<T> : View<T>
|
||||||
|
{
|
||||||
|
[Notify] private IColor? _fill;
|
||||||
|
public override Size GetRequestedSize() => new(Width ?? 0, Height ?? 0);
|
||||||
|
|
||||||
|
protected override void DefaultRenderer(Position position, Size size)
|
||||||
|
{
|
||||||
|
var s = new string('█', Width ?? size.Width);
|
||||||
|
ApplicationContext?.ConsoleDriver.SetBackgroundColor(Fill ?? new Color.ConsoleColor(System.ConsoleColor.Yellow, ColorType.Background));
|
||||||
|
ApplicationContext?.ConsoleDriver.SetForegroundColor(Fill ?? new Color.ConsoleColor(System.ConsoleColor.Yellow, ColorType.Foreground));
|
||||||
|
var height = Height ?? size.Height;
|
||||||
|
for (var i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
ApplicationContext?.ConsoleDriver.SetCursorPosition(position with {Y = position.Y + i});
|
||||||
|
ApplicationContext?.ConsoleDriver.Write(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using PropertyChanged.SourceGenerator;
|
using PropertyChanged.SourceGenerator;
|
||||||
|
using TerminalUI.Color;
|
||||||
using TerminalUI.Extensions;
|
using TerminalUI.Extensions;
|
||||||
using TerminalUI.Models;
|
using TerminalUI.Models;
|
||||||
|
|
||||||
@@ -27,7 +28,9 @@ public partial class TextBlock<T> : View<T>
|
|||||||
RerenderProperties.Add(nameof(Background));
|
RerenderProperties.Add(nameof(Background));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void DefaultRenderer(Position position)
|
public override Size GetRequestedSize() => new(Text?.Length ?? 0, 1);
|
||||||
|
|
||||||
|
protected override void DefaultRenderer(Position position, Size size)
|
||||||
{
|
{
|
||||||
var driver = ApplicationContext!.ConsoleDriver;
|
var driver = ApplicationContext!.ConsoleDriver;
|
||||||
var renderContext = new RenderContext(position, Text, _foreground, _background);
|
var renderContext = new RenderContext(position, Text, _foreground, _background);
|
||||||
|
|||||||
@@ -10,8 +10,30 @@ public abstract partial class View<T> : IView<T>
|
|||||||
{
|
{
|
||||||
private readonly List<IDisposable> _disposables = new();
|
private readonly List<IDisposable> _disposables = new();
|
||||||
[Notify] private T? _dataContext;
|
[Notify] private T? _dataContext;
|
||||||
public Action<Position> RenderMethod { get; set; }
|
[Notify] private int? _minWidth;
|
||||||
public IApplicationContext? ApplicationContext { get; init; }
|
[Notify] private int? _maxWidth;
|
||||||
|
[Notify] private int? _width;
|
||||||
|
[Notify] private int? _minHeight;
|
||||||
|
[Notify] private int? _maxHeight;
|
||||||
|
[Notify] private int? _height;
|
||||||
|
private bool _attached;
|
||||||
|
|
||||||
|
public bool Attached
|
||||||
|
{
|
||||||
|
get => _attached;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_attached == value) return;
|
||||||
|
_attached = value;
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
AttachChildren();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<object> Extensions { get; } = new();
|
||||||
|
public Action<Position, Size> RenderMethod { get; set; }
|
||||||
|
public IApplicationContext? ApplicationContext { get; set; }
|
||||||
public event Action<IView>? Disposed;
|
public event Action<IView>? Disposed;
|
||||||
protected List<string> RerenderProperties { get; } = new();
|
protected List<string> RerenderProperties { get; } = new();
|
||||||
|
|
||||||
@@ -20,6 +42,11 @@ public abstract partial class View<T> : IView<T>
|
|||||||
RenderMethod = DefaultRenderer;
|
RenderMethod = DefaultRenderer;
|
||||||
((INotifyPropertyChanged) this).PropertyChanged += Handle_PropertyChanged;
|
((INotifyPropertyChanged) this).PropertyChanged += Handle_PropertyChanged;
|
||||||
}
|
}
|
||||||
|
public abstract Size GetRequestedSize();
|
||||||
|
|
||||||
|
protected virtual void AttachChildren()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private void Handle_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private void Handle_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -27,15 +54,15 @@ public abstract partial class View<T> : IView<T>
|
|||||||
&& (e.PropertyName == nameof(IView.DataContext)
|
&& (e.PropertyName == nameof(IView.DataContext)
|
||||||
|| RerenderProperties.Contains(e.PropertyName)
|
|| RerenderProperties.Contains(e.PropertyName)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ApplicationContext?.EventLoop.RequestRerender();
|
ApplicationContext?.EventLoop.RequestRerender();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void DefaultRenderer(Position position);
|
protected abstract void DefaultRenderer(Position position, Size size);
|
||||||
|
|
||||||
public void Render(Position position)
|
public void Render(Position position, Size size)
|
||||||
{
|
{
|
||||||
if (RenderMethod is null)
|
if (RenderMethod is null)
|
||||||
{
|
{
|
||||||
@@ -47,16 +74,27 @@ public abstract partial class View<T> : IView<T>
|
|||||||
+ DataContext?.GetType().Name);
|
+ DataContext?.GetType().Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderMethod(position);
|
RenderMethod(position, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TChild CreateChild<TChild>() where TChild : IView<T>, new()
|
public TChild CreateChild<TChild>() where TChild : IView<T>, new()
|
||||||
{
|
{
|
||||||
var child = new TChild
|
var child = new TChild();
|
||||||
{
|
return AddChild(child);
|
||||||
DataContext = DataContext,
|
}
|
||||||
ApplicationContext = ApplicationContext
|
|
||||||
};
|
public TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||||
|
where TChild : IView<TDataContext>, new()
|
||||||
|
{
|
||||||
|
var child = new TChild();
|
||||||
|
return AddChild(child, dataContextMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual TChild AddChild<TChild>(TChild child) where TChild : IView<T>
|
||||||
|
{
|
||||||
|
child.DataContext = DataContext;
|
||||||
|
child.ApplicationContext = ApplicationContext;
|
||||||
|
|
||||||
var mapper = new DataContextMapper<T>(this, d => child.DataContext = d);
|
var mapper = new DataContextMapper<T>(this, d => child.DataContext = d);
|
||||||
AddDisposable(mapper);
|
AddDisposable(mapper);
|
||||||
child.AddDisposable(mapper);
|
child.AddDisposable(mapper);
|
||||||
@@ -64,14 +102,12 @@ public abstract partial class View<T> : IView<T>
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
public virtual TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||||
where TChild : IView<TDataContext>, new()
|
where TChild : IView<TDataContext>
|
||||||
{
|
{
|
||||||
var child = new TChild
|
child.DataContext = dataContextMapper(DataContext);
|
||||||
{
|
child.ApplicationContext = ApplicationContext;
|
||||||
DataContext = dataContextMapper(DataContext),
|
|
||||||
ApplicationContext = ApplicationContext
|
|
||||||
};
|
|
||||||
var mapper = new DataContextMapper<T>(this, d => child.DataContext = dataContextMapper(d));
|
var mapper = new DataContextMapper<T>(this, d => child.DataContext = dataContextMapper(d));
|
||||||
AddDisposable(mapper);
|
AddDisposable(mapper);
|
||||||
child.AddDisposable(mapper);
|
child.AddDisposable(mapper);
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ public class EventLoop : IEventLoop
|
|||||||
viewsToRender = _viewsToRender.ToList();
|
viewsToRender = _viewsToRender.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var size =_applicationContext.ConsoleDriver.GetBufferSize();
|
||||||
foreach (var view in viewsToRender)
|
foreach (var view in viewsToRender)
|
||||||
{
|
{
|
||||||
view.Render(new Position(0, 0));
|
view.Attached = true;
|
||||||
|
view.Render(new Position(0, 0), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
src/Library/TerminalUI/Extensions/SpanExtensions.cs
Normal file
7
src/Library/TerminalUI/Extensions/SpanExtensions.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace TerminalUI.Extensions;
|
||||||
|
|
||||||
|
public static class SpanExtensions
|
||||||
|
{
|
||||||
|
public static T GetFromMatrix<T>(this Span<T> span, int x, int y, int width) => span[y * width + x];
|
||||||
|
public static void SetToMatrix<T>(this Span<T> span, T value, int x, int y, int width) => span[y * width + x] = value;
|
||||||
|
}
|
||||||
14
src/Library/TerminalUI/Extensions/ViewExtensions.cs
Normal file
14
src/Library/TerminalUI/Extensions/ViewExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using TerminalUI.Controls;
|
||||||
|
|
||||||
|
namespace TerminalUI.Extensions;
|
||||||
|
|
||||||
|
public static class ViewExtensions
|
||||||
|
{
|
||||||
|
public static T? GetExtension<T>(this IView view)
|
||||||
|
=> (T?) view.Extensions.FirstOrDefault(e => e is T);
|
||||||
|
|
||||||
|
public static ChildWithDataContextMapper<TSourceDataContext, TTargetDataContext> WithDataContextMapper<TSourceDataContext, TTargetDataContext>(
|
||||||
|
this IView<TTargetDataContext> view,
|
||||||
|
Func<TSourceDataContext?, TTargetDataContext?> dataContextMapper)
|
||||||
|
=> new(view, dataContextMapper);
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
namespace TerminalUI.Models;
|
namespace TerminalUI.Models;
|
||||||
|
|
||||||
public record struct Position(int PosX, int PosY);
|
public record struct Position(int X, int Y);
|
||||||
3
src/Library/TerminalUI/Models/Size.cs
Normal file
3
src/Library/TerminalUI/Models/Size.cs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
namespace TerminalUI.Models;
|
||||||
|
|
||||||
|
public record Size(int Width, int Height);
|
||||||
12
src/Library/TerminalUI/Rendering/RenderingEngine.cs
Normal file
12
src/Library/TerminalUI/Rendering/RenderingEngine.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using TerminalUI.Controls;
|
||||||
|
using TerminalUI.Models;
|
||||||
|
|
||||||
|
namespace TerminalUI.Rendering;
|
||||||
|
|
||||||
|
public class RenderingEngine
|
||||||
|
{
|
||||||
|
public static void Asd(IView root, Size bufferSize)
|
||||||
|
{
|
||||||
|
var rootSize = root.GetRequestedSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,5 +6,5 @@ namespace TerminalUI.Traits;
|
|||||||
public interface IContentRenderer
|
public interface IContentRenderer
|
||||||
{
|
{
|
||||||
IView? Content { get; set; }
|
IView? Content { get; set; }
|
||||||
Action<Position> ContentRendererMethod { get; set; }
|
Action<Position, Size> ContentRendererMethod { get; set; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace TerminalUI.ViewExtensions;
|
||||||
|
|
||||||
|
public record GridPositionExtension(int Row, int Column);
|
||||||
@@ -56,11 +56,11 @@ void HandleStartup(Action action)
|
|||||||
IConfigurationRoot CreateConfiguration()
|
IConfigurationRoot CreateConfiguration()
|
||||||
{
|
{
|
||||||
var configurationBuilder = new ConfigurationBuilder();
|
var configurationBuilder = new ConfigurationBuilder();
|
||||||
configurationBuilder.AddCommandLine(args);
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true);
|
configurationBuilder.AddJsonFile("appsettings.Development.json", optional: true);
|
||||||
configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true);
|
configurationBuilder.AddJsonFile("appsettings.Local.json", optional: true);
|
||||||
#endif
|
#endif
|
||||||
|
configurationBuilder.AddCommandLine(args);
|
||||||
return configurationBuilder.Build();
|
return configurationBuilder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ public class CompressionUserCommandHandler : AggregatedUserCommandHandler
|
|||||||
IClipboardService clipboardService)
|
IClipboardService clipboardService)
|
||||||
{
|
{
|
||||||
_clipboardService = clipboardService;
|
_clipboardService = clipboardService;
|
||||||
_markedItems = appState.SelectedTab.Map(t => t.MarkedItems).Switch();
|
_markedItems = appState.SelectedTab.Map(t => t?.MarkedItems).Switch();
|
||||||
_selectedItem = appState.SelectedTab.Map(t => t.CurrentSelectedItem).Switch();
|
_selectedItem = appState.SelectedTab.Map(t => t?.CurrentSelectedItem).Switch();
|
||||||
|
|
||||||
AddCommandHandler(new[]
|
AddCommandHandler(new[]
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user