Order in Tab instead TabViewModel, Utf8 char handling

This commit is contained in:
2023-08-16 09:04:36 +02:00
parent d175c7bf7e
commit 3ca2fc181f
21 changed files with 286 additions and 189 deletions

View File

@@ -20,7 +20,6 @@ public interface ITabViewModel : IInitable<ITab, int>, IDisposable
IDeclarativeProperty<ObservableCollection<FullName>> MarkedItems { get; } IDeclarativeProperty<ObservableCollection<FullName>> MarkedItems { get; }
IDeclarativeProperty<ObservableCollection<IItemViewModel>> SelectedsChildren { get; } IDeclarativeProperty<ObservableCollection<IItemViewModel>> SelectedsChildren { get; }
IDeclarativeProperty<ObservableCollection<IItemViewModel>> ParentsChildren { get; } IDeclarativeProperty<ObservableCollection<IItemViewModel>> ParentsChildren { get; }
DeclarativeProperty<ItemOrdering?> Ordering { get; }
void ClearMarkedItems(); void ClearMarkedItems();
void RemoveMarkedItem(FullName fullName); void RemoveMarkedItem(FullName fullName);

View File

@@ -73,7 +73,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
{ {
if (_currentSelectedTab is null) return; if (_currentSelectedTab is null) return;
await _currentSelectedTab.Ordering.SetValue(sortItemsCommand.Ordering); await _currentSelectedTab.Tab.Ordering.SetValue(sortItemsCommand.Ordering);
} }
private async Task CopyBase64() private async Task CopyBase64()

View File

@@ -1,6 +1,5 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Reactive.Linq;
using DeclarativeProperty; using DeclarativeProperty;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
@@ -47,7 +46,6 @@ public partial class TabViewModel : ITabViewModel
public IDeclarativeProperty<ObservableCollection<FullName>> MarkedItems { get; } public IDeclarativeProperty<ObservableCollection<FullName>> MarkedItems { get; }
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; } public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; }
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; } public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; }
public DeclarativeProperty<ItemOrdering?> Ordering { get; } = new(ItemOrdering.Name);
public TabViewModel( public TabViewModel(
@@ -82,39 +80,6 @@ public partial class TabViewModel : ITabViewModel
i => MapItemToViewModel(i, ItemViewModelType.Main) i => MapItemToViewModel(i, ItemViewModelType.Main)
) )
) )
).CombineLatest(
Ordering,
(items, ordering) =>
{
if (items is null) return Task.FromResult<ObservableCollection<IItemViewModel>?>(null);
ObservableCollection<IItemViewModel>? 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( 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); CurrentSelectedItemAsContainer = CurrentSelectedItem.Map(i => i as IContainerViewModel);
SelectedsChildren = CurrentSelectedItem SelectedsChildren = CurrentSelectedItem

View File

@@ -62,16 +62,16 @@ public class Timeline
{ {
ForegroundColor = _progressBarTheme?.ForegroundColor, ForegroundColor = _progressBarTheme?.ForegroundColor,
UnfilledForeground = _progressBarTheme?.UnfilledForeground, UnfilledForeground = _progressBarTheme?.UnfilledForeground,
FilledCharacter = '\u2594', FilledCharacter = new('\u2594', '█'),
UnfilledCharacter = '\u2594', UnfilledCharacter = new('\u2594', '█'),
Fraction1Per8Character = '\u2594', Fraction1Per8Character = new('\u2594', ' '),
Fraction2Per8Character = '\u2594', Fraction2Per8Character = new('\u2594', ' '),
Fraction3Per8Character = '\u2594', Fraction3Per8Character = new('\u2594', ' '),
Fraction4Per8Character = '\u2594', Fraction4Per8Character = new('\u2594', ' '),
Fraction5Per8Character = '\u2594', Fraction5Per8Character = new('\u2594', '█'),
Fraction6Per8Character = '\u2594', Fraction6Per8Character = new('\u2594', '█'),
Fraction7Per8Character = '\u2594', Fraction7Per8Character = new('\u2594', '█'),
FractionFull = '\u2594', FractionFull = new('\u2594', '█'),
}, },
Extensions = Extensions =
{ {

View File

@@ -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.App.Core.ViewModels;
using FileTime.ConsoleUI.App.Controls; using FileTime.ConsoleUI.App.Controls;
using FileTime.ConsoleUI.App.Styling; using FileTime.ConsoleUI.App.Styling;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models;
using Humanizer.Bytes;
using TerminalUI; using TerminalUI;
using TerminalUI.Color; using TerminalUI.Color;
using TerminalUI.Controls; using TerminalUI.Controls;
@@ -14,6 +17,16 @@ namespace FileTime.ConsoleUI.App;
public class MainWindow public class MainWindow
{ {
private readonly struct ItemViewRenderOptions
{
public readonly bool ShowAttributes;
public ItemViewRenderOptions(bool showAttributes = false)
{
ShowAttributes = showAttributes;
}
}
private readonly IRootViewModel _rootViewModel; private readonly IRootViewModel _rootViewModel;
private readonly IApplicationContext _applicationContext; private readonly IApplicationContext _applicationContext;
private readonly ITheme _theme; private readonly ITheme _theme;
@@ -251,30 +264,11 @@ public class MainWindow
{ {
var list = new ListView<IRootViewModel, IItemViewModel> var list = new ListView<IRootViewModel, IItemViewModel>
{ {
ListPadding = 8 ListPadding = 8,
Margin = "1 0 1 0"
}; };
list.ItemTemplate = item => list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions(true));
{
var textBlock = item.CreateChild<TextBlock<IItemViewModel>>();
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.Bind( list.Bind(
list, list,
@@ -296,27 +290,7 @@ public class MainWindow
ListPadding = 8 ListPadding = 8
}; };
list.ItemTemplate = item => list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions());
{
var textBlock = item.CreateChild<TextBlock<IItemViewModel>>();
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.Bind( list.Bind(
list, list,
@@ -333,28 +307,7 @@ public class MainWindow
ListPadding = 8 ListPadding = 8
}; };
list.ItemTemplate = item => list.ItemTemplate = item => ItemItemTemplate(item, new ItemViewRenderOptions());
{
var textBlock = item.CreateChild<TextBlock<IItemViewModel>>();
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.Bind( list.Bind(
list, list,
@@ -364,6 +317,98 @@ public class MainWindow
return list; return list;
} }
private IView<IItemViewModel> ItemItemTemplate(
ListViewItem<IItemViewModel, IRootViewModel> item,
ItemViewRenderOptions options
)
{
var root = new Grid<IItemViewModel>
{
ChildInitializer =
{
new Rectangle<IItemViewModel>(),
new Grid<IItemViewModel>
{
Margin = "1 0 1 0",
ColumnDefinitionsObject = "* Auto",
ChildInitializer =
{
new TextBlock<IItemViewModel>()
.Setup(t =>
{
t.Bind(
t,
dc => dc == null ? string.Empty : dc.DisplayNameText,
tb => tb.Text
);
}),
new StackPanel<IItemViewModel>
{
Extensions = {new GridPositionExtension(1, 0)},
ChildInitializer =
{
new TextBlock<IItemViewModel>()
.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<IItemViewModel>
{
Extensions = {new GridPositionExtension(1, 0)},
ChildInitializer =
{
new TextBlock<IItemViewModel>()
.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) => private IColor? ToForegroundColor(ItemViewMode viewMode, AbsolutePathType absolutePathType) =>
(viewMode, absolutePathType) switch (viewMode, absolutePathType) switch
{ {

View File

@@ -1,5 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using DeclarativeProperty; using DeclarativeProperty;
using FileTime.App.Core.Models;
using FileTime.Core.Models; using FileTime.Core.Models;
using InitableService; using InitableService;
@@ -7,10 +8,11 @@ namespace FileTime.Core.Services;
public interface ITab : IAsyncInitable<IContainer>, IDisposable public interface ITab : IAsyncInitable<IContainer>, IDisposable
{ {
public IDeclarativeProperty<IContainer?> CurrentLocation { get; } IDeclarativeProperty<IContainer?> CurrentLocation { get; }
public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; } IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; }
public IDeclarativeProperty<AbsolutePath?> CurrentSelectedItem { get; } IDeclarativeProperty<AbsolutePath?> CurrentSelectedItem { get; }
FullName? LastDeepestSelectedPath { get; } FullName? LastDeepestSelectedPath { get; }
DeclarativeProperty<ItemOrdering?> Ordering { get; }
Task SetCurrentLocation(IContainer newLocation); Task SetCurrentLocation(IContainer newLocation);
void AddItemFilter(ItemFilter filter); void AddItemFilter(ItemFilter filter);

View File

@@ -1,10 +1,8 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using ByteSizeLib;
using DeclarativeProperty; using DeclarativeProperty;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
using Humanizer.Bytes;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace FileTime.Core.Command.Copy; namespace FileTime.Core.Command.Copy;

View File

@@ -5,7 +5,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ByteSize" Version="2.1.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
</ItemGroup> </ItemGroup>

View File

@@ -18,7 +18,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Library\CircularBuffer\CircularBuffer.csproj" /> <ProjectReference Include="..\..\Library\CircularBuffer\CircularBuffer.csproj" />
<ProjectReference Include="..\..\Library\Defer\Defer.csproj" /> <ProjectReference Include="..\..\Library\Defer\Defer.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" /> <ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />

View File

@@ -1,12 +1,14 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using CircularBuffer; using CircularBuffer;
using DeclarativeProperty; using DeclarativeProperty;
using DynamicData; using DynamicData;
using FileTime.App.Core.Services; using FileTime.App.Core.Models;
using FileTime.Core.Helper; using FileTime.Core.Helper;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
using ObservableComputations; using ObservableComputations;
using IContainer = FileTime.Core.Models.IContainer;
namespace FileTime.Core.Services; namespace FileTime.Core.Services;
@@ -14,9 +16,9 @@ public class Tab : ITab
{ {
private readonly ITimelessContentProvider _timelessContentProvider; private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ITabEvents _tabEvents; private readonly ITabEvents _tabEvents;
private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null); private readonly DeclarativeProperty<IContainer?> _currentLocation = new();
private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new(null); private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new();
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null); private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new();
private readonly ObservableCollection<ItemFilter> _itemFilters = new(); private readonly ObservableCollection<ItemFilter> _itemFilters = new();
private readonly CircularBuffer<FullName> _history = new(20); private readonly CircularBuffer<FullName> _history = new(20);
private readonly CircularBuffer<FullName> _future = new(20); private readonly CircularBuffer<FullName> _future = new(20);
@@ -28,11 +30,11 @@ public class Tab : ITab
public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; } public IDeclarativeProperty<ObservableCollection<IItem>?> CurrentItems { get; }
public IDeclarativeProperty<AbsolutePath?> CurrentSelectedItem { get; } public IDeclarativeProperty<AbsolutePath?> CurrentSelectedItem { get; }
public FullName? LastDeepestSelectedPath { get; private set; } public FullName? LastDeepestSelectedPath { get; private set; }
public DeclarativeProperty<ItemOrdering?> Ordering { get; } = new(ItemOrdering.Name);
public Tab( public Tab(
ITimelessContentProvider timelessContentProvider, ITimelessContentProvider timelessContentProvider,
ITabEvents tabEvents, ITabEvents tabEvents)
IRefreshSmoothnessCalculator refreshSmoothnessCalculator)
{ {
_timelessContentProvider = timelessContentProvider; _timelessContentProvider = timelessContentProvider;
_tabEvents = tabEvents; _tabEvents = tabEvents;
@@ -47,7 +49,7 @@ public class Tab : ITab
_currentLocationForced _currentLocationForced
); );
CurrentLocation.Subscribe((c, _) => CurrentLocation.Subscribe((_, _) =>
{ {
if (_currentSelectedItemCached is not null) if (_currentSelectedItemCached is not null)
{ {
@@ -78,6 +80,39 @@ public class Tab : ITab
return Task.FromResult(items); return Task.FromResult(items);
} }
).CombineLatest(
Ordering,
(items, ordering) =>
{
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
ObservableCollection<IItem>? 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)); return Task.FromResult(GetSelectedItemByItems(items));
}).DistinctUntilChanged(); }).DistinctUntilChanged();
CurrentSelectedItem.Subscribe((v) =>
{
refreshSmoothnessCalculator.RegisterChange();
refreshSmoothnessCalculator.RecalculateSmoothness();
});
CurrentSelectedItem.Subscribe(async (s, _) => CurrentSelectedItem.Subscribe(async (s, _) =>
{ {
_currentSelectedItemCached = s; _currentSelectedItemCached = s;

View File

@@ -1,6 +1,6 @@
using System.Globalization; using System.Globalization;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using ByteSizeLib; using Humanizer.Bytes;
namespace FileTime.GuiApp.App.Converters; namespace FileTime.GuiApp.App.Converters;

View File

@@ -87,7 +87,7 @@ public static class DeclarativePropertyExtensions
public static IDeclarativeProperty<TResult?> CombineLatest<T1, T2, TResult>( public static IDeclarativeProperty<TResult?> CombineLatest<T1, T2, TResult>(
this IDeclarativeProperty<T1> prop1, this IDeclarativeProperty<T1> prop1,
IDeclarativeProperty<T2> prop2, IDeclarativeProperty<T2> prop2,
Func<T1, T2, Task<TResult>> func, Func<T1, T2, Task<TResult?>> func,
Action<TResult?>? setValueHook = null) Action<TResult?>? setValueHook = null)
=> new CombineLatestProperty<T1,T2,TResult?>(prop1, prop2, func, setValueHook); => new CombineLatestProperty<T1,T2,TResult?>(prop1, prop2, func, setValueHook);
@@ -96,7 +96,7 @@ public static class DeclarativePropertyExtensions
public static IDeclarativeProperty<TResult?> CombineAll<T, TResult>( public static IDeclarativeProperty<TResult?> CombineAll<T, TResult>(
this IEnumerable<IDeclarativeProperty<T>> sources, this IEnumerable<IDeclarativeProperty<T>> sources,
Func<IEnumerable<T>, Task<TResult>> combiner, Func<IEnumerable<T>, Task<TResult?>> combiner,
Action<TResult?>? setValueHook = null) Action<TResult?>? setValueHook = null)
=> new CombineAllProperty<T,TResult?>(sources, combiner, setValueHook); => new CombineAllProperty<T,TResult?>(sources, combiner, setValueHook);
} }

View File

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

View File

@@ -36,12 +36,16 @@ public class DotnetDriver : IConsoleDriver
public virtual void SetForegroundColor(IColor foreground) public virtual void SetForegroundColor(IColor foreground)
{ {
if (foreground == SpecialColor.None) return;
if (foreground is not ConsoleColor consoleColor) throw new NotSupportedException(); if (foreground is not ConsoleColor consoleColor) throw new NotSupportedException();
Console.ForegroundColor = consoleColor.Color; Console.ForegroundColor = consoleColor.Color;
} }
public virtual void SetBackgroundColor(IColor background) public virtual void SetBackgroundColor(IColor background)
{ {
if (background == SpecialColor.None) return;
if (background is not ConsoleColor consoleColor) throw new NotSupportedException(); if (background is not ConsoleColor consoleColor) throw new NotSupportedException();
Console.BackgroundColor = consoleColor.Color; Console.BackgroundColor = consoleColor.Color;
} }

View File

@@ -29,6 +29,8 @@ public sealed class XTermDriver : DotnetDriver
public override void SetBackgroundColor(IColor background) public override void SetBackgroundColor(IColor background)
{ {
if (background == SpecialColor.None) return;
if (background is ConsoleColor consoleColor) if (background is ConsoleColor consoleColor)
{ {
Console.BackgroundColor = consoleColor.Color; Console.BackgroundColor = consoleColor.Color;
@@ -41,6 +43,8 @@ public sealed class XTermDriver : DotnetDriver
public override void SetForegroundColor(IColor foreground) public override void SetForegroundColor(IColor foreground)
{ {
if (foreground == SpecialColor.None) return;
if (foreground is ConsoleColor consoleColor) if (foreground is ConsoleColor consoleColor)
{ {
Console.ForegroundColor = consoleColor.Color; Console.ForegroundColor = consoleColor.Color;

View File

@@ -13,10 +13,10 @@ public partial class ProgressBar<T> : View<ProgressBar<T>, T>
int Minimum, int Minimum,
int Maximum, int Maximum,
int Value, int Value,
char? LeftCap, SelectiveChar? LeftCap,
char? RightCap, SelectiveChar? RightCap,
char? Fill, SelectiveChar? Fill,
char? Unfilled, SelectiveChar? Unfilled,
IColor? UnfilledForeground, IColor? UnfilledForeground,
IColor? UnfilledBackground); IColor? UnfilledBackground);
@@ -49,10 +49,10 @@ public partial class ProgressBar<T> : View<ProgressBar<T>, T>
var background = Background ?? (_theme ?? theme)?.BackgroundColor ?? renderContext.Background; var background = Background ?? (_theme ?? theme)?.BackgroundColor ?? renderContext.Background;
var unfilledForeground = (_theme ?? theme)?.UnfilledForeground ?? renderContext.Foreground; var unfilledForeground = (_theme ?? theme)?.UnfilledForeground ?? renderContext.Foreground;
var unfilledBackground = (_theme ?? theme)?.UnfilledBackground ?? renderContext.Background; var unfilledBackground = (_theme ?? theme)?.UnfilledBackground ?? renderContext.Background;
var unfilledCharacter = (_theme ?? theme)?.UnfilledCharacter ?? ApplicationContext?.EmptyCharacter ?? ' '; var unfilledCharacterS = (_theme ?? theme)?.UnfilledCharacter ?? new SelectiveChar(ApplicationContext?.EmptyCharacter ?? ' ');
var fillCharacter = (_theme ?? theme)?.FilledCharacter ?? '█'; var fillCharacterS = (_theme ?? theme)?.FilledCharacter ?? new SelectiveChar('█');
var leftCap = (_theme ?? theme)?.LeftCap; var leftCapS = (_theme ?? theme)?.LeftCap;
var rightCap = (_theme ?? theme)?.RightCap; var rightCapS = (_theme ?? theme)?.RightCap;
var renderState = new RenderState( var renderState = new RenderState(
position, position,
@@ -60,15 +60,21 @@ public partial class ProgressBar<T> : View<ProgressBar<T>, T>
Minimum, Minimum,
Maximum, Maximum,
Value, Value,
leftCap, leftCapS,
rightCap, rightCapS,
fillCharacter, fillCharacterS,
unfilledCharacter, unfilledCharacterS,
unfilledForeground, unfilledForeground,
unfilledBackground); unfilledBackground);
if (!renderContext.ForceRerender && !NeedsRerender(renderState)) return false; 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; _lastRenderState = renderState;
var driver = renderContext.ConsoleDriver; var driver = renderContext.ConsoleDriver;
@@ -90,17 +96,18 @@ public partial class ProgressBar<T> : View<ProgressBar<T>, T>
if (ApplicationContext!.SupportUtf8Output) if (ApplicationContext!.SupportUtf8Output)
{ {
var remained = progressWidth - progressQuotientWidth; var remained = progressWidth - progressQuotientWidth;
var t = _theme ?? theme;
transientChar = remained switch transientChar = remained switch
{ {
< 0.125 => unfilledCharacter, < 0.125 => unfilledCharacter,
< 0.250 => (_theme ?? theme)?.Fraction1Per8Character ?? '\u258F', < 0.250 => (t?.Fraction1Per8Character ?? new SelectiveChar('\u258F', ' ')).GetChar(utf8Support),
< 0.375 => (_theme ?? theme)?.Fraction2Per8Character ?? '\u258E', < 0.375 => (t?.Fraction2Per8Character ?? new SelectiveChar('\u258E', ' ')).GetChar(utf8Support),
< 0.500 => (_theme ?? theme)?.Fraction3Per8Character ?? '\u258D', < 0.500 => (t?.Fraction3Per8Character ?? new SelectiveChar('\u258D', ' ')).GetChar(utf8Support),
< 0.675 => (_theme ?? theme)?.Fraction4Per8Character ?? '\u258C', < 0.675 => (t?.Fraction4Per8Character ?? new SelectiveChar('\u258C', ' ')).GetChar(utf8Support),
< 0.750 => (_theme ?? theme)?.Fraction5Per8Character ?? '\u258B', < 0.750 => (t?.Fraction5Per8Character ?? new SelectiveChar('\u258B', ' ')).GetChar(utf8Support),
< 0.875 => (_theme ?? theme)?.Fraction6Per8Character ?? '\u258A', < 0.875 => (t?.Fraction6Per8Character ?? new SelectiveChar('\u258A', ' ')).GetChar(utf8Support),
< 0_001 => (_theme ?? theme)?.Fraction7Per8Character ?? '\u2589', < 0_001 => (t?.Fraction7Per8Character ?? new SelectiveChar('\u2589', ' ')).GetChar(utf8Support),
_ => (_theme ?? theme)?.FractionFull ?? '\u2588', _ => (t?.FractionFull ?? new SelectiveChar('\u2588', ' ')).GetChar(utf8Support),
}; };
} }

View File

@@ -10,24 +10,26 @@ public sealed partial class Rectangle<T> : View<Rectangle<T>, T>, IDisplayView
private record RenderState( private record RenderState(
Position Position, Position Position,
Size Size, Size Size,
IColor? Fill); IColor? Color);
private RenderState? _lastRenderState; private RenderState? _lastRenderState;
[Notify] private IColor? _fill;
protected override Size CalculateSize() => new(Width ?? 0, Height ?? 0); protected override Size CalculateSize() => new(Width ?? 0, Height ?? 0);
protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size) protected override bool DefaultRenderer(in RenderContext renderContext, Position position, Size size)
{ {
var renderState = new RenderState(position, size, Fill); var color = Background ?? renderContext.Background;
if ((!renderContext.ForceRerender && !NeedsRerender(renderState)) || Fill is null) return false; var renderState = new RenderState(position, size, color);
if (!renderContext.ForceRerender && !NeedsRerender(renderState)) return false;
_lastRenderState = renderState; _lastRenderState = renderState;
var driver = renderContext.ConsoleDriver; var driver = renderContext.ConsoleDriver;
var s = new string('', size.Width); var s = new string(' ', size.Width);
driver.SetBackgroundColor(Fill); driver.ResetColor();
driver.SetForegroundColor(Fill); if (color is not null)
{
driver.SetForegroundColor(color);
}
var height = size.Height; var height = size.Height;
for (var i = 0; i < height; i++) for (var i = 0; i < height; i++)

View File

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

View File

@@ -1,4 +1,5 @@
using TerminalUI.Color; using TerminalUI.Color;
using TerminalUI.Models;
namespace TerminalUI.Styling.Controls; namespace TerminalUI.Styling.Controls;
@@ -8,16 +9,16 @@ public interface IProgressBarTheme
IColor? BackgroundColor { get; init; } IColor? BackgroundColor { get; init; }
IColor? UnfilledForeground { get; init; } IColor? UnfilledForeground { get; init; }
IColor? UnfilledBackground { get; init; } IColor? UnfilledBackground { get; init; }
char? FilledCharacter { get; init; } SelectiveChar? FilledCharacter { get; init; }
char? UnfilledCharacter { get; init; } SelectiveChar? UnfilledCharacter { get; init; }
char? Fraction1Per8Character { get; init; } SelectiveChar? Fraction1Per8Character { get; init; }
char? Fraction2Per8Character { get; init; } SelectiveChar? Fraction2Per8Character { get; init; }
char? Fraction3Per8Character { get; init; } SelectiveChar? Fraction3Per8Character { get; init; }
char? Fraction4Per8Character { get; init; } SelectiveChar? Fraction4Per8Character { get; init; }
char? Fraction5Per8Character { get; init; } SelectiveChar? Fraction5Per8Character { get; init; }
char? Fraction6Per8Character { get; init; } SelectiveChar? Fraction6Per8Character { get; init; }
char? Fraction7Per8Character { get; init; } SelectiveChar? Fraction7Per8Character { get; init; }
char? FractionFull { get; init; } SelectiveChar? FractionFull { get; init; }
char? LeftCap { get; init; } SelectiveChar? LeftCap { get; init; }
char? RightCap { get; init; } SelectiveChar? RightCap { get; init; }
} }

View File

@@ -1,4 +1,5 @@
using TerminalUI.Color; using TerminalUI.Color;
using TerminalUI.Models;
namespace TerminalUI.Styling.Controls; namespace TerminalUI.Styling.Controls;
@@ -28,16 +29,16 @@ public class ProgressBarTheme : IProgressBarTheme
public IColor? BackgroundColor { get; init; } public IColor? BackgroundColor { get; init; }
public IColor? UnfilledForeground { get; init; } public IColor? UnfilledForeground { get; init; }
public IColor? UnfilledBackground { get; init; } public IColor? UnfilledBackground { get; init; }
public char? FilledCharacter { get; init; } public SelectiveChar? FilledCharacter { get; init; }
public char? UnfilledCharacter { get; init; } public SelectiveChar? UnfilledCharacter { get; init; }
public char? Fraction1Per8Character { get; init; } public SelectiveChar? Fraction1Per8Character { get; init; }
public char? Fraction2Per8Character { get; init; } public SelectiveChar? Fraction2Per8Character { get; init; }
public char? Fraction3Per8Character { get; init; } public SelectiveChar? Fraction3Per8Character { get; init; }
public char? Fraction4Per8Character { get; init; } public SelectiveChar? Fraction4Per8Character { get; init; }
public char? Fraction5Per8Character { get; init; } public SelectiveChar? Fraction5Per8Character { get; init; }
public char? Fraction6Per8Character { get; init; } public SelectiveChar? Fraction6Per8Character { get; init; }
public char? Fraction7Per8Character { get; init; } public SelectiveChar? Fraction7Per8Character { get; init; }
public char? FractionFull { get; init; } public SelectiveChar? FractionFull { get; init; }
public char? LeftCap { get; init; } public SelectiveChar? LeftCap { get; init; }
public char? RightCap { get; init; } public SelectiveChar? RightCap { get; init; }
} }