File scoped namespace

This commit is contained in:
2022-05-07 19:40:54 +02:00
parent b161ded92e
commit 9bf95ebe4e
126 changed files with 2562 additions and 2598 deletions

View File

@@ -1,2 +1,4 @@
[*.cs]
dotnet_diagnostic.RCS1196.severity = none
dotnet_diagnostic.RCS1196.severity = none
csharp_style_namespace_declarations = file_scoped:error
dotnet_diagnostic.IDE0161.severity = error

View File

@@ -1,66 +1,65 @@
namespace FileTime.App.Core.Command
{
public enum Commands
{
None,
namespace FileTime.App.Core.Command;
AutoRefresh,
ChangeTimelineMode,
CloseTab,
Compress,
Copy,
CopyHash,
CopyPath,
CreateContainer,
CreateElement,
Cut,
Edit,
EnterRapidTravel,
FindByName,
FindByNameRegex,
GoToHome,
GoToPath,
GoToProvider,
GoToRoot,
GoUp,
HardDelete,
Mark,
MoveCursorDown,
MoveCursorDownPage,
MoveCursorUp,
MoveCursorUpPage,
MoveToFirst,
MoveToLast,
NextTimelineBlock,
NextTimelineCommand,
Open,
OpenInFileBrowser,
OpenOrRun,
PasteMerge,
PasteOverwrite,
PasteSkip,
PinFavorite,
PreviousTimelineBlock,
PreviousTimelineCommand,
Refresh,
Rename,
RunCommand,
ScanContainerSize,
ShowAllShotcut,
SoftDelete,
SwitchToLastTab,
SwitchToTab1,
SwitchToTab2,
SwitchToTab3,
SwitchToTab4,
SwitchToTab5,
SwitchToTab6,
SwitchToTab7,
SwitchToTab8,
TimelinePause,
TimelineRefresh,
TimelineStart,
ToggleAdvancedIcons,
ToggleHidden,
}
public enum Commands
{
None,
AutoRefresh,
ChangeTimelineMode,
CloseTab,
Compress,
Copy,
CopyHash,
CopyPath,
CreateContainer,
CreateElement,
Cut,
Edit,
EnterRapidTravel,
FindByName,
FindByNameRegex,
GoToHome,
GoToPath,
GoToProvider,
GoToRoot,
GoUp,
HardDelete,
Mark,
MoveCursorDown,
MoveCursorDownPage,
MoveCursorUp,
MoveCursorUpPage,
MoveToFirst,
MoveToLast,
NextTimelineBlock,
NextTimelineCommand,
Open,
OpenInFileBrowser,
OpenOrRun,
PasteMerge,
PasteOverwrite,
PasteSkip,
PinFavorite,
PreviousTimelineBlock,
PreviousTimelineCommand,
Refresh,
Rename,
RunCommand,
ScanContainerSize,
ShowAllShotcut,
SoftDelete,
SwitchToLastTab,
SwitchToTab1,
SwitchToTab2,
SwitchToTab3,
SwitchToTab4,
SwitchToTab5,
SwitchToTab6,
SwitchToTab7,
SwitchToTab8,
TimelinePause,
TimelineRefresh,
TimelineStart,
ToggleAdvancedIcons,
ToggleHidden,
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Reactive" Version="5.0.0" />
</ItemGroup>

View File

@@ -1,26 +1,25 @@
using System.Collections.ObjectModel;
using DynamicData;
namespace FileTime.App.Core.Models
namespace FileTime.App.Core.Models;
public class BindedCollection<T> : IDisposable
{
public class BindedCollection<T> : IDisposable
private readonly IDisposable _disposable;
public ReadOnlyObservableCollection<T> Collection { get; }
public BindedCollection(IObservable<IChangeSet<T>> dynamicList)
{
private readonly IDisposable _disposable;
public ReadOnlyObservableCollection<T> Collection { get; }
public BindedCollection(IObservable<IChangeSet<T>> dynamicList)
{
_disposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
_disposable = dynamicList
.Bind(out var collection)
.DisposeMany()
.Subscribe();
Collection = collection;
}
Collection = collection;
}
public void Dispose()
{
_disposable.Dispose();
GC.SuppressFinalize(this);
}
public void Dispose()
{
_disposable.Dispose();
GC.SuppressFinalize(this);
}
}

View File

@@ -1,10 +1,9 @@
namespace FileTime.App.Core.Models.Enums
namespace FileTime.App.Core.Models.Enums;
public enum ItemAttributeType
{
public enum ItemAttributeType
{
File,
Element,
Container,
SizeContainer
}
File,
Element,
Container,
SizeContainer
}

View File

@@ -1,12 +1,11 @@
namespace FileTime.App.Core.Models.Enums
namespace FileTime.App.Core.Models.Enums;
public enum ItemViewMode
{
public enum ItemViewMode
{
Default,
Alternative,
Selected,
Marked,
MarkedSelected,
MarkedAlternative
}
Default,
Alternative,
Selected,
Marked,
MarkedSelected,
MarkedAlternative
}

View File

@@ -1,8 +1,7 @@
namespace FileTime.App.Core.Models.Enums
namespace FileTime.App.Core.Models.Enums;
public enum ViewMode
{
public enum ViewMode
{
Default,
RapidTravel
}
Default,
RapidTravel
}

View File

@@ -1,14 +1,13 @@
namespace FileTime.App.Core.Models
{
public class ItemNamePart
{
public string Text { get; set; }
public bool IsSpecial { get; set; }
namespace FileTime.App.Core.Models;
public ItemNamePart(string text, bool isSpecial = false)
{
Text = text;
IsSpecial = isSpecial;
}
public class ItemNamePart
{
public string Text { get; set; }
public bool IsSpecial { get; set; }
public ItemNamePart(string text, bool isSpecial = false)
{
Text = text;
IsSpecial = isSpecial;
}
}

View File

@@ -1,16 +1,15 @@
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services
{
public interface IClipboardService
{
Type? CommandType { get; }
IReadOnlyList<IAbsolutePath> Content { get; }
namespace FileTime.App.Core.Services;
void AddContent(IAbsolutePath absolutePath);
void RemoveContent(IAbsolutePath absolutePath);
void Clear();
void SetCommand<T>() where T : ITransportationCommand;
}
public interface IClipboardService
{
Type? CommandType { get; }
IReadOnlyList<IAbsolutePath> Content { get; }
void AddContent(IAbsolutePath absolutePath);
void RemoveContent(IAbsolutePath absolutePath);
void Clear();
void SetCommand<T>() where T : ITransportationCommand;
}

View File

@@ -1,10 +1,9 @@
using FileTime.App.Core.Command;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public interface ICommandHandler
{
public interface ICommandHandler
{
bool CanHandleCommand(Commands command);
Task HandleCommandAsync(Commands command);
}
bool CanHandleCommand(Commands command);
Task HandleCommandAsync(Commands command);
}

View File

@@ -1,9 +1,8 @@
using FileTime.App.Core.Command;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public interface ICommandHandlerService
{
public interface ICommandHandlerService
{
Task HandleCommandAsync(Commands command);
}
Task HandleCommandAsync(Commands command);
}

View File

@@ -1,11 +1,10 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public interface IItemNameConverterService
{
public interface IItemNameConverterService
{
List<ItemNamePart> GetDisplayName(string name, string? searchText);
string GetFileExtension(string fullName);
string GetFileName(string fullName);
}
List<ItemNamePart> GetDisplayName(string name, string? searchText);
string GetFileExtension(string fullName);
string GetFileName(string fullName);
}

View File

@@ -1,10 +1,9 @@
using System.Reactive.Concurrency;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public interface IRxSchedulerService
{
public interface IRxSchedulerService
{
IScheduler GetWorkerScheduler();
IScheduler GetUIScheduler();
}
IScheduler GetWorkerScheduler();
IScheduler GetUIScheduler();
}

View File

@@ -1,17 +1,16 @@
using System.Collections.ObjectModel;
using FileTime.App.Core.Models.Enums;
namespace FileTime.App.Core.ViewModels
{
public interface IAppState
{
ObservableCollection<ITabViewModel> Tabs { get; }
IObservable<ITabViewModel?> SelectedTab { get; }
IObservable<string?> SearchText { get; }
ViewMode ViewMode { get; }
namespace FileTime.App.Core.ViewModels;
void AddTab(ITabViewModel tabViewModel);
void RemoveTab(ITabViewModel tabViewModel);
void SetSearchText(string? searchText);
}
public interface IAppState
{
ObservableCollection<ITabViewModel> Tabs { get; }
IObservable<ITabViewModel?> SelectedTab { get; }
IObservable<string?> SearchText { get; }
ViewMode ViewMode { get; }
void AddTab(ITabViewModel tabViewModel);
void RemoveTab(ITabViewModel tabViewModel);
void SetSearchText(string? searchText);
}

View File

@@ -1,10 +1,9 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface IContainerSizeContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel>
{
public interface IContainerSizeContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel>
{
long Size { get; set; }
}
long Size { get; set; }
}

View File

@@ -1,10 +1,9 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface IContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel>
{
public interface IContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel>
{
IContainer? Container { get; }
}
IContainer? Container { get; }
}

View File

@@ -1,10 +1,9 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface IElementViewModel : IItemViewModel, IInitable<IElement, ITabViewModel>
{
public interface IElementViewModel : IItemViewModel, IInitable<IElement, ITabViewModel>
{
long? Size { get; set; }
}
long? Size { get; set; }
}

View File

@@ -1,9 +1,8 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface IFileViewModel : IElementViewModel, IInitable<IFileElement, ITabViewModel>
{
public interface IFileViewModel : IElementViewModel, IInitable<IFileElement, ITabViewModel>
{
}
}

View File

@@ -3,19 +3,18 @@ using FileTime.App.Core.Models.Enums;
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface IItemViewModel : IInitable<IItem, ITabViewModel>
{
public interface IItemViewModel : IInitable<IItem, ITabViewModel>
{
IItem? BaseItem { get; set; }
IObservable<IReadOnlyList<ItemNamePart>>? DisplayName { get; set; }
string? DisplayNameText { get; set; }
IObservable<bool>? IsSelected { get; set; }
IObservable<bool>? IsMarked { get; set; }
IObservable<bool> IsAlternative { get; }
IObservable<ItemViewMode> ViewMode { get; set; }
DateTime? CreatedAt { get; set; }
string? Attributes { get; set; }
bool EqualsTo(IItemViewModel? itemViewModel);
}
IItem? BaseItem { get; set; }
IObservable<IReadOnlyList<ItemNamePart>>? DisplayName { get; set; }
string? DisplayNameText { get; set; }
IObservable<bool>? IsSelected { get; set; }
IObservable<bool>? IsMarked { get; set; }
IObservable<bool> IsAlternative { get; }
IObservable<ItemViewMode> ViewMode { get; set; }
DateTime? CreatedAt { get; set; }
string? Attributes { get; set; }
bool EqualsTo(IItemViewModel? itemViewModel);
}

View File

@@ -5,26 +5,25 @@ using FileTime.Core.Models;
using FileTime.Core.Services;
using InitableService;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
public interface ITabViewModel : IInitable<ITab, int>
{
public interface ITabViewModel : IInitable<ITab, int>
{
ITab? Tab { get; }
int TabNumber { get; }
IObservable<bool> IsSelected { get; }
IObservable<IContainer?> CurrentLocation { get; }
IObservable<IItemViewModel?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; }
IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; }
BindedCollection<IItemViewModel>? CurrentItemsCollection { get; }
BindedCollection<IItemViewModel>? SelectedsChildrenCollection { get; }
BindedCollection<IItemViewModel>? ParentsChildrenCollection { get; }
IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; }
void ClearMarkedItems();
void RemoveMarkedItem(IAbsolutePath item);
void AddMarkedItem(IAbsolutePath item);
void ToggleMarkedItem(IAbsolutePath item);
}
ITab? Tab { get; }
int TabNumber { get; }
IObservable<bool> IsSelected { get; }
IObservable<IContainer?> CurrentLocation { get; }
IObservable<IItemViewModel?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; }
IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; }
BindedCollection<IItemViewModel>? CurrentItemsCollection { get; }
BindedCollection<IItemViewModel>? SelectedsChildrenCollection { get; }
BindedCollection<IItemViewModel>? ParentsChildrenCollection { get; }
IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; }
void ClearMarkedItems();
void RemoveMarkedItem(IAbsolutePath item);
void AddMarkedItem(IAbsolutePath item);
void ToggleMarkedItem(IAbsolutePath item);
}

View File

@@ -1,9 +1,8 @@
using System.Reactive.Linq;
namespace FileTime.App.Core.Extensions
namespace FileTime.App.Core.Extensions;
public static class ObservableExtensions
{
public static class ObservableExtensions
{
public static IObservable<T> WhereNotNull<T>(this IObservable<T?> source) => source.Where(c => c != null)!;
}
public static IObservable<T> WhereNotNull<T>(this IObservable<T?> source) => source.Where(c => c != null)!;
}

View File

@@ -1,14 +1,13 @@
using FileTime.App.Core.ViewModels;
using FileTime.Core.Models;
namespace FileTime.App.Core.Extensions
namespace FileTime.App.Core.Extensions;
public static class ViewModelExtensions
{
public static class ViewModelExtensions
public static IAbsolutePath ToAbsolutePath(this IItemViewModel itemViewModel)
{
public static IAbsolutePath ToAbsolutePath(this IItemViewModel itemViewModel)
{
var item = itemViewModel.BaseItem ?? throw new ArgumentException($"{nameof(itemViewModel)} does not have {nameof(IItemViewModel.BaseItem)}");
return new AbsolutePath(item);
}
var item = itemViewModel.BaseItem ?? throw new ArgumentException($"{nameof(itemViewModel)} does not have {nameof(IItemViewModel.BaseItem)}");
return new AbsolutePath(item);
}
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="morelinq" Version="3.3.2" />
<PackageReference Include="MvvmGen" Version="1.1.5" />

View File

@@ -1,51 +1,50 @@
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public class ClipboardService : IClipboardService
{
public class ClipboardService : IClipboardService
private List<IAbsolutePath> _content;
public IReadOnlyList<IAbsolutePath> Content { get; private set; }
public Type? CommandType { get; private set; }
public ClipboardService()
{
private List<IAbsolutePath> _content;
public IReadOnlyList<IAbsolutePath> Content { get; private set; }
public Type? CommandType { get; private set; }
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
}
public ClipboardService()
public void AddContent(IAbsolutePath absolutePath)
{
foreach (var content in _content)
{
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
if (content.Equals(absolutePath)) return;
}
public void AddContent(IAbsolutePath absolutePath)
_content.Add(absolutePath);
}
public void RemoveContent(IAbsolutePath absolutePath)
{
for (var i = 0; i < _content.Count; i++)
{
foreach (var content in _content)
if (_content[i].Equals(absolutePath))
{
if (content.Equals(absolutePath)) return;
_content.RemoveAt(i--);
}
_content.Add(absolutePath);
}
public void RemoveContent(IAbsolutePath absolutePath)
{
for (var i = 0; i < _content.Count; i++)
{
if (_content[i].Equals(absolutePath))
{
_content.RemoveAt(i--);
}
}
}
public void Clear()
{
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
CommandType = null;
}
public void SetCommand<T>() where T : ITransportationCommand
{
CommandType = typeof(T);
}
}
public void Clear()
{
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
CommandType = null;
}
public void SetCommand<T>() where T : ITransportationCommand
{
CommandType = typeof(T);
}
}

View File

@@ -4,51 +4,50 @@ using FileTime.App.Core.Command;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
namespace FileTime.App.Core.Services.CommandHandler;
public abstract class CommandHandlerBase : ICommandHandler
{
public abstract class CommandHandlerBase : ICommandHandler
private readonly Dictionary<Commands, Func<Task>> _commandHandlers = new();
private readonly IAppState? _appState;
protected CommandHandlerBase(IAppState? appState = null)
{
private readonly Dictionary<Commands, Func<Task>> _commandHandlers = new();
private readonly IAppState? _appState;
_appState = appState;
}
protected CommandHandlerBase(IAppState? appState = null)
public bool CanHandleCommand(Commands command) => _commandHandlers.ContainsKey(command);
public async Task HandleCommandAsync(Commands command) => await _commandHandlers[command].Invoke();
protected void AddCommandHandler(Commands command, Func<Task> handler) => _commandHandlers.Add(command, handler);
protected void AddCommandHandlers(IEnumerable<(Commands command, Func<Task> handler)> commandHandlers)
{
foreach (var (command, handler) in commandHandlers)
{
_appState = appState;
}
public bool CanHandleCommand(Commands command) => _commandHandlers.ContainsKey(command);
public async Task HandleCommandAsync(Commands command) => await _commandHandlers[command].Invoke();
protected void AddCommandHandler(Commands command, Func<Task> handler) => _commandHandlers.Add(command, handler);
protected void AddCommandHandlers(IEnumerable<(Commands command, Func<Task> handler)> commandHandlers)
{
foreach (var (command, handler) in commandHandlers)
{
AddCommandHandler(command, handler);
}
}
protected void RemoveCommandHandler(Commands command) => _commandHandlers.Remove(command);
protected IDisposable SaveSelectedTab(Action<ITabViewModel?> handler) => RunWithAppState(appState => appState.SelectedTab.Subscribe(handler));
protected IDisposable SaveCurrentSelectedItem(Action<IItemViewModel?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IItemViewModel?>(null) : t.CurrentSelectedItem).Switch().Subscribe(handler));
protected IDisposable SaveCurrentLocation(Action<IContainer?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IContainer?>(null) : t.CurrentLocation).Switch().Subscribe(handler));
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?)Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
protected IDisposable SaveMarkedItems(Action<IChangeSet<IAbsolutePath>> handler)
=> RunWithAppState(appstate => appstate.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<IAbsolutePath>>() : t.MarkedItems).Switch().Subscribe(handler));
private IDisposable RunWithAppState(Func<IAppState, IDisposable> act)
{
if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(CommandHandlerBase)}.");
return act(_appState);
AddCommandHandler(command, handler);
}
}
protected void RemoveCommandHandler(Commands command) => _commandHandlers.Remove(command);
protected IDisposable SaveSelectedTab(Action<ITabViewModel?> handler) => RunWithAppState(appState => appState.SelectedTab.Subscribe(handler));
protected IDisposable SaveCurrentSelectedItem(Action<IItemViewModel?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IItemViewModel?>(null) : t.CurrentSelectedItem).Switch().Subscribe(handler));
protected IDisposable SaveCurrentLocation(Action<IContainer?> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IContainer?>(null) : t.CurrentLocation).Switch().Subscribe(handler));
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?)Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
protected IDisposable SaveMarkedItems(Action<IChangeSet<IAbsolutePath>> handler)
=> RunWithAppState(appstate => appstate.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<IAbsolutePath>>() : t.MarkedItems).Switch().Subscribe(handler));
private IDisposable RunWithAppState(Func<IAppState, IDisposable> act)
{
if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(CommandHandlerBase)}.");
return act(_appState);
}
}

View File

@@ -6,91 +6,90 @@ using FileTime.Core.Command;
using FileTime.Core.Command.Copy;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
namespace FileTime.App.Core.Services.CommandHandler;
public class ItemManipulationCommandHandler : CommandHandlerBase
{
public class ItemManipulationCommandHandler : CommandHandlerBase
private ITabViewModel? _selectedTab;
private IItemViewModel? _currentSelectedItem;
private readonly ICommandHandlerService _commandHandlerService;
private readonly IClipboardService _clipboardService;
private BindedCollection<IAbsolutePath>? _markedItems;
public ItemManipulationCommandHandler(
IAppState appState,
ICommandHandlerService commandHandlerService,
IClipboardService clipboardService) : base(appState)
{
private ITabViewModel? _selectedTab;
private IItemViewModel? _currentSelectedItem;
private readonly ICommandHandlerService _commandHandlerService;
private readonly IClipboardService _clipboardService;
private BindedCollection<IAbsolutePath>? _markedItems;
_commandHandlerService = commandHandlerService;
_clipboardService = clipboardService;
public ItemManipulationCommandHandler(
IAppState appState,
ICommandHandlerService commandHandlerService,
IClipboardService clipboardService) : base(appState)
SaveSelectedTab(t =>
{
_commandHandlerService = commandHandlerService;
_clipboardService = clipboardService;
_selectedTab = t;
_markedItems?.Dispose();
_markedItems = t == null ? null : new BindedCollection<IAbsolutePath>(t.MarkedItems);
});
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveSelectedTab(t =>
{
_selectedTab = t;
_markedItems?.Dispose();
_markedItems = t == null ? null : new BindedCollection<IAbsolutePath>(t.MarkedItems);
});
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
AddCommandHandlers(new (Commands, Func<Task>)[]
{
(Commands.Copy, Copy),
(Commands.Mark, MarkItem),
(Commands.PasteMerge, PasteMerge),
(Commands.PasteOverwrite, PasteOverwrite),
(Commands.PasteSkip, PasteSkip),
});
}
private async Task MarkItem()
AddCommandHandlers(new (Commands, Func<Task>)[]
{
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
(Commands.Copy, Copy),
(Commands.Mark, MarkItem),
(Commands.PasteMerge, PasteMerge),
(Commands.PasteOverwrite, PasteOverwrite),
(Commands.PasteSkip, PasteSkip),
});
}
_selectedTab.ToggleMarkedItem(new AbsolutePath(_currentSelectedItem.BaseItem));
await _commandHandlerService.HandleCommandAsync(Commands.MoveCursorDown);
}
private async Task MarkItem()
{
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
private Task Copy()
_selectedTab.ToggleMarkedItem(new AbsolutePath(_currentSelectedItem.BaseItem));
await _commandHandlerService.HandleCommandAsync(Commands.MoveCursorDown);
}
private Task Copy()
{
_clipboardService.Clear();
_clipboardService.SetCommand<CopyCommand>();
if ((_markedItems?.Collection.Count ?? 0) > 0)
{
_clipboardService.Clear();
_clipboardService.SetCommand<CopyCommand>();
if ((_markedItems?.Collection.Count ?? 0) > 0)
foreach (var item in _markedItems!.Collection)
{
foreach (var item in _markedItems!.Collection)
{
_clipboardService.AddContent(item);
}
_selectedTab?.ClearMarkedItems();
}
else if (_currentSelectedItem?.BaseItem != null)
{
_clipboardService.AddContent(new AbsolutePath(_currentSelectedItem.BaseItem));
_clipboardService.AddContent(item);
}
return Task.CompletedTask;
_selectedTab?.ClearMarkedItems();
}
else if (_currentSelectedItem?.BaseItem != null)
{
_clipboardService.AddContent(new AbsolutePath(_currentSelectedItem.BaseItem));
}
private async Task PasteMerge()
{
await Paste(TransportMode.Merge);
}
return Task.CompletedTask;
}
private async Task PasteOverwrite()
{
await Paste(TransportMode.Overwrite);
}
private async Task PasteMerge()
{
await Paste(TransportMode.Merge);
}
private async Task PasteSkip()
{
await Paste(TransportMode.Skip);
}
private async Task PasteOverwrite()
{
await Paste(TransportMode.Overwrite);
}
private Task Paste(TransportMode skip)
{
if (_clipboardService.CommandType is null) return Task.CompletedTask;
return Task.CompletedTask;
}
private async Task PasteSkip()
{
await Paste(TransportMode.Skip);
}
private Task Paste(TransportMode skip)
{
if (_clipboardService.CommandType is null) return Task.CompletedTask;
return Task.CompletedTask;
}
}

View File

@@ -4,65 +4,64 @@ using FileTime.App.Core.Extensions;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
namespace FileTime.App.Core.Services.CommandHandler;
public class NavigationCommandHandler : CommandHandlerBase
{
public class NavigationCommandHandler : CommandHandlerBase
private ITabViewModel? _selectedTab;
private IContainer? _currentLocation;
private IItemViewModel? _currentSelectedItem;
private IEnumerable<IItemViewModel> _currentItems = Enumerable.Empty<IItemViewModel>();
public NavigationCommandHandler(IAppState appState) : base(appState)
{
private ITabViewModel? _selectedTab;
private IContainer? _currentLocation;
private IItemViewModel? _currentSelectedItem;
private IEnumerable<IItemViewModel> _currentItems = Enumerable.Empty<IItemViewModel>();
SaveSelectedTab(t => _selectedTab = t);
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentItems(i => _currentItems = i);
public NavigationCommandHandler(IAppState appState) : base(appState)
AddCommandHandlers(new (Commands, Func<Task>)[]
{
SaveSelectedTab(t => _selectedTab = t);
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentItems(i => _currentItems = i);
(Commands.GoUp, GoUp),
(Commands.MoveCursorDown, MoveCursorDown),
(Commands.MoveCursorUp, MoveCursorUp),
(Commands.Open, OpenContainer),
});
}
AddCommandHandlers(new (Commands, Func<Task>)[]
{
(Commands.GoUp, GoUp),
(Commands.MoveCursorDown, MoveCursorDown),
(Commands.MoveCursorUp, MoveCursorUp),
(Commands.Open, OpenContainer),
});
}
private Task OpenContainer()
{
if (_currentSelectedItem is not IContainerViewModel containerViewModel || containerViewModel.Container is null) return Task.CompletedTask;
private Task OpenContainer()
{
if (_currentSelectedItem is not IContainerViewModel containerViewModel || containerViewModel.Container is null) return Task.CompletedTask;
_selectedTab?.Tab?.SetCurrentLocation(containerViewModel.Container);
return Task.CompletedTask;
}
_selectedTab?.Tab?.SetCurrentLocation(containerViewModel.Container);
return Task.CompletedTask;
}
private async Task GoUp()
{
if (_currentLocation?.Parent is not IAbsolutePath parentPath || await parentPath.ResolveAsyncSafe() is not IContainer newContainer) return;
_selectedTab?.Tab?.SetCurrentLocation(newContainer);
}
private async Task GoUp()
{
if (_currentLocation?.Parent is not IAbsolutePath parentPath || await parentPath.ResolveAsyncSafe() is not IContainer newContainer) return;
_selectedTab?.Tab?.SetCurrentLocation(newContainer);
}
private Task MoveCursorDown()
{
SelectNewSelectedItem(i => i.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).Skip(1).FirstOrDefault());
return Task.CompletedTask;
}
private Task MoveCursorDown()
{
SelectNewSelectedItem(i => i.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).Skip(1).FirstOrDefault());
return Task.CompletedTask;
}
private Task MoveCursorUp()
{
SelectNewSelectedItem(i => i.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).LastOrDefault());
return Task.CompletedTask;
}
private Task MoveCursorUp()
{
SelectNewSelectedItem(i => i.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).LastOrDefault());
return Task.CompletedTask;
}
private void SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
{
if (_selectedTab is null || _currentLocation is null) return;
private void SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
{
if (_selectedTab is null || _currentLocation is null) return;
var newSelectedItem = getNewSelected(_currentItems);
if (newSelectedItem == null) return;
var newSelectedItem = getNewSelected(_currentItems);
if (newSelectedItem == null) return;
_selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath());
}
_selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath());
}
}

View File

@@ -1,80 +1,79 @@
using FileTime.App.Core.Command;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public class CommandHandlerService : ICommandHandlerService
{
public class CommandHandlerService : ICommandHandlerService
private readonly Lazy<IEnumerable<ICommandHandler>> _commandHandlers;
public CommandHandlerService(IServiceProvider serviceProvider)
{
private readonly Lazy<IEnumerable<ICommandHandler>> _commandHandlers;
_commandHandlers = new Lazy<IEnumerable<ICommandHandler>>(() => serviceProvider.GetServices<ICommandHandler>());
public CommandHandlerService(IServiceProvider serviceProvider)
//(Commands.AutoRefresh, ToggleAutoRefresh),
//(Commands.ChangeTimelineMode, ChangeTimelineMode),
//(Commands.CloseTab, CloseTab),
//(Commands.Compress, Compress),
//(Commands.Copy, Copy),
//(Commands.CopyHash, CopyHash),
//(Commands.CopyPath, CopyPath),
//(Commands.CreateContainer, CreateContainer),
//(Commands.CreateElement, CreateElement),
//(Commands.Cut, Cut),
//(Commands.Edit, Edit),
//(Commands.EnterRapidTravel, EnterRapidTravelMode),
//(Commands.FindByName, FindByName),
//(Commands.FindByNameRegex, FindByNameRegex),
//(Commands.GoToHome, GotToHome),
//(Commands.GoToPath, GoToContainer),
//(Commands.GoToProvider, GotToProvider),
//(Commands.GoToRoot, GotToRoot),
//(Commands.HardDelete, HardDelete),
//(Commands.Mark, MarkCurrentItem),
//(Commands.MoveCursorDownPage, MoveCursorDownPage),
//(Commands.MoveCursorUpPage, MoveCursorUpPage),
//(Commands.MoveToFirst, MoveToFirst),
//(Commands.MoveToLast, MoveToLast),
//(Commands.NextTimelineBlock, SelectNextTimelineBlock),
//(Commands.NextTimelineCommand, SelectNextTimelineCommand),
//(Commands.OpenInFileBrowser, OpenInDefaultFileExplorer),
//(Commands.OpenOrRun, OpenOrRun),
//(Commands.PasteMerge, PasteMerge),
//(Commands.PasteOverwrite, PasteOverwrite),
//(Commands.PasteSkip, PasteSkip),
//(Commands.PinFavorite, PinFavorite),
//(Commands.PreviousTimelineBlock, SelectPreviousTimelineBlock),
//(Commands.PreviousTimelineCommand, SelectPreviousTimelineCommand),
//(Commands.Refresh, RefreshCurrentLocation),
//(Commands.Rename, Rename),
//(Commands.RunCommand, RunCommandInContainer),
//(Commands.ScanContainerSize, ScanContainerSize),
//(Commands.ShowAllShotcut, ShowAllShortcut),
//(Commands.SoftDelete, SoftDelete),
//(Commands.SwitchToLastTab, async() => await SwitchToTab(-1)),
//(Commands.SwitchToTab1, async() => await SwitchToTab(1)),
//(Commands.SwitchToTab2, async() => await SwitchToTab(2)),
//(Commands.SwitchToTab3, async() => await SwitchToTab(3)),
//(Commands.SwitchToTab4, async() => await SwitchToTab(4)),
//(Commands.SwitchToTab5, async() => await SwitchToTab(5)),
//(Commands.SwitchToTab6, async() => await SwitchToTab(6)),
//(Commands.SwitchToTab7, async() => await SwitchToTab(7)),
//(Commands.SwitchToTab8, async() => await SwitchToTab(8)),
//(Commands.TimelinePause, PauseTimeline),
//(Commands.TimelineRefresh, RefreshTimeline),
//(Commands.TimelineStart, ContinueTimeline),
//(Commands.ToggleAdvancedIcons, ToggleAdvancedIcons),
//(Commands.ToggleHidden, ToggleHidden),
}
public async Task HandleCommandAsync(Commands command)
{
var handler = _commandHandlers.Value.FirstOrDefault(h => h.CanHandleCommand(command));
if (handler != null)
{
_commandHandlers = new Lazy<IEnumerable<ICommandHandler>>(() => serviceProvider.GetServices<ICommandHandler>());
//(Commands.AutoRefresh, ToggleAutoRefresh),
//(Commands.ChangeTimelineMode, ChangeTimelineMode),
//(Commands.CloseTab, CloseTab),
//(Commands.Compress, Compress),
//(Commands.Copy, Copy),
//(Commands.CopyHash, CopyHash),
//(Commands.CopyPath, CopyPath),
//(Commands.CreateContainer, CreateContainer),
//(Commands.CreateElement, CreateElement),
//(Commands.Cut, Cut),
//(Commands.Edit, Edit),
//(Commands.EnterRapidTravel, EnterRapidTravelMode),
//(Commands.FindByName, FindByName),
//(Commands.FindByNameRegex, FindByNameRegex),
//(Commands.GoToHome, GotToHome),
//(Commands.GoToPath, GoToContainer),
//(Commands.GoToProvider, GotToProvider),
//(Commands.GoToRoot, GotToRoot),
//(Commands.HardDelete, HardDelete),
//(Commands.Mark, MarkCurrentItem),
//(Commands.MoveCursorDownPage, MoveCursorDownPage),
//(Commands.MoveCursorUpPage, MoveCursorUpPage),
//(Commands.MoveToFirst, MoveToFirst),
//(Commands.MoveToLast, MoveToLast),
//(Commands.NextTimelineBlock, SelectNextTimelineBlock),
//(Commands.NextTimelineCommand, SelectNextTimelineCommand),
//(Commands.OpenInFileBrowser, OpenInDefaultFileExplorer),
//(Commands.OpenOrRun, OpenOrRun),
//(Commands.PasteMerge, PasteMerge),
//(Commands.PasteOverwrite, PasteOverwrite),
//(Commands.PasteSkip, PasteSkip),
//(Commands.PinFavorite, PinFavorite),
//(Commands.PreviousTimelineBlock, SelectPreviousTimelineBlock),
//(Commands.PreviousTimelineCommand, SelectPreviousTimelineCommand),
//(Commands.Refresh, RefreshCurrentLocation),
//(Commands.Rename, Rename),
//(Commands.RunCommand, RunCommandInContainer),
//(Commands.ScanContainerSize, ScanContainerSize),
//(Commands.ShowAllShotcut, ShowAllShortcut),
//(Commands.SoftDelete, SoftDelete),
//(Commands.SwitchToLastTab, async() => await SwitchToTab(-1)),
//(Commands.SwitchToTab1, async() => await SwitchToTab(1)),
//(Commands.SwitchToTab2, async() => await SwitchToTab(2)),
//(Commands.SwitchToTab3, async() => await SwitchToTab(3)),
//(Commands.SwitchToTab4, async() => await SwitchToTab(4)),
//(Commands.SwitchToTab5, async() => await SwitchToTab(5)),
//(Commands.SwitchToTab6, async() => await SwitchToTab(6)),
//(Commands.SwitchToTab7, async() => await SwitchToTab(7)),
//(Commands.SwitchToTab8, async() => await SwitchToTab(8)),
//(Commands.TimelinePause, PauseTimeline),
//(Commands.TimelineRefresh, RefreshTimeline),
//(Commands.TimelineStart, ContinueTimeline),
//(Commands.ToggleAdvancedIcons, ToggleAdvancedIcons),
//(Commands.ToggleHidden, ToggleHidden),
}
public async Task HandleCommandAsync(Commands command)
{
var handler = _commandHandlers.Value.FirstOrDefault(h => h.CanHandleCommand(command));
if (handler != null)
{
await handler.HandleCommandAsync(command);
}
await handler.HandleCommandAsync(command);
}
}
}

View File

@@ -1,56 +1,55 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.Services
namespace FileTime.App.Core.Services;
public class ItemNameConverterService : IItemNameConverterService
{
public class ItemNameConverterService : IItemNameConverterService
public List<ItemNamePart> GetDisplayName(string name, string? searchText)
{
public List<ItemNamePart> GetDisplayName(string name, string? searchText)
var nameParts = new List<ItemNamePart>();
searchText = searchText?.ToLower();
if (!string.IsNullOrEmpty(searchText))
{
var nameParts = new List<ItemNamePart>();
searchText = searchText?.ToLower();
var nameLeft = name;
if (!string.IsNullOrEmpty(searchText))
while (nameLeft.ToLower().IndexOf(searchText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
{
var nameLeft = name;
var before = rapidTextStart > 0 ? nameLeft.Substring(0, rapidTextStart) : null;
var rapidTravel = nameLeft.Substring(rapidTextStart, searchText.Length);
while (nameLeft.ToLower().IndexOf(searchText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
nameLeft = nameLeft.Substring(rapidTextStart + searchText.Length);
if (before != null)
{
var before = rapidTextStart > 0 ? nameLeft.Substring(0, rapidTextStart) : null;
var rapidTravel = nameLeft.Substring(rapidTextStart, searchText.Length);
nameLeft = nameLeft.Substring(rapidTextStart + searchText.Length);
if (before != null)
{
nameParts.Add(new ItemNamePart(before));
}
nameParts.Add(new ItemNamePart(rapidTravel, true));
nameParts.Add(new ItemNamePart(before));
}
if (nameLeft.Length > 0)
{
nameParts.Add(new ItemNamePart(nameLeft));
}
nameParts.Add(new ItemNamePart(rapidTravel, true));
}
else
if (nameLeft.Length > 0)
{
nameParts.Add(new ItemNamePart(name));
nameParts.Add(new ItemNamePart(nameLeft));
}
return nameParts;
}
public string GetFileName(string fullName)
else
{
var parts = fullName.Split('.');
var fileName = string.Join('.', parts[..^1]);
return string.IsNullOrEmpty(fileName) ? fullName : fileName;
}
public string GetFileExtension(string fullName)
{
var parts = fullName.Split('.');
return parts.Length == 1 || (parts.Length == 2 && string.IsNullOrEmpty(parts[0])) ? "" : parts[^1];
nameParts.Add(new ItemNamePart(name));
}
return nameParts;
}
}
public string GetFileName(string fullName)
{
var parts = fullName.Split('.');
var fileName = string.Join('.', parts[..^1]);
return string.IsNullOrEmpty(fileName) ? fullName : fileName;
}
public string GetFileExtension(string fullName)
{
var parts = fullName.Split('.');
return parts.Length == 1 || (parts.Length == 2 && string.IsNullOrEmpty(parts[0])) ? "" : parts[^1];
}
}

View File

@@ -3,27 +3,26 @@ using FileTime.App.Core.Services.CommandHandler;
using FileTime.App.Core.ViewModels;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.App.Core
{
public static class Startup
{
public static IServiceCollection AddCoreAppServices(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddTransient<ITabViewModel, TabViewModel>()
.AddTransient<IContainerViewModel, ContainerViewModel>()
.AddTransient<IElementViewModel, ElementViewModel>()
.AddTransient<IItemNameConverterService, ItemNameConverterService>()
.AddSingleton<ICommandHandlerService, CommandHandlerService>()
.AddSingleton<IClipboardService, ClipboardService>()
.AddCommandHandlers();
}
namespace FileTime.App.Core;
private static IServiceCollection AddCommandHandlers(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<ICommandHandler, NavigationCommandHandler>()
.AddSingleton<ICommandHandler, ItemManipulationCommandHandler>();
}
public static class Startup
{
public static IServiceCollection AddCoreAppServices(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddTransient<ITabViewModel, TabViewModel>()
.AddTransient<IContainerViewModel, ContainerViewModel>()
.AddTransient<IElementViewModel, ElementViewModel>()
.AddTransient<IItemNameConverterService, ItemNameConverterService>()
.AddSingleton<ICommandHandlerService, CommandHandlerService>()
.AddSingleton<IClipboardService, ClipboardService>()
.AddCommandHandlers();
}
private static IServiceCollection AddCommandHandlers(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<ICommandHandler, NavigationCommandHandler>()
.AddSingleton<ICommandHandler, ItemManipulationCommandHandler>();
}
}

View File

@@ -5,50 +5,49 @@ using FileTime.App.Core.Models.Enums;
using MvvmGen;
using MoreLinq;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel]
public abstract partial class AppStateBase : IAppState
{
[ViewModel]
public abstract partial class AppStateBase : IAppState
private readonly BehaviorSubject<string?> _searchText = new(null);
private readonly BehaviorSubject<ITabViewModel?> _selectedTab = new(null);
private readonly BehaviorSubject<IEnumerable<ITabViewModel>> _tabs = new(Enumerable.Empty<ITabViewModel>());
[Property]
private ViewMode _viewMode;
public ObservableCollection<ITabViewModel> Tabs { get; } = new();
public IObservable<string?> SearchText { get; private set; }
public IObservable<ITabViewModel?> SelectedTab { get; private set; }
partial void OnInitialize()
{
private readonly BehaviorSubject<string?> _searchText = new(null);
private readonly BehaviorSubject<ITabViewModel?> _selectedTab = new(null);
private readonly BehaviorSubject<IEnumerable<ITabViewModel>> _tabs = new(Enumerable.Empty<ITabViewModel>());
Tabs.CollectionChanged += (_, _) => _tabs.OnNext(Tabs);
SearchText = _searchText.AsObservable();
SelectedTab = Observable.CombineLatest(_tabs, _selectedTab, GetSelectedTab);
}
[Property]
private ViewMode _viewMode;
public void AddTab(ITabViewModel tabViewModel)
{
Tabs.Add(tabViewModel);
}
public ObservableCollection<ITabViewModel> Tabs { get; } = new();
public IObservable<string?> SearchText { get; private set; }
public void RemoveTab(ITabViewModel tabViewModel)
{
if (!Tabs.Contains(tabViewModel)) return;
public IObservable<ITabViewModel?> SelectedTab { get; private set; }
Tabs.Remove(tabViewModel);
}
partial void OnInitialize()
{
Tabs.CollectionChanged += (_, _) => _tabs.OnNext(Tabs);
SearchText = _searchText.AsObservable();
SelectedTab = Observable.CombineLatest(_tabs, _selectedTab, GetSelectedTab);
}
public void SetSearchText(string? searchText) => _searchText.OnNext(searchText);
public void AddTab(ITabViewModel tabViewModel)
{
Tabs.Add(tabViewModel);
}
public void SetSelectedTab(ITabViewModel tabToSelect) => _selectedTab.OnNext(tabToSelect);
public void RemoveTab(ITabViewModel tabViewModel)
{
if (!Tabs.Contains(tabViewModel)) return;
Tabs.Remove(tabViewModel);
}
public void SetSearchText(string? searchText) => _searchText.OnNext(searchText);
public void SetSelectedTab(ITabViewModel tabToSelect) => _selectedTab.OnNext(tabToSelect);
private ITabViewModel? GetSelectedTab(IEnumerable<ITabViewModel> tabs, ITabViewModel? expectedSelectedTab)
{
var (prefered, others) = tabs.OrderBy(t => t.TabNumber).Partition(t => t.TabNumber >= (expectedSelectedTab?.TabNumber ?? 0));
return prefered.Concat(others).FirstOrDefault();
}
private ITabViewModel? GetSelectedTab(IEnumerable<ITabViewModel> tabs, ITabViewModel? expectedSelectedTab)
{
var (prefered, others) = tabs.OrderBy(t => t.TabNumber).Partition(t => t.TabNumber >= (expectedSelectedTab?.TabNumber ?? 0));
return prefered.Concat(others).FirstOrDefault();
}
}

View File

@@ -2,21 +2,20 @@ using FileTime.App.Core.Services;
using FileTime.Core.Models;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel(GenerateConstructor = false)]
public partial class ContainerSizeContainerViewModel : ItemViewModel, IContainerSizeContainerViewModel
{
[ViewModel(GenerateConstructor = false)]
public partial class ContainerSizeContainerViewModel : ItemViewModel, IContainerSizeContainerViewModel
[Property]
private long _size;
public ContainerSizeContainerViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
[Property]
private long _size;
}
public ContainerSizeContainerViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
}
public void Init(IContainer item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
public void Init(IContainer item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
}

View File

@@ -2,20 +2,19 @@ using FileTime.App.Core.Services;
using FileTime.Core.Models;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel(GenerateConstructor = false)]
public partial class ContainerViewModel : ItemViewModel, IContainerViewModel
{
[ViewModel(GenerateConstructor = false)]
public partial class ContainerViewModel : ItemViewModel, IContainerViewModel
public IContainer? Container => BaseItem as IContainer;
public ContainerViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
public IContainer? Container => BaseItem as IContainer;
}
public ContainerViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
}
public void Init(IContainer item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
public void Init(IContainer item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
}

View File

@@ -2,21 +2,20 @@ using FileTime.App.Core.Services;
using FileTime.Core.Models;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel(GenerateConstructor = false)]
public partial class ElementViewModel : ItemViewModel, IElementViewModel
{
[ViewModel(GenerateConstructor = false)]
public partial class ElementViewModel : ItemViewModel, IElementViewModel
[Property]
private long? _size;
public ElementViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
[Property]
private long? _size;
}
public ElementViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
}
public void Init(IElement item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
public void Init(IElement item, ITabViewModel parentTab)
{
Init((IItem)item, parentTab);
}
}

View File

@@ -2,18 +2,17 @@ using FileTime.App.Core.Services;
using FileTime.Core.Models;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
{
[ViewModel(GenerateConstructor = false)]
public partial class FileViewModel : ElementViewModel, IFileViewModel
{
public FileViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
}
namespace FileTime.App.Core.ViewModels;
public void Init(IFileElement item, ITabViewModel parentTab)
{
Init((IElement)item, parentTab);
}
[ViewModel(GenerateConstructor = false)]
public partial class FileViewModel : ElementViewModel, IFileViewModel
{
public FileViewModel(IItemNameConverterService _itemNameConverterService, IAppState _appState) : base(_itemNameConverterService, _appState)
{
}
public void Init(IFileElement item, ITabViewModel parentTab)
{
Init((IElement)item, parentTab);
}
}

View File

@@ -7,54 +7,54 @@ using FileTime.Core.Models;
using MoreLinq;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel]
[Inject(typeof(IAppState), "_appState")]
[Inject(typeof(IItemNameConverterService), "_itemNameConverterService")]
public abstract partial class ItemViewModel : IItemViewModel
{
[ViewModel]
[Inject(typeof(IAppState), "_appState")]
[Inject(typeof(IItemNameConverterService), "_itemNameConverterService")]
public abstract partial class ItemViewModel : IItemViewModel
[Property]
private IItem? _baseItem;
[Property]
private IObservable<IReadOnlyList<ItemNamePart>>? _displayName;
[Property]
private string? _displayNameText;
[Property]
private IObservable<bool>? _isSelected;
[Property]
private IObservable<bool>? _isMarked;
[Property]
private IObservable<ItemViewMode> _viewMode;
[Property]
private DateTime? _createdAt;
[Property]
private string? _attributes;
[Property]
private IObservable<bool> _isAlternative;
public void Init(IItem item, ITabViewModel parentTab)
{
[Property]
private IItem? _baseItem;
BaseItem = item;
DisplayName = _appState.SearchText.Select(s => _itemNameConverterService.GetDisplayName(item.DisplayName, s));
DisplayNameText = item.DisplayName;
IsMarked = parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path.Path == item.FullName?.Path));
IsSelected = parentTab.CurrentSelectedItem.Select(EqualsTo);
IsAlternative = parentTab.CurrentItemsCollectionObservable.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode);
Attributes = item.Attributes;
CreatedAt = item.CreatedAt;
}
[Property]
private IObservable<IReadOnlyList<ItemNamePart>>? _displayName;
[Property]
private string? _displayNameText;
[Property]
private IObservable<bool>? _isSelected;
[Property]
private IObservable<bool>? _isMarked;
[Property]
private IObservable<ItemViewMode> _viewMode;
[Property]
private DateTime? _createdAt;
[Property]
private string? _attributes;
[Property]
private IObservable<bool> _isAlternative;
public void Init(IItem item, ITabViewModel parentTab)
{
BaseItem = item;
DisplayName = _appState.SearchText.Select(s => _itemNameConverterService.GetDisplayName(item.DisplayName, s));
DisplayNameText = item.DisplayName;
IsMarked = parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path.Path == item.FullName?.Path));
IsSelected = parentTab.CurrentSelectedItem.Select(EqualsTo);
IsAlternative = parentTab.CurrentItemsCollectionObservable.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode);
Attributes = item.Attributes;
CreatedAt = item.CreatedAt;
}
private ItemViewMode GenerateViewMode(bool isMarked, bool isSelected, bool sAlternative)
private ItemViewMode GenerateViewMode(bool isMarked, bool isSelected, bool sAlternative)
=> (isMarked, isSelected, sAlternative) switch
{
(true, true, _) => ItemViewMode.MarkedSelected,
@@ -65,9 +65,8 @@ namespace FileTime.App.Core.ViewModels
_ => ItemViewMode.Default
};
public bool EqualsTo(IItemViewModel? itemViewModel)
{
return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path;
}
public bool EqualsTo(IItemViewModel? itemViewModel)
{
return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path;
}
}

View File

@@ -9,118 +9,118 @@ using FileTime.Tools.Extensions;
using Microsoft.Extensions.DependencyInjection;
using MvvmGen;
namespace FileTime.App.Core.ViewModels
namespace FileTime.App.Core.ViewModels;
[ViewModel]
public partial class TabViewModel : ITabViewModel, IDisposable
{
[ViewModel]
public partial class TabViewModel : ITabViewModel, IDisposable
private readonly IServiceProvider _serviceProvider;
private readonly IItemNameConverterService _itemNameConverterService;
private readonly IAppState _appState;
private readonly IRxSchedulerService _rxSchedulerService;
private readonly SourceList<IAbsolutePath> _markedItems = new();
private readonly List<IDisposable> _disposables = new();
private bool disposed;
public ITab? Tab { get; private set; }
public int TabNumber { get; private set; }
public IObservable<bool> IsSelected { get; }
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!;
public IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } = null!;
[Property]
private BindedCollection<IItemViewModel>? _currentItemsCollection;
[Property]
private BindedCollection<IItemViewModel>? _parentsChildrenCollection;
[Property]
private BindedCollection<IItemViewModel>? _selectedsChildrenCollection;
public TabViewModel(
IServiceProvider serviceProvider,
IItemNameConverterService itemNameConverterService,
IAppState appState,
IRxSchedulerService rxSchedulerService)
{
private readonly IServiceProvider _serviceProvider;
private readonly IItemNameConverterService _itemNameConverterService;
private readonly IAppState _appState;
private readonly IRxSchedulerService _rxSchedulerService;
private readonly SourceList<IAbsolutePath> _markedItems = new();
private readonly List<IDisposable> _disposables = new();
private bool disposed;
_serviceProvider = serviceProvider;
_itemNameConverterService = itemNameConverterService;
_appState = appState;
public ITab? Tab { get; private set; }
public int TabNumber { get; private set; }
MarkedItems = _markedItems.Connect().StartWithEmpty();
IsSelected = _appState.SelectedTab.Select(s => s == this);
_rxSchedulerService = rxSchedulerService;
}
public IObservable<bool> IsSelected { get; }
public void Init(ITab tab, int tabNumber)
{
Tab = tab;
TabNumber = tabNumber;
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!;
public IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
CurrentLocation = tab.CurrentLocation.AsObservable();
CurrentItems = tab.CurrentItems
.Select(items => items?.Transform(MapItemToViewModel))
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } = null!;
[Property]
private BindedCollection<IItemViewModel>? _currentItemsCollection;
[Property]
private BindedCollection<IItemViewModel>? _parentsChildrenCollection;
[Property]
private BindedCollection<IItemViewModel>? _selectedsChildrenCollection;
public TabViewModel(
IServiceProvider serviceProvider,
IItemNameConverterService itemNameConverterService,
IAppState appState,
IRxSchedulerService rxSchedulerService)
{
_serviceProvider = serviceProvider;
_itemNameConverterService = itemNameConverterService;
_appState = appState;
MarkedItems = _markedItems.Connect().StartWithEmpty();
IsSelected = _appState.SelectedTab.Select(s => s == this);
_rxSchedulerService = rxSchedulerService;
}
public void Init(ITab tab, int tabNumber)
{
Tab = tab;
TabNumber = tabNumber;
CurrentLocation = tab.CurrentLocation.AsObservable();
CurrentItems = tab.CurrentItems
.Select(items => items?.Transform(MapItemToViewModel))
.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
CurrentSelectedItem =
Observable.CombineLatest(
CurrentSelectedItem =
Observable.CombineLatest(
CurrentItems,
tab.CurrentSelectedItem,
(currentItems, currentSelectedItemPath) =>
currentItems == null
? Observable.Return((IItemViewModel?)null)
: currentItems
.ToCollection()
.Select(items => items.FirstOrDefault(i => i.BaseItem?.FullName == currentSelectedItemPath?.Path))
? Observable.Return((IItemViewModel?)null)
: currentItems
.ToCollection()
.Select(items => items.FirstOrDefault(i => i.BaseItem?.FullName == currentSelectedItemPath?.Path))
)
.Switch()
.Publish(null)
.RefCount();
SelectedsChildren = InitSelectedsChildren();
ParentsChildren = InitParentsChildren();
SelectedsChildren = InitSelectedsChildren();
ParentsChildren = InitParentsChildren();
CurrentItemsCollectionObservable = CurrentItems
.Select(c => c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?)null))
.Switch()
.Publish(null)
.RefCount();
CurrentItemsCollectionObservable = CurrentItems
.Select(c => c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?)null))
.Switch()
.Publish(null)
.RefCount();
CurrentItems.Subscribe(children =>
{
CurrentItemsCollection?.Dispose();
CurrentItemsCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
CurrentItems.Subscribe(children =>
{
CurrentItemsCollection?.Dispose();
CurrentItemsCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
ParentsChildren.Subscribe(children =>
{
ParentsChildrenCollection?.Dispose();
ParentsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
ParentsChildren.Subscribe(children =>
{
ParentsChildrenCollection?.Dispose();
ParentsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
SelectedsChildren.Subscribe(children =>
{
SelectedsChildrenCollection?.Dispose();
SelectedsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
SelectedsChildren.Subscribe(children =>
{
SelectedsChildrenCollection?.Dispose();
SelectedsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
tab.CurrentLocation.Subscribe((_) => _markedItems.Clear());
tab.CurrentLocation.Subscribe((_) => _markedItems.Clear());
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitSelectedsChildren()
{
var currentSelectedItemThrottled = CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)).Publish(null).RefCount();
return Observable.Merge(
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitSelectedsChildren()
{
var currentSelectedItemThrottled = CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)).Publish(null).RefCount();
return Observable.Merge(
currentSelectedItemThrottled
.WhereNotNull()
.OfType<IContainerViewModel>()
@@ -136,17 +136,17 @@ namespace FileTime.App.Core.ViewModels
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
}
}
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitParentsChildren()
{
var parentThrottled = CurrentLocation
.Select(l => l?.Parent)
.DistinctUntilChanged()
.Publish(null)
.RefCount();
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitParentsChildren()
{
var parentThrottled = CurrentLocation
.Select(l => l?.Parent)
.DistinctUntilChanged()
.Publish(null)
.RefCount();
return Observable.Merge(
return Observable.Merge(
parentThrottled
.Where(p => p is not null)
.Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync()))
@@ -162,85 +162,84 @@ namespace FileTime.App.Core.ViewModels
.SubscribeOn(_rxSchedulerService.GetUIScheduler())
.Publish(null)
.RefCount();
}
}
private static async Task<IItem> MapItem(IAbsolutePath item)
=> await item.ResolveAsync(forceResolve: true, itemInitializationSettings: new ItemInitializationSettings(true));
private IItemViewModel MapItemToViewModel(IItem item)
{
if (item is IContainer container)
{
var containerViewModel = _serviceProvider.GetInitableResolver<IContainer, ITabViewModel>(container, this).GetRequiredService<IContainerViewModel>();
return containerViewModel;
}
else if (item is IFileElement fileElement)
{
var fileViewModel = _serviceProvider.GetInitableResolver<IFileElement, ITabViewModel>(fileElement, this).GetRequiredService<IFileViewModel>();
fileViewModel.Size = fileElement.Size;
return fileViewModel;
}
else if (item is IElement element)
{
var elementViewModel = _serviceProvider.GetInitableResolver<IElement, ITabViewModel>(element, this).GetRequiredService<IElementViewModel>();
return elementViewModel;
}
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
}
public void AddMarkedItem(IAbsolutePath item) => _markedItems.Add(item);
public void RemoveMarkedItem(IAbsolutePath item)
{
var itemsToRemove = _markedItems.Items.Where(i => i.Path.Path == item.Path.Path).ToList();
_markedItems.RemoveMany(itemsToRemove);
}
public void ToggleMarkedItem(IAbsolutePath item)
{
if (_markedItems.Items.Any(i => i.Path.Path == item.Path.Path))
{
RemoveMarkedItem(item);
}
else
{
AddMarkedItem(item);
}
}
public void ClearMarkedItems()
{
_markedItems.Clear();
}
~TabViewModel() => Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed && disposing)
{
foreach (var disposable in _disposables)
{
try
{
disposable.Dispose();
}
catch { }
}
}
disposed = true;
}
}
private static async Task<IItem> MapItem(IAbsolutePath item)
=> await item.ResolveAsync(forceResolve: true, itemInitializationSettings: new ItemInitializationSettings(true));
private IItemViewModel MapItemToViewModel(IItem item)
{
if (item is IContainer container)
{
var containerViewModel = _serviceProvider.GetInitableResolver<IContainer, ITabViewModel>(container, this).GetRequiredService<IContainerViewModel>();
return containerViewModel;
}
else if (item is IFileElement fileElement)
{
var fileViewModel = _serviceProvider.GetInitableResolver<IFileElement, ITabViewModel>(fileElement, this).GetRequiredService<IFileViewModel>();
fileViewModel.Size = fileElement.Size;
return fileViewModel;
}
else if (item is IElement element)
{
var elementViewModel = _serviceProvider.GetInitableResolver<IElement, ITabViewModel>(element, this).GetRequiredService<IElementViewModel>();
return elementViewModel;
}
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
}
public void AddMarkedItem(IAbsolutePath item) => _markedItems.Add(item);
public void RemoveMarkedItem(IAbsolutePath item)
{
var itemsToRemove = _markedItems.Items.Where(i => i.Path.Path == item.Path.Path).ToList();
_markedItems.RemoveMany(itemsToRemove);
}
public void ToggleMarkedItem(IAbsolutePath item)
{
if (_markedItems.Items.Any(i => i.Path.Path == item.Path.Path))
{
RemoveMarkedItem(item);
}
else
{
AddMarkedItem(item);
}
}
public void ClearMarkedItems()
{
_markedItems.Clear();
}
~TabViewModel() => Dispose(false);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed && disposing)
{
foreach (var disposable in _disposables)
{
try
{
disposable.Dispose();
}
catch { }
}
}
disposed = true;
}
}

View File

@@ -3,18 +3,17 @@ using FileTime.Core.Services;
using FileTime.Providers.Local;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.App.DependencyInjection
{
public static class DependencyInjection
{
public static IServiceCollection RegisterDefaultServices(IServiceCollection? serviceCollection = null)
{
serviceCollection ??= new ServiceCollection();
namespace FileTime.App.DependencyInjection;
return serviceCollection
.AddTransient<ITab, Tab>()
.AddCoreAppServices()
.AddLocalServices();
}
public static class DependencyInjection
{
public static IServiceCollection RegisterDefaultServices(IServiceCollection? serviceCollection = null)
{
serviceCollection ??= new ServiceCollection();
return serviceCollection
.AddTransient<ITab, Tab>()
.AddCoreAppServices()
.AddLocalServices();
}
}

View File

@@ -5,6 +5,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
</ItemGroup>

View File

@@ -6,4 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>

View File

@@ -7,6 +7,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" />
</ItemGroup>

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Behaviors
namespace FileTime.Core.Behaviors;
public interface IOnContainerEnter
{
public interface IOnContainerEnter
{
Task OnEnter();
}
Task OnEnter();
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Command
namespace FileTime.Core.Command;
public interface ICommand
{
public interface ICommand
{
}
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Command
namespace FileTime.Core.Command;
public interface ITransportationCommand : ICommand
{
public interface ITransportationCommand : ICommand
{
}
}

View File

@@ -1,9 +1,8 @@
namespace FileTime.Core.Enums
namespace FileTime.Core.Enums;
public enum AbsolutePathType
{
public enum AbsolutePathType
{
Unknown,
Container,
Element
}
Unknown,
Container,
Element
}

View File

@@ -1,9 +1,8 @@
namespace FileTime.Core.Enums
namespace FileTime.Core.Enums;
public enum SupportsDelete
{
public enum SupportsDelete
{
False,
True,
HardDeleteOnly,
}
False,
True,
HardDeleteOnly,
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DynamicData" Version="7.6.7" />
<PackageReference Include="System.Reactive" Version="5.0.0" />

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public static class Constants
{
public static class Constants
{
public const char SeparatorChar = '/';
}
public const char SeparatorChar = '/';
}

View File

@@ -1,17 +1,16 @@
namespace FileTime.Core.Models
{
public record FullName(string Path)
{
public FullName? GetParent()
{
if (Path is null) return null;
namespace FileTime.Core.Models;
var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar);
return pathParts.Length switch
{
> 1 => new(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))),
_ => null
};
}
public record FullName(string Path)
{
public FullName? GetParent()
{
if (Path is null) return null;
var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar);
return pathParts.Length switch
{
> 1 => new(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))),
_ => null
};
}
}

View File

@@ -1,16 +1,15 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
{
public interface IAbsolutePath
{
IContentProvider ContentProvider { get; }
IContentProvider? VirtualContentProvider { get; }
FullName Path { get; }
AbsolutePathType Type { get; }
namespace FileTime.Core.Models;
Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
}
public interface IAbsolutePath
{
IContentProvider ContentProvider { get; }
IContentProvider? VirtualContentProvider { get; }
FullName Path { get; }
AbsolutePathType Type { get; }
Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default);
}

View File

@@ -1,10 +1,9 @@
using DynamicData;
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public interface IContainer : IItem
{
public interface IContainer : IItem
{
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; }
IObservable<bool> IsLoading { get; }
}
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; }
IObservable<bool> IsLoading { get; }
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public interface IElement : IItem
{
public interface IElement : IItem
{
}
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public interface IFileElement : IElement
{
public interface IFileElement : IElement
{
long Size { get; }
}
long Size { get; }
}

View File

@@ -1,23 +1,22 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public interface IItem
{
public interface IItem
{
string Name { get; }
string DisplayName { get; }
FullName? FullName { get; }
NativePath? NativePath { get; }
IAbsolutePath? Parent { get; }
bool IsHidden { get; }
bool IsExists { get; }
DateTime? CreatedAt { get; }
SupportsDelete CanDelete { get; }
bool CanRename { get; }
IContentProvider Provider { get; }
string? Attributes { get; }
AbsolutePathType Type { get; }
IObservable<IEnumerable<Exception>> Exceptions { get; }
}
string Name { get; }
string DisplayName { get; }
FullName? FullName { get; }
NativePath? NativePath { get; }
IAbsolutePath? Parent { get; }
bool IsHidden { get; }
bool IsExists { get; }
DateTime? CreatedAt { get; }
SupportsDelete CanDelete { get; }
bool CanRename { get; }
IContentProvider Provider { get; }
string? Attributes { get; }
AbsolutePathType Type { get; }
IObservable<IEnumerable<Exception>> Exceptions { get; }
}

View File

@@ -1,12 +1,11 @@
namespace FileTime.Core.Models
{
public readonly struct ItemInitializationSettings
{
public readonly bool SkipChildInitialization;
namespace FileTime.Core.Models;
public ItemInitializationSettings(bool skipChildInitialization)
{
SkipChildInitialization = skipChildInitialization;
}
public readonly struct ItemInitializationSettings
{
public readonly bool SkipChildInitialization;
public ItemInitializationSettings(bool skipChildInitialization)
{
SkipChildInitialization = skipChildInitialization;
}
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Models
{
public record ItemsTransformator(
string Name,
Func<IEnumerable<IItem>, Task<IEnumerable<IItem>>> Transformator
);
}
namespace FileTime.Core.Models;
public record ItemsTransformator(
string Name,
Func<IEnumerable<IItem>, Task<IEnumerable<IItem>>> Transformator
);

View File

@@ -1,4 +1,3 @@
namespace FileTime.Core.Models
{
public record NativePath(string Path);
}
namespace FileTime.Core.Models;
public record NativePath(string Path);

View File

@@ -2,22 +2,21 @@ using FileTime.Core.Behaviors;
using FileTime.Core.Enums;
using FileTime.Core.Models;
namespace FileTime.Core.Services
namespace FileTime.Core.Services;
public interface IContentProvider : IContainer, IOnContainerEnter
{
public interface IContentProvider : IContainer, IOnContainerEnter
{
Task<IItem> GetItemByFullNameAsync(
FullName fullName,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
Task<IItem> GetItemByFullNameAsync(
FullName fullName,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
}
Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
}

View File

@@ -2,18 +2,17 @@ using DynamicData;
using FileTime.Core.Models;
using InitableService;
namespace FileTime.Core.Services
{
public interface ITab : IInitable<IContainer>
{
IObservable<IContainer?> CurrentLocation { get; }
IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
namespace FileTime.Core.Services;
void SetCurrentLocation(IContainer newLocation);
void AddSelectedItemsTransformator(ItemsTransformator transformator);
void RemoveSelectedItemsTransformator(ItemsTransformator transformator);
void RemoveSelectedItemsTransformatorByName(string name);
void SetSelectedItem(IAbsolutePath newSelectedItem);
}
public interface ITab : IInitable<IContainer>
{
IObservable<IContainer?> CurrentLocation { get; }
IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
void SetCurrentLocation(IContainer newLocation);
void AddSelectedItemsTransformator(ItemsTransformator transformator);
void RemoveSelectedItemsTransformator(ItemsTransformator transformator);
void RemoveSelectedItemsTransformatorByName(string name);
void SetSelectedItem(IAbsolutePath newSelectedItem);
}

View File

@@ -1,7 +1,6 @@
namespace FileTime.Core.Command.Copy
{
public class CopyCommand : ITransportationCommand
{
namespace FileTime.Core.Command.Copy;
public class CopyCommand : ITransportationCommand
{
}
}

View File

@@ -1,9 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
</ItemGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>

View File

@@ -1,9 +1,8 @@
namespace FileTime.Core.Command
namespace FileTime.Core.Command;
public enum TransportMode
{
public enum TransportMode
{
Merge,
Overwrite,
Skip
}
Merge,
Overwrite,
Skip
}

View File

@@ -1,45 +1,44 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public class AbsolutePath : IAbsolutePath
{
public class AbsolutePath : IAbsolutePath
public IContentProvider ContentProvider { get; }
public IContentProvider? VirtualContentProvider { get; }
public FullName Path { get; }
public AbsolutePathType Type { get; }
public AbsolutePath(IContentProvider contentProvider, FullName path, AbsolutePathType type, IContentProvider? virtualContentProvider = null)
{
public IContentProvider ContentProvider { get; }
public IContentProvider? VirtualContentProvider { get; }
ContentProvider = contentProvider;
Path = path;
VirtualContentProvider = virtualContentProvider;
Type = type;
}
public FullName Path { get; }
public AbsolutePathType Type { get; }
public AbsolutePath(IItem item, IContentProvider? virtualContentProvider = null)
{
ContentProvider = item.Provider;
Path = item.FullName ?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item));
VirtualContentProvider = virtualContentProvider;
Type = item.Type;
}
public AbsolutePath(IContentProvider contentProvider, FullName path, AbsolutePathType type, IContentProvider? virtualContentProvider = null)
public async Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
var provider = VirtualContentProvider ?? ContentProvider;
return await provider.GetItemByFullNameAsync(Path, forceResolve, Type, itemInitializationSettings);
}
public async Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
try
{
ContentProvider = contentProvider;
Path = path;
VirtualContentProvider = virtualContentProvider;
Type = type;
}
public AbsolutePath(IItem item, IContentProvider? virtualContentProvider = null)
{
ContentProvider = item.Provider;
Path = item.FullName ?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item));
VirtualContentProvider = virtualContentProvider;
Type = item.Type;
}
public async Task<IItem> ResolveAsync(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
var provider = VirtualContentProvider ?? ContentProvider;
return await provider.GetItemByFullNameAsync(Path, forceResolve, Type, itemInitializationSettings);
}
public async Task<IItem?> ResolveAsyncSafe(bool forceResolve = false, ItemInitializationSettings itemInitializationSettings = default)
{
try
{
return await ResolveAsync(forceResolve, itemInitializationSettings);
}
catch { return null; }
return await ResolveAsync(forceResolve, itemInitializationSettings);
}
catch { return null; }
}
}

View File

@@ -4,26 +4,25 @@ using DynamicData;
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public record Container(
string Name,
string DisplayName,
FullName FullName,
NativePath NativePath,
IAbsolutePath? Parent,
bool IsHidden,
bool IsExists,
DateTime? CreatedAt,
SupportsDelete CanDelete,
bool CanRename,
string? Attributes,
IContentProvider Provider,
IObservable<IEnumerable<Exception>> Exceptions,
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items) : IContainer
{
public record Container(
string Name,
string DisplayName,
FullName FullName,
NativePath NativePath,
IAbsolutePath? Parent,
bool IsHidden,
bool IsExists,
DateTime? CreatedAt,
SupportsDelete CanDelete,
bool CanRename,
string? Attributes,
IContentProvider Provider,
IObservable<IEnumerable<Exception>> Exceptions,
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> Items) : IContainer
{
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container;
}
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container;
}

View File

@@ -1,23 +1,22 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
namespace FileTime.Core.Models;
public record Element(
string Name,
string DisplayName,
FullName FullName,
NativePath NativePath,
IAbsolutePath? Parent,
bool IsHidden,
bool IsExists,
DateTime? CreatedAt,
SupportsDelete CanDelete,
bool CanRename,
string? Attributes,
IContentProvider Provider,
IObservable<IEnumerable<Exception>> Exceptions) : IElement
{
public record Element(
string Name,
string DisplayName,
FullName FullName,
NativePath NativePath,
IAbsolutePath? Parent,
bool IsHidden,
bool IsExists,
DateTime? CreatedAt,
SupportsDelete CanDelete,
bool CanRename,
string? Attributes,
IContentProvider Provider,
IObservable<IEnumerable<Exception>> Exceptions) : IElement
{
public AbsolutePathType Type => AbsolutePathType.Element;
}
public AbsolutePathType Type => AbsolutePathType.Element;
}

View File

@@ -1,9 +1,9 @@
using FileTime.Core.Enums;
using FileTime.Core.Services;
namespace FileTime.Core.Models
{
public record FileElement(
namespace FileTime.Core.Models;
public record FileElement(
string Name,
string DisplayName,
FullName FullName,
@@ -32,5 +32,4 @@ namespace FileTime.Core.Models
Attributes,
Provider,
Exceptions
), IFileElement;
}
), IFileElement;

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
</ItemGroup>

View File

@@ -4,64 +4,63 @@ using DynamicData;
using FileTime.Core.Enums;
using FileTime.Core.Models;
namespace FileTime.Core.Services
namespace FileTime.Core.Services;
public abstract class ContentProviderBase : IContentProvider
{
public abstract class ContentProviderBase : IContentProvider
protected BehaviorSubject<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } = new (null);
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> IContainer.Items => Items;
public string Name { get; }
public string DisplayName { get; }
public FullName? FullName => null;
public NativePath? NativePath => null;
public bool IsHidden => false;
public bool IsExists => true;
public SupportsDelete CanDelete => SupportsDelete.False;
public bool CanRename => false;
public IContentProvider Provider => this;
public IAbsolutePath? Parent => null;
public DateTime? CreatedAt => null;
public string? Attributes => null;
protected BehaviorSubject<bool> IsLoading { get; } = new(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container;
public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>());
protected ContentProviderBase(string name)
{
protected BehaviorSubject<IObservable<IChangeSet<IAbsolutePath>>?> Items { get; } = new (null);
IObservable<IObservable<IChangeSet<IAbsolutePath>>?> IContainer.Items => Items;
public string Name { get; }
public string DisplayName { get; }
public FullName? FullName => null;
public NativePath? NativePath => null;
public bool IsHidden => false;
public bool IsExists => true;
public SupportsDelete CanDelete => SupportsDelete.False;
public bool CanRename => false;
public IContentProvider Provider => this;
public IAbsolutePath? Parent => null;
public DateTime? CreatedAt => null;
public string? Attributes => null;
protected BehaviorSubject<bool> IsLoading { get; } = new(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container;
public IObservable<IEnumerable<Exception>> Exceptions => Observable.Return(Enumerable.Empty<Exception>());
protected ContentProviderBase(string name)
{
DisplayName = Name = name;
}
public virtual Task OnEnter() => Task.CompletedTask;
public virtual async Task<IItem> GetItemByFullNameAsync(
FullName fullName,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
=> await GetItemByNativePathAsync(GetNativePath(fullName), forceResolve, forceResolvePathType, itemInitializationSettings);
public abstract Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
public abstract Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
public abstract NativePath GetNativePath(FullName fullName);
DisplayName = Name = name;
}
public virtual Task OnEnter() => Task.CompletedTask;
public virtual async Task<IItem> GetItemByFullNameAsync(
FullName fullName,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default)
=> await GetItemByNativePathAsync(GetNativePath(fullName), forceResolve, forceResolvePathType, itemInitializationSettings);
public abstract Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default);
public abstract Task<List<IAbsolutePath>> GetItemsByContainerAsync(FullName fullName);
public abstract NativePath GetNativePath(FullName fullName);
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Interactive.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />

View File

@@ -3,24 +3,24 @@ using System.Reactive.Subjects;
using DynamicData;
using FileTime.Core.Models;
namespace FileTime.Core.Services
namespace FileTime.Core.Services;
public class Tab : ITab
{
public class Tab : ITab
private readonly BehaviorSubject<IContainer?> _currentLocation = new(null);
private readonly BehaviorSubject<IAbsolutePath?> _currentSelectedItem = new(null);
private readonly List<ItemsTransformator> _transformators = new();
private IAbsolutePath? _currentSelectedItemCached;
public IObservable<IContainer?> CurrentLocation { get; }
public IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
public IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
public Tab()
{
private readonly BehaviorSubject<IContainer?> _currentLocation = new(null);
private readonly BehaviorSubject<IAbsolutePath?> _currentSelectedItem = new(null);
private readonly List<ItemsTransformator> _transformators = new();
private IAbsolutePath? _currentSelectedItemCached;
public IObservable<IContainer?> CurrentLocation { get; }
public IObservable<IObservable<IChangeSet<IItem>>?> CurrentItems { get; }
public IObservable<IAbsolutePath?> CurrentSelectedItem { get; }
public Tab()
{
CurrentLocation = _currentLocation.DistinctUntilChanged().Publish(null).RefCount();
CurrentItems =
Observable.Merge(
CurrentLocation = _currentLocation.DistinctUntilChanged().Publish(null).RefCount();
CurrentItems =
Observable.Merge(
CurrentLocation
.Where(c => c is not null)
.Select(c => c!.Items)
@@ -33,13 +33,13 @@ namespace FileTime.Core.Services
.Publish((IObservable<IChangeSet<IItem>>?)null)
.RefCount();
CurrentSelectedItem =
Observable.CombineLatest(
CurrentSelectedItem =
Observable.CombineLatest(
CurrentItems
.Select(c =>
c == null
? Observable.Return<IReadOnlyCollection<IItem>?>(null)
: c.ToCollection()
? Observable.Return<IReadOnlyCollection<IItem>?>(null)
: c.ToCollection()
)
.Switch(),
_currentSelectedItem,
@@ -55,41 +55,40 @@ namespace FileTime.Core.Services
.Publish(null)
.RefCount();
CurrentSelectedItem.Subscribe(s =>
{
_currentSelectedItemCached = s;
_currentSelectedItem.OnNext(s);
});
}
private async Task<IItem> MapItem(IAbsolutePath item) => await item.ResolveAsync(true);
public void Init(IContainer currentLocation)
CurrentSelectedItem.Subscribe(s =>
{
_currentLocation.OnNext(currentLocation);
}
_currentSelectedItemCached = s;
_currentSelectedItem.OnNext(s);
});
}
private static IAbsolutePath? GetSelectedItemByItems(IEnumerable<IItem> items)
{
//TODO:
return new AbsolutePath(items.First());
}
private async Task<IItem> MapItem(IAbsolutePath item) => await item.ResolveAsync(true);
public void SetCurrentLocation(IContainer newLocation) => _currentLocation.OnNext(newLocation);
public void Init(IContainer currentLocation)
{
_currentLocation.OnNext(currentLocation);
}
public void SetSelectedItem(IAbsolutePath newSelectedItem) => _currentSelectedItem.OnNext(newSelectedItem);
private static IAbsolutePath? GetSelectedItemByItems(IEnumerable<IItem> items)
{
//TODO:
return new AbsolutePath(items.First());
}
public void AddSelectedItemsTransformator(ItemsTransformator transformator) => _transformators.Add(transformator);
public void RemoveSelectedItemsTransformator(ItemsTransformator transformator) => _transformators.Remove(transformator);
public void RemoveSelectedItemsTransformatorByName(string name) => _transformators.RemoveAll(t => t.Name == name);
public void SetCurrentLocation(IContainer newLocation) => _currentLocation.OnNext(newLocation);
public async Task OpenSelected()
{
if (_currentSelectedItemCached == null) return;
var resolvedSelectedItem = await _currentSelectedItemCached.ContentProvider.GetItemByFullNameAsync(_currentSelectedItemCached.Path);
public void SetSelectedItem(IAbsolutePath newSelectedItem) => _currentSelectedItem.OnNext(newSelectedItem);
if (resolvedSelectedItem is not IContainer resolvedContainer) return;
SetCurrentLocation(resolvedContainer);
}
public void AddSelectedItemsTransformator(ItemsTransformator transformator) => _transformators.Add(transformator);
public void RemoveSelectedItemsTransformator(ItemsTransformator transformator) => _transformators.Remove(transformator);
public void RemoveSelectedItemsTransformatorByName(string name) => _transformators.RemoveAll(t => t.Name == name);
public async Task OpenSelected()
{
if (_currentSelectedItemCached == null) return;
var resolvedSelectedItem = await _currentSelectedItemCached.ContentProvider.GetItemByFullNameAsync(_currentSelectedItemCached.Path);
if (resolvedSelectedItem is not IContainer resolvedContainer) return;
SetCurrentLocation(resolvedContainer);
}
}

View File

@@ -1,89 +1,88 @@
using Avalonia.Input;
using FileTime.App.Core.Command;
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public class CommandBindingConfiguration
{
public class CommandBindingConfiguration
public List<KeyConfig> Keys { get; set; } = new List<KeyConfig>();
public Commands Command { get; set; } = Commands.None;
public string KeysDisplayText => GetKeysDisplayText();
public CommandBindingConfiguration() { }
public CommandBindingConfiguration(Commands command, IEnumerable<KeyConfig> keys)
{
public List<KeyConfig> Keys { get; set; } = new List<KeyConfig>();
Keys = new List<KeyConfig>(keys);
Command = command;
}
public Commands Command { get; set; } = Commands.None;
public CommandBindingConfiguration(Commands command, KeyConfig key)
{
Keys = new List<KeyConfig>() { key };
Command = command;
}
public string KeysDisplayText => GetKeysDisplayText();
public CommandBindingConfiguration(Commands command, IEnumerable<Key> keys)
{
Keys = keys.Select(k => new KeyConfig(k)).ToList();
Command = command;
}
public CommandBindingConfiguration() { }
public CommandBindingConfiguration(Commands command, Key key)
{
Keys = new List<KeyConfig>() { new KeyConfig(key) };
Command = command;
}
public CommandBindingConfiguration(Commands command, IEnumerable<KeyConfig> keys)
public string GetKeysDisplayText()
{
var s = "";
foreach (var k in Keys)
{
Keys = new List<KeyConfig>(keys);
Command = command;
}
var keyString = k.Key.ToString();
public CommandBindingConfiguration(Commands command, KeyConfig key)
{
Keys = new List<KeyConfig>() { key };
Command = command;
}
public CommandBindingConfiguration(Commands command, IEnumerable<Key> keys)
{
Keys = keys.Select(k => new KeyConfig(k)).ToList();
Command = command;
}
public CommandBindingConfiguration(Commands command, Key key)
{
Keys = new List<KeyConfig>() { new KeyConfig(key) };
Command = command;
}
public string GetKeysDisplayText()
{
var s = "";
foreach (var k in Keys)
if (keyString.Length == 1)
{
var keyString = k.Key.ToString();
if (keyString.Length == 1)
{
s += AddKeyWithCtrlOrAlt(k, s, (_, _, _) => k.Shift ? keyString.ToUpper() : keyString.ToLower());
}
else
{
s += AddKeyWithCtrlOrAlt(k, s, AddSpecialKey);
}
s += AddKeyWithCtrlOrAlt(k, s, (_, _, _) => k.Shift ? keyString.ToUpper() : keyString.ToLower());
}
else
{
s += AddKeyWithCtrlOrAlt(k, s, AddSpecialKey);
}
return s;
}
private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func<KeyConfig, string, bool, string> keyProcessor)
{
var s = "";
return s;
}
bool ctrlOrAlt = key.Ctrl || key.Alt;
private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func<KeyConfig, string, bool, string> keyProcessor)
{
var s = "";
if (ctrlOrAlt && currentText.Length > 0 && currentText.Last() != ' ') s += " ";
bool ctrlOrAlt = key.Ctrl || key.Alt;
if (key.Ctrl) s += "CTRL+";
if (key.Alt) s += "ALT+";
s += keyProcessor(key, currentText, ctrlOrAlt);
if (ctrlOrAlt && currentText.Length > 0 && currentText.Last() != ' ') s += " ";
if (ctrlOrAlt) s += " ";
if (key.Ctrl) s += "CTRL+";
if (key.Alt) s += "ALT+";
s += keyProcessor(key, currentText, ctrlOrAlt);
return s;
}
if (ctrlOrAlt) s += " ";
private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt)
{
var s = "";
return s;
}
if (currentText.Length > 0 && currentText.Last() != ' ' && !wasCtrlOrAlt) s += " ";
s += key.Key.ToString();
if (!wasCtrlOrAlt) s += " ";
private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt)
{
var s = "";
return s;
}
if (currentText.Length > 0 && currentText.Last() != ' ' && !wasCtrlOrAlt) s += " ";
s += key.Key.ToString();
if (!wasCtrlOrAlt) s += " ";
return s;
}
}

View File

@@ -1,9 +1,8 @@
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public class KeyBindingConfiguration
{
public class KeyBindingConfiguration
{
public bool UseDefaultBindings { get; set; } = true;
public List<CommandBindingConfiguration> DefaultKeyBindings { get; set; } = new();
public List<CommandBindingConfiguration> KeyBindings { get; set; } = new();
}
public bool UseDefaultBindings { get; set; } = true;
public List<CommandBindingConfiguration> DefaultKeyBindings { get; set; } = new();
public List<CommandBindingConfiguration> KeyBindings { get; set; } = new();
}

View File

@@ -1,32 +1,31 @@
using Avalonia.Input;
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public class KeyConfig
{
public class KeyConfig
public Key Key { get; set; }
public bool Shift { get; set; }
public bool Alt { get; set; }
public bool Ctrl { get; set; }
public KeyConfig() { }
public KeyConfig(
Key key,
bool shift = false,
bool alt = false,
bool ctrl = false)
{
public Key Key { get; set; }
public bool Shift { get; set; }
public bool Alt { get; set; }
public bool Ctrl { get; set; }
public KeyConfig() { }
public KeyConfig(
Key key,
bool shift = false,
bool alt = false,
bool ctrl = false)
{
Key = key;
Shift = shift;
Alt = alt;
Ctrl = ctrl;
}
public bool AreEquals(KeyConfig otherKeyConfig) =>
Key == otherKeyConfig.Key
&& Alt == otherKeyConfig.Alt
&& Shift == otherKeyConfig.Shift
&& Ctrl == otherKeyConfig.Ctrl;
Key = key;
Shift = shift;
Alt = alt;
Ctrl = ctrl;
}
public bool AreEquals(KeyConfig otherKeyConfig) =>
Key == otherKeyConfig.Key
&& Alt == otherKeyConfig.Alt
&& Shift == otherKeyConfig.Shift
&& Ctrl == otherKeyConfig.Ctrl;
}

View File

@@ -3,125 +3,124 @@ using FileTime.App.Core.Command;
using System;
using System.Collections.Generic;
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public static class MainConfiguration
{
public static class MainConfiguration
private static readonly Lazy<List<CommandBindingConfiguration>> _defaultKeybindings = new(InitDefaultKeyBindings);
public static Dictionary<string, string> Configuration { get; }
static MainConfiguration()
{
private static readonly Lazy<List<CommandBindingConfiguration>> _defaultKeybindings = new(InitDefaultKeyBindings);
Configuration = new();
PopulateDefaultEditorPrograms(Configuration);
PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, SectionNames.KeybindingSectionName + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings));
}
public static Dictionary<string, string> Configuration { get; }
static MainConfiguration()
private static void PopulateDefaultKeyBindings(Dictionary<string, string> configuration, List<CommandBindingConfiguration> commandBindingConfigs, string basePath)
{
for (var i = 0; i < commandBindingConfigs.Count; i++)
{
Configuration = new();
PopulateDefaultEditorPrograms(Configuration);
PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, SectionNames.KeybindingSectionName + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings));
}
var baseKey = basePath + $":[{i}]:";
var commandBindingConfig = commandBindingConfigs[i];
configuration.Add(baseKey + nameof(CommandBindingConfiguration.Command), commandBindingConfig.Command.ToString());
private static void PopulateDefaultKeyBindings(Dictionary<string, string> configuration, List<CommandBindingConfiguration> commandBindingConfigs, string basePath)
{
for (var i = 0; i < commandBindingConfigs.Count; i++)
for (var j = 0; j < commandBindingConfig.Keys.Count; j++)
{
var baseKey = basePath + $":[{i}]:";
var commandBindingConfig = commandBindingConfigs[i];
configuration.Add(baseKey + nameof(CommandBindingConfiguration.Command), commandBindingConfig.Command.ToString());
for (var j = 0; j < commandBindingConfig.Keys.Count; j++)
{
var key = commandBindingConfig.Keys[j];
var keyBaseKey = baseKey + $"keys:[{j}]:";
configuration.Add(keyBaseKey + nameof(KeyConfig.Key), key.Key.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Shift), key.Shift.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Alt), key.Alt.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Ctrl), key.Ctrl.ToString());
}
var key = commandBindingConfig.Keys[j];
var keyBaseKey = baseKey + $"keys:[{j}]:";
configuration.Add(keyBaseKey + nameof(KeyConfig.Key), key.Key.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Shift), key.Shift.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Alt), key.Alt.ToString());
configuration.Add(keyBaseKey + nameof(KeyConfig.Ctrl), key.Ctrl.ToString());
}
}
}
private static List<CommandBindingConfiguration> InitDefaultKeyBindings()
private static List<CommandBindingConfiguration> InitDefaultKeyBindings()
{
return new List<CommandBindingConfiguration>()
{
return new List<CommandBindingConfiguration>()
{
new CommandBindingConfiguration(Commands.AutoRefresh, new KeyConfig(Key.R, shift: true)),
new CommandBindingConfiguration(Commands.ChangeTimelineMode, new[] { Key.T, Key.M }),
new CommandBindingConfiguration(Commands.CloseTab, Key.Q),
new CommandBindingConfiguration(Commands.Compress, new[] { Key.Y, Key.C }),
new CommandBindingConfiguration(Commands.Copy, new[] { Key.Y, Key.Y }),
new CommandBindingConfiguration(Commands.CopyHash, new[] { Key.C, Key.H }),
new CommandBindingConfiguration(Commands.CopyPath, new[] { Key.C, Key.P }),
new CommandBindingConfiguration(Commands.CreateContainer, Key.F7),
new CommandBindingConfiguration(Commands.CreateContainer, new[] { Key.C, Key.C }),
new CommandBindingConfiguration(Commands.CreateElement, new[] { Key.C, Key.E }),
new CommandBindingConfiguration(Commands.Cut, new[] { Key.D, Key.D }),
new CommandBindingConfiguration(Commands.Edit, new KeyConfig(Key.F4)),
new CommandBindingConfiguration(Commands.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)),
new CommandBindingConfiguration(Commands.FindByName, new[] { Key.F, Key.N }),
new CommandBindingConfiguration(Commands.FindByNameRegex, new[] { Key.F, Key.R }),
new CommandBindingConfiguration(Commands.GoToHome, new[] { Key.G, Key.H }),
new CommandBindingConfiguration(Commands.GoToPath, new KeyConfig(Key.L, ctrl: true)),
new CommandBindingConfiguration(Commands.GoToPath, new[] { Key.G, Key.P }),
new CommandBindingConfiguration(Commands.GoToProvider, new[] { Key.G, Key.T }),
new CommandBindingConfiguration(Commands.GoToRoot, new[] { Key.G, Key.R }),
new CommandBindingConfiguration(Commands.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }),
new CommandBindingConfiguration(Commands.Mark, Key.Space),
new CommandBindingConfiguration(Commands.MoveToLast, new KeyConfig(Key.G, shift: true)),
new CommandBindingConfiguration(Commands.MoveToFirst, new[] { Key.G, Key.G }),
new CommandBindingConfiguration(Commands.NextTimelineBlock, Key.L ),
new CommandBindingConfiguration(Commands.NextTimelineCommand, Key.J ),
new CommandBindingConfiguration(Commands.OpenInFileBrowser, new[] { Key.O, Key.E }),
new CommandBindingConfiguration(Commands.PasteMerge, new[] { Key.P, Key.P }),
new CommandBindingConfiguration(Commands.PasteOverwrite, new[] { Key.P, Key.O }),
new CommandBindingConfiguration(Commands.PasteSkip, new[] { Key.P, Key.S }),
new CommandBindingConfiguration(Commands.PinFavorite, new[] { Key.F, Key.P }),
new CommandBindingConfiguration(Commands.PreviousTimelineBlock, Key.H ),
new CommandBindingConfiguration(Commands.PreviousTimelineCommand, Key.K ),
new CommandBindingConfiguration(Commands.Refresh, Key.R),
new CommandBindingConfiguration(Commands.Rename, Key.F2),
new CommandBindingConfiguration(Commands.Rename, new[] { Key.C, Key.W }),
new CommandBindingConfiguration(Commands.RunCommand, new KeyConfig(Key.D4, shift: true)),
new CommandBindingConfiguration(Commands.ScanContainerSize, new[] { Key.C, Key.S }),
new CommandBindingConfiguration(Commands.ShowAllShotcut, Key.F1),
new CommandBindingConfiguration(Commands.SoftDelete, new[] { new KeyConfig(Key.D), new KeyConfig(Key.D, shift: true) }),
new CommandBindingConfiguration(Commands.SwitchToLastTab, Key.D9),
new CommandBindingConfiguration(Commands.SwitchToTab1, Key.D1),
new CommandBindingConfiguration(Commands.SwitchToTab2, Key.D2),
new CommandBindingConfiguration(Commands.SwitchToTab3, Key.D3),
new CommandBindingConfiguration(Commands.SwitchToTab4, Key.D4),
new CommandBindingConfiguration(Commands.SwitchToTab5, Key.D5),
new CommandBindingConfiguration(Commands.SwitchToTab6, Key.D6),
new CommandBindingConfiguration(Commands.SwitchToTab7, Key.D7),
new CommandBindingConfiguration(Commands.SwitchToTab8, Key.D8),
new CommandBindingConfiguration(Commands.TimelinePause, new[] { Key.T, Key.P }),
new CommandBindingConfiguration(Commands.TimelineRefresh, new[] { Key.T, Key.R }),
new CommandBindingConfiguration(Commands.TimelineStart, new[] { Key.T, Key.S }),
new CommandBindingConfiguration(Commands.ToggleAdvancedIcons, new[] { Key.Z, Key.I }),
new CommandBindingConfiguration(Commands.GoUp, Key.Left),
new CommandBindingConfiguration(Commands.Open, Key.Right),
new CommandBindingConfiguration(Commands.OpenOrRun, Key.Enter),
new CommandBindingConfiguration(Commands.MoveCursorUp, Key.Up),
new CommandBindingConfiguration(Commands.MoveCursorDown, Key.Down),
new CommandBindingConfiguration(Commands.MoveCursorUpPage, Key.PageUp),
new CommandBindingConfiguration(Commands.MoveCursorDownPage, Key.PageDown),
};
}
new CommandBindingConfiguration(Commands.AutoRefresh, new KeyConfig(Key.R, shift: true)),
new CommandBindingConfiguration(Commands.ChangeTimelineMode, new[] { Key.T, Key.M }),
new CommandBindingConfiguration(Commands.CloseTab, Key.Q),
new CommandBindingConfiguration(Commands.Compress, new[] { Key.Y, Key.C }),
new CommandBindingConfiguration(Commands.Copy, new[] { Key.Y, Key.Y }),
new CommandBindingConfiguration(Commands.CopyHash, new[] { Key.C, Key.H }),
new CommandBindingConfiguration(Commands.CopyPath, new[] { Key.C, Key.P }),
new CommandBindingConfiguration(Commands.CreateContainer, Key.F7),
new CommandBindingConfiguration(Commands.CreateContainer, new[] { Key.C, Key.C }),
new CommandBindingConfiguration(Commands.CreateElement, new[] { Key.C, Key.E }),
new CommandBindingConfiguration(Commands.Cut, new[] { Key.D, Key.D }),
new CommandBindingConfiguration(Commands.Edit, new KeyConfig(Key.F4)),
new CommandBindingConfiguration(Commands.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)),
new CommandBindingConfiguration(Commands.FindByName, new[] { Key.F, Key.N }),
new CommandBindingConfiguration(Commands.FindByNameRegex, new[] { Key.F, Key.R }),
new CommandBindingConfiguration(Commands.GoToHome, new[] { Key.G, Key.H }),
new CommandBindingConfiguration(Commands.GoToPath, new KeyConfig(Key.L, ctrl: true)),
new CommandBindingConfiguration(Commands.GoToPath, new[] { Key.G, Key.P }),
new CommandBindingConfiguration(Commands.GoToProvider, new[] { Key.G, Key.T }),
new CommandBindingConfiguration(Commands.GoToRoot, new[] { Key.G, Key.R }),
new CommandBindingConfiguration(Commands.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }),
new CommandBindingConfiguration(Commands.Mark, Key.Space),
new CommandBindingConfiguration(Commands.MoveToLast, new KeyConfig(Key.G, shift: true)),
new CommandBindingConfiguration(Commands.MoveToFirst, new[] { Key.G, Key.G }),
new CommandBindingConfiguration(Commands.NextTimelineBlock, Key.L ),
new CommandBindingConfiguration(Commands.NextTimelineCommand, Key.J ),
new CommandBindingConfiguration(Commands.OpenInFileBrowser, new[] { Key.O, Key.E }),
new CommandBindingConfiguration(Commands.PasteMerge, new[] { Key.P, Key.P }),
new CommandBindingConfiguration(Commands.PasteOverwrite, new[] { Key.P, Key.O }),
new CommandBindingConfiguration(Commands.PasteSkip, new[] { Key.P, Key.S }),
new CommandBindingConfiguration(Commands.PinFavorite, new[] { Key.F, Key.P }),
new CommandBindingConfiguration(Commands.PreviousTimelineBlock, Key.H ),
new CommandBindingConfiguration(Commands.PreviousTimelineCommand, Key.K ),
new CommandBindingConfiguration(Commands.Refresh, Key.R),
new CommandBindingConfiguration(Commands.Rename, Key.F2),
new CommandBindingConfiguration(Commands.Rename, new[] { Key.C, Key.W }),
new CommandBindingConfiguration(Commands.RunCommand, new KeyConfig(Key.D4, shift: true)),
new CommandBindingConfiguration(Commands.ScanContainerSize, new[] { Key.C, Key.S }),
new CommandBindingConfiguration(Commands.ShowAllShotcut, Key.F1),
new CommandBindingConfiguration(Commands.SoftDelete, new[] { new KeyConfig(Key.D), new KeyConfig(Key.D, shift: true) }),
new CommandBindingConfiguration(Commands.SwitchToLastTab, Key.D9),
new CommandBindingConfiguration(Commands.SwitchToTab1, Key.D1),
new CommandBindingConfiguration(Commands.SwitchToTab2, Key.D2),
new CommandBindingConfiguration(Commands.SwitchToTab3, Key.D3),
new CommandBindingConfiguration(Commands.SwitchToTab4, Key.D4),
new CommandBindingConfiguration(Commands.SwitchToTab5, Key.D5),
new CommandBindingConfiguration(Commands.SwitchToTab6, Key.D6),
new CommandBindingConfiguration(Commands.SwitchToTab7, Key.D7),
new CommandBindingConfiguration(Commands.SwitchToTab8, Key.D8),
new CommandBindingConfiguration(Commands.TimelinePause, new[] { Key.T, Key.P }),
new CommandBindingConfiguration(Commands.TimelineRefresh, new[] { Key.T, Key.R }),
new CommandBindingConfiguration(Commands.TimelineStart, new[] { Key.T, Key.S }),
new CommandBindingConfiguration(Commands.ToggleAdvancedIcons, new[] { Key.Z, Key.I }),
new CommandBindingConfiguration(Commands.GoUp, Key.Left),
new CommandBindingConfiguration(Commands.Open, Key.Right),
new CommandBindingConfiguration(Commands.OpenOrRun, Key.Enter),
new CommandBindingConfiguration(Commands.MoveCursorUp, Key.Up),
new CommandBindingConfiguration(Commands.MoveCursorDown, Key.Down),
new CommandBindingConfiguration(Commands.MoveCursorUpPage, Key.PageUp),
new CommandBindingConfiguration(Commands.MoveCursorDownPage, Key.PageDown),
};
}
private static void PopulateDefaultEditorPrograms(Dictionary<string, string> configuration)
private static void PopulateDefaultEditorPrograms(Dictionary<string, string> configuration)
{
var editorPrograms = new List<ProgramConfiguration>()
{
var editorPrograms = new List<ProgramConfiguration>()
{
new ProgramConfiguration(@"c:\Program Files\Notepad++\notepad++.exe"),
new ProgramConfiguration("notepad.exe"),
};
new ProgramConfiguration(@"c:\Program Files\Notepad++\notepad++.exe"),
new ProgramConfiguration("notepad.exe"),
};
for (var i = 0; i < editorPrograms.Count; i++)
{
if (editorPrograms[i].Path is not string path) continue;
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}", path);
for (var i = 0; i < editorPrograms.Count; i++)
{
if (editorPrograms[i].Path is not string path) continue;
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Path)}", path);
if (editorPrograms[i].Arguments is string arguments)
{
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}", arguments);
}
if (editorPrograms[i].Arguments is string arguments)
{
configuration.Add($"{SectionNames.ProgramsSectionName}:{nameof(ProgramsConfiguration.DefaultEditorPrograms)}:[{i}]:{nameof(ProgramConfiguration.Arguments)}", arguments);
}
}
}

View File

@@ -1,16 +1,15 @@
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public class ProgramConfiguration
{
public class ProgramConfiguration
public string? Path { get; set; }
public string? Arguments { get; set; }
public ProgramConfiguration() { }
public ProgramConfiguration(string? path, string? arguments = null)
{
public string? Path { get; set; }
public string? Arguments { get; set; }
public ProgramConfiguration() { }
public ProgramConfiguration(string? path, string? arguments = null)
{
Path = path;
Arguments = arguments;
}
Path = path;
Arguments = arguments;
}
}

View File

@@ -1,10 +1,9 @@
using System.Collections.Generic;
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public class ProgramsConfiguration
{
public class ProgramsConfiguration
{
public List<ProgramConfiguration> DefaultEditorPrograms { get; set; } = new();
public List<ProgramConfiguration> EditorPrograms { get; set; } = new();
}
public List<ProgramConfiguration> DefaultEditorPrograms { get; set; } = new();
public List<ProgramConfiguration> EditorPrograms { get; set; } = new();
}

View File

@@ -1,8 +1,7 @@
namespace FileTime.GuiApp.Configuration
namespace FileTime.GuiApp.Configuration;
public static class SectionNames
{
public static class SectionNames
{
public const string KeybindingSectionName = "KeyBindings";
public const string ProgramsSectionName = "Programs";
}
public const string KeybindingSectionName = "KeyBindings";
public const string ProgramsSectionName = "Programs";
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.13" />
</ItemGroup>

View File

@@ -1,4 +1,3 @@
namespace FileTime.GuiApp.Models
{
public record SpecialKeysStatus(bool IsAltPressed, bool IsShiftPressed, bool IsCtrlPressed);
}
namespace FileTime.GuiApp.Models;
public record SpecialKeysStatus(bool IsAltPressed, bool IsShiftPressed, bool IsCtrlPressed);

View File

@@ -1,4 +1,3 @@
namespace FileTime.GuiApp.Services
{
public interface IDefaultModeKeyInputHandler : IKeyInputHandler { }
}
namespace FileTime.GuiApp.Services;
public interface IDefaultModeKeyInputHandler : IKeyInputHandler { }

View File

@@ -1,10 +1,9 @@
using Avalonia.Input;
using FileTime.GuiApp.Models;
namespace FileTime.GuiApp.Services
namespace FileTime.GuiApp.Services;
public interface IKeyInputHandler
{
public interface IKeyInputHandler
{
Task HandleInputKey(Key key, SpecialKeysStatus specialKeysStatus, Action<bool> setHandled);
}
Task HandleInputKey(Key key, SpecialKeysStatus specialKeysStatus, Action<bool> setHandled);
}

View File

@@ -1,9 +1,8 @@
using Avalonia.Input;
namespace FileTime.GuiApp.Services
namespace FileTime.GuiApp.Services;
public interface IKeyInputHandlerService
{
public interface IKeyInputHandlerService
{
Task ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled);
}
Task ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled);
}

View File

@@ -1,11 +1,10 @@
using FileTime.GuiApp.Configuration;
namespace FileTime.GuiApp.Services
namespace FileTime.GuiApp.Services;
public interface IKeyboardConfigurationService
{
public interface IKeyboardConfigurationService
{
IReadOnlyList<CommandBindingConfiguration> CommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> UniversalCommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> AllShortcut { get; }
}
IReadOnlyList<CommandBindingConfiguration> CommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> UniversalCommandBindings { get; }
IReadOnlyList<CommandBindingConfiguration> AllShortcut { get; }
}

View File

@@ -1,4 +1,3 @@
namespace FileTime.GuiApp.Services
{
public interface IRapidTravelModeKeyInputHandler : IKeyInputHandler { }
}
namespace FileTime.GuiApp.Services;
public interface IRapidTravelModeKeyInputHandler : IKeyInputHandler { }

View File

@@ -1,14 +1,13 @@
using FileTime.App.Core.ViewModels;
using FileTime.GuiApp.Configuration;
namespace FileTime.GuiApp.ViewModels
namespace FileTime.GuiApp.ViewModels;
public interface IGuiAppState : IAppState
{
public interface IGuiAppState : IAppState
{
List<KeyConfig> PreviousKeys { get; }
bool IsAllShortcutVisible { get; set; }
bool NoCommandFound { get; set; }
string? MessageBoxText { get; set; }
List<CommandBindingConfiguration> PossibleCommands { get; set; }
}
List<KeyConfig> PreviousKeys { get; }
bool IsAllShortcutVisible { get; set; }
bool NoCommandFound { get; set; }
string? MessageBoxText { get; set; }
List<CommandBindingConfiguration> PossibleCommands { get; set; }
}

View File

@@ -7,40 +7,39 @@ using FileTime.GuiApp.Views;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace FileTime.GuiApp
namespace FileTime.GuiApp;
public partial class App : Application
{
public partial class App : Application
static App()
{
static App()
{
DI.ServiceProvider ??= DependencyInjection
.RegisterDefaultServices()
.AddConfiguration()
.RegisterLogging()
.RegisterServices()
.AddViewModels()
.BuildServiceProvider()
.InitSerilog();
DI.ServiceProvider ??= DependencyInjection
.RegisterDefaultServices()
.AddConfiguration()
.RegisterLogging()
.RegisterServices()
.AddViewModels()
.BuildServiceProvider()
.InitSerilog();
var logger = DI.ServiceProvider.GetRequiredService<ILogger<App>>();
logger.LogInformation("App initialization completed");
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
var logger = DI.ServiceProvider.GetRequiredService<ILogger<App>>();
logger.LogInformation("App initialization completed");
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow
{
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowLoadingViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
DataContext = new MainWindowLoadingViewModel(),
};
}
base.OnFrameworkInitializationCompleted();
}
}

View File

@@ -9,6 +9,11 @@
<ApplicationIcon>filetime.ico</ApplicationIcon>
<Version>0.0.1</Version>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<Content Include="filetime.ico" />
</ItemGroup>

View File

@@ -3,19 +3,19 @@ using System.IO;
using Avalonia;
using Avalonia.ReactiveUI;
namespace FileTime.GuiApp
namespace FileTime.GuiApp;
public static class Program
{
public static class Program
public static string AppDataRoot { get; }
public static string EnvironmentName { get; }
static Program()
{
public static string AppDataRoot { get; }
public static string EnvironmentName { get; }
static Program()
{
#if DEBUG
EnvironmentName = "Development";
EnvironmentName = "Development";
AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata");
AppDataRoot = Path.Combine(Environment.CurrentDirectory, "appdata");
#else
EnvironmentName = "Release";
@@ -43,20 +43,19 @@ namespace FileTime.GuiApp
if (appDataRoot == null) throw new UnauthorizedAccessException();
AppDataRoot = appDataRoot;
#endif
}
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToTrace();
}
}
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToTrace();
}

View File

@@ -11,70 +11,69 @@ using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Configuration;
namespace FileTime.GuiApp
namespace FileTime.GuiApp;
public static class Startup
{
public static class Startup
internal static IServiceCollection AddViewModels(this IServiceCollection serviceCollection)
{
internal static IServiceCollection AddViewModels(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<MainWindowViewModel>()
.AddSingleton<GuiAppState>()
.AddSingleton<IAppState, GuiAppState>(s => s.GetRequiredService<GuiAppState>())
.AddSingleton<IGuiAppState, GuiAppState>(s => s.GetRequiredService<GuiAppState>());
}
internal static IServiceCollection RegisterServices(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<IRxSchedulerService, AvaloniaRxSchedulerService>()
.AddSingleton<IKeyInputHandlerService, KeyInputHandlerService>()
.AddSingleton<IDefaultModeKeyInputHandler, DefaultModeKeyInputHandler>()
.AddSingleton<IKeyboardConfigurationService, KeyboardConfigurationService>()
.AddSingleton<IRapidTravelModeKeyInputHandler, RapidTravelModeKeyInputHandler>();
}
return serviceCollection
.AddSingleton<MainWindowViewModel>()
.AddSingleton<GuiAppState>()
.AddSingleton<IAppState, GuiAppState>(s => s.GetRequiredService<GuiAppState>())
.AddSingleton<IGuiAppState, GuiAppState>(s => s.GetRequiredService<GuiAppState>());
}
internal static IServiceCollection RegisterServices(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<IRxSchedulerService, AvaloniaRxSchedulerService>()
.AddSingleton<IKeyInputHandlerService, KeyInputHandlerService>()
.AddSingleton<IDefaultModeKeyInputHandler, DefaultModeKeyInputHandler>()
.AddSingleton<IKeyboardConfigurationService, KeyboardConfigurationService>()
.AddSingleton<IRapidTravelModeKeyInputHandler, RapidTravelModeKeyInputHandler>();
}
internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection)
{
return serviceCollection.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true)
);
}
internal static IServiceCollection RegisterLogging(this IServiceCollection serviceCollection)
{
return serviceCollection.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true)
);
}
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(MainConfiguration.Configuration)
.AddJsonFile("appsettings.json", optional: true)
.AddJsonFile($"appsettings.{Program.EnvironmentName}.json", true)
.Build();
internal static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(MainConfiguration.Configuration)
.AddJsonFile("appsettings.json", optional: true)
.AddJsonFile($"appsettings.{Program.EnvironmentName}.json", true)
.Build();
return serviceCollection
.Configure<ProgramsConfiguration>(configuration.GetSection(SectionNames.ProgramsSectionName))
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
.AddSingleton<IConfiguration>(configuration);
}
return serviceCollection
.Configure<ProgramsConfiguration>(configuration.GetSection(SectionNames.ProgramsSectionName))
.Configure<KeyBindingConfiguration>(configuration.GetSection(SectionNames.KeybindingSectionName))
.AddSingleton<IConfiguration>(configuration);
}
internal static IServiceProvider InitSerilog(this IServiceProvider serviceProvider)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(serviceProvider.GetService<IConfiguration>())
.Enrich.FromLogContext()
.WriteTo.File(
Path.Combine(Program.AppDataRoot, "logs", "appLog.log"),
fileSizeLimitBytes: 10 * 1024 * 1024,
rollOnFileSizeLimit: true,
rollingInterval: RollingInterval.Day)
.WriteTo.MessageBoxSink(serviceProvider)
.CreateLogger();
internal static IServiceProvider InitSerilog(this IServiceProvider serviceProvider)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(serviceProvider.GetService<IConfiguration>())
.Enrich.FromLogContext()
.WriteTo.File(
Path.Combine(Program.AppDataRoot, "logs", "appLog.log"),
fileSizeLimitBytes: 10 * 1024 * 1024,
rollOnFileSizeLimit: true,
rollingInterval: RollingInterval.Day)
.WriteTo.MessageBoxSink(serviceProvider)
.CreateLogger();
return serviceProvider;
}
return serviceProvider;
}
internal static LoggerConfiguration MessageBoxSink(
this LoggerSinkConfiguration loggerConfiguration,
IServiceProvider serviceProvider)
{
return loggerConfiguration.Sink(serviceProvider.GetService<ToastMessageSink>());
}
internal static LoggerConfiguration MessageBoxSink(
this LoggerSinkConfiguration loggerConfiguration,
IServiceProvider serviceProvider)
{
return loggerConfiguration.Sink(serviceProvider.GetService<ToastMessageSink>());
}
}

View File

@@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core\FileTime.App.Core.csproj" />
<ProjectReference Include="..\FileTime.GuiApp.Abstractions\FileTime.GuiApp.Abstractions.csproj" />

View File

@@ -2,23 +2,22 @@ using FileTime.App.Core.ViewModels;
using FileTime.GuiApp.Configuration;
using MvvmGen;
namespace FileTime.GuiApp.ViewModels
namespace FileTime.GuiApp.ViewModels;
[ViewModel]
public partial class GuiAppState : AppStateBase, IGuiAppState
{
[ViewModel]
public partial class GuiAppState : AppStateBase, IGuiAppState
{
[Property]
private bool _isAllShortcutVisible;
[Property]
private bool _isAllShortcutVisible;
[Property]
private bool _noCommandFound;
[Property]
private bool _noCommandFound;
[Property]
private string? _messageBoxText;
[Property]
private string? _messageBoxText;
[Property]
private List<CommandBindingConfiguration> _possibleCommands = new();
[Property]
private List<CommandBindingConfiguration> _possibleCommands = new();
public List<KeyConfig> PreviousKeys { get; } = new();
}
public List<KeyConfig> PreviousKeys { get; } = new();
}

View File

@@ -1,54 +1,53 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public enum ComparisonCondition
{
public enum ComparisonCondition
Equal,
GreaterThan,
LessThan,
LessThanOrEqual,
NotEqual,
GreaterThanOrEqual
}
public class CompareConverter : IValueConverter
{
public ComparisonCondition ComparisonCondition { get; set; } = ComparisonCondition.Equal;
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
Equal,
GreaterThan,
LessThan,
LessThanOrEqual,
NotEqual,
GreaterThanOrEqual
return Compare(value, parameter);
}
public class CompareConverter : IValueConverter
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public ComparisonCondition ComparisonCondition { get; set; } = ComparisonCondition.Equal;
throw new NotImplementedException();
}
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
private bool Compare(object? value, object? parameter)
{
if (ComparisonCondition == ComparisonCondition.GreaterThan)
{
return Compare(value, parameter);
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt > parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble > parameterDouble;
else throw new NotSupportedException();
}
else if (ComparisonCondition == ComparisonCondition.NotEqual)
{
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt != parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble != parameterDouble;
return value != parameter;
}
else if (ComparisonCondition == ComparisonCondition.Equal)
{
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt == parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble == parameterDouble;
else if (value?.GetType().IsEnum ?? false && Enum.TryParse(value.GetType(), parameter?.ToString(), out var _)) return value.ToString() == parameter?.ToString();
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private bool Compare(object? value, object? parameter)
{
if (ComparisonCondition == ComparisonCondition.GreaterThan)
{
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt > parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble > parameterDouble;
else throw new NotSupportedException();
}
else if (ComparisonCondition == ComparisonCondition.NotEqual)
{
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt != parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble != parameterDouble;
return value != parameter;
}
else if (ComparisonCondition == ComparisonCondition.Equal)
{
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt == parameterInt;
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble == parameterDouble;
else if (value?.GetType().IsEnum ?? false && Enum.TryParse(value.GetType(), parameter?.ToString(), out var _)) return value.ToString() == parameter?.ToString();
}
return value == parameter;
}
return value == parameter;
}
}

View File

@@ -1,18 +1,17 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.GuiApp.Converters
{
public class DateTimeConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is DateTime dateTime && parameter is string parameterS
? dateTime.ToString(parameterS)
: value;
namespace FileTime.GuiApp.Converters;
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public class DateTimeConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is DateTime dateTime && parameter is string parameterS
? dateTime.ToString(parameterS)
: value;
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -1,45 +1,44 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class ExceptionToStringConverter : IValueConverter
{
public class ExceptionToStringConverter : IValueConverter
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
if (value is not Exception e) return value;
if (e is UnauthorizedAccessException)
{
if (value is not Exception e) return value;
if (e is UnauthorizedAccessException)
{
return e.Message;
}
else if (e.InnerException != null)
{
return TraverseInnerException(e);
}
return FormatException(e);
return e.Message;
}
else if (e.InnerException != null)
{
return TraverseInnerException(e);
}
private static string TraverseInnerException(Exception e)
{
string s = "";
if (e.InnerException != null) s += TraverseInnerException(e.InnerException) + Environment.NewLine;
else return FormatException(e);
return FormatException(e);
}
s += "In: " + FormatException(e);
private static string TraverseInnerException(Exception e)
{
string s = "";
if (e.InnerException != null) s += TraverseInnerException(e.InnerException) + Environment.NewLine;
else return FormatException(e);
return s;
}
s += "In: " + FormatException(e);
private static string FormatException(Exception e)
{
return $"{e.Message} ({e.GetType().FullName})";
}
return s;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private static string FormatException(Exception e)
{
return $"{e.Message} ({e.GetType().FullName})";
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -1,47 +1,46 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class FormatSizeConverter : IValueConverter
{
public class FormatSizeConverter : IValueConverter
private const long OneKiloByte = 1024;
private const long OneMegaByte = OneKiloByte * 1024;
private const long OneGigaByte = OneMegaByte * 1024;
private const long OneTerraByte = OneGigaByte * 1024;
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
private const long OneKiloByte = 1024;
private const long OneMegaByte = OneKiloByte * 1024;
private const long OneGigaByte = OneMegaByte * 1024;
private const long OneTerraByte = OneGigaByte * 1024;
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
return (value, int.TryParse(parameter?.ToString(), out var prec)) switch
{
return (value, int.TryParse(parameter?.ToString(), out var prec)) switch
{
(long size, true) => ToSizeString(size, prec),
(long size, false) => ToSizeString(size),
(null, _) => "...",
_ => value
};
}
(long size, true) => ToSizeString(size, prec),
(long size, false) => ToSizeString(size),
(null, _) => "...",
_ => value
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public static string ToSizeString(long fileSize, int precision = 1)
{
var fileSizeD = (decimal)fileSize;
var (size, suffix) = fileSize switch
{
throw new NotImplementedException();
}
> OneTerraByte => (fileSizeD / OneTerraByte, "T"),
> OneGigaByte => (fileSizeD / OneGigaByte, "G"),
> OneMegaByte => (fileSizeD / OneMegaByte, "M"),
> OneKiloByte => (fileSizeD / OneKiloByte, "K"),
_ => (fileSizeD, "B")
};
public static string ToSizeString(long fileSize, int precision = 1)
{
var fileSizeD = (decimal)fileSize;
var (size, suffix) = fileSize switch
{
> OneTerraByte => (fileSizeD / OneTerraByte, "T"),
> OneGigaByte => (fileSizeD / OneGigaByte, "G"),
> OneMegaByte => (fileSizeD / OneMegaByte, "M"),
> OneKiloByte => (fileSizeD / OneKiloByte, "K"),
_ => (fileSizeD, "B")
};
var result = string.Format("{0:N" + precision + "}", size).Replace(',', '.');
var result = string.Format("{0:N" + precision + "}", size).Replace(',', '.');
if (result.Contains('.')) result = result.TrimEnd('0').TrimEnd('.');
return result + " " + suffix;
}
if (result.Contains('.')) result = result.TrimEnd('0').TrimEnd('.');
return result + " " + suffix;
}
}

View File

@@ -3,22 +3,21 @@ using Avalonia.Data.Converters;
using FileTime.App.Core.Services;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class GetFileExtensionConverter : IValueConverter
{
public class GetFileExtensionConverter : IValueConverter
private IItemNameConverterService? _itemNameConverterService;
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
private IItemNameConverterService? _itemNameConverterService;
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is not string fullName) return value;
_itemNameConverterService ??= DI.ServiceProvider.GetRequiredService<IItemNameConverterService>();
if (value is not string fullName) return value;
_itemNameConverterService ??= DI.ServiceProvider.GetRequiredService<IItemNameConverterService>();
return _itemNameConverterService.GetFileExtension(fullName);
}
return _itemNameConverterService.GetFileExtension(fullName);
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -3,39 +3,38 @@ using Avalonia.Data.Converters;
using Avalonia.Media;
using FileTime.App.Core.Models.Enums;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class ItemViewModeToBrushConverter : IValueConverter
{
public class ItemViewModeToBrushConverter : IValueConverter
public Brush? DefaultBrush { get; set; }
public Brush? AlternativeBrush { get; set; }
public Brush? SelectedBrush { get; set; }
public Brush? MarkedBrush { get; set; }
public Brush? MarkedSelectedBrush { get; set; }
public Brush? MarkedAlternativeBrush { get; set; }
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public Brush? DefaultBrush { get; set; }
public Brush? AlternativeBrush { get; set; }
public Brush? SelectedBrush { get; set; }
public Brush? MarkedBrush { get; set; }
public Brush? MarkedSelectedBrush { get; set; }
public Brush? MarkedAlternativeBrush { get; set; }
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
if (value is ItemViewMode viewMode)
{
if (value is ItemViewMode viewMode)
return viewMode switch
{
return viewMode switch
{
ItemViewMode.Default => DefaultBrush,
ItemViewMode.Alternative => AlternativeBrush,
ItemViewMode.Selected => SelectedBrush,
ItemViewMode.Marked => MarkedBrush,
ItemViewMode.MarkedSelected => MarkedSelectedBrush,
ItemViewMode.MarkedAlternative => MarkedAlternativeBrush,
_ => throw new NotImplementedException()
};
}
return value;
ItemViewMode.Default => DefaultBrush,
ItemViewMode.Alternative => AlternativeBrush,
ItemViewMode.Selected => SelectedBrush,
ItemViewMode.Marked => MarkedBrush,
ItemViewMode.MarkedSelected => MarkedSelectedBrush,
ItemViewMode.MarkedAlternative => MarkedAlternativeBrush,
_ => throw new NotImplementedException()
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
return value;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -3,35 +3,34 @@ using Avalonia.Data.Converters;
using FileTime.App.Core.Models.Enums;
using FileTime.App.Core.ViewModels;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class ItemViewModelIsAttributeTypeConverter : IValueConverter
{
public class ItemViewModelIsAttributeTypeConverter : IValueConverter
public bool Invert { get; set; }
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public bool Invert { get; set; }
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var attributeType = GetAttributeType(value);
if (parameter == null) return attributeType;
var result = parameter is ItemAttributeType targetAttribute && attributeType == targetAttribute;
if (Invert && parameter is ItemAttributeType) result = !result;
return result;
}
var attributeType = GetAttributeType(value);
if (parameter == null) return attributeType;
var result = parameter is ItemAttributeType targetAttribute && attributeType == targetAttribute;
if (Invert && parameter is ItemAttributeType) result = !result;
return result;
}
private static ItemAttributeType? GetAttributeType(object? value)
private static ItemAttributeType? GetAttributeType(object? value)
{
return value switch
{
return value switch
{
IFileViewModel => ItemAttributeType.File,
IContainerSizeContainerViewModel => ItemAttributeType.SizeContainer,
IElementViewModel => ItemAttributeType.Element,
IContainerViewModel => ItemAttributeType.Container,
_ => null
};
}
IFileViewModel => ItemAttributeType.File,
IContainerSizeContainerViewModel => ItemAttributeType.SizeContainer,
IElementViewModel => ItemAttributeType.Element,
IContainerViewModel => ItemAttributeType.Container,
_ => null
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -4,112 +4,111 @@ using Avalonia.Media;
using FileTime.App.Core.Models;
using FileTime.GuiApp.ViewModels;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class NamePartShrinkerConverter : IMultiValueConverter
{
public class NamePartShrinkerConverter : IMultiValueConverter
private const int PixelPerChar = 8;
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
private const int PixelPerChar = 8;
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
if (values.Count > 0 && values[0] is IList<ItemNamePart> nameParts)
{
if (values.Count > 0 && values[0] is IList<ItemNamePart> nameParts)
var attributeWidth = values[2] is bool b && b ? 340 : 0;
var newNameParts = nameParts;
if (values.Count > 1 && values[1] is double width && width > 0)
{
var attributeWidth = values[2] is bool b && b ? 340 : 0;
var newNameParts = nameParts;
if (values.Count > 1 && values[1] is double width && width > 0)
{
newNameParts = GetNamePartsForWidth(nameParts, width - attributeWidth);
}
return newNameParts.Select(p => new ItemNamePartViewModel(p.Text, p.IsSpecial ? TextDecorations.Underline : null)).ToList();
newNameParts = GetNamePartsForWidth(nameParts, width - attributeWidth);
}
return null;
return newNameParts.Select(p => new ItemNamePartViewModel(p.Text, p.IsSpecial ? TextDecorations.Underline : null)).ToList();
}
return null;
}
private static List<ItemNamePart> GetNamePartsForWidth(IList<ItemNamePart> nameParts, double maxWidth)
{
//Best case, we are in the range
var textLength = nameParts.Select(p => p.Text.Length).Sum();
if (textLength * PixelPerChar <= maxWidth)
{
return nameParts.ToList();
}
private static List<ItemNamePart> GetNamePartsForWidth(IList<ItemNamePart> nameParts, double maxWidth)
//Trying at least with the special parts
var newNameParts = new ItemNamePart?[nameParts.Count];
for (var i = 0; i < nameParts.Count; i++)
{
//Best case, we are in the range
var textLength = nameParts.Select(p => p.Text.Length).Sum();
if (textLength * PixelPerChar <= maxWidth)
if (nameParts[i].IsSpecial)
{
return nameParts.ToList();
newNameParts[i] = nameParts[i];
}
//Trying at least with the special parts
var newNameParts = new ItemNamePart?[nameParts.Count];
for (var i = 0; i < nameParts.Count; i++)
{
if (nameParts[i].IsSpecial)
{
newNameParts[i] = nameParts[i];
}
}
return GetNamePartsForWidthOptimistic(nameParts, newNameParts, maxWidth);
}
private static List<ItemNamePart> GetNamePartsForWidthOptimistic(IList<ItemNamePart> nameParts, ItemNamePart?[] newNameParts, double maxWidth)
return GetNamePartsForWidthOptimistic(nameParts, newNameParts, maxWidth);
}
private static List<ItemNamePart> GetNamePartsForWidthOptimistic(IList<ItemNamePart> nameParts, ItemNamePart?[] newNameParts, double maxWidth)
{
var trimmedIndexes = new List<int>();
for (var i = 0; i < newNameParts.Length; i++)
{
var trimmedIndexes = new List<int>();
for (var i = 0; i < newNameParts.Length; i++)
if (newNameParts[i] == null)
{
if (newNameParts[i] == null)
{
trimmedIndexes.Add(i);
newNameParts[i] = new ItemNamePart("...");
}
trimmedIndexes.Add(i);
newNameParts[i] = new ItemNamePart("...");
}
var textLength = newNameParts.Select(p => p?.Text.Length ?? 0).Sum();
if (textLength * PixelPerChar > maxWidth)
{
return GetNamePartsForWidthPessimistic(nameParts, maxWidth);
}
foreach (var trimmedIndex in trimmedIndexes)
{
var baseTextLength = newNameParts.Select((p, i) => i == trimmedIndex ? 0 : (p?.Text.Length ?? 0)).Sum();
var proposedText = nameParts[trimmedIndex].Text;
var trimmed = false;
while ((baseTextLength + proposedText.Length + (trimmed ? 3 : 0)) * PixelPerChar > maxWidth)
{
proposedText = proposedText[0..^1];
trimmed = true;
}
newNameParts[trimmedIndex] = new ItemNamePart(proposedText + (trimmed ? "..." : ""));
if (trimmed) break;
}
return newNameParts.Where(f => f is not null).ToList()!;
}
private static List<ItemNamePart> GetNamePartsForWidthPessimistic(IList<ItemNamePart> nameParts, double maxWidth)
var textLength = newNameParts.Select(p => p?.Text.Length ?? 0).Sum();
if (textLength * PixelPerChar > maxWidth)
{
var newNameParts = new List<ItemNamePart>(nameParts);
foreach (var namePart in nameParts)
{
var baseTextLength = newNameParts.Select(p => p.Text.Length).Sum();
var proposedText = namePart.Text;
var trimmed = false;
while ((baseTextLength + 3) * PixelPerChar > maxWidth && proposedText != "")
{
proposedText = proposedText[0..^1];
trimmed = true;
}
if (!string.IsNullOrWhiteSpace(proposedText)) newNameParts.Add(new ItemNamePart(proposedText, namePart.IsSpecial));
if (trimmed) break;
}
if (newNameParts.Last().IsSpecial)
{
newNameParts.Add(new ItemNamePart("..."));
}
else
{
var last = newNameParts.Last();
last.Text += "...";
}
return newNameParts;
return GetNamePartsForWidthPessimistic(nameParts, maxWidth);
}
foreach (var trimmedIndex in trimmedIndexes)
{
var baseTextLength = newNameParts.Select((p, i) => i == trimmedIndex ? 0 : (p?.Text.Length ?? 0)).Sum();
var proposedText = nameParts[trimmedIndex].Text;
var trimmed = false;
while ((baseTextLength + proposedText.Length + (trimmed ? 3 : 0)) * PixelPerChar > maxWidth)
{
proposedText = proposedText[0..^1];
trimmed = true;
}
newNameParts[trimmedIndex] = new ItemNamePart(proposedText + (trimmed ? "..." : ""));
if (trimmed) break;
}
return newNameParts.Where(f => f is not null).ToList()!;
}
private static List<ItemNamePart> GetNamePartsForWidthPessimistic(IList<ItemNamePart> nameParts, double maxWidth)
{
var newNameParts = new List<ItemNamePart>(nameParts);
foreach (var namePart in nameParts)
{
var baseTextLength = newNameParts.Select(p => p.Text.Length).Sum();
var proposedText = namePart.Text;
var trimmed = false;
while ((baseTextLength + 3) * PixelPerChar > maxWidth && proposedText != "")
{
proposedText = proposedText[0..^1];
trimmed = true;
}
if (!string.IsNullOrWhiteSpace(proposedText)) newNameParts.Add(new ItemNamePart(proposedText, namePart.IsSpecial));
if (trimmed) break;
}
if (newNameParts.Last().IsSpecial)
{
newNameParts.Add(new ItemNamePart("..."));
}
else
{
var last = newNameParts.Last();
last.Text += "...";
}
return newNameParts;
}
}

View File

@@ -1,27 +1,26 @@
using System.Globalization;
using Avalonia.Data.Converters;
namespace FileTime.GuiApp.Converters
namespace FileTime.GuiApp.Converters;
public class SplitStringConverter : IValueConverter
{
public class SplitStringConverter : IValueConverter
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
if (value is string path && parameter is string separator)
{
if (value is string path && parameter is string separator)
{
return path.Split(separator);
}
else if (value is string path2 && parameter is char separator2)
{
return path2.Split(separator2);
}
return value;
return path.Split(separator);
}
else if (value is string path2 && parameter is char separator2)
{
return path2.Split(separator2);
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
return value;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Some files were not shown because too many files have changed in this diff Show More