File scoped namespace
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
[*.cs]
|
||||
dotnet_diagnostic.RCS1196.severity = none
|
||||
csharp_style_namespace_declarations = file_scoped:error
|
||||
dotnet_diagnostic.IDE0161.severity = error
|
||||
@@ -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,
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)!;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
nameParts.Add(new ItemNamePart(name));
|
||||
}
|
||||
return nameParts;
|
||||
}
|
||||
|
||||
public string GetFileExtension(string fullName)
|
||||
{
|
||||
var parts = fullName.Split('.');
|
||||
return parts.Length == 1 || (parts.Length == 2 && string.IsNullOrEmpty(parts[0])) ? "" : parts[^1];
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -6,4 +6,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FileTime.ConsoleUI.App\FileTime.ConsoleUI.App.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace FileTime.Core.Behaviors
|
||||
namespace FileTime.Core.Behaviors;
|
||||
|
||||
public interface IOnContainerEnter
|
||||
{
|
||||
public interface IOnContainerEnter
|
||||
{
|
||||
Task OnEnter();
|
||||
}
|
||||
Task OnEnter();
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace FileTime.Core.Command
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
namespace FileTime.Core.Command;
|
||||
|
||||
public interface ICommand
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace FileTime.Core.Command
|
||||
{
|
||||
public interface ITransportationCommand : ICommand
|
||||
{
|
||||
namespace FileTime.Core.Command;
|
||||
|
||||
public interface ITransportationCommand : ICommand
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace FileTime.Core.Enums
|
||||
namespace FileTime.Core.Enums;
|
||||
|
||||
public enum AbsolutePathType
|
||||
{
|
||||
public enum AbsolutePathType
|
||||
{
|
||||
Unknown,
|
||||
Container,
|
||||
Element
|
||||
}
|
||||
Unknown,
|
||||
Container,
|
||||
Element
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace FileTime.Core.Enums
|
||||
namespace FileTime.Core.Enums;
|
||||
|
||||
public enum SupportsDelete
|
||||
{
|
||||
public enum SupportsDelete
|
||||
{
|
||||
False,
|
||||
True,
|
||||
HardDeleteOnly,
|
||||
}
|
||||
False,
|
||||
True,
|
||||
HardDeleteOnly,
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 = '/';
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace FileTime.Core.Models
|
||||
{
|
||||
public interface IElement : IItem
|
||||
{
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
public interface IElement : IItem
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -1,4 +1,3 @@
|
||||
namespace FileTime.Core.Models
|
||||
{
|
||||
public record NativePath(string Path);
|
||||
}
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
public record NativePath(string Path);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace FileTime.Core.Command.Copy
|
||||
{
|
||||
public class CopyCommand : ITransportationCommand
|
||||
{
|
||||
namespace FileTime.Core.Command.Copy;
|
||||
|
||||
public class CopyCommand : ITransportationCommand
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,10 @@
|
||||
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace FileTime.Core.Command
|
||||
namespace FileTime.Core.Command;
|
||||
|
||||
public enum TransportMode
|
||||
{
|
||||
public enum TransportMode
|
||||
{
|
||||
Merge,
|
||||
Overwrite,
|
||||
Skip
|
||||
}
|
||||
Merge,
|
||||
Overwrite,
|
||||
Skip
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
@@ -33,4 +33,3 @@ namespace FileTime.Core.Models
|
||||
Provider,
|
||||
Exceptions
|
||||
), IFileElement;
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
@@ -6,6 +6,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.10.13" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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);
|
||||
@@ -1,4 +1,3 @@
|
||||
namespace FileTime.GuiApp.Services
|
||||
{
|
||||
public interface IDefaultModeKeyInputHandler : IKeyInputHandler { }
|
||||
}
|
||||
namespace FileTime.GuiApp.Services;
|
||||
|
||||
public interface IDefaultModeKeyInputHandler : IKeyInputHandler { }
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
namespace FileTime.GuiApp.Services
|
||||
{
|
||||
public interface IRapidTravelModeKeyInputHandler : IKeyInputHandler { }
|
||||
}
|
||||
namespace FileTime.GuiApp.Services;
|
||||
|
||||
public interface IRapidTravelModeKeyInputHandler : IKeyInputHandler { }
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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>());
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user