From 3ca2fc181fe415cc09d2c10c01d340e4b8c16c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Wed, 16 Aug 2023 09:04:36 +0200 Subject: [PATCH] Order in Tab instead TabViewModel, Utf8 char handling --- .../ViewModels/ITabViewModel.cs | 1 - .../ToolUserCommandHandlerService.cs | 2 +- .../ViewModels/TabViewModel.cs | 41 +--- .../Controls/Timeline.cs | 20 +- .../FileTime.ConsoleUI.App/MainWindow.cs | 177 +++++++++++------- .../Models/ItemOrdering.cs | 0 .../Services/ITab.cs | 8 +- .../FileTime.Core.Command/Copy/CopyCommand.cs | 4 +- .../FileTime.Core.Command.csproj | 2 +- .../FileTime.Core.Services.csproj | 1 - src/Core/FileTime.Core.Services/Tab.cs | 55 ++++-- .../Converters/FormatSizeConverter.cs | 2 +- .../DeclarativePropertyExtensions.cs | 4 +- src/Library/TerminalUI/Color/SpecialColors.cs | 14 ++ .../TerminalUI/ConsoleDrivers/DotnetDriver.cs | 4 + .../TerminalUI/ConsoleDrivers/XTermDriver.cs | 4 + .../TerminalUI/Controls/ProgressBar.cs | 47 +++-- src/Library/TerminalUI/Controls/Rectangle.cs | 18 +- .../TerminalUI/Models/SelectiveChar.cs | 21 +++ .../Styling/Controls/IProgressBarTheme.cs | 25 +-- .../Styling/Controls/ProgressBarTheme.cs | 25 +-- 21 files changed, 286 insertions(+), 189 deletions(-) rename src/{AppCommon/FileTime.App.Core.Abstraction => Core/FileTime.Core.Abstraction}/Models/ItemOrdering.cs (100%) create mode 100644 src/Library/TerminalUI/Color/SpecialColors.cs create mode 100644 src/Library/TerminalUI/Models/SelectiveChar.cs diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs index f18531d..098e487 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/ITabViewModel.cs @@ -20,7 +20,6 @@ public interface ITabViewModel : IInitable, IDisposable IDeclarativeProperty> MarkedItems { get; } IDeclarativeProperty> SelectedsChildren { get; } IDeclarativeProperty> ParentsChildren { get; } - DeclarativeProperty Ordering { get; } void ClearMarkedItems(); void RemoveMarkedItem(FullName fullName); diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs index 64c2576..b153af5 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs @@ -73,7 +73,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase { if (_currentSelectedTab is null) return; - await _currentSelectedTab.Ordering.SetValue(sortItemsCommand.Ordering); + await _currentSelectedTab.Tab.Ordering.SetValue(sortItemsCommand.Ordering); } private async Task CopyBase64() diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs index 919e5e1..6594f63 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs @@ -1,6 +1,5 @@ using System.Collections.ObjectModel; using System.ComponentModel; -using System.Reactive.Linq; using DeclarativeProperty; using DynamicData; using DynamicData.Binding; @@ -47,7 +46,6 @@ public partial class TabViewModel : ITabViewModel public IDeclarativeProperty> MarkedItems { get; } public IDeclarativeProperty?> SelectedsChildren { get; private set; } public IDeclarativeProperty?> ParentsChildren { get; private set; } - public DeclarativeProperty Ordering { get; } = new(ItemOrdering.Name); public TabViewModel( @@ -82,39 +80,6 @@ public partial class TabViewModel : ITabViewModel i => MapItemToViewModel(i, ItemViewModelType.Main) ) ) - ).CombineLatest( - Ordering, - (items, ordering) => - { - if (items is null) return Task.FromResult?>(null); - - ObservableCollection? orderedItems = ordering switch - { - ItemOrdering.Name => - items - .Ordering(i => i.BaseItem!.Type) - .ThenOrdering(i => i.DisplayNameText), - ItemOrdering.NameDesc => - items - .Ordering(i => i.BaseItem!.Type) - .ThenOrdering(i => i.DisplayNameText, ListSortDirection.Descending), - ItemOrdering.CreationDate => - items - .Ordering(i => i.CreatedAt), - ItemOrdering.CreationDateDesc => - items - .Ordering(i => i.CreatedAt, ListSortDirection.Descending), - ItemOrdering.LastModifyDate => - items - .Ordering(i => i.ModifiedAt), - ItemOrdering.LastModifyDateDesc => - items - .Ordering(i => i.ModifiedAt, ListSortDirection.Descending), - _ => throw new NotImplementedException() - }; - - return Task.FromResult(orderedItems); - } ); using var _ = Defer( @@ -135,6 +100,12 @@ public partial class TabViewModel : ITabViewModel } ); + CurrentSelectedItem.Subscribe((v) => + { + _refreshSmoothnessCalculator.RegisterChange(); + _refreshSmoothnessCalculator.RecalculateSmoothness(); + }); + CurrentSelectedItemAsContainer = CurrentSelectedItem.Map(i => i as IContainerViewModel); SelectedsChildren = CurrentSelectedItem diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/Controls/Timeline.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/Controls/Timeline.cs index 50fa472..042205c 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/Controls/Timeline.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/Controls/Timeline.cs @@ -62,16 +62,16 @@ public class Timeline { ForegroundColor = _progressBarTheme?.ForegroundColor, UnfilledForeground = _progressBarTheme?.UnfilledForeground, - FilledCharacter = '\u2594', - UnfilledCharacter = '\u2594', - Fraction1Per8Character = '\u2594', - Fraction2Per8Character = '\u2594', - Fraction3Per8Character = '\u2594', - Fraction4Per8Character = '\u2594', - Fraction5Per8Character = '\u2594', - Fraction6Per8Character = '\u2594', - Fraction7Per8Character = '\u2594', - FractionFull = '\u2594', + FilledCharacter = new('\u2594', '█'), + UnfilledCharacter = new('\u2594', '█'), + Fraction1Per8Character = new('\u2594', ' '), + Fraction2Per8Character = new('\u2594', ' '), + Fraction3Per8Character = new('\u2594', ' '), + Fraction4Per8Character = new('\u2594', ' '), + Fraction5Per8Character = new('\u2594', '█'), + Fraction6Per8Character = new('\u2594', '█'), + Fraction7Per8Character = new('\u2594', '█'), + FractionFull = new('\u2594', '█'), }, Extensions = { diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs index f93fcd1..9959cfb 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/MainWindow.cs @@ -1,8 +1,11 @@ -using FileTime.App.Core.Models.Enums; +using System.Globalization; +using FileTime.App.Core.Models.Enums; using FileTime.App.Core.ViewModels; using FileTime.ConsoleUI.App.Controls; using FileTime.ConsoleUI.App.Styling; using FileTime.Core.Enums; +using FileTime.Core.Models; +using Humanizer.Bytes; using TerminalUI; using TerminalUI.Color; using TerminalUI.Controls; @@ -14,6 +17,16 @@ namespace FileTime.ConsoleUI.App; public class MainWindow { + private readonly struct ItemViewRenderOptions + { + public readonly bool ShowAttributes; + + public ItemViewRenderOptions(bool showAttributes = false) + { + ShowAttributes = showAttributes; + } + } + private readonly IRootViewModel _rootViewModel; private readonly IApplicationContext _applicationContext; private readonly ITheme _theme; @@ -251,30 +264,11 @@ public class MainWindow { var list = new ListView { - ListPadding = 8 + ListPadding = 8, + Margin = "1 0 1 0" }; - list.ItemTemplate = item => - { - var textBlock = item.CreateChild>(); - textBlock.Bind( - textBlock, - dc => dc == null ? string.Empty : dc.DisplayNameText, - tb => tb.Text - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultForegroundColor : ToForegroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Foreground - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultBackgroundColor : ToBackgroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Background - ); - - return textBlock; - }; + list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions(true)); list.Bind( list, @@ -296,27 +290,7 @@ public class MainWindow ListPadding = 8 }; - list.ItemTemplate = item => - { - var textBlock = item.CreateChild>(); - textBlock.Bind( - textBlock, - dc => dc == null ? string.Empty : dc.DisplayNameText, - tb => tb.Text - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultForegroundColor : ToForegroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Foreground - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultBackgroundColor : ToBackgroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Background - ); - - return textBlock; - }; + list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions()); list.Bind( list, @@ -333,28 +307,7 @@ public class MainWindow ListPadding = 8 }; - list.ItemTemplate = item => - { - var textBlock = item.CreateChild>(); - textBlock.Bind( - textBlock, - dc => dc == null ? string.Empty : dc.DisplayNameText, - tb => tb.Text - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultForegroundColor : ToForegroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Foreground, - v => v - ); - textBlock.Bind( - textBlock, - dc => dc == null ? _theme.DefaultBackgroundColor : ToBackgroundColor(dc.ViewMode.Value, dc.BaseItem.Type), - tb => tb.Background - ); - - return textBlock; - }; + list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions()); list.Bind( list, @@ -364,6 +317,98 @@ public class MainWindow return list; } + private IView ItemItemTemplate( + ListViewItem item, + ItemViewRenderOptions options + ) + { + var root = new Grid + { + ChildInitializer = + { + new Rectangle(), + new Grid + { + Margin = "1 0 1 0", + ColumnDefinitionsObject = "* Auto", + ChildInitializer = + { + new TextBlock() + .Setup(t => + { + t.Bind( + t, + dc => dc == null ? string.Empty : dc.DisplayNameText, + tb => tb.Text + ); + }), + new StackPanel + { + Extensions = {new GridPositionExtension(1, 0)}, + ChildInitializer = + { + new TextBlock() + .Setup(t => + { + if (!options.ShowAttributes) return; + t.Bind( + t, + dc => ((IContainer) dc.BaseItem).Items.Count, + tb => tb.Text, + t => $" {t}"); + }) + } + } + .Setup(s => s.Bind( + s, + dc => dc.BaseItem.Type == AbsolutePathType.Container, + s => s.IsVisible)), + new StackPanel + { + Extensions = {new GridPositionExtension(1, 0)}, + ChildInitializer = + { + new TextBlock() + .Setup(t => + { + if (!options.ShowAttributes) return; + t.Bind( + t, + dc => ((IElementViewModel) dc).Size.Value, + tb => tb.Text, + v => + { + var b = ByteSize.FromBytes(v); + + return $"{b.LargestWholeNumberValue:0.#} " + b.GetLargestWholeNumberSymbol(NumberFormatInfo.CurrentInfo).First(); + }); + }) + } + } + .Setup(s => s.Bind( + s, + dc => dc.BaseItem.Type == AbsolutePathType.Element, + s => s.IsVisible)) + } + } + } + }; + + root.Bind( + root, + dc => dc == null ? _theme.DefaultForegroundColor : ToForegroundColor(dc.ViewMode.Value, dc.BaseItem.Type), + tb => tb.Foreground + ); + + root.Bind( + root, + dc => dc == null ? _theme.DefaultBackgroundColor : ToBackgroundColor(dc.ViewMode.Value, dc.BaseItem.Type), + tb => tb.Background + ); + + return root; + } + private IColor? ToForegroundColor(ItemViewMode viewMode, AbsolutePathType absolutePathType) => (viewMode, absolutePathType) switch { diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Models/ItemOrdering.cs b/src/Core/FileTime.Core.Abstraction/Models/ItemOrdering.cs similarity index 100% rename from src/AppCommon/FileTime.App.Core.Abstraction/Models/ItemOrdering.cs rename to src/Core/FileTime.Core.Abstraction/Models/ItemOrdering.cs diff --git a/src/Core/FileTime.Core.Abstraction/Services/ITab.cs b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs index 67a1fb2..475b7aa 100644 --- a/src/Core/FileTime.Core.Abstraction/Services/ITab.cs +++ b/src/Core/FileTime.Core.Abstraction/Services/ITab.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using DeclarativeProperty; +using FileTime.App.Core.Models; using FileTime.Core.Models; using InitableService; @@ -7,10 +8,11 @@ namespace FileTime.Core.Services; public interface ITab : IAsyncInitable, IDisposable { - public IDeclarativeProperty CurrentLocation { get; } - public IDeclarativeProperty?> CurrentItems { get; } - public IDeclarativeProperty CurrentSelectedItem { get; } + IDeclarativeProperty CurrentLocation { get; } + IDeclarativeProperty?> CurrentItems { get; } + IDeclarativeProperty CurrentSelectedItem { get; } FullName? LastDeepestSelectedPath { get; } + DeclarativeProperty Ordering { get; } Task SetCurrentLocation(IContainer newLocation); void AddItemFilter(ItemFilter filter); diff --git a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs index 2498c12..88ffe1d 100644 --- a/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs +++ b/src/Core/FileTime.Core.Command/Copy/CopyCommand.cs @@ -1,10 +1,8 @@ -using System.Reactive.Linq; -using System.Reactive.Subjects; -using ByteSizeLib; using DeclarativeProperty; using FileTime.Core.Enums; using FileTime.Core.Models; using FileTime.Core.Timeline; +using Humanizer.Bytes; using Microsoft.Extensions.Logging; namespace FileTime.Core.Command.Copy; diff --git a/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj b/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj index 50c2c46..75ac274 100644 --- a/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj +++ b/src/Core/FileTime.Core.Command/FileTime.Core.Command.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj b/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj index 35164c0..2372acd 100644 --- a/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj +++ b/src/Core/FileTime.Core.Services/FileTime.Core.Services.csproj @@ -18,7 +18,6 @@ - diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index 05e8f57..5307132 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -1,12 +1,14 @@ using System.Collections.ObjectModel; +using System.ComponentModel; using CircularBuffer; using DeclarativeProperty; using DynamicData; -using FileTime.App.Core.Services; +using FileTime.App.Core.Models; using FileTime.Core.Helper; using FileTime.Core.Models; using FileTime.Core.Timeline; using ObservableComputations; +using IContainer = FileTime.Core.Models.IContainer; namespace FileTime.Core.Services; @@ -14,9 +16,9 @@ public class Tab : ITab { private readonly ITimelessContentProvider _timelessContentProvider; private readonly ITabEvents _tabEvents; - private readonly DeclarativeProperty _currentLocation = new(null); - private readonly DeclarativeProperty _currentLocationForced = new(null); - private readonly DeclarativeProperty _currentRequestItem = new(null); + private readonly DeclarativeProperty _currentLocation = new(); + private readonly DeclarativeProperty _currentLocationForced = new(); + private readonly DeclarativeProperty _currentRequestItem = new(); private readonly ObservableCollection _itemFilters = new(); private readonly CircularBuffer _history = new(20); private readonly CircularBuffer _future = new(20); @@ -28,11 +30,11 @@ public class Tab : ITab public IDeclarativeProperty?> CurrentItems { get; } public IDeclarativeProperty CurrentSelectedItem { get; } public FullName? LastDeepestSelectedPath { get; private set; } + public DeclarativeProperty Ordering { get; } = new(ItemOrdering.Name); public Tab( ITimelessContentProvider timelessContentProvider, - ITabEvents tabEvents, - IRefreshSmoothnessCalculator refreshSmoothnessCalculator) + ITabEvents tabEvents) { _timelessContentProvider = timelessContentProvider; _tabEvents = tabEvents; @@ -47,7 +49,7 @@ public class Tab : ITab _currentLocationForced ); - CurrentLocation.Subscribe((c, _) => + CurrentLocation.Subscribe((_, _) => { if (_currentSelectedItemCached is not null) { @@ -78,6 +80,39 @@ public class Tab : ITab return Task.FromResult(items); } + ).CombineLatest( + Ordering, + (items, ordering) => + { + if (items is null) return Task.FromResult?>(null); + + ObservableCollection? orderedItems = ordering switch + { + ItemOrdering.Name => + items + .Ordering(i => i.Type) + .ThenOrdering(i => i.DisplayName), + ItemOrdering.NameDesc => + items + .Ordering(i => i.Type) + .ThenOrdering(i => i.DisplayName, ListSortDirection.Descending), + ItemOrdering.CreationDate => + items + .Ordering(i => i.CreatedAt), + ItemOrdering.CreationDateDesc => + items + .Ordering(i => i.CreatedAt, ListSortDirection.Descending), + ItemOrdering.LastModifyDate => + items + .Ordering(i => i.ModifiedAt), + ItemOrdering.LastModifyDateDesc => + items + .Ordering(i => i.ModifiedAt, ListSortDirection.Descending), + _ => throw new NotImplementedException() + }; + + return Task.FromResult(orderedItems); + } ); @@ -92,12 +127,6 @@ public class Tab : ITab return Task.FromResult(GetSelectedItemByItems(items)); }).DistinctUntilChanged(); - CurrentSelectedItem.Subscribe((v) => - { - refreshSmoothnessCalculator.RegisterChange(); - refreshSmoothnessCalculator.RecalculateSmoothness(); - }); - CurrentSelectedItem.Subscribe(async (s, _) => { _currentSelectedItemCached = s; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Converters/FormatSizeConverter.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Converters/FormatSizeConverter.cs index ad0a555..079dd2a 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.App/Converters/FormatSizeConverter.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.App/Converters/FormatSizeConverter.cs @@ -1,6 +1,6 @@ using System.Globalization; using Avalonia.Data.Converters; -using ByteSizeLib; +using Humanizer.Bytes; namespace FileTime.GuiApp.App.Converters; diff --git a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs index e1225ce..179ba7a 100644 --- a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs +++ b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs @@ -87,7 +87,7 @@ public static class DeclarativePropertyExtensions public static IDeclarativeProperty CombineLatest( this IDeclarativeProperty prop1, IDeclarativeProperty prop2, - Func> func, + Func> func, Action? setValueHook = null) => new CombineLatestProperty(prop1, prop2, func, setValueHook); @@ -96,7 +96,7 @@ public static class DeclarativePropertyExtensions public static IDeclarativeProperty CombineAll( this IEnumerable> sources, - Func, Task> combiner, + Func, Task> combiner, Action? setValueHook = null) => new CombineAllProperty(sources, combiner, setValueHook); } \ No newline at end of file diff --git a/src/Library/TerminalUI/Color/SpecialColors.cs b/src/Library/TerminalUI/Color/SpecialColors.cs new file mode 100644 index 0000000..f49dd24 --- /dev/null +++ b/src/Library/TerminalUI/Color/SpecialColors.cs @@ -0,0 +1,14 @@ +namespace TerminalUI.Color; + +public class SpecialColor : IColor +{ + private SpecialColor(){} + public ColorType Type => ColorType.Unknown; + public string ToConsoleColor() => throw new NotImplementedException(); + + public IColor AsForeground() => this; + + public IColor AsBackground() => this; + + public static SpecialColor None { get; } = new(); +} \ No newline at end of file diff --git a/src/Library/TerminalUI/ConsoleDrivers/DotnetDriver.cs b/src/Library/TerminalUI/ConsoleDrivers/DotnetDriver.cs index 422ae51..d9f9d24 100644 --- a/src/Library/TerminalUI/ConsoleDrivers/DotnetDriver.cs +++ b/src/Library/TerminalUI/ConsoleDrivers/DotnetDriver.cs @@ -36,12 +36,16 @@ public class DotnetDriver : IConsoleDriver public virtual void SetForegroundColor(IColor foreground) { + if (foreground == SpecialColor.None) return; + if (foreground is not ConsoleColor consoleColor) throw new NotSupportedException(); Console.ForegroundColor = consoleColor.Color; } public virtual void SetBackgroundColor(IColor background) { + if (background == SpecialColor.None) return; + if (background is not ConsoleColor consoleColor) throw new NotSupportedException(); Console.BackgroundColor = consoleColor.Color; } diff --git a/src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs b/src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs index e39a54f..ba402c1 100644 --- a/src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs +++ b/src/Library/TerminalUI/ConsoleDrivers/XTermDriver.cs @@ -29,6 +29,8 @@ public sealed class XTermDriver : DotnetDriver public override void SetBackgroundColor(IColor background) { + if (background == SpecialColor.None) return; + if (background is ConsoleColor consoleColor) { Console.BackgroundColor = consoleColor.Color; @@ -41,6 +43,8 @@ public sealed class XTermDriver : DotnetDriver public override void SetForegroundColor(IColor foreground) { + if (foreground == SpecialColor.None) return; + if (foreground is ConsoleColor consoleColor) { Console.ForegroundColor = consoleColor.Color; diff --git a/src/Library/TerminalUI/Controls/ProgressBar.cs b/src/Library/TerminalUI/Controls/ProgressBar.cs index e1278b5..a9fbc7f 100644 --- a/src/Library/TerminalUI/Controls/ProgressBar.cs +++ b/src/Library/TerminalUI/Controls/ProgressBar.cs @@ -13,10 +13,10 @@ public partial class ProgressBar : View, T> int Minimum, int Maximum, int Value, - char? LeftCap, - char? RightCap, - char? Fill, - char? Unfilled, + SelectiveChar? LeftCap, + SelectiveChar? RightCap, + SelectiveChar? Fill, + SelectiveChar? Unfilled, IColor? UnfilledForeground, IColor? UnfilledBackground); @@ -49,10 +49,10 @@ public partial class ProgressBar : View, T> var background = Background ?? (_theme ?? theme)?.BackgroundColor ?? renderContext.Background; var unfilledForeground = (_theme ?? theme)?.UnfilledForeground ?? renderContext.Foreground; var unfilledBackground = (_theme ?? theme)?.UnfilledBackground ?? renderContext.Background; - var unfilledCharacter = (_theme ?? theme)?.UnfilledCharacter ?? ApplicationContext?.EmptyCharacter ?? ' '; - var fillCharacter = (_theme ?? theme)?.FilledCharacter ?? '█'; - var leftCap = (_theme ?? theme)?.LeftCap; - var rightCap = (_theme ?? theme)?.RightCap; + var unfilledCharacterS = (_theme ?? theme)?.UnfilledCharacter ?? new SelectiveChar(ApplicationContext?.EmptyCharacter ?? ' '); + var fillCharacterS = (_theme ?? theme)?.FilledCharacter ?? new SelectiveChar('█'); + var leftCapS = (_theme ?? theme)?.LeftCap; + var rightCapS = (_theme ?? theme)?.RightCap; var renderState = new RenderState( position, @@ -60,15 +60,21 @@ public partial class ProgressBar : View, T> Minimum, Maximum, Value, - leftCap, - rightCap, - fillCharacter, - unfilledCharacter, + leftCapS, + rightCapS, + fillCharacterS, + unfilledCharacterS, unfilledForeground, unfilledBackground); if (!renderContext.ForceRerender && !NeedsRerender(renderState)) return false; + var utf8Support = ApplicationContext!.SupportUtf8Output; + var unfilledCharacter = unfilledCharacterS.GetChar(utf8Support); + var fillCharacter = fillCharacterS.GetChar(utf8Support); + var leftCap = leftCapS?.GetChar(utf8Support); + var rightCap = rightCapS?.GetChar(utf8Support); + _lastRenderState = renderState; var driver = renderContext.ConsoleDriver; @@ -90,17 +96,18 @@ public partial class ProgressBar : View, T> if (ApplicationContext!.SupportUtf8Output) { var remained = progressWidth - progressQuotientWidth; + var t = _theme ?? theme; transientChar = remained switch { < 0.125 => unfilledCharacter, - < 0.250 => (_theme ?? theme)?.Fraction1Per8Character ?? '\u258F', - < 0.375 => (_theme ?? theme)?.Fraction2Per8Character ?? '\u258E', - < 0.500 => (_theme ?? theme)?.Fraction3Per8Character ?? '\u258D', - < 0.675 => (_theme ?? theme)?.Fraction4Per8Character ?? '\u258C', - < 0.750 => (_theme ?? theme)?.Fraction5Per8Character ?? '\u258B', - < 0.875 => (_theme ?? theme)?.Fraction6Per8Character ?? '\u258A', - < 0_001 => (_theme ?? theme)?.Fraction7Per8Character ?? '\u2589', - _ => (_theme ?? theme)?.FractionFull ?? '\u2588', + < 0.250 => (t?.Fraction1Per8Character ?? new SelectiveChar('\u258F', ' ')).GetChar(utf8Support), + < 0.375 => (t?.Fraction2Per8Character ?? new SelectiveChar('\u258E', ' ')).GetChar(utf8Support), + < 0.500 => (t?.Fraction3Per8Character ?? new SelectiveChar('\u258D', ' ')).GetChar(utf8Support), + < 0.675 => (t?.Fraction4Per8Character ?? new SelectiveChar('\u258C', ' ')).GetChar(utf8Support), + < 0.750 => (t?.Fraction5Per8Character ?? new SelectiveChar('\u258B', ' ')).GetChar(utf8Support), + < 0.875 => (t?.Fraction6Per8Character ?? new SelectiveChar('\u258A', ' ')).GetChar(utf8Support), + < 0_001 => (t?.Fraction7Per8Character ?? new SelectiveChar('\u2589', ' ')).GetChar(utf8Support), + _ => (t?.FractionFull ?? new SelectiveChar('\u2588', ' ')).GetChar(utf8Support), }; } diff --git a/src/Library/TerminalUI/Controls/Rectangle.cs b/src/Library/TerminalUI/Controls/Rectangle.cs index 7de3738..d1aa501 100644 --- a/src/Library/TerminalUI/Controls/Rectangle.cs +++ b/src/Library/TerminalUI/Controls/Rectangle.cs @@ -10,24 +10,26 @@ public sealed partial class Rectangle : View, T>, IDisplayView private record RenderState( Position Position, Size Size, - IColor? Fill); + IColor? Color); private RenderState? _lastRenderState; - - [Notify] private IColor? _fill; protected override Size CalculateSize() => new(Width ?? 0, Height ?? 0); protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) { - var renderState = new RenderState(position, size, Fill); - if ((!renderContext.ForceRerender && !NeedsRerender(renderState)) || Fill is null) return false; + var color = Background ?? renderContext.Background; + var renderState = new RenderState(position, size, color); + if (!renderContext.ForceRerender && !NeedsRerender(renderState)) return false; _lastRenderState = renderState; var driver = renderContext.ConsoleDriver; - var s = new string('█', size.Width); - driver.SetBackgroundColor(Fill); - driver.SetForegroundColor(Fill); + var s = new string(' ', size.Width); + driver.ResetColor(); + if (color is not null) + { + driver.SetForegroundColor(color); + } var height = size.Height; for (var i = 0; i < height; i++) diff --git a/src/Library/TerminalUI/Models/SelectiveChar.cs b/src/Library/TerminalUI/Models/SelectiveChar.cs new file mode 100644 index 0000000..a707292 --- /dev/null +++ b/src/Library/TerminalUI/Models/SelectiveChar.cs @@ -0,0 +1,21 @@ +namespace TerminalUI.Models; + +public readonly struct SelectiveChar +{ + public readonly char Utf8Char; + public readonly char AsciiChar; + + public SelectiveChar(char c) + { + Utf8Char = c; + AsciiChar = c; + } + + public SelectiveChar(char utf8Char, char asciiChar) + { + Utf8Char = utf8Char; + AsciiChar = asciiChar; + } + + public char GetChar(bool enableUtf8) => enableUtf8 ? Utf8Char : AsciiChar; +} \ No newline at end of file diff --git a/src/Library/TerminalUI/Styling/Controls/IProgressBarTheme.cs b/src/Library/TerminalUI/Styling/Controls/IProgressBarTheme.cs index 439a700..ca3c0cc 100644 --- a/src/Library/TerminalUI/Styling/Controls/IProgressBarTheme.cs +++ b/src/Library/TerminalUI/Styling/Controls/IProgressBarTheme.cs @@ -1,4 +1,5 @@ using TerminalUI.Color; +using TerminalUI.Models; namespace TerminalUI.Styling.Controls; @@ -8,16 +9,16 @@ public interface IProgressBarTheme IColor? BackgroundColor { get; init; } IColor? UnfilledForeground { get; init; } IColor? UnfilledBackground { get; init; } - char? FilledCharacter { get; init; } - char? UnfilledCharacter { get; init; } - char? Fraction1Per8Character { get; init; } - char? Fraction2Per8Character { get; init; } - char? Fraction3Per8Character { get; init; } - char? Fraction4Per8Character { get; init; } - char? Fraction5Per8Character { get; init; } - char? Fraction6Per8Character { get; init; } - char? Fraction7Per8Character { get; init; } - char? FractionFull { get; init; } - char? LeftCap { get; init; } - char? RightCap { get; init; } + SelectiveChar? FilledCharacter { get; init; } + SelectiveChar? UnfilledCharacter { get; init; } + SelectiveChar? Fraction1Per8Character { get; init; } + SelectiveChar? Fraction2Per8Character { get; init; } + SelectiveChar? Fraction3Per8Character { get; init; } + SelectiveChar? Fraction4Per8Character { get; init; } + SelectiveChar? Fraction5Per8Character { get; init; } + SelectiveChar? Fraction6Per8Character { get; init; } + SelectiveChar? Fraction7Per8Character { get; init; } + SelectiveChar? FractionFull { get; init; } + SelectiveChar? LeftCap { get; init; } + SelectiveChar? RightCap { get; init; } } \ No newline at end of file diff --git a/src/Library/TerminalUI/Styling/Controls/ProgressBarTheme.cs b/src/Library/TerminalUI/Styling/Controls/ProgressBarTheme.cs index a87b85b..94310d3 100644 --- a/src/Library/TerminalUI/Styling/Controls/ProgressBarTheme.cs +++ b/src/Library/TerminalUI/Styling/Controls/ProgressBarTheme.cs @@ -1,4 +1,5 @@ using TerminalUI.Color; +using TerminalUI.Models; namespace TerminalUI.Styling.Controls; @@ -28,16 +29,16 @@ public class ProgressBarTheme : IProgressBarTheme public IColor? BackgroundColor { get; init; } public IColor? UnfilledForeground { get; init; } public IColor? UnfilledBackground { get; init; } - public char? FilledCharacter { get; init; } - public char? UnfilledCharacter { get; init; } - public char? Fraction1Per8Character { get; init; } - public char? Fraction2Per8Character { get; init; } - public char? Fraction3Per8Character { get; init; } - public char? Fraction4Per8Character { get; init; } - public char? Fraction5Per8Character { get; init; } - public char? Fraction6Per8Character { get; init; } - public char? Fraction7Per8Character { get; init; } - public char? FractionFull { get; init; } - public char? LeftCap { get; init; } - public char? RightCap { get; init; } + public SelectiveChar? FilledCharacter { get; init; } + public SelectiveChar? UnfilledCharacter { get; init; } + public SelectiveChar? Fraction1Per8Character { get; init; } + public SelectiveChar? Fraction2Per8Character { get; init; } + public SelectiveChar? Fraction3Per8Character { get; init; } + public SelectiveChar? Fraction4Per8Character { get; init; } + public SelectiveChar? Fraction5Per8Character { get; init; } + public SelectiveChar? Fraction6Per8Character { get; init; } + public SelectiveChar? Fraction7Per8Character { get; init; } + public SelectiveChar? FractionFull { get; init; } + public SelectiveChar? LeftCap { get; init; } + public SelectiveChar? RightCap { get; init; } } \ No newline at end of file