diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Models/Enums/PasteMode.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Models/Enums/PasteMode.cs new file mode 100644 index 0000000..d1099c7 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Models/Enums/PasteMode.cs @@ -0,0 +1,8 @@ +namespace FileTime.App.Core.Models.Enums; + +public enum PasteMode +{ + Merge, + Overwrite, + Skip +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandler.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandler.cs deleted file mode 100644 index 92883c6..0000000 --- a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandler.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FileTime.App.Core.Command; - -namespace FileTime.App.Core.Services; - -public interface ICommandHandler -{ - bool CanHandleCommand(Command.Command command); - Task HandleCommandAsync(Command.Command command); -} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandlerService.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandlerService.cs deleted file mode 100644 index 362c78c..0000000 --- a/src/AppCommon/FileTime.App.Core.Abstraction/Services/ICommandHandlerService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using FileTime.App.Core.Command; - -namespace FileTime.App.Core.Services; - -public interface ICommandHandlerService -{ - Task HandleCommandAsync(Command.Command command); -} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/IIdentifiableUserCommandService.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IIdentifiableUserCommandService.cs new file mode 100644 index 0000000..631f2e9 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IIdentifiableUserCommandService.cs @@ -0,0 +1,9 @@ +using FileTime.App.Core.UserCommand; + +namespace FileTime.App.Core.Services; + +public interface IIdentifiableUserCommandService +{ + void AddIdentifiableUserCommandFactory(string identifier, Func commandFactory); + IIdentifiableUserCommand GetCommand(string identifier); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandler.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandler.cs new file mode 100644 index 0000000..fdf4a40 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandler.cs @@ -0,0 +1,9 @@ +using FileTime.App.Core.UserCommand; + +namespace FileTime.App.Core.Services; + +public interface IUserCommandHandler +{ + bool CanHandleCommand(IUserCommand command); + Task HandleCommandAsync(IUserCommand command); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandlerService.cs new file mode 100644 index 0000000..c015e3a --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Services/IUserCommandHandlerService.cs @@ -0,0 +1,7 @@ +namespace FileTime.App.Core.Services; + +public interface IUserCommandHandlerService +{ + Task HandleCommandAsync(UserCommand.IUserCommand command); + Task HandleCommandAsync() where TCommand : UserCommand.IUserCommand, new(); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CloseTabCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CloseTabCommand.cs new file mode 100644 index 0000000..affbd2a --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CloseTabCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class CloseTabCommand : IIdentifiableUserCommand +{ + public const string CommandName = "close_tab"; + public static CloseTabCommand Instance { get; } = new CloseTabCommand(); + + private CloseTabCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyCommand.cs new file mode 100644 index 0000000..bb869d2 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/CopyCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class CopyCommand : IIdentifiableUserCommand +{ + public const string CommandName = "copy"; + public static CopyCommand Instance { get; } = new CopyCommand(); + + private CopyCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EnterRapidTravelCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EnterRapidTravelCommand.cs new file mode 100644 index 0000000..5dceca1 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/EnterRapidTravelCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class EnterRapidTravelCommand : IIdentifiableUserCommand +{ + public const string CommandName = "exter_rapid_travel_mode"; + public static EnterRapidTravelCommand Instance { get; } = new EnterRapidTravelCommand(); + + private EnterRapidTravelCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/ExitRapidTravelCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/ExitRapidTravelCommand.cs new file mode 100644 index 0000000..7266591 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/ExitRapidTravelCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class ExitRapidTravelCommand : IIdentifiableUserCommand +{ + public const string CommandName = "exit_rapid_travel_mode"; + public static ExitRapidTravelCommand Instance { get; } = new ExitRapidTravelCommand(); + + private ExitRapidTravelCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoUpCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoUpCommand.cs new file mode 100644 index 0000000..dd3fc49 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/GoUpCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class GoUpCommand : IIdentifiableUserCommand +{ + public const string CommandName = "go_up"; + public static GoUpCommand Instance { get; } = new GoUpCommand(); + + private GoUpCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IIdentifiableUserCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IIdentifiableUserCommand.cs new file mode 100644 index 0000000..2c0987c --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IIdentifiableUserCommand.cs @@ -0,0 +1,6 @@ +namespace FileTime.App.Core.UserCommand; + +public interface IIdentifiableUserCommand : IUserCommand +{ + string UserCommandID { get; } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IUserCommand.cs similarity index 90% rename from src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs rename to src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IUserCommand.cs index d0b093a..6ed440e 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/IUserCommand.cs @@ -1,6 +1,11 @@ -namespace FileTime.App.Core.Command; +namespace FileTime.App.Core.UserCommand; -public enum Command +public interface IUserCommand +{ + +} + +/*public enum Command { None, @@ -63,4 +68,4 @@ public enum Command TimelineStart, ToggleAdvancedIcons, ToggleHidden, -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MarkCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MarkCommand.cs new file mode 100644 index 0000000..c26b28b --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MarkCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class MarkCommand : IIdentifiableUserCommand +{ + public const string CommandName = "mark_selected"; + public static MarkCommand Instance { get; } = new MarkCommand(); + + private MarkCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorDownCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorDownCommand.cs new file mode 100644 index 0000000..695e176 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorDownCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class MoveCursorDownCommand : IIdentifiableUserCommand +{ + public const string CommandName = "move_cursor_down"; + public static MoveCursorDownCommand Instance { get; } = new MoveCursorDownCommand(); + + private MoveCursorDownCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorUpCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorUpCommand.cs new file mode 100644 index 0000000..fde03f3 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/MoveCursorUpCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class MoveCursorUpCommand : IIdentifiableUserCommand +{ + public const string CommandName = "move_cursor_up"; + public static MoveCursorUpCommand Instance { get; } = new MoveCursorUpCommand(); + + private MoveCursorUpCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenContainerCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenContainerCommand.cs new file mode 100644 index 0000000..86d5222 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenContainerCommand.cs @@ -0,0 +1,13 @@ +using FileTime.Core.Models; + +namespace FileTime.App.Core.UserCommand; + +public class OpenContainerCommand : IUserCommand +{ + public IAbsolutePath Path { get; } + + private OpenContainerCommand(IAbsolutePath path) + { + Path = path; + } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenSelectedCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenSelectedCommand.cs new file mode 100644 index 0000000..65cef13 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/OpenSelectedCommand.cs @@ -0,0 +1,13 @@ +namespace FileTime.App.Core.UserCommand; + +public class OpenSelectedCommand : IIdentifiableUserCommand +{ + public const string CommandName = "open_selected"; + public static OpenSelectedCommand Instance { get; } = new OpenSelectedCommand(); + + private OpenSelectedCommand() + { + } + + public string UserCommandID => CommandName; +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/PasteCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/PasteCommand.cs new file mode 100644 index 0000000..30bf858 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/PasteCommand.cs @@ -0,0 +1,24 @@ +using FileTime.App.Core.Models.Enums; + +namespace FileTime.App.Core.UserCommand; + +public class PasteCommand : IIdentifiableUserCommand +{ + public const string PasteMergeCommandName = "paste_merge"; + public const string PasteOverwriteCommandName = "paste_overwrite"; + public const string PasteSkipCommandName = "paste_skip"; + + public static PasteCommand Merge { get; } = new PasteCommand(PasteMode.Merge, PasteMergeCommandName); + public static PasteCommand Overwrite { get; } = new PasteCommand(PasteMode.Overwrite, PasteOverwriteCommandName); + public static PasteCommand Skip { get; } = new PasteCommand(PasteMode.Skip, PasteSkipCommandName); + + public PasteMode PasteMode { get; } + + private PasteCommand(PasteMode pasteMode, string commandName) + { + PasteMode = pasteMode; + UserCommandID = commandName; + } + + public string UserCommandID { get; } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/SwitchToTabCommand.cs b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/SwitchToTabCommand.cs new file mode 100644 index 0000000..e747921 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core.Abstraction/UserCommand/SwitchToTabCommand.cs @@ -0,0 +1,34 @@ +namespace FileTime.App.Core.UserCommand; + +public class SwitchToTabCommand : IIdentifiableUserCommand +{ + private const string SwitchToTabBase = "switch_to_tab"; + public const string SwitchToTab1CommandName = SwitchToTabBase + "1"; + public const string SwitchToTab2CommandName = SwitchToTabBase + "2"; + public const string SwitchToTab3CommandName = SwitchToTabBase + "3"; + public const string SwitchToTab4CommandName = SwitchToTabBase + "4"; + public const string SwitchToTab5CommandName = SwitchToTabBase + "5"; + public const string SwitchToTab6CommandName = SwitchToTabBase + "6"; + public const string SwitchToTab7CommandName = SwitchToTabBase + "7"; + public const string SwitchToTab8CommandName = SwitchToTabBase + "8"; + public const string SwitchToLastTabCommandName = "switch_to_last_tab"; + + public static SwitchToTabCommand SwitchToTab1 { get; } = new(1, SwitchToTab1CommandName); + public static SwitchToTabCommand SwitchToTab2 { get; } = new(2, SwitchToTab2CommandName); + public static SwitchToTabCommand SwitchToTab3 { get; } = new(3, SwitchToTab3CommandName); + public static SwitchToTabCommand SwitchToTab4 { get; } = new(4, SwitchToTab4CommandName); + public static SwitchToTabCommand SwitchToTab5 { get; } = new(5, SwitchToTab5CommandName); + public static SwitchToTabCommand SwitchToTab6 { get; } = new(6, SwitchToTab6CommandName); + public static SwitchToTabCommand SwitchToTab7 { get; } = new(7, SwitchToTab7CommandName); + public static SwitchToTabCommand SwitchToTab8 { get; } = new(8, SwitchToTab8CommandName); + public static SwitchToTabCommand SwitchToLastTab { get; } = new(-1, SwitchToLastTabCommandName); + + private SwitchToTabCommand(int tabNumber, string commandName) + { + TabNumber = tabNumber; + UserCommandID = commandName; + } + + public string UserCommandID { get; } + public int TabNumber { get; } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/IdentifiableUserCommandService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/IdentifiableUserCommandService.cs new file mode 100644 index 0000000..a1a0c7b --- /dev/null +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/IdentifiableUserCommandService.cs @@ -0,0 +1,19 @@ +using FileTime.App.Core.UserCommand; + +namespace FileTime.App.Core.Services.UserCommandHandler; + +public class IdentifiableUserCommandService : IIdentifiableUserCommandService +{ + private readonly Dictionary> _identifiableUserCommands = new(); + + public void AddIdentifiableUserCommandFactory(string identifier, Func commandFactory) + => _identifiableUserCommands.Add(identifier, commandFactory); + + public IIdentifiableUserCommand GetCommand(string identifier) + { + if (!_identifiableUserCommands.ContainsKey(identifier)) + throw new IndexOutOfRangeException($"No command factory is registered for command {identifier}"); + + return _identifiableUserCommands[identifier].Invoke(); + } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/ItemManipulationCommandHandler.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs similarity index 62% rename from src/AppCommon/FileTime.App.Core/Services/CommandHandler/ItemManipulationCommandHandler.cs rename to src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs index d0e66e6..7a8e6c2 100644 --- a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/ItemManipulationCommandHandler.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ItemManipulationUserCommandHandlerService.cs @@ -1,27 +1,28 @@ using System.Reactive.Linq; -using FileTime.App.Core.Command; using FileTime.App.Core.Models; +using FileTime.App.Core.Models.Enums; +using FileTime.App.Core.UserCommand; using FileTime.App.Core.ViewModels; using FileTime.Core.Command; -using FileTime.Core.Command.Copy; using FileTime.Core.Models; +using CopyCommand = FileTime.Core.Command.Copy.CopyCommand; -namespace FileTime.App.Core.Services.CommandHandler; +namespace FileTime.App.Core.Services.UserCommandHandler; -public class ItemManipulationCommandHandler : CommandHandlerBase +public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServiceBase { private ITabViewModel? _selectedTab; private IItemViewModel? _currentSelectedItem; - private readonly ICommandHandlerService _commandHandlerService; + private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly IClipboardService _clipboardService; private readonly BindedCollection? _markedItems; - public ItemManipulationCommandHandler( + public ItemManipulationUserCommandHandlerService( IAppState appState, - ICommandHandlerService commandHandlerService, + IUserCommandHandlerService userCommandHandlerService, IClipboardService clipboardService) : base(appState) { - _commandHandlerService = commandHandlerService; + _userCommandHandlerService = userCommandHandlerService; _clipboardService = clipboardService; SaveSelectedTab(t => _selectedTab = t); @@ -29,13 +30,11 @@ public class ItemManipulationCommandHandler : CommandHandlerBase _markedItems = new BindedCollection(appState.SelectedTab.Select(t => t?.MarkedItems)); - AddCommandHandlers(new (Command.Command, Func)[] + AddCommandHandlers(new IUserCommandHandler[] { - (Command.Command.Copy, Copy), - (Command.Command.Mark, MarkItem), - (Command.Command.PasteMerge, PasteMerge), - (Command.Command.PasteOverwrite, PasteOverwrite), - (Command.Command.PasteSkip, PasteSkip), + new TypeUserCommandHandler(Copy), + new TypeUserCommandHandler(MarkItem), + new TypeUserCommandHandler(Paste), }); } @@ -44,7 +43,7 @@ public class ItemManipulationCommandHandler : CommandHandlerBase if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return; _selectedTab.ToggleMarkedItem(new AbsolutePath(_currentSelectedItem.BaseItem)); - await _commandHandlerService.HandleCommandAsync(Command.Command.MoveCursorDown); + await _userCommandHandlerService.HandleCommandAsync(MoveCursorDownCommand.Instance); } private Task Copy() @@ -69,6 +68,17 @@ public class ItemManipulationCommandHandler : CommandHandlerBase return Task.CompletedTask; } + private async Task Paste(PasteCommand command) + { + await (command.PasteMode switch + { + PasteMode.Merge => PasteMerge(), + PasteMode.Overwrite => PasteOverwrite(), + PasteMode.Skip => PasteSkip(), + _ => throw new ArgumentException($"Unknown {nameof(PasteMode)} value: {command.PasteMode}") + }); + } + private async Task PasteMerge() { await Paste(TransportMode.Merge); diff --git a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs similarity index 69% rename from src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs rename to src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs index cade34c..df711bb 100644 --- a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs @@ -1,37 +1,36 @@ -using System.Reactive.Linq; -using FileTime.App.Core.Command; using FileTime.App.Core.Extensions; using FileTime.App.Core.Models.Enums; +using FileTime.App.Core.UserCommand; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; using FileTime.Core.Services; using FileTime.Providers.Local; using Microsoft.Extensions.DependencyInjection; -namespace FileTime.App.Core.Services.CommandHandler; +namespace FileTime.App.Core.Services.UserCommandHandler; -public class NavigationCommandHandler : CommandHandlerBase +public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase { private readonly IAppState _appState; private readonly IServiceProvider _serviceProvider; private readonly ILocalContentProvider _localContentProvider; - private readonly ICommandHandlerService _commandHandlerService; + private readonly IUserCommandHandlerService _userCommandHandlerService; private ITabViewModel? _selectedTab; private IContainer? _currentLocation; private IItemViewModel? _currentSelectedItem; private IEnumerable _currentItems = Enumerable.Empty(); private ViewMode _viewMode; - public NavigationCommandHandler( + public NavigationUserCommandHandlerService( IAppState appState, IServiceProvider serviceProvider, ILocalContentProvider localContentProvider, - ICommandHandlerService commandHandlerService) : base(appState) + IUserCommandHandlerService userCommandHandlerService) : base(appState) { _appState = appState; _serviceProvider = serviceProvider; _localContentProvider = localContentProvider; - _commandHandlerService = commandHandlerService; + _userCommandHandlerService = userCommandHandlerService; SaveSelectedTab(t => _selectedTab = t); SaveCurrentSelectedItem(i => _currentSelectedItem = i); @@ -40,24 +39,16 @@ public class NavigationCommandHandler : CommandHandlerBase appState.ViewMode.Subscribe(v => _viewMode = v); - AddCommandHandlers(new (Command.Command, Func)[] + AddCommandHandlers(new IUserCommandHandler[] { - (Command.Command.CloseTab, CloseTab), - (Command.Command.EnterRapidTravel, EnterRapidTravel), - (Command.Command.ExitRapidTravel, ExitRapidTravel), - (Command.Command.GoUp, GoUp), - (Command.Command.MoveCursorDown, MoveCursorDown), - (Command.Command.MoveCursorUp, MoveCursorUp), - (Command.Command.Open, OpenContainer), - (Command.Command.SwitchToLastTab, async () => await SwitchToTab(-1)), - (Command.Command.SwitchToTab1, async () => await SwitchToTab(1)), - (Command.Command.SwitchToTab2, async () => await SwitchToTab(2)), - (Command.Command.SwitchToTab3, async () => await SwitchToTab(3)), - (Command.Command.SwitchToTab4, async () => await SwitchToTab(4)), - (Command.Command.SwitchToTab5, async () => await SwitchToTab(5)), - (Command.Command.SwitchToTab6, async () => await SwitchToTab(6)), - (Command.Command.SwitchToTab7, async () => await SwitchToTab(7)), - (Command.Command.SwitchToTab8, async () => await SwitchToTab(8)), + new TypeUserCommandHandler(CloseTab), + new TypeUserCommandHandler(EnterRapidTravel), + new TypeUserCommandHandler(ExitRapidTravel), + new TypeUserCommandHandler(GoUp), + new TypeUserCommandHandler(MoveCursorDown), + new TypeUserCommandHandler(MoveCursorUp), + new TypeUserCommandHandler(OpenContainer), + new TypeUserCommandHandler(SwitchToTab), }); } @@ -109,8 +100,9 @@ public class NavigationCommandHandler : CommandHandlerBase return Task.CompletedTask; } - private Task SwitchToTab(int number) + private Task SwitchToTab(SwitchToTabCommand command) { + var number = command.TabNumber; var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number); if (number == -1) @@ -129,7 +121,7 @@ public class NavigationCommandHandler : CommandHandlerBase if (_viewMode == ViewMode.RapidTravel) { - _commandHandlerService.HandleCommandAsync(Command.Command.ExitRapidTravel); + _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance); } _appState.SetSelectedTab(tabViewModel!); diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/TypeUserCommandHandler.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/TypeUserCommandHandler.cs new file mode 100644 index 0000000..7c1beee --- /dev/null +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/TypeUserCommandHandler.cs @@ -0,0 +1,26 @@ +using FileTime.App.Core.UserCommand; + +namespace FileTime.App.Core.Services.UserCommandHandler; + +public class TypeUserCommandHandler : IUserCommandHandler +{ + private readonly Func _handler; + + public TypeUserCommandHandler(Func handler) + { + _handler = handler; + } + + public TypeUserCommandHandler(Func handler) + { + _handler = async (_) => await handler(); + } + + public bool CanHandleCommand(IUserCommand command) => command is T; + + public Task HandleCommandAsync(IUserCommand command) + { + if (command is not T typedCommand) throw new ArgumentException($"Parameter '{nameof(command)}' is not of type '{typeof(T).Name}'"); + return _handler(typedCommand); + } +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandler.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandler.cs new file mode 100644 index 0000000..4d15fda --- /dev/null +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandler.cs @@ -0,0 +1,16 @@ +namespace FileTime.App.Core.Services.UserCommandHandler; + +public sealed class UserCommandHandler : IUserCommandHandler +{ + private readonly Func _canHandle; + private readonly Func _handle; + + public UserCommandHandler(Func canHandle, Func handle) + { + _canHandle = canHandle; + _handle = handle; + } + + public bool CanHandleCommand(UserCommand.IUserCommand command) => _canHandle(command); + public async Task HandleCommandAsync(UserCommand.IUserCommand command) => await _handle(command); +} \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/CommandHandlerBase.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandlerServiceBase.cs similarity index 51% rename from src/AppCommon/FileTime.App.Core/Services/CommandHandler/CommandHandlerBase.cs rename to src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandlerServiceBase.cs index 6e2d5fa..e4b5705 100644 --- a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/CommandHandlerBase.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/UserCommandHandlerServiceBase.cs @@ -1,37 +1,44 @@ using System.Reactive.Linq; using DynamicData; -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.UserCommandHandler; -public abstract class CommandHandlerBase : ICommandHandler +public abstract class UserCommandHandlerServiceBase : IUserCommandHandler { - private readonly Dictionary> _commandHandlers = new(); + private readonly List _userCommandHandlers = new(); private readonly IAppState? _appState; - protected CommandHandlerBase(IAppState? appState = null) + protected UserCommandHandlerServiceBase(IAppState? appState = null) { _appState = appState; } - public bool CanHandleCommand(Command.Command command) => _commandHandlers.ContainsKey(command); + public bool CanHandleCommand(UserCommand.IUserCommand command) => _userCommandHandlers.Any(h => h.CanHandleCommand(command)); - public async Task HandleCommandAsync(Command.Command command) => await _commandHandlers[command].Invoke(); - - protected void AddCommandHandler(Command.Command command, Func handler) => _commandHandlers.Add(command, handler); - protected void AddCommandHandlers(IEnumerable<(Command.Command command, Func handler)> commandHandlers) + public async Task HandleCommandAsync(UserCommand.IUserCommand command) { - foreach (var (command, handler) in commandHandlers) + var handler = _userCommandHandlers.Find(h => h.CanHandleCommand(command)); + + if (handler is null) return; + await handler.HandleCommandAsync(command); + } + + protected void AddCommandHandler(IUserCommandHandler userCommandHandler) => _userCommandHandlers.Add(userCommandHandler); + + protected void AddCommandHandlers(IEnumerable commandHandlers) + { + foreach (var userCommandHandler in commandHandlers) { - AddCommandHandler(command, handler); + AddCommandHandler(userCommandHandler); } } - protected void RemoveCommandHandler(Command.Command command) => _commandHandlers.Remove(command); + protected void RemoveCommandHandler(IUserCommandHandler userCommandHandler) => _userCommandHandlers.Remove(userCommandHandler); protected IDisposable SaveSelectedTab(Action handler) => RunWithAppState(appState => appState.SelectedTab.Subscribe(handler)); + protected IDisposable SaveCurrentSelectedItem(Action handler) => RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return(null) : t.CurrentSelectedItem).Switch().Subscribe(handler)); @@ -39,14 +46,14 @@ public abstract class CommandHandlerBase : ICommandHandler => RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return(null) : t.CurrentLocation).Switch().Subscribe(handler)); protected IDisposable SaveCurrentItems(Action> handler) - => RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable?)Enumerable.Empty())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty()))); + => RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable?) Enumerable.Empty())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty()))); protected IDisposable SaveMarkedItems(Action> handler) - => RunWithAppState(appstate => appstate.SelectedTab.Select(t => t == null ? Observable.Empty>() : t.MarkedItems).Switch().Subscribe(handler)); + => RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Empty>() : t.MarkedItems).Switch().Subscribe(handler)); private IDisposable RunWithAppState(Func act) { - if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(CommandHandlerBase)}."); + if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(UserCommandHandlerServiceBase)}."); return act(_appState); } diff --git a/src/AppCommon/FileTime.App.Core/Services/CommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandlerService.cs similarity index 84% rename from src/AppCommon/FileTime.App.Core/Services/CommandHandlerService.cs rename to src/AppCommon/FileTime.App.Core/Services/UserCommandHandlerService.cs index 0e79fa0..6e202b3 100644 --- a/src/AppCommon/FileTime.App.Core/Services/CommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandlerService.cs @@ -1,15 +1,14 @@ -using FileTime.App.Core.Command; using Microsoft.Extensions.DependencyInjection; namespace FileTime.App.Core.Services; -public class CommandHandlerService : ICommandHandlerService +public class UserCommandHandlerService : IUserCommandHandlerService { - private readonly Lazy> _commandHandlers; + private readonly Lazy> _commandHandlers; - public CommandHandlerService(IServiceProvider serviceProvider) + public UserCommandHandlerService(IServiceProvider serviceProvider) { - _commandHandlers = new Lazy>(() => serviceProvider.GetServices()); + _commandHandlers = new Lazy>(serviceProvider.GetServices); //(Commands.AutoRefresh, ToggleAutoRefresh), //(Commands.ChangeTimelineMode, ChangeTimelineMode), @@ -67,7 +66,7 @@ public class CommandHandlerService : ICommandHandlerService //(Commands.ToggleHidden, ToggleHidden), } - public async Task HandleCommandAsync(Command.Command command) + public async Task HandleCommandAsync(UserCommand.IUserCommand command) { var handler = _commandHandlers.Value.FirstOrDefault(h => h.CanHandleCommand(command)); @@ -76,4 +75,7 @@ public class CommandHandlerService : ICommandHandlerService await handler.HandleCommandAsync(command); } } + + public async Task HandleCommandAsync() where TUserCommand : UserCommand.IUserCommand, new() + => await HandleCommandAsync(new TUserCommand()); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/Startup.cs b/src/AppCommon/FileTime.App.Core/Startup.cs index 0c5524b..79e2853 100644 --- a/src/AppCommon/FileTime.App.Core/Startup.cs +++ b/src/AppCommon/FileTime.App.Core/Startup.cs @@ -1,5 +1,6 @@ using FileTime.App.Core.Services; -using FileTime.App.Core.Services.CommandHandler; +using FileTime.App.Core.Services.UserCommandHandler; +using FileTime.App.Core.StartupServices; using FileTime.App.Core.ViewModels; using Microsoft.Extensions.DependencyInjection; @@ -14,15 +15,17 @@ public static class Startup .AddTransient() .AddTransient() .AddTransient() - .AddSingleton() + .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() .AddCommandHandlers(); } private static IServiceCollection AddCommandHandlers(this IServiceCollection serviceCollection) { return serviceCollection - .AddSingleton() - .AddSingleton(); + .AddSingleton() + .AddSingleton(); } } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs new file mode 100644 index 0000000..297c556 --- /dev/null +++ b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs @@ -0,0 +1,39 @@ +using FileTime.App.Core.Services; +using FileTime.App.Core.UserCommand; + +namespace FileTime.App.Core.StartupServices; + +public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler +{ + private readonly IIdentifiableUserCommandService _service; + + public DefaultIdentifiableCommandHandlerRegister(IIdentifiableUserCommandService service) + { + _service = service; + + AddUserCommand(CloseTabCommand.Instance); + AddUserCommand(CopyCommand.Instance); + AddUserCommand(EnterRapidTravelCommand.Instance); + AddUserCommand(ExitRapidTravelCommand.Instance); + AddUserCommand(GoUpCommand.Instance); + AddUserCommand(MarkCommand.Instance); + AddUserCommand(MoveCursorDownCommand.Instance); + AddUserCommand(MoveCursorUpCommand.Instance); + AddUserCommand(OpenSelectedCommand.Instance); + AddUserCommand(PasteCommand.Merge); + AddUserCommand(PasteCommand.Overwrite); + AddUserCommand(PasteCommand.Skip); + AddUserCommand(SwitchToTabCommand.SwitchToTab1); + AddUserCommand(SwitchToTabCommand.SwitchToTab2); + AddUserCommand(SwitchToTabCommand.SwitchToTab3); + AddUserCommand(SwitchToTabCommand.SwitchToTab4); + AddUserCommand(SwitchToTabCommand.SwitchToTab5); + AddUserCommand(SwitchToTabCommand.SwitchToTab6); + AddUserCommand(SwitchToTabCommand.SwitchToTab7); + AddUserCommand(SwitchToTabCommand.SwitchToTab8); + AddUserCommand(SwitchToTabCommand.SwitchToLastTab); + } + + private void AddUserCommand(IIdentifiableUserCommand command) + => _service.AddIdentifiableUserCommandFactory(command.UserCommandID, () => command); +} \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/CommandBindingConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/CommandBindingConfiguration.cs index ae62bb2..b501f73 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/CommandBindingConfiguration.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/CommandBindingConfiguration.cs @@ -1,37 +1,40 @@ using Avalonia.Input; -using FileTime.App.Core.Command; namespace FileTime.GuiApp.Configuration; public class CommandBindingConfiguration { - public List Keys { get; set; } = new List(); + public List Keys { get; set; } - public Command Command { get; set; } = Command.None; + public string Command { get; set; } public string KeysDisplayText => GetKeysDisplayText(); - public CommandBindingConfiguration() { } + public CommandBindingConfiguration() + { + Command = null!; + Keys = null!; + } - public CommandBindingConfiguration(Command command, IEnumerable keys) + public CommandBindingConfiguration(string command, IEnumerable keys) { Keys = new List(keys); Command = command; } - public CommandBindingConfiguration(Command command, KeyConfig key) + public CommandBindingConfiguration(string command, KeyConfig key) { Keys = new List() { key }; Command = command; } - public CommandBindingConfiguration(Command command, IEnumerable keys) + public CommandBindingConfiguration(string command, IEnumerable keys) { Keys = keys.Select(k => new KeyConfig(k)).ToList(); Command = command; } - public CommandBindingConfiguration(Command command, Key key) + public CommandBindingConfiguration(string command, Key key) { Keys = new List() { new KeyConfig(key) }; Command = command; @@ -62,7 +65,7 @@ public class CommandBindingConfiguration { var s = ""; - bool ctrlOrAlt = key.Ctrl || key.Alt; + var ctrlOrAlt = key.Ctrl || key.Alt; if (ctrlOrAlt && currentText.Length > 0 && currentText.Last() != ' ') s += " "; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs index 8703ff2..25f16de 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/MainConfiguration.cs @@ -1,7 +1,5 @@ using Avalonia.Input; -using FileTime.App.Core.Command; -using System; -using System.Collections.Generic; +using FileTime.App.Core.UserCommand; namespace FileTime.GuiApp.Configuration; @@ -42,66 +40,66 @@ public static class MainConfiguration { return new List() { - new CommandBindingConfiguration(Command.AutoRefresh, new KeyConfig(Key.R, shift: true)), - new CommandBindingConfiguration(Command.ChangeTimelineMode, new[] { Key.T, Key.M }), - new CommandBindingConfiguration(Command.CloseTab, Key.Q), - new CommandBindingConfiguration(Command.Compress, new[] { Key.Y, Key.C }), - new CommandBindingConfiguration(Command.Copy, new[] { Key.Y, Key.Y }), - new CommandBindingConfiguration(Command.CopyHash, new[] { Key.C, Key.H }), - new CommandBindingConfiguration(Command.CopyPath, new[] { Key.C, Key.P }), - new CommandBindingConfiguration(Command.CreateContainer, Key.F7), - new CommandBindingConfiguration(Command.CreateContainer, new[] { Key.C, Key.C }), - new CommandBindingConfiguration(Command.CreateElement, new[] { Key.C, Key.E }), - new CommandBindingConfiguration(Command.Cut, new[] { Key.D, Key.D }), - new CommandBindingConfiguration(Command.Edit, new KeyConfig(Key.F4)), - new CommandBindingConfiguration(Command.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)), - new CommandBindingConfiguration(Command.FindByName, new[] { Key.F, Key.N }), - new CommandBindingConfiguration(Command.FindByNameRegex, new[] { Key.F, Key.R }), - new CommandBindingConfiguration(Command.GoToHome, new[] { Key.G, Key.H }), - new CommandBindingConfiguration(Command.GoToPath, new KeyConfig(Key.L, ctrl: true)), - new CommandBindingConfiguration(Command.GoToPath, new[] { Key.G, Key.P }), - new CommandBindingConfiguration(Command.GoToProvider, new[] { Key.G, Key.T }), - new CommandBindingConfiguration(Command.GoToRoot, new[] { Key.G, Key.R }), - new CommandBindingConfiguration(Command.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }), - new CommandBindingConfiguration(Command.Mark, Key.Space), - new CommandBindingConfiguration(Command.MoveToLast, new KeyConfig(Key.G, shift: true)), - new CommandBindingConfiguration(Command.MoveToFirst, new[] { Key.G, Key.G }), - new CommandBindingConfiguration(Command.NextTimelineBlock, Key.L ), - new CommandBindingConfiguration(Command.NextTimelineCommand, Key.J ), - new CommandBindingConfiguration(Command.OpenInFileBrowser, new[] { Key.O, Key.E }), - new CommandBindingConfiguration(Command.PasteMerge, new[] { Key.P, Key.P }), - new CommandBindingConfiguration(Command.PasteOverwrite, new[] { Key.P, Key.O }), - new CommandBindingConfiguration(Command.PasteSkip, new[] { Key.P, Key.S }), - new CommandBindingConfiguration(Command.PinFavorite, new[] { Key.F, Key.P }), - new CommandBindingConfiguration(Command.PreviousTimelineBlock, Key.H ), - new CommandBindingConfiguration(Command.PreviousTimelineCommand, Key.K ), - new CommandBindingConfiguration(Command.Refresh, Key.R), - new CommandBindingConfiguration(Command.Rename, Key.F2), - new CommandBindingConfiguration(Command.Rename, new[] { Key.C, Key.W }), - new CommandBindingConfiguration(Command.RunCommand, new KeyConfig(Key.D4, shift: true)), - new CommandBindingConfiguration(Command.ScanContainerSize, new[] { Key.C, Key.S }), - new CommandBindingConfiguration(Command.ShowAllShortcut, Key.F1), - new CommandBindingConfiguration(Command.SoftDelete, new[] { new KeyConfig(Key.D), new KeyConfig(Key.D, shift: true) }), - new CommandBindingConfiguration(Command.SwitchToLastTab, Key.D9), - new CommandBindingConfiguration(Command.SwitchToTab1, Key.D1), - new CommandBindingConfiguration(Command.SwitchToTab2, Key.D2), - new CommandBindingConfiguration(Command.SwitchToTab3, Key.D3), - new CommandBindingConfiguration(Command.SwitchToTab4, Key.D4), - new CommandBindingConfiguration(Command.SwitchToTab5, Key.D5), - new CommandBindingConfiguration(Command.SwitchToTab6, Key.D6), - new CommandBindingConfiguration(Command.SwitchToTab7, Key.D7), - new CommandBindingConfiguration(Command.SwitchToTab8, Key.D8), - new CommandBindingConfiguration(Command.TimelinePause, new[] { Key.T, Key.P }), - new CommandBindingConfiguration(Command.TimelineRefresh, new[] { Key.T, Key.R }), - new CommandBindingConfiguration(Command.TimelineStart, new[] { Key.T, Key.S }), - new CommandBindingConfiguration(Command.ToggleAdvancedIcons, new[] { Key.Z, Key.I }), - new CommandBindingConfiguration(Command.GoUp, Key.Left), - new CommandBindingConfiguration(Command.Open, Key.Right), - new CommandBindingConfiguration(Command.OpenOrRun, Key.Enter), - new CommandBindingConfiguration(Command.MoveCursorUp, Key.Up), - new CommandBindingConfiguration(Command.MoveCursorDown, Key.Down), - new CommandBindingConfiguration(Command.MoveCursorUpPage, Key.PageUp), - new CommandBindingConfiguration(Command.MoveCursorDownPage, Key.PageDown), + //new CommandBindingConfiguration(ConfigCommand.AutoRefresh, new KeyConfig(Key.R, shift: true)), + //new CommandBindingConfiguration(ConfigCommand.ChangeTimelineMode, new[] { Key.T, Key.M }), + new CommandBindingConfiguration(CloseTabCommand.CommandName, Key.Q), + //new CommandBindingConfiguration(ConfigCommand.Compress, new[] { Key.Y, Key.C }), + new CommandBindingConfiguration(CopyCommand.CommandName, new[] { Key.Y, Key.Y }), + //new CommandBindingConfiguration(ConfigCommand.CopyHash, new[] { Key.C, Key.H }), + //new CommandBindingConfiguration(ConfigCommand.CopyPath, new[] { Key.C, Key.P }), + //new CommandBindingConfiguration(ConfigCommand.CreateContainer, Key.F7), + //new CommandBindingConfiguration(ConfigCommand.CreateContainer, new[] { Key.C, Key.C }), + //new CommandBindingConfiguration(ConfigCommand.CreateElement, new[] { Key.C, Key.E }), + //new CommandBindingConfiguration(ConfigCommand.Cut, new[] { Key.D, Key.D }), + //new CommandBindingConfiguration(ConfigCommand.Edit, new KeyConfig(Key.F4)), + new CommandBindingConfiguration(EnterRapidTravelCommand.CommandName, new KeyConfig(Key.OemComma, shift: true)), + //new CommandBindingConfiguration(ConfigCommand.FindByName, new[] { Key.F, Key.N }), + //new CommandBindingConfiguration(ConfigCommand.FindByNameRegex, new[] { Key.F, Key.R }), + //new CommandBindingConfiguration(ConfigCommand.GoToHome, new[] { Key.G, Key.H }), + //new CommandBindingConfiguration(ConfigCommand.GoToPath, new KeyConfig(Key.L, ctrl: true)), + //new CommandBindingConfiguration(ConfigCommand.GoToPath, new[] { Key.G, Key.P }), + //new CommandBindingConfiguration(ConfigCommand.GoToProvider, new[] { Key.G, Key.T }), + //new CommandBindingConfiguration(ConfigCommand.GoToRoot, new[] { Key.G, Key.R }), + //new CommandBindingConfiguration(ConfigCommand.HardDelete, new[] { new KeyConfig(Key.D,shift: true), new KeyConfig(Key.D, shift: true) }), + new CommandBindingConfiguration(MarkCommand.CommandName, Key.Space), + //new CommandBindingConfiguration(ConfigCommand.MoveToLast, new KeyConfig(Key.G, shift: true)), + //new CommandBindingConfiguration(ConfigCommand.MoveToFirst, new[] { Key.G, Key.G }), + //new CommandBindingConfiguration(ConfigCommand.NextTimelineBlock, Key.L ), + //new CommandBindingConfiguration(ConfigCommand.NextTimelineCommand, Key.J ), + //new CommandBindingConfiguration(ConfigCommand.OpenInFileBrowser, new[] { Key.O, Key.E }), + new CommandBindingConfiguration(PasteCommand.PasteMergeCommandName, new[] { Key.P, Key.P }), + new CommandBindingConfiguration(PasteCommand.PasteOverwriteCommandName, new[] { Key.P, Key.O }), + new CommandBindingConfiguration(PasteCommand.PasteSkipCommandName, new[] { Key.P, Key.S }), + //new CommandBindingConfiguration(ConfigCommand.PinFavorite, new[] { Key.F, Key.P }), + //new CommandBindingConfiguration(ConfigCommand.PreviousTimelineBlock, Key.H ), + //new CommandBindingConfiguration(ConfigCommand.PreviousTimelineCommand, Key.K ), + //new CommandBindingConfiguration(ConfigCommand.Refresh, Key.R), + //new CommandBindingConfiguration(ConfigCommand.Rename, Key.F2), + //new CommandBindingConfiguration(ConfigCommand.Rename, new[] { Key.C, Key.W }), + //new CommandBindingConfiguration(ConfigCommand.RunCommand, new KeyConfig(Key.D4, shift: true)), + //new CommandBindingConfiguration(ConfigCommand.ScanContainerSize, new[] { Key.C, Key.S }), + //new CommandBindingConfiguration(ConfigCommand.ShowAllShortcut, Key.F1), + //new CommandBindingConfiguration(ConfigCommand.SoftDelete, new[] { new KeyConfig(Key.D), new KeyConfig(Key.D, shift: true) }), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToLastTabCommandName, Key.D9), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab1CommandName, Key.D1), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab2CommandName, Key.D2), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab3CommandName, Key.D3), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab4CommandName, Key.D4), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab5CommandName, Key.D5), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab6CommandName, Key.D6), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab7CommandName, Key.D7), + new CommandBindingConfiguration(SwitchToTabCommand.SwitchToTab8CommandName, Key.D8), + //new CommandBindingConfiguration(ConfigCommand.TimelinePause, new[] { Key.T, Key.P }), + //new CommandBindingConfiguration(ConfigCommand.TimelineRefresh, new[] { Key.T, Key.R }), + //new CommandBindingConfiguration(ConfigCommand.TimelineStart, new[] { Key.T, Key.S }), + //new CommandBindingConfiguration(ConfigCommand.ToggleAdvancedIcons, new[] { Key.Z, Key.I }), + new CommandBindingConfiguration(GoUpCommand.CommandName, Key.Left), + new CommandBindingConfiguration(OpenSelectedCommand.CommandName, Key.Right), + //new CommandBindingConfiguration(ConfigCommand.OpenOrRun, Key.Enter), + new CommandBindingConfiguration(MoveCursorUpCommand.CommandName, Key.Up), + new CommandBindingConfiguration(MoveCursorDownCommand.CommandName, Key.Down), + //new CommandBindingConfiguration(ConfigCommand.MoveCursorUpPage, Key.PageUp), + //new CommandBindingConfiguration(ConfigCommand.MoveCursorDownPage, Key.PageDown), }; } diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/ProgramsConfiguration.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/ProgramsConfiguration.cs index 28230f7..dd35f3c 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/ProgramsConfiguration.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Configuration/ProgramsConfiguration.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace FileTime.GuiApp.Configuration; public class ProgramsConfiguration diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Services/IKeyboardConfigurationService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Services/IKeyboardConfigurationService.cs index 224a9ac..24cf0b1 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Services/IKeyboardConfigurationService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/Services/IKeyboardConfigurationService.cs @@ -4,7 +4,7 @@ namespace FileTime.GuiApp.Services; public interface IKeyboardConfigurationService { - IReadOnlyList CommandBindings { get; } - IReadOnlyList UniversalCommandBindings { get; } - IReadOnlyList AllShortcut { get; } + IReadOnlyDictionary CommandBindings { get; } + IReadOnlyDictionary UniversalCommandBindings { get; } + IReadOnlyDictionary AllShortcut { get; } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/ViewModels/IGuiAppState.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/ViewModels/IGuiAppState.cs index 0026e2a..e12bb61 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/ViewModels/IGuiAppState.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.Abstractions/ViewModels/IGuiAppState.cs @@ -1,4 +1,3 @@ -using System.Collections.ObjectModel; using FileTime.App.Core.Models; using FileTime.App.Core.ViewModels; using FileTime.GuiApp.Configuration; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp.CustomImpl/ViewModels/GuiAppState.cs b/src/GuiApp/Avalonia/FileTime.GuiApp.CustomImpl/ViewModels/GuiAppState.cs index d6da2d3..f71414e 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp.CustomImpl/ViewModels/GuiAppState.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp.CustomImpl/ViewModels/GuiAppState.cs @@ -1,4 +1,3 @@ -using System.Collections.ObjectModel; using FileTime.App.Core.Models; using FileTime.App.Core.ViewModels; using FileTime.GuiApp.Configuration; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Converters/CommandToCommandNameConverter.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Converters/CommandToCommandNameConverter.cs index e12710f..e52e154 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Converters/CommandToCommandNameConverter.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Converters/CommandToCommandNameConverter.cs @@ -1,6 +1,6 @@ using System.Globalization; using Avalonia.Data.Converters; -using FileTime.App.Core.Command; +using FileTime.App.Core.UserCommand; namespace FileTime.GuiApp.Converters; @@ -8,7 +8,7 @@ public class CommandToCommandNameConverter : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { - if(value is not Command command) return value; + if(value is not IUserCommand command) return value; //TODO: implement return command.ToString(); diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DefaultModeKeyInputHandler.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DefaultModeKeyInputHandler.cs index 20b7feb..28bd785 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DefaultModeKeyInputHandler.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/DefaultModeKeyInputHandler.cs @@ -1,7 +1,7 @@ using System.Reactive.Linq; using Avalonia.Input; -using FileTime.App.Core.Command; using FileTime.App.Core.Services; +using FileTime.App.Core.UserCommand; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; using FileTime.GuiApp.Configuration; @@ -20,18 +20,21 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler private ITabViewModel? _selectedTab; private IContainer? _currentLocation; private readonly ILogger _logger; - private readonly ICommandHandlerService _commandHandlerService; + private readonly IUserCommandHandlerService _userCommandHandlerService; + private readonly IIdentifiableUserCommandService _identifiableUserCommandService; public DefaultModeKeyInputHandler( IGuiAppState appState, IKeyboardConfigurationService keyboardConfigurationService, ILogger logger, - ICommandHandlerService commandHandlerService) + IUserCommandHandlerService userCommandHandlerService, + IIdentifiableUserCommandService identifiableUserCommandService) { _appState = appState; _keyboardConfigurationService = keyboardConfigurationService; _logger = logger; - _commandHandlerService = commandHandlerService; + _userCommandHandlerService = userCommandHandlerService; + _identifiableUserCommandService = identifiableUserCommandService; _appState.SelectedTab.Subscribe(t => _selectedTab = t); _appState.SelectedTab.Select(t => t == null ? Observable.Return(null) : t.CurrentLocation!).Switch().Subscribe(l => _currentLocation = l); @@ -51,8 +54,8 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler var keyWithModifiers = new KeyConfig(key, shift: specialKeysStatus.IsShiftPressed, alt: specialKeysStatus.IsAltPressed, ctrl: specialKeysStatus.IsCtrlPressed); _appState.PreviousKeys.Add(keyWithModifiers); - var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => c.Keys.AreKeysEqual(_appState.PreviousKeys)); - selectedCommandBinding ??= _keyboardConfigurationService.CommandBindings.FirstOrDefault(c => c.Keys.AreKeysEqual(_appState.PreviousKeys)); + var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.Values.FirstOrDefault(c => c.Keys.AreKeysEqual(_appState.PreviousKeys)); + selectedCommandBinding ??= _keyboardConfigurationService.CommandBindings.Values.FirstOrDefault(c => c.Keys.AreKeysEqual(_appState.PreviousKeys)); if (key == Key.Escape) { @@ -104,7 +107,7 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler setHandled(true); _appState.PreviousKeys.Clear(); _appState.PossibleCommands = new(); - await CallCommandAsync(selectedCommandBinding.Command); + await CallCommandAsync(_identifiableUserCommandService.GetCommand(selectedCommandBinding.Command)); } else if (_keysToSkip.Any(k => k.AreKeysEqual(_appState.PreviousKeys))) { @@ -122,7 +125,7 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler else { setHandled(true); - var possibleCommands = _keyboardConfigurationService.AllShortcut.Where(c => c.Keys[0].AreEquals(keyWithModifiers)).ToList(); + var possibleCommands = _keyboardConfigurationService.AllShortcut.Values.Where(c => c.Keys[0].AreEquals(keyWithModifiers)).ToList(); if (possibleCommands.Count == 0) { @@ -136,15 +139,15 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler } } - private async Task CallCommandAsync(Command command) + private async Task CallCommandAsync(IUserCommand command) { try { - await _commandHandlerService.HandleCommandAsync(command); + await _userCommandHandlerService.HandleCommandAsync(command); } catch (Exception e) { - _logger.LogError(e, "Unknown error while running command. {Command} {Error}", command, e); + _logger.LogError(e, "Unknown error while running command. {Command} {Error}", command.GetType().Name, e); } } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyInputHandlerService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyInputHandlerService.cs index 7fdb666..dcb4a8d 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyInputHandlerService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyInputHandlerService.cs @@ -1,6 +1,5 @@ using Avalonia.Input; using FileTime.App.Core.Models.Enums; -using FileTime.GuiApp.Configuration; using FileTime.GuiApp.Models; using FileTime.GuiApp.ViewModels; diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyboardConfigurationService.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyboardConfigurationService.cs index 66b6533..328a5dc 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyboardConfigurationService.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/KeyboardConfigurationService.cs @@ -1,4 +1,4 @@ -using FileTime.App.Core.Command; +using System.Collections.ObjectModel; using FileTime.GuiApp.Configuration; using Microsoft.Extensions.Options; @@ -6,14 +6,14 @@ namespace FileTime.GuiApp.Services; public class KeyboardConfigurationService : IKeyboardConfigurationService { - public IReadOnlyList CommandBindings { get; } - public IReadOnlyList UniversalCommandBindings { get; } - public IReadOnlyList AllShortcut { get; } + public IReadOnlyDictionary CommandBindings { get; } + public IReadOnlyDictionary UniversalCommandBindings { get; } + public IReadOnlyDictionary AllShortcut { get; } public KeyboardConfigurationService(IOptions keyBindingConfiguration) { - var commandBindings = new List(); - var universalCommandBindings = new List(); + var commandBindings = new Dictionary(); + var universalCommandBindings = new Dictionary(); IEnumerable keyBindings = keyBindingConfiguration.Value.KeyBindings; if (keyBindingConfiguration.Value.UseDefaultBindings) @@ -23,7 +23,7 @@ public class KeyboardConfigurationService : IKeyboardConfigurationService foreach (var keyBinding in keyBindings) { - if (keyBinding.Command == Command.None) + if (string.IsNullOrWhiteSpace(keyBinding.Command)) { throw new FormatException($"No command is set in keybinding for keys '{keyBinding.KeysDisplayText}'"); } @@ -34,21 +34,22 @@ public class KeyboardConfigurationService : IKeyboardConfigurationService if (IsUniversal(keyBinding)) { - universalCommandBindings.Add(keyBinding); + universalCommandBindings.Add(keyBinding.Command, keyBinding); } else { - commandBindings.Add(keyBinding); + commandBindings.Add(keyBinding.Command, keyBinding); } } - CommandBindings = commandBindings.AsReadOnly(); - UniversalCommandBindings = universalCommandBindings.AsReadOnly(); - AllShortcut = new List(CommandBindings.Concat(UniversalCommandBindings)).AsReadOnly(); + CommandBindings = new ReadOnlyDictionary(commandBindings); + UniversalCommandBindings = new ReadOnlyDictionary(universalCommandBindings); + AllShortcut = new ReadOnlyDictionary(new Dictionary(CommandBindings.Concat(UniversalCommandBindings))); } private static bool IsUniversal(CommandBindingConfiguration keyMapping) { - return keyMapping.Command is Command.GoUp or Command.Open or Command.OpenOrRun or Command.MoveCursorUp or Command.MoveCursorDown or Command.MoveCursorUpPage or Command.MoveCursorDownPage; + return false; + //return keyMapping.Command is ConfigCommand.GoUp or ConfigCommand.Open or ConfigCommand.OpenOrRun or ConfigCommand.MoveCursorUp or ConfigCommand.MoveCursorDown or ConfigCommand.MoveCursorUpPage or ConfigCommand.MoveCursorDownPage; } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs index b3aa70c..b20aa7a 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs @@ -1,8 +1,7 @@ using Avalonia.Input; -using FileTime.App.Core.Command; using FileTime.App.Core.Models; -using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Services; +using FileTime.App.Core.UserCommand; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; using FileTime.Core.Services; @@ -15,12 +14,14 @@ namespace FileTime.GuiApp.Services; public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler { - private const string RAPIDTRAVELFILTERNAME = "rapid_travel_filter"; + private const string RapidTravelFilterName = "rapid_travel_filter"; + private readonly IAppState _appState; private readonly IModalService _modalService; private readonly IKeyboardConfigurationService _keyboardConfigurationService; - private readonly ICommandHandlerService _commandHandlerService; + private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly ILogger _logger; + private readonly IIdentifiableUserCommandService _identifiableUserCommandService; private readonly BindedCollection _openModals; private ITabViewModel? _selectedTab; @@ -28,15 +29,17 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler IAppState appState, IModalService modalService, IKeyboardConfigurationService keyboardConfigurationService, - ICommandHandlerService commandHandlerService, - ILogger logger) + IUserCommandHandlerService userCommandHandlerService, + ILogger logger, + IIdentifiableUserCommandService identifiableUserCommandService) { _appState = appState; _modalService = modalService; _keyboardConfigurationService = keyboardConfigurationService; - _commandHandlerService = commandHandlerService; + _userCommandHandlerService = userCommandHandlerService; _logger = logger; - + _identifiableUserCommandService = identifiableUserCommandService; + _appState.SelectedTab.Subscribe(t => _selectedTab = t); _openModals = new BindedCollection(modalService.OpenModals); @@ -56,7 +59,7 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler } else { - await CallCommandAsync(Command.ExitRapidTravel); + await CallCommandAsync(ExitRapidTravelCommand.Instance); } } else if (key == Key.Back) @@ -77,11 +80,11 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler else { var currentKeyAsList = new List() {new KeyConfig(key)}; - var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => c.Keys.AreKeysEqual(currentKeyAsList)); + var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.Values.FirstOrDefault(c => c.Keys.AreKeysEqual(currentKeyAsList)); if (selectedCommandBinding != null) { setHandled(true); - await CallCommandAsync(selectedCommandBinding.Command); + await CallCommandAsync(_identifiableUserCommandService.GetCommand(selectedCommandBinding.Command)); } } @@ -89,8 +92,8 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler { if (_selectedTab?.Tab is not ITab tab) return; - tab.RemoveItemFilter(RAPIDTRAVELFILTERNAME); - tab.AddItemFilter(new ItemFilter(RAPIDTRAVELFILTERNAME, i => i.Name.ToLower().Contains(_appState.RapidTravelText))); + tab.RemoveItemFilter(RapidTravelFilterName); + tab.AddItemFilter(new ItemFilter(RapidTravelFilterName, i => i.Name.ToLower().Contains(_appState.RapidTravelText))); /*var currentLocation = await _appState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(MainPageViewModel.RAPIDTRAVEL); var newLocation = new VirtualContainer( currentLocation, @@ -122,15 +125,15 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler } } - private async Task CallCommandAsync(Command command) + private async Task CallCommandAsync(IUserCommand command) { try { - await _commandHandlerService.HandleCommandAsync(command); + await _userCommandHandlerService.HandleCommandAsync(command); } catch (Exception e) { - _logger.LogError(e, "Unknown error while running command. {Command} {Error}", command, e); + _logger.LogError(e, "Unknown error while running command. {Command} {Error}", command.GetType().Name, e); } } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs index 4dfbf55..4fe4a3c 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/ViewModels/MainWindowViewModel.cs @@ -1,7 +1,5 @@ -using System; -using System.Reflection; +using System.Reflection; using Avalonia.Input; -using FileTime.App.Core; using FileTime.App.Core.Services; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; @@ -20,7 +18,7 @@ namespace FileTime.GuiApp.ViewModels; [Inject(typeof(IServiceProvider), PropertyName = "_serviceProvider")] [Inject(typeof(ILogger), PropertyName = "_logger")] [Inject(typeof(IKeyInputHandlerService), PropertyName = "_keyInputHandlerService")] -[Inject(typeof(ICommandHandlerService), PropertyAccessModifier = AccessModifier.Public)] +[Inject(typeof(IUserCommandHandlerService), PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(LifecycleService), PropertyName = "_lifecycleService")] public partial class MainWindowViewModel : IMainWindowViewModelBase { diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs index 85b4efc..475d151 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/MainWindow.axaml.cs @@ -1,9 +1,6 @@ -using System; using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using Avalonia.Markup.Xaml; -using Avalonia.Threading; using FileTime.Core.Models; using FileTime.GuiApp.Models; using FileTime.GuiApp.ViewModels;