From af140ff6b454f7d92b6b135f95ce8774de20e386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Wed, 9 Aug 2023 23:24:30 +0200 Subject: [PATCH] Console improvements, info providers --- .../ViewModels/ITabViewModel.cs | 2 +- .../Configuration/MainConfiguration.cs | 18 +-- .../ViewModels/TabViewModel.cs | 4 +- .../IColorSampleProvider.cs | 7 + .../ITheme.cs | 2 +- .../IKeyInputHandlerService.cs | 2 +- src/ConsoleApp/FileTime.ConsoleUI.App/App.cs | 12 +- .../KeyInputHandlerService.cs | 6 +- .../FileTime.ConsoleUI.App/MainWindow.cs | 65 +++++++-- .../FileTime.ConsoleUI.Styles/DefaultTheme.cs | 20 ++- src/ConsoleApp/FileTime.ConsoleUI/DI.cs | 1 + .../InfoProviders/ColorSchema.cs | 129 ++++++++++++++++++ .../{ => InfoProviders}/Help.cs | 15 +- src/ConsoleApp/FileTime.ConsoleUI/Program.cs | 37 ++++- src/ConsoleApp/FileTime.ConsoleUI/Startup.cs | 2 - .../Views/MainWindow.axaml | 2 +- .../Avalonia/FileTime.GuiApp/Program.cs | 1 + .../Avalonia/FileTime.GuiApp/Startup.cs | 1 + src/Library/TerminalUI/Color/Colors.cs | 38 +++--- src/Library/TerminalUI/Controls/Grid.cs | 15 ++ src/Library/TerminalUI/Controls/ListView.cs | 96 ++++++++++++- 21 files changed, 406 insertions(+), 69 deletions(-) create mode 100644 src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/IColorSampleProvider.cs create mode 100644 src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/ColorSchema.cs rename src/ConsoleApp/FileTime.ConsoleUI/{ => InfoProviders}/Help.cs (52%) diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs index 8b5df3b..f18531d 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs @@ -12,7 +12,7 @@ public interface ITabViewModel : IInitable, IDisposable { ITab? Tab { get; } int TabNumber { get; } - IObservable IsSelected { get; } + IDeclarativeProperty IsSelected { get; } IDeclarativeProperty CurrentLocation { get; } IDeclarativeProperty CurrentSelectedItem { get; } IDeclarativeProperty CurrentSelectedItemAsContainer { get; } diff --git a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs index a968eec..e2f024f 100644 --- a/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs +++ b/src/AppCommon/FileTime.App.Core/Configuration/MainConfiguration.cs @@ -103,15 +103,15 @@ public class MainConfiguration //new CommandBindingConfiguration(ConfigCommand.ShowAllShortcut, Keys.F1), new(DeleteCommand.SoftDeleteCommandName, new[] {new KeyConfig(Keys.D), new KeyConfig(Keys.D, shift: true)}), new(IdentifiableSearchCommand.SearchByNameContainsCommandName, new[] {Keys.S, Keys.N}), - new(SwitchToTabCommand.SwitchToLastTabCommandName, new[] {new KeyConfig(Keys.F9, alt: true)}), - new(SwitchToTabCommand.SwitchToTab1CommandName, new[] {new KeyConfig(Keys.F1, alt: true)}), - new(SwitchToTabCommand.SwitchToTab2CommandName, new[] {new KeyConfig(Keys.F2, alt: true)}), - new(SwitchToTabCommand.SwitchToTab3CommandName, new[] {new KeyConfig(Keys.F3, alt: true)}), - new(SwitchToTabCommand.SwitchToTab4CommandName, new[] {new KeyConfig(Keys.F4, alt: true)}), - new(SwitchToTabCommand.SwitchToTab5CommandName, new[] {new KeyConfig(Keys.F5, alt: true)}), - new(SwitchToTabCommand.SwitchToTab6CommandName, new[] {new KeyConfig(Keys.F6, alt: true)}), - new(SwitchToTabCommand.SwitchToTab7CommandName, new[] {new KeyConfig(Keys.F7, alt: true)}), - new(SwitchToTabCommand.SwitchToTab8CommandName, new[] {new KeyConfig(Keys.F8, alt: true)}), + new(SwitchToTabCommand.SwitchToLastTabCommandName, new[] {new KeyConfig(Keys.Num9, alt: true)}), + new(SwitchToTabCommand.SwitchToTab1CommandName, new[] {new KeyConfig(Keys.Num1, alt: true)}), + new(SwitchToTabCommand.SwitchToTab2CommandName, new[] {new KeyConfig(Keys.Num2, alt: true)}), + new(SwitchToTabCommand.SwitchToTab3CommandName, new[] {new KeyConfig(Keys.Num3, alt: true)}), + new(SwitchToTabCommand.SwitchToTab4CommandName, new[] {new KeyConfig(Keys.Num4, alt: true)}), + new(SwitchToTabCommand.SwitchToTab5CommandName, new[] {new KeyConfig(Keys.Num5, alt: true)}), + new(SwitchToTabCommand.SwitchToTab6CommandName, new[] {new KeyConfig(Keys.Num6, alt: true)}), + new(SwitchToTabCommand.SwitchToTab7CommandName, new[] {new KeyConfig(Keys.Num7, alt: true)}), + new(SwitchToTabCommand.SwitchToTab8CommandName, new[] {new KeyConfig(Keys.Num8, alt: true)}), new(PauseCommandSchedulerCommand.CommandName, new[] {Keys.T, Keys.P}), //new CommandBindingConfiguration(ConfigCommand.TimelineRefresh, new[] { Keys.T, Keys.R }), new(StartCommandSchedulerCommand.CommandName, new[] {Keys.T, Keys.S}), diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs index 82cbf7f..919e5e1 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs @@ -38,7 +38,7 @@ public partial class TabViewModel : ITabViewModel public ITab? Tab { get; private set; } public int TabNumber { get; private set; } - public IObservable IsSelected { get; } + public IDeclarativeProperty IsSelected { get; } public IDeclarativeProperty CurrentLocation { get; private set; } public IDeclarativeProperty CurrentSelectedItem { get; private set; } @@ -60,7 +60,7 @@ public partial class TabViewModel : ITabViewModel _appState = appState; MarkedItems = _markedItems.Watch(); - IsSelected = _appState.SelectedTab.Select(s => s == this); + IsSelected = _appState.SelectedTab.Map(s => s == this); _timelessContentProvider = timelessContentProvider; _refreshSmoothnessCalculator = refreshSmoothnessCalculator; } diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/IColorSampleProvider.cs b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/IColorSampleProvider.cs new file mode 100644 index 0000000..9faeeea --- /dev/null +++ b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/IColorSampleProvider.cs @@ -0,0 +1,7 @@ +namespace FileTime.ConsoleUI.App; + +public interface IColorSampleProvider +{ + public Type? ForegroundColors { get; } + public Type? BackgroundColors { get; } +} \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/ITheme.cs b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/ITheme.cs index c9633ba..4e21269 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/ITheme.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/ITheme.cs @@ -1,5 +1,4 @@ using TerminalUI.Color; -using TerminalUI.Models; namespace FileTime.ConsoleUI.App; @@ -10,4 +9,5 @@ public interface ITheme IColor? ElementColor { get; } IColor? ContainerColor { get; } IColor? MarkedItemColor { get; } + IColor? SelectedTabBackgroundColor { get; } } \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/KeyInputHandling/IKeyInputHandlerService.cs b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/KeyInputHandling/IKeyInputHandlerService.cs index 2bb8d8a..e2ca6b8 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/KeyInputHandling/IKeyInputHandlerService.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App.Abstractions/KeyInputHandling/IKeyInputHandlerService.cs @@ -4,5 +4,5 @@ namespace FileTime.ConsoleUI.App.KeyInputHandling; public interface IKeyInputHandlerService { - void HandleKeyInput(GeneralKeyEventArgs keyEvent); + void HandleKeyInput(GeneralKeyEventArgs keyEvent, SpecialKeysStatus specialKeysStatus); } \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/App.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/App.cs index 5c034c9..0dc486f 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/App.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/App.cs @@ -50,7 +50,7 @@ public class App : IApplication ((INotifyCollectionChanged) _appState.Tabs).CollectionChanged += (_, _) => { - if(_appState.Tabs.Count == 0) + if (_appState.Tabs.Count == 0) _applicationContext.IsRunning = false; }; @@ -68,13 +68,21 @@ public class App : IApplication if (_consoleDriver.CanRead()) { var key = _consoleDriver.ReadKey(); + if (_appKeyService.MapKey(key.Key) is { } mappedKey) { + SpecialKeysStatus specialKeysStatus = new( + (key.Modifiers & ConsoleModifiers.Alt) != 0, + (key.Modifiers & ConsoleModifiers.Shift) != 0, + (key.Modifiers & ConsoleModifiers.Control) != 0 + ); + var keyEventArgs = new GeneralKeyEventArgs { Key = mappedKey }; - _keyInputHandlerService.HandleKeyInput(keyEventArgs); + + _keyInputHandlerService.HandleKeyInput(keyEventArgs, specialKeysStatus); } } diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/KeyInputHandling/KeyInputHandlerService.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/KeyInputHandling/KeyInputHandlerService.cs index 2eda004..7baf10a 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/KeyInputHandling/KeyInputHandlerService.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/KeyInputHandling/KeyInputHandlerService.cs @@ -10,9 +10,6 @@ public class KeyInputHandlerService : IKeyInputHandlerService private readonly IAppState _appState; private readonly IDefaultModeKeyInputHandler _defaultModeKeyInputHandler; private readonly IRapidTravelModeKeyInputHandler _rapidTravelModeKeyInputHandler; - bool _isCtrlPressed = false; - bool _isShiftPressed = false; - bool _isAltPressed = false; public KeyInputHandlerService( IAppState appState, @@ -24,9 +21,8 @@ public class KeyInputHandlerService : IKeyInputHandlerService _rapidTravelModeKeyInputHandler = rapidTravelModeKeyInputHandler; } - public void HandleKeyInput(GeneralKeyEventArgs keyEvent) + public void HandleKeyInput(GeneralKeyEventArgs keyEvent, SpecialKeysStatus specialKeysStatus) { - var specialKeysStatus = new SpecialKeysStatus(_isAltPressed, _isShiftPressed, _isCtrlPressed); if (_appState.ViewMode.Value == ViewMode.Default) { Task.Run(async () => await _defaultModeKeyInputHandler.HandleInputKey(keyEvent, specialKeysStatus)).Wait(); diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs index 832d559..3faa617 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs @@ -6,6 +6,7 @@ using TerminalUI; using TerminalUI.Color; using TerminalUI.Controls; using TerminalUI.Extensions; +using TerminalUI.Models; using TerminalUI.ViewExtensions; using ConsoleColor = TerminalUI.Color.ConsoleColor; @@ -38,15 +39,23 @@ public class MainWindow RowDefinitionsObject = "Auto *", ChildInitializer = { - new TextBlock() - .Setup(t => - t.Bind( - t, - appState => appState.SelectedTab.Value.CurrentLocation.Value.FullName.Path, - tb => tb.Text, - value => value - ) - ), + new Grid + { + ColumnDefinitionsObject = "* Auto", + ChildInitializer = + { + new TextBlock() + .Setup(t => + t.Bind( + t, + appState => appState.SelectedTab.Value.CurrentLocation.Value.FullName.Path, + tb => tb.Text, + value => value + ) + ), + TabControl() + } + }, new Grid { ColumnDefinitionsObject = "* 4* 4*", @@ -66,6 +75,44 @@ public class MainWindow _root = root; } + private IView TabControl() + { + var tabList = new ListView + { + Orientation = Orientation.Horizontal, + Extensions = + { + new GridPositionExtension(1, 0) + }, + ItemTemplate = item => + { + var textBlock = item.CreateChild>(); + textBlock.Foreground = _theme.DefaultForegroundColor; + + textBlock.Bind( + textBlock, + dc => dc.TabNumber.ToString(), + tb => tb.Text, + fallbackValue: "?"); + + textBlock.Bind( + textBlock, + dc => dc.IsSelected.Value ? _theme.SelectedTabBackgroundColor : null, + tb => tb.Background, + fallbackValue: null + ); + return textBlock; + } + }; + + tabList.Bind( + tabList, + appState => appState == null ? null : appState.Tabs, + v => v.ItemsSource); + + return tabList; + } + private ListView SelectedItemsView() { var list = new ListView diff --git a/src/ConsoleApp/FileTime.ConsoleUI.Styles/DefaultTheme.cs b/src/ConsoleApp/FileTime.ConsoleUI.Styles/DefaultTheme.cs index a7de4f1..6ab5533 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.Styles/DefaultTheme.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.Styles/DefaultTheme.cs @@ -8,22 +8,32 @@ public record Theme( IColor? DefaultBackgroundColor, IColor? ElementColor, IColor? ContainerColor, - IColor? MarkedItemColor) : ITheme; + IColor? MarkedItemColor, + IColor? SelectedTabBackgroundColor, + Type? ForegroundColors, + Type? BackgroundColors) : ITheme, IColorSampleProvider; public static class DefaultThemes { public static Theme Color256Theme => new( DefaultForegroundColor: Color256Colors.Foregrounds.Gray, - DefaultBackgroundColor: Color256Colors.Foregrounds.Black, + DefaultBackgroundColor: null, ElementColor: Color256Colors.Foregrounds.Gray, ContainerColor: Color256Colors.Foregrounds.Blue, - MarkedItemColor: Color256Colors.Foregrounds.Black + MarkedItemColor: Color256Colors.Foregrounds.Black, + SelectedTabBackgroundColor: Color256Colors.Backgrounds.Green, + ForegroundColors: typeof(Color256Colors.Foregrounds), + BackgroundColors: typeof(Color256Colors.Backgrounds) ); public static Theme ConsoleColorTheme => new( DefaultForegroundColor: ConsoleColors.Foregrounds.Gray, - DefaultBackgroundColor: ConsoleColors.Foregrounds.Black, + DefaultBackgroundColor: ConsoleColors.Backgrounds.Black, ElementColor: ConsoleColors.Foregrounds.Gray, ContainerColor: ConsoleColors.Foregrounds.Blue, - MarkedItemColor: ConsoleColors.Foregrounds.Black); + MarkedItemColor: ConsoleColors.Foregrounds.Black, + SelectedTabBackgroundColor: ConsoleColors.Backgrounds.Green, + ForegroundColors: typeof(ConsoleColors.Foregrounds), + BackgroundColors: typeof(ConsoleColors.Backgrounds) + ); } \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI/DI.cs b/src/ConsoleApp/FileTime.ConsoleUI/DI.cs index 3b30423..68de79a 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/DI.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI/DI.cs @@ -49,6 +49,7 @@ public static class DI .WriteTo.File( Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), fileSizeLimitBytes: 10 * 1024 * 1024, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true) .WriteTo.Sink(serviceProvider.GetRequiredService()); diff --git a/src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/ColorSchema.cs b/src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/ColorSchema.cs new file mode 100644 index 0000000..db4b765 --- /dev/null +++ b/src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/ColorSchema.cs @@ -0,0 +1,129 @@ +using FileTime.ConsoleUI.App; +using TerminalUI.Color; +using TerminalUI.ConsoleDrivers; +using TerminalUI.Models; + +namespace FileTime.ConsoleUI.InfoProviders; + +public static class ColorSchema +{ + private const int ColorTextMargin = 5; + + public static void PrintColorSchema(ITheme theme, IConsoleDriver consoleDriver) + { + consoleDriver.Dispose(); + consoleDriver.ResetColor(); + PrintThemeColors(theme, consoleDriver); + + if (theme is IColorSampleProvider colorSampleProvider) + PrintColorPalette(colorSampleProvider, consoleDriver); + } + + private static void PrintThemeColors(ITheme theme, IConsoleDriver consoleDriver) + { + consoleDriver.Write("Theme colors:" + Environment.NewLine); + + var colorType = typeof(IColor); + var colorProperties = typeof(ITheme) + .GetProperties() + .Where(p => p.PropertyType.IsAssignableTo(colorType)) + .OrderBy(p => p.Name) + .ToList(); + + if (colorProperties.Count == 0) + { + consoleDriver.Write("No colors properties found"); + return; + } + + var colorTextStartX = colorProperties.Max(p => p.Name.Length) + ColorTextMargin; + + foreach (var colorProperty in colorProperties) + { + var color = colorProperty.GetValue(theme) as IColor; + + PrintColor(consoleDriver, colorProperty.Name, color, colorTextStartX); + } + + consoleDriver.ResetColor(); + consoleDriver.Write(Environment.NewLine); + } + + private static void PrintColorPalette(IColorSampleProvider colorSampleProvider, IConsoleDriver consoleDriver) + { + if (colorSampleProvider.ForegroundColors is { } foregroundColors) + { + PrintColorPalette("foreground", foregroundColors, consoleDriver); + } + + if (colorSampleProvider.BackgroundColors is { } backgroundColors) + { + PrintColorPalette("background", backgroundColors, consoleDriver); + } + } + + private static void PrintColorPalette(string paletteName, Type colorPalette, IConsoleDriver consoleDriver) + { + var colorType = typeof(IColor); + var colorFields = colorPalette + .GetFields() + .Where(f => f.FieldType.IsAssignableTo(colorType) && f.IsStatic) + .ToDictionary(f => f.Name, f => (IColor?) f.GetValue(null)); + var colorProperties = colorPalette + .GetProperties() + .Where(p => p.PropertyType.IsAssignableTo(colorType) && (p.GetMethod?.IsStatic ?? false)) + .ToDictionary(p => p.Name, p => (IColor?) p.GetValue(null)); + + var colors = colorFields + .Concat(colorProperties) + .OrderBy(v => v.Key) + .ToDictionary(k => k.Key, v => v.Value); + + consoleDriver.Write("Color theme for " + paletteName + Environment.NewLine); + + if (colors.Count == 0) + { + consoleDriver.Write("No colors found"); + consoleDriver.Write(Environment.NewLine); + return; + } + + var colorTextStartX = colors.Max(p => p.Key.Length) + ColorTextMargin; + foreach (var (key, value) in colors) + { + PrintColor(consoleDriver, key, value, colorTextStartX); + } + + consoleDriver.ResetColor(); + consoleDriver.Write(Environment.NewLine); + } + + private static void PrintColor(IConsoleDriver consoleDriver, string name, IColor? color, int colorTextStartX) + { + consoleDriver.ResetColor(); + consoleDriver.Write(name + ":"); + var y = consoleDriver.GetCursorPosition().Y; + consoleDriver.SetCursorPosition(new Position(colorTextStartX, y)); + + if (color is null) + { + consoleDriver.Write(""); + } + else + { + if (color.Type == ColorType.Foreground) + { + consoleDriver.SetForegroundColor(color); + } + else + { + consoleDriver.SetBackgroundColor(color); + } + + consoleDriver.Write("Sample text"); + } + + consoleDriver.ResetColor(); + consoleDriver.Write(Environment.NewLine); + } +} \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI/Help.cs b/src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/Help.cs similarity index 52% rename from src/ConsoleApp/FileTime.ConsoleUI/Help.cs rename to src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/Help.cs index db47f74..522d9b5 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/Help.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI/InfoProviders/Help.cs @@ -2,23 +2,30 @@ using FileTime.App.Core.Configuration; using FileTime.ConsoleUI.App.Configuration; -namespace FileTime.ConsoleUI; +namespace FileTime.ConsoleUI.InfoProviders; public static class Help { - public static void PrintHelp() + public static void PrintHelp(IEnumerable infoProvidersKeys) { StringBuilder sb = new(); sb.AppendLine("Options:"); PrintDriverOption(sb); + sb.AppendLine(); + sb.AppendLine("Info providers:"); + foreach (var infoProviderKey in infoProvidersKeys.Order()) + { + sb.AppendLine("\t" + infoProviderKey); + } + Console.Write(sb.ToString()); } - public static void PrintDriverOption(StringBuilder sb) + private static void PrintDriverOption(StringBuilder sb) { sb.AppendLine($"--{SectionNames.ApplicationSectionName}.{nameof(ConsoleApplicationConfiguration.ConsoleDriver)}"); - foreach (var driver in Startup.Drivers.Keys) + foreach (var driver in Startup.Drivers.Keys.Order()) { sb.AppendLine("\t" + driver); } diff --git a/src/ConsoleApp/FileTime.ConsoleUI/Program.cs b/src/ConsoleApp/FileTime.ConsoleUI/Program.cs index df3ced5..9a5e3c9 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/Program.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI/Program.cs @@ -3,18 +3,13 @@ using FileTime.App.Core; using FileTime.App.Core.Configuration; using FileTime.ConsoleUI; using FileTime.ConsoleUI.App; +using FileTime.ConsoleUI.InfoProviders; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Debugging; using TerminalUI.ConsoleDrivers; -if(args.Contains("--help")) -{ - Help.PrintHelp(); - return; -} - IConsoleDriver? driver = null; (AppDataRoot, EnvironmentName) = Init.InitDevelopment(); @@ -25,8 +20,11 @@ try var serviceProvider = DI.Initialize(configuration); + if (HandleInfoProviders(args, serviceProvider)) return; + driver = serviceProvider.GetRequiredService(); Log.Logger.Debug("Using driver {Driver}", driver.GetType().Name); + driver.SetCursorVisible(false); var app = serviceProvider.GetRequiredService(); @@ -54,6 +52,7 @@ static void InitLogging() .WriteTo.File( Path.Combine(logFolder, "appLog.log"), fileSizeLimitBytes: 10 * 1024 * 1024, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true) .CreateBootstrapLogger(); @@ -72,6 +71,32 @@ static IConfigurationRoot CreateConfiguration(string[] strings) return configurationRoot; } +static bool HandleInfoProviders(string[] args, IServiceProvider serviceProvider) +{ + Dictionary infoProviders = new() + { + { + "--info=colors", + () => ColorSchema.PrintColorSchema( + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService() + ) + }, + }; + infoProviders.Add("--help", () => Help.PrintHelp(infoProviders.Keys)); + foreach (var infoProviderKey in infoProviders.Keys) + { + if (args.Contains(infoProviderKey)) + { + infoProviders[infoProviderKey](); + return true; + } + } + + + return false; +} + public partial class Program { public static string AppDataRoot { get; private set; } diff --git a/src/ConsoleApp/FileTime.ConsoleUI/Startup.cs b/src/ConsoleApp/FileTime.ConsoleUI/Startup.cs index 35d517f..966f994 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI/Startup.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI/Startup.cs @@ -32,8 +32,6 @@ public static class Startup if (driver == null) { driver = new XTermDriver(); - var asd = driver.GetCursorPosition(); - driver.Init(); if (!driver.Init()) { driver = new DotnetDriver(); diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Views/MainWindow.axaml b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Views/MainWindow.axaml index f17274b..a1f0ab6 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Views/MainWindow.axaml +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Views/MainWindow.axaml @@ -426,7 +426,7 @@ + IsVisible="{Binding IsSelected.Value}" /> diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs index 9a26c92..e599a5a 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Program.cs @@ -35,6 +35,7 @@ public static class Program .WriteTo.File( Path.Combine(logFolder, "appLog.log"), fileSizeLimitBytes: 10 * 1024 * 1024, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true) .CreateBootstrapLogger(); diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs index 111f135..8e1e0dc 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Startup.cs @@ -94,6 +94,7 @@ public static class Startup .WriteTo.File( Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), fileSizeLimitBytes: 10 * 1024 * 1024, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true) .WriteTo.Sink(serviceProvider.GetRequiredService()); diff --git a/src/Library/TerminalUI/Color/Colors.cs b/src/Library/TerminalUI/Color/Colors.cs index f74e082..bdf9b6a 100644 --- a/src/Library/TerminalUI/Color/Colors.cs +++ b/src/Library/TerminalUI/Color/Colors.cs @@ -5,38 +5,40 @@ public static class Color256Colors public static class Backgrounds { public static readonly Color256 Black = new(0, ColorType.Background); - public static readonly Color256 Blue = new(9, ColorType.Background); - public static readonly Color256 Cyan = new(11, ColorType.Background); - public static readonly Color256 DarkBlue = new(1, ColorType.Background); - public static readonly Color256 DarkCyan = new(3, ColorType.Background); + public static readonly Color256 Blue = new(12, ColorType.Background); + public static readonly Color256 Cyan = new(14, ColorType.Background); + public static readonly Color256 DarkBlue = new(4, ColorType.Background); + public static readonly Color256 DarkCyan = new(6, ColorType.Background); public static readonly Color256 DarkGray = new(8, ColorType.Background); public static readonly Color256 DarkGreen = new(2, ColorType.Background); public static readonly Color256 DarkMagenta = new(5, ColorType.Background); - public static readonly Color256 DarkRed = new(4, ColorType.Background); - public static readonly Color256 DarkYellow = new(6, ColorType.Background); - public static readonly Color256 Gray = new(7, ColorType.Background); + public static readonly Color256 DarkRed = new(1, ColorType.Background); + public static readonly Color256 DarkYellow = new(3, ColorType.Background); + public static readonly Color256 Gray = new(15, ColorType.Background); public static readonly Color256 Green = new(10, ColorType.Background); public static readonly Color256 Magenta = new(13, ColorType.Background); - public static readonly Color256 Red = new(12, ColorType.Background); - public static readonly Color256 White = new(15, ColorType.Background); + public static readonly Color256 Red = new(9, ColorType.Background); + public static readonly Color256 White = new(7, ColorType.Background); + public static readonly Color256 Yellow = new(11, ColorType.Background); } public static class Foregrounds { public static readonly Color256 Black = new(0, ColorType.Foreground); - public static readonly Color256 Blue = new(9, ColorType.Foreground); - public static readonly Color256 Cyan = new(11, ColorType.Foreground); - public static readonly Color256 DarkBlue = new(1, ColorType.Foreground); - public static readonly Color256 DarkCyan = new(3, ColorType.Foreground); + public static readonly Color256 Blue = new(12, ColorType.Foreground); + public static readonly Color256 Cyan = new(14, ColorType.Foreground); + public static readonly Color256 DarkBlue = new(4, ColorType.Foreground); + public static readonly Color256 DarkCyan = new(6, ColorType.Foreground); public static readonly Color256 DarkGray = new(8, ColorType.Foreground); public static readonly Color256 DarkGreen = new(2, ColorType.Foreground); public static readonly Color256 DarkMagenta = new(5, ColorType.Foreground); - public static readonly Color256 DarkRed = new(4, ColorType.Foreground); - public static readonly Color256 DarkYellow = new(6, ColorType.Foreground); - public static readonly Color256 Gray = new(7, ColorType.Foreground); + public static readonly Color256 DarkRed = new(1, ColorType.Foreground); + public static readonly Color256 DarkYellow = new(3, ColorType.Foreground); + public static readonly Color256 Gray = new(15, ColorType.Foreground); public static readonly Color256 Green = new(10, ColorType.Foreground); public static readonly Color256 Magenta = new(13, ColorType.Foreground); - public static readonly Color256 Red = new(12, ColorType.Foreground); - public static readonly Color256 White = new(15, ColorType.Foreground); + public static readonly Color256 Red = new(9, ColorType.Foreground); + public static readonly Color256 White = new(7, ColorType.Foreground); + public static readonly Color256 Yellow = new(11, ColorType.Background); } } diff --git a/src/Library/TerminalUI/Controls/Grid.cs b/src/Library/TerminalUI/Controls/Grid.cs index 9de5840..bbc5925 100644 --- a/src/Library/TerminalUI/Controls/Grid.cs +++ b/src/Library/TerminalUI/Controls/Grid.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using Microsoft.Extensions.Logging; using TerminalUI.Extensions; using TerminalUI.Models; using TerminalUI.ViewExtensions; @@ -7,6 +8,8 @@ namespace TerminalUI.Controls; public class Grid : ChildContainerView { + private ILogger>? Logger => ApplicationContext?.LoggerFactory?.CreateLogger>(); + private delegate void WithSizes(Span widths, Span heights); private delegate TResult WithSizes(Span widths, Span heights); @@ -91,6 +94,18 @@ public class Grid : ChildContainerView var x = positionExtension?.Column ?? 0; var y = positionExtension?.Row ?? 0; + if (x > columnWidths.Length) + { + Logger?.LogWarning("Child {Child} is out of bounds, x: {X}, y: {Y}", child, x, y); + x = 0; + } + + if (y > rowHeights.Length) + { + Logger?.LogWarning("Child {Child} is out of bounds, x: {X}, y: {Y}", child, x, y); + y = 0; + } + var width = columnWidths[x]; var height = rowHeights[y]; diff --git a/src/Library/TerminalUI/Controls/ListView.cs b/src/Library/TerminalUI/Controls/ListView.cs index 6968b21..d11d467 100644 --- a/src/Library/TerminalUI/Controls/ListView.cs +++ b/src/Library/TerminalUI/Controls/ListView.cs @@ -126,13 +126,103 @@ public partial class ListView : View if (_listViewItems is null || _listViewItemLength == 0) return new Size(0, 0); - var itemSize = _listViewItems[0].GetRequestedSize(); - _requestedItemSize = itemSize; - return itemSize with {Height = itemSize.Height * _listViewItemLength}; + if (Orientation == Orientation.Vertical) + { + var itemSize = _listViewItems[0].GetRequestedSize(); + _requestedItemSize = itemSize; + return itemSize with {Height = itemSize.Height * _listViewItemLength}; + } + else + { + var width = 0; + var height = 0; + for (var i = 0; i < _listViewItemLength; i++) + { + var item = _listViewItems[i]; + width += item.GetRequestedSize().Width; + height = Math.Max(height, item.GetRequestedSize().Height); + } + + return new Size(width, height); + } } protected override void DefaultRenderer(Position position, Size size) { + if (Orientation == Orientation.Vertical) + RenderVertical(position, size); + else + RenderHorizontal(position, size); + } + + private void RenderHorizontal(Position position, Size size) + { + //Note: no support for same width elements + var listViewItems = InstantiateItemViews(); + if (listViewItems.Length == 0) return; + + Span requestedSizes = stackalloc Size[_listViewItemLength]; + + var totalRequestedWidth = 0; + Span widthSumUpToIndex = stackalloc int[_listViewItemLength]; + for (var i = 0; i < listViewItems.Length; i++) + { + widthSumUpToIndex[i] = totalRequestedWidth; + var item = listViewItems[i]; + var requestedItemSize = item.GetRequestedSize(); + totalRequestedWidth += requestedItemSize.Width; + requestedSizes[i] = requestedItemSize; + } + + var renderStartIndex = _renderStartIndex; + var lastItemIndex = _listViewItemLength; + + if (totalRequestedWidth > size.Width) + { + //Moving the render "window" to the right + //Until the selected item's end is in it + //So when RenderStartPosition (ie all the widths up to RenderStartIndex) + size.Width >= SelectedItemEnd + var selectedIndexEnd = widthSumUpToIndex[SelectedIndex] + requestedSizes[SelectedIndex].Width; + var startXOfRenderStartItem = widthSumUpToIndex[renderStartIndex]; + + while (selectedIndexEnd > startXOfRenderStartItem + size.Width) + { + startXOfRenderStartItem += requestedSizes[renderStartIndex].Width; + renderStartIndex++; + } + + //Moving the render "window" to the left + //Until the selected item's start is in it + //So when RenderStartPosition (ie all the widths up to RenderStartIndex) <= SelectedItemStart + var selectedIndexStart = widthSumUpToIndex[SelectedIndex]; + startXOfRenderStartItem = widthSumUpToIndex[renderStartIndex]; + while (selectedIndexStart < startXOfRenderStartItem) + { + renderStartIndex--; + startXOfRenderStartItem -= requestedSizes[renderStartIndex].Width; + } + } + + var deltaX = 0; + for (var i = renderStartIndex; i < _listViewItemLength; i++) + { + var item = listViewItems[i]; + var requestedItemSize = requestedSizes[i]; + var width = requestedItemSize.Width; + var nextDeltaX = deltaX + requestedItemSize.Width; + if (nextDeltaX > size.Width) + { + width = size.Width - deltaX; + } + + item.Render(position with {X = position.X + deltaX}, size with {Width = width}); + deltaX = nextDeltaX; + } + } + + private void RenderVertical(Position position, Size size) + { + //Note: only same height is supported var requestedItemSize = _requestedItemSize; if (requestedItemSize.Height == 0 || requestedItemSize.Width == 0) return;