diff --git a/src/AppCommon/FileTime.App.Core/Command/Commands.cs b/src/AppCommon/FileTime.App.Core/Command/Commands.cs index f12fff2..fe66263 100644 --- a/src/AppCommon/FileTime.App.Core/Command/Commands.cs +++ b/src/AppCommon/FileTime.App.Core/Command/Commands.cs @@ -2,34 +2,58 @@ namespace FileTime.App.Core.Command { public enum Commands { + None, + + AutoRefresh, + ChangeTimelineMode, CloseTab, Copy, + CopyPath, CreateContainer, CreateElement, Cut, EnterRapidTravel, GoToHome, + GoToPath, GoToProvider, GoToRoot, GoUp, - Delete, + HardDelete, + Mark, MoveCursorDown, MoveCursorDownPage, MoveCursorUp, MoveCursorUpPage, - MoveToBottom, MoveToFirst, MoveToLast, - MoveToTop, + NextTimelineBlock, + NextTimelineCommand, Open, + OpenInFileBrowser, OpenOrRun, PasteMerge, PasteOverwrite, PasteSkip, - Select, - ToggleHidden, - Rename, - Dummy, + PreviousTimelineBlock, + PreviousTimelineCommand, Refresh, + Rename, + RunCommand, + ShowAllShotcut, + SoftDelete, + SwitchToLastTab, + SwitchToTab1, + SwitchToTab2, + SwitchToTab3, + SwitchToTab4, + SwitchToTab5, + SwitchToTab6, + SwitchToTab7, + SwitchToTab8, + TimelinePause, + TimelineRefresh, + TimelineStart, + ToggleAdvancedIcons, + ToggleHidden, } } \ No newline at end of file diff --git a/src/ConsoleApp/FileTime.ConsoleUI.App/Application.cs b/src/ConsoleApp/FileTime.ConsoleUI.App/Application.cs index dcccbd9..e787f76 100644 --- a/src/ConsoleApp/FileTime.ConsoleUI.App/Application.cs +++ b/src/ConsoleApp/FileTime.ConsoleUI.App/Application.cs @@ -90,7 +90,7 @@ namespace FileTime.ConsoleUI.App new CommandBinding("open", Commands.Open, new[] { new ConsoleKeyInfo('→', ConsoleKey.RightArrow, false, false, false) }, Open), new CommandBinding( "go to top", - Commands.MoveToTop, + Commands.MoveToFirst, new[] { new ConsoleKeyInfo('g', ConsoleKey.G, false, false, false), @@ -99,7 +99,7 @@ namespace FileTime.ConsoleUI.App MoveCursorToTop), new CommandBinding( "go to bottom", - Commands.MoveToBottom, + Commands.MoveToLast, new[] { new ConsoleKeyInfo('G', ConsoleKey.G, true, false, false) @@ -114,7 +114,7 @@ namespace FileTime.ConsoleUI.App new ConsoleKeyInfo('h', ConsoleKey.H, false, false, false) }, ToggleHidden), - new CommandBinding("select", Commands.Select, new[] { new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false) }, Select), + new CommandBinding("select", Commands.Mark, new[] { new ConsoleKeyInfo(' ', ConsoleKey.Spacebar, false, false, false) }, Select), new CommandBinding( "copy", Commands.Copy, diff --git a/src/GuiApp/FileTime.Avalonia/App.axaml b/src/GuiApp/FileTime.Avalonia/App.axaml index ae519f3..5043341 100644 --- a/src/GuiApp/FileTime.Avalonia/App.axaml +++ b/src/GuiApp/FileTime.Avalonia/App.axaml @@ -134,6 +134,7 @@ + @@ -186,15 +187,5 @@ - - diff --git a/src/GuiApp/FileTime.Avalonia/Application/AppState.cs b/src/GuiApp/FileTime.Avalonia/Application/AppState.cs index 5ed418d..68accd5 100644 --- a/src/GuiApp/FileTime.Avalonia/Application/AppState.cs +++ b/src/GuiApp/FileTime.Avalonia/Application/AppState.cs @@ -7,6 +7,10 @@ using FileTime.App.Core.Tab; using System.Threading.Tasks; using FileTime.Core.Models; using System.Threading; +using FileTime.Avalonia.Configuration; +using FileTime.Avalonia.Misc; +using FileTime.Core.Extensions; +using FileTime.Avalonia.ViewModels; namespace FileTime.Avalonia.Application { @@ -26,6 +30,28 @@ namespace FileTime.Avalonia.Application [Property] private string _rapidTravelText = ""; + [Property] + private List _possibleCommands = new(); + + [Property] + private List _inputs; + + [Property] + private string _messageBoxText; + + [Property] + private ObservableCollection _popupTexts = new(); + + [Property] + private bool _isAllShortcutVisible; + + [Property] + private bool _noCommandFound; + + public List PreviousKeys { get; } = new(); + + public ObservableCollection TimelineCommands { get; } = new(); + partial void OnInitialize() { _tabs.CollectionChanged += TabsChanged; @@ -66,7 +92,7 @@ namespace FileTime.Avalonia.Application private void SelectedTabChanged() { - foreach(var tab in Tabs) + foreach (var tab in Tabs) { tab.IsSelected = tab == SelectedTab; } @@ -99,5 +125,16 @@ namespace FileTime.Avalonia.Application } } } + + public async Task ExitRapidTravelMode() + { + ViewMode = ViewMode.Default; + + PreviousKeys.Clear(); + PossibleCommands = new(); + RapidTravelText = ""; + + await SelectedTab.OpenContainer(await SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(MainPageViewModel.RAPIDTRAVEL)); + } } } diff --git a/src/GuiApp/FileTime.Avalonia/Command/CommandBinding.cs b/src/GuiApp/FileTime.Avalonia/Command/CommandBinding.cs index 5db1521..ccbcb23 100644 --- a/src/GuiApp/FileTime.Avalonia/Command/CommandBinding.cs +++ b/src/GuiApp/FileTime.Avalonia/Command/CommandBinding.cs @@ -1,4 +1,4 @@ -using FileTime.App.Core.Command; +/* using FileTime.App.Core.Command; using FileTime.Avalonia.Misc; using System; using System.Collections.Generic; @@ -14,11 +14,11 @@ namespace FileTime.Avalonia.Command public string Name { get; } public Commands? Command { get; } - public KeyWithModifiers[] Keys { get; } + public KeyConfig[] Keys { get; } public string KeysDisplayText => GetKeysDisplayText(); - public CommandBinding(string name, Commands? command, KeyWithModifiers[] keys, Func commandHandler) + public CommandBinding(string name, Commands? command, KeyConfig[] keys, Func commandHandler) { _commandHandler = commandHandler; Name = name; @@ -48,7 +48,7 @@ namespace FileTime.Avalonia.Command return s; } - private static string AddKeyWithCtrlOrAlt(KeyWithModifiers key, string currentText, Func keyProcessor) + private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func keyProcessor) { var s = ""; @@ -65,7 +65,7 @@ namespace FileTime.Avalonia.Command return s; } - private static string AddSpecialKey(KeyWithModifiers key, string currentText, bool wasCtrlOrAlt) + private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt) { var s = ""; @@ -77,3 +77,4 @@ namespace FileTime.Avalonia.Command } } } + */ \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Configuration/CommandBindingConfiguration.cs b/src/GuiApp/FileTime.Avalonia/Configuration/CommandBindingConfiguration.cs new file mode 100644 index 0000000..3f92661 --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Configuration/CommandBindingConfiguration.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Avalonia.Input; +using FileTime.App.Core.Command; + +namespace FileTime.Avalonia.Configuration +{ + public class CommandBindingConfiguration + { + public List Keys { get; set; } = new List(); + + public Commands Command { get; set; } = Commands.None; + + public string KeysDisplayText => GetKeysDisplayText(); + + public CommandBindingConfiguration() { } + + public CommandBindingConfiguration(Commands command, IEnumerable keys) + { + Keys = new List(keys); + Command = command; + } + + public CommandBindingConfiguration(Commands command, KeyConfig key) + { + Keys = new List() { key }; + Command = command; + } + + public CommandBindingConfiguration(Commands command, IEnumerable keys) + { + Keys = keys.Select(k => new KeyConfig(k)).ToList(); + Command = command; + } + + public CommandBindingConfiguration(Commands command, Key key) + { + Keys = new List() { new KeyConfig(key) }; + Command = command; + } + + public string GetKeysDisplayText() + { + var s = ""; + + foreach (var k in Keys) + { + 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); + } + } + + return s; + } + + private static string AddKeyWithCtrlOrAlt(KeyConfig key, string currentText, Func keyProcessor) + { + var s = ""; + + bool ctrlOrAlt = key.Ctrl || key.Alt; + + if (ctrlOrAlt && currentText.Length > 0 && currentText.Last() != ' ') s += " "; + + if (key.Ctrl) s += "CTRL+"; + if (key.Alt) s += "ALT+"; + s += keyProcessor(key, currentText, ctrlOrAlt); + + if (ctrlOrAlt) s += " "; + + return s; + } + + private static string AddSpecialKey(KeyConfig key, string currentText, bool wasCtrlOrAlt) + { + var s = ""; + + if (currentText.Length > 0 && currentText.Last() != ' ' && !wasCtrlOrAlt) s += " "; + s += key.Key.ToString(); + if (!wasCtrlOrAlt) s += " "; + + return s; + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Configuration/KeyBindingConfiguration.cs b/src/GuiApp/FileTime.Avalonia/Configuration/KeyBindingConfiguration.cs new file mode 100644 index 0000000..40fd876 --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Configuration/KeyBindingConfiguration.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace FileTime.Avalonia.Configuration +{ + public class KeyBindingConfiguration + { + public bool UseDefaultBindings { get; set; } = true; + public List DefaultKeyBindings { get; set; } = new(); + public List KeyBindings { get; set; } = new(); + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Configuration/KeyConfig.cs b/src/GuiApp/FileTime.Avalonia/Configuration/KeyConfig.cs new file mode 100644 index 0000000..524d8d0 --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Configuration/KeyConfig.cs @@ -0,0 +1,22 @@ +using Avalonia.Input; + +namespace FileTime.Avalonia.Configuration +{ + 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) + { + Key = key; + Shift = shift; + Alt = alt; + Ctrl = ctrl; + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Configuration/MainConfiguration.cs b/src/GuiApp/FileTime.Avalonia/Configuration/MainConfiguration.cs index 2897136..048e379 100644 --- a/src/GuiApp/FileTime.Avalonia/Configuration/MainConfiguration.cs +++ b/src/GuiApp/FileTime.Avalonia/Configuration/MainConfiguration.cs @@ -1,9 +1,101 @@ +using Avalonia.Input; +using FileTime.App.Core.Command; +using System; using System.Collections.Generic; namespace FileTime.Avalonia.Configuration { public static class MainConfiguration { - public static readonly Dictionary Configuration = new(); + private static readonly Lazy> _defaultKeybindings = new(InitDefaultKeyBindings); + internal const string KeybindingBaseConfigKey = "KeyBindings"; + + public static Dictionary Configuration { get; } + + static MainConfiguration() + { + Configuration = new(); + PopulateDefaultKeyBindings(Configuration, _defaultKeybindings.Value, KeybindingBaseConfigKey + ":" + nameof(KeyBindingConfiguration.DefaultKeyBindings)); + } + + private static void PopulateDefaultKeyBindings(Dictionary keybindings, List commandBindingConfigs, string basePath) + { + for (var i = 0; i < commandBindingConfigs.Count; i++) + { + var baseKey = basePath + $":[{i}]:"; + var commandBindingConfig = commandBindingConfigs[i]; + keybindings.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}]:"; + keybindings.Add(keyBaseKey + nameof(KeyConfig.Key), key.Key.ToString()); + keybindings.Add(keyBaseKey + nameof(KeyConfig.Shift), key.Shift.ToString()); + keybindings.Add(keyBaseKey + nameof(KeyConfig.Alt), key.Alt.ToString()); + keybindings.Add(keyBaseKey + nameof(KeyConfig.Ctrl), key.Ctrl.ToString()); + } + } + } + + private static List InitDefaultKeyBindings() + { + return new List() + { + 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.Copy, new[] { Key.Y, Key.Y }), + 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.EnterRapidTravel, new KeyConfig(Key.OemComma, shift: true)), + new CommandBindingConfiguration(Commands.GoToHome, new[] { Key.G, Key.H }), + new CommandBindingConfiguration(Commands.GoToPath, new KeyConfig(Key.OemComma, 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.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.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), + }; + } } } \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Converters/CommandToCommandNameConverter.cs b/src/GuiApp/FileTime.Avalonia/Converters/CommandToCommandNameConverter.cs new file mode 100644 index 0000000..304c86a --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Converters/CommandToCommandNameConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using FileTime.App.Core.Command; + +namespace FileTime.Avalonia.Converters +{ + public class CommandToCommandNameConverter : IValueConverter + { + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if(value is not Commands command) return value; + + //TODO: implement + return command.ToString(); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Misc/KeyWithModifiers.cs b/src/GuiApp/FileTime.Avalonia/Misc/KeyWithModifiers.cs deleted file mode 100644 index 257a8d2..0000000 --- a/src/GuiApp/FileTime.Avalonia/Misc/KeyWithModifiers.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Avalonia.Input; - -namespace FileTime.Avalonia.Misc -{ - public class KeyWithModifiers - { - public Key Key { get; } - - public bool? Alt { get; } - public bool? Shift { get; } - public bool? Ctrl { get; } - - public KeyWithModifiers(Key key, bool alt = false, bool shift = false, bool ctrl = false) - { - Key = key; - Alt = alt; - Shift = shift; - Ctrl = ctrl; - } - } -} diff --git a/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs b/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs new file mode 100644 index 0000000..5006458 --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Services/CommandHandlerService.cs @@ -0,0 +1,783 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Threading; +using FileTime.App.Core.Clipboard; +using FileTime.App.Core.Command; +using FileTime.Avalonia.Application; +using FileTime.Avalonia.IconProviders; +using FileTime.Avalonia.Misc; +using FileTime.Avalonia.ViewModels; +using FileTime.Core.Command; +using FileTime.Core.Components; +using FileTime.Core.Interactions; +using FileTime.Core.Models; +using FileTime.Core.Providers; +using FileTime.Core.Timeline; +using FileTime.Providers.Local; + +namespace FileTime.Avalonia.Services +{ + public class CommandHandlerService + { + private bool _addCommandToNextBatch; + + private readonly AppState _appState; + private readonly LocalContentProvider _localContentProvider; + private readonly ItemNameConverterService _itemNameConverterService; + private readonly DialogService _dialogService; + private readonly IClipboard _clipboard; + private readonly TimeRunner _timeRunner; + private readonly IIconProvider _iconProvider; + private readonly IEnumerable _contentProviders; + private readonly Dictionary> _commandHandlers; + + public CommandHandlerService( + AppState appState, + LocalContentProvider localContentProvider, + ItemNameConverterService itemNameConverterService, + DialogService dialogService, + IClipboard clipboard, + TimeRunner timeRunner, + IIconProvider iconProvider, + IEnumerable contentProviders) + { + _appState = appState; + _localContentProvider = localContentProvider; + _itemNameConverterService = itemNameConverterService; + _dialogService = dialogService; + _clipboard = clipboard; + _timeRunner = timeRunner; + _iconProvider = iconProvider; + _contentProviders = contentProviders; + + _commandHandlers = new Dictionary> + { + {Commands.AutoRefresh, ToggleAutoRefresh}, + {Commands.ChangeTimelineMode, ChangeTimelineMode}, + {Commands.CloseTab, CloseTab}, + {Commands.Copy, Copy}, + {Commands.CopyPath, CopyPath}, + {Commands.CreateContainer, CreateContainer}, + {Commands.CreateElement, CreateElement}, + {Commands.Cut, Cut}, + {Commands.EnterRapidTravel, EnterRapidTravelMode}, + {Commands.GoToHome, GotToHome}, + {Commands.GoToPath, GoToContainer}, + {Commands.GoToProvider, GotToProvider}, + {Commands.GoToRoot, GotToRoot}, + {Commands.GoUp, GoUp}, + {Commands.HardDelete, HardDelete}, + {Commands.Mark, MarkCurrentItem}, + {Commands.MoveCursorDown, MoveCursorDown}, + {Commands.MoveCursorDownPage, MoveCursorDownPage}, + {Commands.MoveCursorUp, MoveCursorUp}, + {Commands.MoveCursorUpPage, MoveCursorUpPage}, + {Commands.MoveToFirst, MoveToFirst}, + {Commands.MoveToLast, MoveToLast}, + {Commands.NextTimelineBlock, SelectNextTimelineBlock}, + {Commands.NextTimelineCommand, SelectNextTimelineCommand}, + {Commands.Open, OpenContainer}, + {Commands.OpenInFileBrowser, OpenInDefaultFileExplorer}, + {Commands.OpenOrRun, OpenOrRun}, + {Commands.PasteMerge, PasteMerge}, + {Commands.PasteOverwrite, PasteOverwrite}, + {Commands.PasteSkip, PasteSkip}, + {Commands.PreviousTimelineBlock, SelectPreviousTimelineBlock}, + {Commands.PreviousTimelineCommand, SelectPreviousTimelineCommand}, + {Commands.Refresh, RefreshCurrentLocation}, + {Commands.Rename, Rename}, + {Commands.RunCommand, RunCommandInContainer}, + {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) => + await _commandHandlers[command].Invoke(); + + private async Task OpenContainer() + { + _appState.RapidTravelText = ""; + await _appState.SelectedTab.Open(); + } + + public async Task OpenContainer(IContainer container) + { + _appState.RapidTravelText = ""; + await _appState.SelectedTab.OpenContainer(container); + } + + private async Task OpenOrRun() + { + if (_appState.SelectedTab.SelectedItem is ContainerViewModel) + { + await OpenContainer(); + } + else if (_appState.SelectedTab.SelectedItem is ElementViewModel elementViewModel && elementViewModel.Element is LocalFile localFile) + { + Process.Start(new ProcessStartInfo(localFile.File.FullName) { UseShellExecute = true }); + + if (_appState.ViewMode == ViewMode.RapidTravel) + { + await _appState.ExitRapidTravelMode(); + } + } + } + + private async Task GoUp() + { + await _appState.SelectedTab.GoUp(); + } + + private async Task MoveCursorUp() + { + await _appState.SelectedTab.MoveCursorUp(); + } + + private async Task MoveCursorDown() + { + await _appState.SelectedTab.MoveCursorDown(); + } + + private async Task MoveCursorUpPage() + { + await _appState.SelectedTab.MoveCursorUpPage(); + } + + private async Task MoveCursorDownPage() + { + await _appState.SelectedTab.MoveCursorDownPage(); + } + + private async Task MoveToFirst() + { + await _appState.SelectedTab.MoveCursorToFirst(); + } + + private async Task MoveToLast() + { + await _appState.SelectedTab.MoveCursorToLast(); + } + + private async Task GotToProvider() + { + await _appState.SelectedTab.GotToProvider(); + } + + private async Task GotToRoot() + { + await _appState.SelectedTab.GotToRoot(); + } + + private async Task GotToHome() + { + await _appState.SelectedTab.GotToHome(); + } + + private Task EnterRapidTravelMode() + { + _appState.ViewMode = ViewMode.RapidTravel; + + _appState.PreviousKeys.Clear(); + _appState.PossibleCommands = new(); + + return Task.CompletedTask; + } + + private async Task SwitchToTab(int number) + { + var tabContainer = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number); + + if (number == -1) + { + var greatestNumber = _appState.Tabs.Max(t => t.TabNumber); + tabContainer = _appState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber); + } + else if (tabContainer == null) + { + var newContainer = await _appState.SelectedTab.CurrentLocation.Container.Clone(); + + var newTab = new Tab(); + await newTab.Init(newContainer); + + tabContainer = new TabContainer(newTab, _localContentProvider, _itemNameConverterService); + await tabContainer.Init(number); + + var i = 0; + for (i = 0; i < _appState.Tabs.Count; i++) + { + if (_appState.Tabs[i].TabNumber > number) break; + } + _appState.Tabs.Insert(i, tabContainer); + } + + if (_appState.ViewMode == ViewMode.RapidTravel) + { + await _appState.ExitRapidTravelMode(); + } + + _appState.SelectedTab = tabContainer; + } + + private async Task CloseTab() + { + var tabs = _appState.Tabs; + if (tabs.Count > 1) + { + var currentTab = tabs.FirstOrDefault(t => t == _appState.SelectedTab); + + if (currentTab != null) + { + tabs.Remove(currentTab); + var tabNumber = tabs[0].TabNumber; + for (var i = 0; i < tabs.Count; i++) + { + tabNumber = tabs[i].TabNumber; + if (tabs[i].TabNumber > currentTab.TabNumber) break; + } + await SwitchToTab(tabNumber); + } + } + } + + private Task CreateContainer() + { + var handler = async (List inputs) => + { + var container = _appState.SelectedTab.CurrentLocation.Container; + var createContainerCommand = new CreateContainerCommand(new AbsolutePath(container), inputs[0].Value); + await AddCommand(createContainerCommand); + }; + + _dialogService.ReadInputs(new List() { new InputElement("Container name", InputType.Text) }, handler); + + return Task.CompletedTask; + } + + private Task CreateElement() + { + var handler = async (List inputs) => + { + var container = _appState.SelectedTab.CurrentLocation.Container; + var createElementCommand = new CreateElementCommand(new AbsolutePath(container), inputs[0].Value); + await AddCommand(createElementCommand); + }; + + _dialogService.ReadInputs(new List() { new InputElement("Element name", InputType.Text) }, handler); + + return Task.CompletedTask; + } + + private async Task MarkCurrentItem() + { + await _appState.SelectedTab.MarkCurrentItem(); + } + + private async Task Copy() + { + _clipboard.Clear(); + _clipboard.SetCommand(); + + var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems(); + if (currentSelectedItems.Count > 0) + { + foreach (var selectedItem in currentSelectedItems) + { + _clipboard.AddContent(selectedItem); + } + await _appState.SelectedTab.TabState.ClearCurrentMarkedItems(); + } + else + { + var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item; + if (currentSelectedItem != null) + { + _clipboard.AddContent(new AbsolutePath(currentSelectedItem)); + } + } + } + + private Task Cut() + { + _clipboard.Clear(); + _clipboard.SetCommand(); + + return Task.CompletedTask; + } + + private async Task SoftDelete() => await Delete(false); + + private async Task HardDelete() => await Delete(true); + + public async Task Delete(bool hardDelete = false) + { + IList? itemsToDelete = null; + var askForDelete = false; + var questionText = ""; + var shouldDelete = false; + var shouldClearMarkedItems = false; + + var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems(); + var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item; + if (currentSelectedItems.Count > 0) + { + itemsToDelete = new List(currentSelectedItems); + shouldClearMarkedItems = true; + + //FIXME: check 'is Container' + if (currentSelectedItems.Count == 1) + { + if ((await currentSelectedItems[0].Resolve()) is IContainer container + && (await container.GetItems())?.Count > 0) + { + askForDelete = true; + questionText = $"The container '{container.Name}' is not empty. Proceed with delete?"; + } + else + { + shouldDelete = true; + } + } + else + { + askForDelete = true; + questionText = $"Are you sure you want to delete {itemsToDelete.Count} item?"; + } + } + else if (currentSelectedItem != null) + { + itemsToDelete = new List() + { + new AbsolutePath(currentSelectedItem) + }; + + if (currentSelectedItem is IContainer container && (await container.GetItems())?.Count > 0) + { + askForDelete = true; + questionText = $"The container '{container.Name}' is not empty. Proceed with delete?"; + } + else + { + shouldDelete = true; + } + } + + if (itemsToDelete?.Count > 0) + { + if (askForDelete) + { + _dialogService.ShowMessageBox(questionText, HandleDelete); + } + else if (shouldDelete) + { + await HandleDelete(); + } + } + + async Task HandleDelete() + { + var deleteCommand = new DeleteCommand + { + HardDelete = hardDelete + }; + + foreach (var itemToDelete in itemsToDelete!) + { + deleteCommand.ItemsToDelete.Add(itemToDelete); + } + + await AddCommand(deleteCommand); + _clipboard.Clear(); + if (shouldClearMarkedItems) + { + await _appState.SelectedTab.TabState.ClearCurrentMarkedItems(); + } + } + } + + private async Task PasteMerge() + { + await Paste(TransportMode.Merge); + } + private async Task PasteOverwrite() + { + await Paste(TransportMode.Overwrite); + } + + private async Task PasteSkip() + { + await Paste(TransportMode.Skip); + } + + private async Task Paste(TransportMode transportMode) + { + if (_clipboard.CommandType != null) + { + var command = (ITransportationCommand)Activator.CreateInstance(_clipboard.CommandType!)!; + command.TransportMode = transportMode; + + command.Sources.Clear(); + + foreach (var item in _clipboard.Content) + { + command.Sources.Add(item); + } + + var currentLocation = _appState.SelectedTab.CurrentLocation.Container; + command.Target = currentLocation is VirtualContainer virtualContainer + ? virtualContainer.BaseContainer + : currentLocation; + + await AddCommand(command); + + _clipboard.Clear(); + } + } + + private Task Rename() + { + var selectedItem = _appState.SelectedTab.SelectedItem?.Item; + if (selectedItem != null) + { + var handler = async (List inputs) => + { + var renameCommand = new RenameCommand(new AbsolutePath(selectedItem), inputs[0].Value); + await AddCommand(renameCommand); + }; + + _dialogService.ReadInputs(new List() { new InputElement("New name", InputType.Text, selectedItem.Name) }, handler); + } + return Task.CompletedTask; + } + + private async Task RefreshCurrentLocation() + { + await _appState.SelectedTab.CurrentLocation.Container.RefreshAsync(); + await _appState.SelectedTab.UpdateCurrentSelectedItem(); + } + + private Task PauseTimeline() + { + _timeRunner.EnableRunning = false; + return Task.CompletedTask; + } + + private async Task ContinueTimeline() + { + _timeRunner.EnableRunning = true; + await _timeRunner.TryStartCommandRunner(); + } + + private async Task RefreshTimeline() + { + await _timeRunner.Refresh(); + } + + private Task ChangeTimelineMode() + { + _addCommandToNextBatch = !_addCommandToNextBatch; + _dialogService.ShowToastMessage("Timeline mode: " + (_addCommandToNextBatch ? "Continuous" : "Parallel")); + + return Task.CompletedTask; + } + + private Task GoToContainer() + { + var handler = async (List inputs) => + { + var path = inputs[0].Value; + foreach (var contentProvider in _contentProviders) + { + if (contentProvider.CanHandlePath(path)) + { + var possibleContainer = await contentProvider.GetByPath(path); + if (possibleContainer is IContainer container) + { + await _appState.SelectedTab.OpenContainer(container); + } + //TODO: multiple possible content provider handler + return; + } + } + }; + + _dialogService.ReadInputs(new List() { new InputElement("Path", InputType.Text) }, handler); + + return Task.CompletedTask; + } + + private Task ToggleAdvancedIcons() + { + _iconProvider.EnableAdvancedIcons = !_iconProvider.EnableAdvancedIcons; + _dialogService.ShowToastMessage("Advanced icons are: " + (_iconProvider.EnableAdvancedIcons ? "ON" : "OFF")); + + return Task.CompletedTask; + } + + private Task ToggleHidden() + { + throw new NotImplementedException(); + } + + private Task OpenInDefaultFileExplorer() + { + if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) + { + var path = localFolder.Directory.FullName; + if (path != null) + { + Process.Start("explorer.exe", "\"" + path + "\""); + } + } + + return Task.CompletedTask; + } + + private async Task CopyPath() + { + string? textToCopy = null; + if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) + { + textToCopy = localFolder.Directory.FullName; + } + if (_appState.SelectedTab.CurrentLocation.Container is LocalFile localFile) + { + textToCopy = localFile.File.FullName; + } + else if (_appState.SelectedTab.CurrentLocation.Container.FullName is string fullName) + { + textToCopy = fullName; + } + + if (textToCopy != null && global::Avalonia.Application.Current?.Clipboard is not null) + { + await global::Avalonia.Application.Current.Clipboard.SetTextAsync(textToCopy); + } + } + + private Task ShowAllShortcut() + { + _appState.IsAllShortcutVisible = true; + return Task.CompletedTask; + } + + private Task RunCommandInContainer() + { + var handler = (List inputs) => + { + var input = inputs[0].Value; + string? path = null; + string? arguments = null; + + if (input.StartsWith("\"")) + { + var pathEnd = input.IndexOf('\"', 1); + + path = input.Substring(1, pathEnd); + arguments = input.Substring(pathEnd + 1).Trim(); + } + else + { + var inputParts = input.Split(' '); + path = inputParts[0]; + arguments = inputParts.Length > 1 ? string.Join(' ', inputParts[1..]).Trim() : null; + } + + if (!string.IsNullOrWhiteSpace(path)) + { + using var process = new Process(); + process.StartInfo.FileName = path; + + if (!string.IsNullOrWhiteSpace(arguments)) + { + process.StartInfo.Arguments = arguments; + } + if (_appState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) + { + process.StartInfo.WorkingDirectory = localFolder.Directory.FullName; + } + process.Start(); + } + + return Task.CompletedTask; + }; + + _dialogService.ReadInputs(new List() { new InputElement("Command", InputType.Text) }, handler); + + return Task.CompletedTask; + } + + private Task SelectPreviousTimelineBlock() + { + var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); + if (currentSelected == null) return Task.CompletedTask; + + ParallelCommandsViewModel? newBlockVM = null; + ParallelCommandsViewModel? previousBlockVM = null; + + foreach (var timelineBlock in _appState.TimelineCommands) + { + + foreach (var command in timelineBlock.ParallelCommands) + { + if (command.IsSelected) + { + newBlockVM = previousBlockVM; + break; + } + } + + previousBlockVM = timelineBlock; + } + + if (newBlockVM == null) return Task.CompletedTask; + + foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t)) + { + val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0; + } + + return Task.CompletedTask; + } + + private Task SelectNextTimelineCommand() + { + var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); + if (currentSelected == null) return Task.CompletedTask; + + ParallelCommandViewModel? lastCommand = null; + var any = false; + foreach (var command in _appState.TimelineCommands.SelectMany(t => t.ParallelCommands)) + { + var isSelected = lastCommand == currentSelected; + command.IsSelected = isSelected; + any = any || isSelected; + lastCommand = command; + } + if (!any && lastCommand != null) lastCommand.IsSelected = true; + return Task.CompletedTask; + } + + private Task SelectPreviousTimelineCommand() + { + var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); + if (currentSelected == null) return Task.CompletedTask; + + ParallelCommandViewModel? lastCommand = null; + foreach (var command in _appState.TimelineCommands.SelectMany(t => t.ParallelCommands)) + { + if (lastCommand != null) + { + lastCommand.IsSelected = command == currentSelected; + } + lastCommand = command; + } + if (lastCommand != null) lastCommand.IsSelected = false; + return Task.CompletedTask; + } + + private Task SelectNextTimelineBlock() + { + var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); + if (currentSelected == null) return Task.CompletedTask; + + ParallelCommandsViewModel? newBlockVM = null; + var select = false; + foreach (var timelineBlock in _appState.TimelineCommands) + { + if (select) + { + newBlockVM = timelineBlock; + break; + } + foreach (var command in timelineBlock.ParallelCommands) + { + if (command.IsSelected) + { + select = true; + break; + } + } + } + + if (newBlockVM == null) return Task.CompletedTask; + + foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t)) + { + val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0; + } + + return Task.CompletedTask; + } + + private ParallelCommandViewModel? GetSelectedTimelineCommandOrSelectFirst() + { + var currentSelected = _appState.TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault(c => c.IsSelected); + if (currentSelected != null) return currentSelected; + + var firstCommand = _appState.TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault(); + if (firstCommand != null) + { + firstCommand.IsSelected = true; + } + + return null; + } + + private async Task AddCommand(ICommand command) + { + if (_addCommandToNextBatch) + { + await _timeRunner.AddCommand(command, toNewBatch: true); + } + else + { + ParallelCommandsViewModel? batchToAdd = null; + foreach (var val in _appState.TimelineCommands.Select(t => t.ParallelCommands.Select(c => (ParalellCommandVM: t, CommandVM: c))).SelectMany(t => t)) + { + if (val.CommandVM.IsSelected) + { + batchToAdd = val.ParalellCommandVM; + break; + } + } + + if (batchToAdd != null) + { + await _timeRunner.AddCommand(command, batchToAdd.Id); + } + else + { + await _timeRunner.AddCommand(command); + } + } + } + + private Task ToggleAutoRefresh() + { + var tab = _appState.SelectedTab.TabState.Tab; + tab.AutoRefresh = !tab.AutoRefresh; + + _dialogService.ShowToastMessage("Auto refresh is: " + (tab.AutoRefresh ? "ON" : "OFF")); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Services/DialogService.cs b/src/GuiApp/FileTime.Avalonia/Services/DialogService.cs new file mode 100644 index 0000000..303d2fd --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Services/DialogService.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Threading; +using FileTime.Avalonia.Application; +using FileTime.Avalonia.Misc; +using FileTime.Core.Interactions; + +namespace FileTime.Avalonia.Services +{ + public class DialogService + { + private readonly AppState _appState; + + private Func, Task>? _inputHandler; + + public DialogService(AppState appState) + { + _appState = appState; + } + public void ReadInputs(List inputs, Action> inputHandler) => + ReadInputs(inputs, (inputs) => { inputHandler(inputs); return Task.CompletedTask; }); + + public void ReadInputs(List inputs, Func, Task> inputHandler) + { + _appState.Inputs = inputs.ConvertAll(i => new InputElementWrapper(i, i.DefaultValue)); + _inputHandler = inputHandler; + } + + public async Task ReadInputs(IEnumerable fields) + { + var waiting = true; + var result = Array.Empty(); + ReadInputs(fields.ToList(), (inputs) => + { + if (inputs != null) + { + result = inputs.Select(i => i.Value).ToArray(); + } + waiting = false; + }); + + while (waiting) await Task.Delay(100); + + return result; + } + + public void ClearInputs() + { + _appState.Inputs = null; + _inputHandler = null; + } + + public async Task ProcessInputs() + { + try + { + if (_inputHandler != null) + { + await _inputHandler.Invoke(_appState.Inputs); + } + } + catch { } + + ClearInputs(); + } + + public void CancelInputs() + { + ClearInputs(); + } + + public void ShowMessageBox(string text, Func inputHandler) + { + _appState.MessageBoxText = text; + _inputHandler = async (_) => await inputHandler(); + } + + public void ProcessMessageBox() + { + _inputHandler?.Invoke(null!); + + _appState.MessageBoxText = null; + _inputHandler = null; + } + + public void CancelMessageBox() + { + _appState.MessageBoxText = null; + _inputHandler = null; + } + + public void ShowToastMessage(string text) + { + _appState.PopupTexts.Add(text); + + Task.Run(async () => + { + await Task.Delay(5000); + await Dispatcher.UIThread.InvokeAsync(() => _appState.PopupTexts.Remove(text)); + }); + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Services/KeyInputHandlerService.cs b/src/GuiApp/FileTime.Avalonia/Services/KeyInputHandlerService.cs new file mode 100644 index 0000000..4f7662a --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Services/KeyInputHandlerService.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Input; +using FileTime.Avalonia.Application; +using FileTime.Avalonia.Configuration; +using FileTime.Avalonia.ViewModels; +using FileTime.Core.Extensions; +using FileTime.Core.Models; + +namespace FileTime.Avalonia.Services +{ + public class KeyInputHandlerService + { + private readonly List _keysToSkip = new(); + private readonly AppState _appState; + private readonly KeyboardConfigurationService _keyboardConfigurationService; + private readonly CommandHandlerService _commandHandlerService; + private readonly DialogService _dialogService; + + public KeyInputHandlerService( + AppState appState, + KeyboardConfigurationService keyboardConfigurationService, + CommandHandlerService commandHandlerService, + DialogService dialogService) + { + _appState = appState; + _keyboardConfigurationService = keyboardConfigurationService; + _commandHandlerService = commandHandlerService; + _dialogService = dialogService; + + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Up) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Down) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.Tab) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageDown) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.PageUp) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.F4, alt: true) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.LWin) }); + _keysToSkip.Add(new KeyConfig[] { new KeyConfig(Key.RWin) }); + } + public async Task ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action setHandled) + { + if (key == Key.LeftAlt + || key == Key.RightAlt + || key == Key.LeftShift + || key == Key.RightShift + || key == Key.LeftCtrl + || key == Key.RightCtrl) return; + + _appState.NoCommandFound = false; + + var isAltPressed = (keyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt; + var isShiftPressed = (keyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift; + var isCtrlPressed = (keyModifiers & KeyModifiers.Control) == KeyModifiers.Control; + + if (_appState.ViewMode == ViewMode.Default) + { + var keyWithModifiers = new KeyConfig(key, shift: isShiftPressed, alt: isAltPressed, ctrl: isCtrlPressed); + _appState.PreviousKeys.Add(keyWithModifiers); + + var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, _appState.PreviousKeys)); + selectedCommandBinding ??= _keyboardConfigurationService.CommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, _appState.PreviousKeys)); + + if (key == Key.Escape) + { + _appState.IsAllShortcutVisible = false; + _appState.MessageBoxText = null; + _appState.PreviousKeys.Clear(); + _appState.PossibleCommands = new(); + setHandled(true); + } + else if (key == Key.Enter + && _appState.MessageBoxText != null) + { + _appState.PreviousKeys.Clear(); + _dialogService.ProcessMessageBox(); + setHandled(true); + } + else if (selectedCommandBinding != null) + { + setHandled(true); + await _commandHandlerService.HandleCommandAsync(selectedCommandBinding.Command); + _appState.PreviousKeys.Clear(); + _appState.PossibleCommands = new(); + } + else if (_keysToSkip.Any(k => AreKeysEqual(k, _appState.PreviousKeys))) + { + _appState.PreviousKeys.Clear(); + _appState.PossibleCommands = new(); + return; + } + else if (_appState.PreviousKeys.Count == 2) + { + setHandled(true); + _appState.NoCommandFound = true; + _appState.PreviousKeys.Clear(); + _appState.PossibleCommands = new(); + } + else + { + var possibleCommands = _keyboardConfigurationService.AllShortcut.Where(c => AreKeysEqual(c.Keys[0], keyWithModifiers)).ToList(); + + if (possibleCommands.Count == 0) + { + _appState.NoCommandFound = true; + _appState.PreviousKeys.Clear(); + } + else + { + _appState.PossibleCommands = possibleCommands; + } + setHandled(true); + } + } + else + { + var keyString = key.ToString(); + var updateRapidTravelFilter = false; + + if (key == Key.Escape) + { + setHandled(true); + if (_appState.IsAllShortcutVisible) + { + _appState.IsAllShortcutVisible = false; + } + else if (_appState.MessageBoxText != null) + { + _appState.MessageBoxText = null; + } + else + { + await _appState.ExitRapidTravelMode(); + } + } + else if (key == Key.Back) + { + if (_appState.RapidTravelText.Length > 0) + { + setHandled(true); + _appState.RapidTravelText = _appState.RapidTravelText.Substring(0, _appState.RapidTravelText.Length - 1); + updateRapidTravelFilter = true; + } + } + else if (keyString.Length == 1) + { + setHandled(true); + _appState.RapidTravelText += keyString.ToLower(); + updateRapidTravelFilter = true; + } + else + { + var currentKeyAsList = new List() { new KeyConfig(key) }; + var selectedCommandBinding = _keyboardConfigurationService.UniversalCommandBindings.FirstOrDefault(c => AreKeysEqual(c.Keys, currentKeyAsList)); + if (selectedCommandBinding != null) + { + setHandled(true); + await _commandHandlerService.HandleCommandAsync(selectedCommandBinding.Command); + } + } + + if (updateRapidTravelFilter) + { + var currentLocation = await _appState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(MainPageViewModel.RAPIDTRAVEL); + var newLocation = new VirtualContainer( + currentLocation, + new List, IEnumerable>>() + { + container => container.Where(c => c.Name.ToLower().Contains(_appState.RapidTravelText)) + }, + new List, IEnumerable>>() + { + element => element.Where(e => e.Name.ToLower().Contains(_appState.RapidTravelText)) + }, + virtualContainerName: MainPageViewModel.RAPIDTRAVEL + ); + + await newLocation.Init(); + + await _appState.SelectedTab.OpenContainer(newLocation); + + var selectedItemName = _appState.SelectedTab.SelectedItem?.Item.Name; + var currentLocationItems = await _appState.SelectedTab.CurrentLocation.GetItems(); + if (currentLocationItems.FirstOrDefault(i => string.Equals(i.Item.Name, _appState.RapidTravelText, StringComparison.OrdinalIgnoreCase)) is IItemViewModel matchItem) + { + await _appState.SelectedTab.SetCurrentSelectedItem(matchItem.Item); + } + else if (!currentLocationItems.Select(i => i.Item.Name).Any(n => n == selectedItemName)) + { + await _appState.SelectedTab.MoveCursorToFirst(); + } + } + } + } + + private static bool AreKeysEqual(IReadOnlyList collection1, IReadOnlyList collection2) + { + if (collection1.Count != collection2.Count) return false; + + for (var i = 0; i < collection1.Count; i++) + { + if (!AreKeysEqual(collection1[i], collection2[i])) return false; + } + + return true; + } + + private static bool AreKeysEqual(KeyConfig key1, KeyConfig key2) => + key1.Key == key2.Key + && key1.Alt == key2.Alt + && key1.Shift == key2.Shift + && key1.Ctrl == key2.Ctrl; + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Services/KeyboardConfigurationService.cs b/src/GuiApp/FileTime.Avalonia/Services/KeyboardConfigurationService.cs new file mode 100644 index 0000000..4428a0a --- /dev/null +++ b/src/GuiApp/FileTime.Avalonia/Services/KeyboardConfigurationService.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using FileTime.Avalonia.Configuration; +using FileTime.App.Core.Command; +using Microsoft.Extensions.Options; + +namespace FileTime.Avalonia.Services +{ + public class KeyboardConfigurationService + { + public IReadOnlyList CommandBindings { get; } + public IReadOnlyList UniversalCommandBindings { get; } + public IReadOnlyList AllShortcut { get; } + + public KeyboardConfigurationService(IOptions keyBindingConfiguration) + { + List commandBindings = new(); + List universalCommandBindings = new(); + IEnumerable keyBindings = keyBindingConfiguration.Value.KeyBindings; + + if (keyBindingConfiguration.Value.UseDefaultBindings) + { + keyBindings = keyBindings.Concat(keyBindingConfiguration.Value.DefaultKeyBindings); + } + + foreach (var keyBinding in keyBindings) + { + if (keyBinding.Command == Commands.None) + { + throw new FormatException($"No command is set in keybinding for keys '{keyBinding.KeysDisplayText}'"); + } + else if (keyBinding.Keys.Count == 0) + { + throw new FormatException($"No keys set in keybinding for command '{keyBinding.Command}'."); + } + + if (IsUniversal(keyBinding)) + { + universalCommandBindings.Add(keyBinding); + } + else + { + commandBindings.Add(keyBinding); + } + } + + CommandBindings = commandBindings.AsReadOnly(); + UniversalCommandBindings = universalCommandBindings.AsReadOnly(); + AllShortcut = new List(CommandBindings.Concat(UniversalCommandBindings)).AsReadOnly(); + } + + private static bool IsUniversal(CommandBindingConfiguration keyMapping) + { + return keyMapping.Command == Commands.GoUp + || keyMapping.Command == Commands.Open + || keyMapping.Command == Commands.OpenOrRun + || keyMapping.Command == Commands.MoveCursorUp + || keyMapping.Command == Commands.MoveCursorUpPage + || keyMapping.Command == Commands.MoveCursorDownPage; + } + } +} \ No newline at end of file diff --git a/src/GuiApp/FileTime.Avalonia/Startup.cs b/src/GuiApp/FileTime.Avalonia/Startup.cs index e5457af..3ff725a 100644 --- a/src/GuiApp/FileTime.Avalonia/Startup.cs +++ b/src/GuiApp/FileTime.Avalonia/Startup.cs @@ -27,6 +27,10 @@ namespace FileTime.Avalonia serviceCollection = serviceCollection .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -66,19 +70,20 @@ namespace FileTime.Avalonia .Build(); return serviceCollection - //.Configure(configuration.GetSection("server")) + .Configure(configuration.GetSection(MainConfiguration.KeybindingBaseConfigKey)) .AddSingleton(configuration); } internal static IServiceCollection InitSerilog(this IServiceCollection serviceCollection) { + using var serviceProvider = serviceCollection.BuildServiceProvider(); Log.Logger = new LoggerConfiguration() - .ReadFrom.Configuration(serviceCollection.BuildServiceProvider().GetService()) + .ReadFrom.Configuration(serviceProvider.GetService()) .Enrich.FromLogContext() .WriteTo.File( - Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), - fileSizeLimitBytes: 10*1024*1024, - rollOnFileSizeLimit: true, + Path.Combine(Program.AppDataRoot, "logs", "appLog.log"), + fileSizeLimitBytes: 10 * 1024 * 1024, + rollOnFileSizeLimit: true, rollingInterval: RollingInterval.Day) .CreateLogger(); diff --git a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs index a553135..78525ea 100644 --- a/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs +++ b/src/GuiApp/FileTime.Avalonia/ViewModels/MainPageViewModel.cs @@ -1,34 +1,26 @@ using FileTime.Core.Components; -using FileTime.Core.Extensions; using FileTime.Core.Interactions; using FileTime.Core.Models; using FileTime.Providers.Local; using FileTime.Avalonia.Application; -using FileTime.Avalonia.Command; using FileTime.Avalonia.Misc; using FileTime.Avalonia.Models; using FileTime.Avalonia.Services; using MvvmGen; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; -using Avalonia.Input; -using FileTime.App.Core.Clipboard; using Microsoft.Extensions.DependencyInjection; -using FileTime.Core.Command; using FileTime.Core.Timeline; using FileTime.Core.Providers; using Syroot.Windows.IO; using FileTime.Avalonia.IconProviders; -using Avalonia.Threading; using Microsoft.Extensions.Logging; -using AsyncEvent; using System.Threading; +using Avalonia.Input; namespace FileTime.Avalonia.ViewModels { @@ -38,83 +30,38 @@ namespace FileTime.Avalonia.ViewModels [Inject(typeof(StatePersistenceService), PropertyName = "StatePersistence", PropertyAccessModifier = AccessModifier.Public)] [Inject(typeof(ItemNameConverterService))] [Inject(typeof(ILogger), PropertyName = "_logger")] + [Inject(typeof(KeyboardConfigurationService))] + [Inject(typeof(CommandHandlerService), PropertyAccessModifier = AccessModifier.Public)] + [Inject(typeof(DialogService))] + [Inject(typeof(KeyInputHandlerService))] public partial class MainPageViewModel : IMainPageViewModelBase { - const string RAPIDTRAVEL = "rapidTravel"; + public const string RAPIDTRAVEL = "rapidTravel"; - private readonly List _previousKeys = new(); - private readonly List _keysToSkip = new(); - private readonly List _commandBindings = new(); - private readonly List _universalCommandBindings = new(); - - private IClipboard _clipboard; private TimeRunner _timeRunner; - private IEnumerable _contentProviders; - private IIconProvider _iconProvider; - private bool _addCommandToNextBatch; - - private Func? _inputHandler; [Property] private string _text; - [Property] - private bool _noCommandFound; - - [Property] - private List _possibleCommands = new(); - - [Property] - private List _inputs; - [Property] private List _rootDriveInfos; [Property] private List _places; - [Property] - private string _messageBoxText; - - [Property] - private ObservableCollection _popupTexts = new(); - - [Property] - private bool _isAllShortcutVisible; - - [Property] - private List _allShortcut; - [Property] private bool _loading = true; - public ObservableCollection TimelineCommands { get; } = new(); - async partial void OnInitialize() { _logger?.LogInformation($"Starting {nameof(MainPageViewModel)} initialization..."); - _clipboard = App.ServiceProvider.GetService()!; _timeRunner = App.ServiceProvider.GetService()!; - _contentProviders = App.ServiceProvider.GetService>()!; - _iconProvider = App.ServiceProvider.GetService()!; var inputInterface = (BasicInputHandler)App.ServiceProvider.GetService()!; - inputInterface.InputHandler = ReadInputs; + inputInterface.InputHandler = DialogService.ReadInputs; App.ServiceProvider.GetService(); await StatePersistence.LoadStatesAsync(); _timeRunner.CommandsChangedAsync.Add(UpdateParallelCommands); - InitCommandBindings(); - - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Up) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Down) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Tab) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageDown) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageUp) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.F4, alt: true) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.LWin) }); - _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.RWin) }); - - AllShortcut = _commandBindings.Concat(_universalCommandBindings).ToList(); if (AppState.Tabs.Count == 0) { @@ -220,13 +167,13 @@ namespace FileTime.Avalonia.ViewModels { foreach (var parallelCommand in parallelCommands) { - if (!TimelineCommands.Any(c => c.Id == parallelCommand.Id)) + if (!AppState.TimelineCommands.Any(c => c.Id == parallelCommand.Id)) { - TimelineCommands.Add(new ParallelCommandsViewModel(parallelCommand)); + AppState.TimelineCommands.Add(new ParallelCommandsViewModel(parallelCommand)); } } var itemsToRemove = new List(); - foreach (var parallelCommandVm in TimelineCommands) + foreach (var parallelCommandVm in AppState.TimelineCommands) { if (!parallelCommands.Any(c => c.Id == parallelCommandVm.Id)) { @@ -237,12 +184,12 @@ namespace FileTime.Avalonia.ViewModels for (var i = 0; i < itemsToRemove.Count; i++) { itemsToRemove[i].Destroy(); - TimelineCommands.Remove(itemsToRemove[i]); + AppState.TimelineCommands.Remove(itemsToRemove[i]); } foreach (var parallelCommand in parallelCommands) { - var parallelCommandsVM = TimelineCommands.First(t => t.Id == parallelCommand.Id); + var parallelCommandsVM = AppState.TimelineCommands.First(t => t.Id == parallelCommand.Id); foreach (var command in parallelCommand.Commands) { if (!parallelCommandsVM.ParallelCommands.Any(c => c.CommandTimeState.Command == command.Command)) @@ -280,1207 +227,33 @@ namespace FileTime.Avalonia.ViewModels return await LocalContentProvider.GetByPath(drive.Name) as IContainer; } - private async Task OpenContainer() - { - AppState.RapidTravelText = ""; - await AppState.SelectedTab.Open(); - } - - public async Task OpenContainer(IContainer container) - { - AppState.RapidTravelText = ""; - await AppState.SelectedTab.OpenContainer(container); - } - - private async Task OpenOrRun() - { - if (AppState.SelectedTab.SelectedItem is ContainerViewModel) - { - await OpenContainer(); - } - else if (AppState.SelectedTab.SelectedItem is ElementViewModel elementViewModel && elementViewModel.Element is LocalFile localFile) - { - Process.Start(new ProcessStartInfo(localFile.File.FullName) { UseShellExecute = true }); - - if (AppState.ViewMode == ViewMode.RapidTravel) - { - await ExitRapidTravelMode(); - } - } - } - - private async Task GoUp() - { - await AppState.SelectedTab.GoUp(); - } - - private async Task MoveCursorUp() - { - await AppState.SelectedTab.MoveCursorUp(); - } - - private async Task MoveCursorDown() - { - await AppState.SelectedTab.MoveCursorDown(); - } - - private async Task MoveCursorUpPage() - { - await AppState.SelectedTab.MoveCursorUpPage(); - } - - private async Task MoveCursorDownPage() - { - await AppState.SelectedTab.MoveCursorDownPage(); - } - - private async Task MoveToFirst() - { - await AppState.SelectedTab.MoveCursorToFirst(); - } - - private async Task MoveToLast() - { - await AppState.SelectedTab.MoveCursorToLast(); - } - - private async Task GotToProvider() - { - await AppState.SelectedTab.GotToProvider(); - } - - private async Task GotToRoot() - { - await AppState.SelectedTab.GotToRoot(); - } - - private async Task GotToHome() - { - await AppState.SelectedTab.GotToHome(); - } - - private Task EnterRapidTravelMode() - { - AppState.ViewMode = ViewMode.RapidTravel; - - _previousKeys.Clear(); - PossibleCommands = new(); - - return Task.CompletedTask; - } - - private async Task ExitRapidTravelMode() - { - AppState.ViewMode = ViewMode.Default; - - _previousKeys.Clear(); - PossibleCommands = new(); - AppState.RapidTravelText = ""; - - await AppState.SelectedTab.OpenContainer(await AppState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(RAPIDTRAVEL)); - } - - private async Task SwitchToTab(int number) - { - var tabContainer = AppState.Tabs.FirstOrDefault(t => t.TabNumber == number); - - if (number == -1) - { - var greatestNumber = AppState.Tabs.Select(t => t.TabNumber).Max(); - tabContainer = AppState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber); - } - else if (tabContainer == null) - { - var newContainer = await AppState.SelectedTab.CurrentLocation.Container.Clone(); - - var newTab = new Tab(); - await newTab.Init(newContainer); - - tabContainer = new TabContainer(newTab, LocalContentProvider, ItemNameConverterService); - await tabContainer.Init(number); - - var i = 0; - for (i = 0; i < AppState.Tabs.Count; i++) - { - if (AppState.Tabs[i].TabNumber > number) break; - } - AppState.Tabs.Insert(i, tabContainer); - } - - if (AppState.ViewMode == ViewMode.RapidTravel) - { - await ExitRapidTravelMode(); - } - - AppState.SelectedTab = tabContainer; - } - - private async Task CloseTab() - { - var tabs = AppState.Tabs; - if (tabs.Count > 1) - { - var currentTab = tabs.FirstOrDefault(t => t == AppState.SelectedTab); - - if (currentTab != null) - { - tabs.Remove(currentTab); - var tabNumber = tabs[0].TabNumber; - for (var i = 0; i < tabs.Count; i++) - { - tabNumber = tabs[i].TabNumber; - if (tabs[i].TabNumber > currentTab.TabNumber) break; - } - await SwitchToTab(tabNumber); - } - } - } - - private Task CreateContainer() - { - var handler = async () => - { - if (Inputs != null) - { - var container = AppState.SelectedTab.CurrentLocation.Container; - var createContainerCommand = new CreateContainerCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value); - await AddCommand(createContainerCommand); - Inputs = null; - } - }; - - ReadInputs(new List() { new Core.Interactions.InputElement("Container name", InputType.Text) }, handler); - - return Task.CompletedTask; - } - - private Task CreateElement() - { - var handler = async () => - { - if (Inputs != null) - { - var container = AppState.SelectedTab.CurrentLocation.Container; - var createElementCommand = new CreateElementCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value); - await AddCommand(createElementCommand); - Inputs = null; - } - }; - - ReadInputs(new List() { new Core.Interactions.InputElement("Element name", InputType.Text) }, handler); - - return Task.CompletedTask; - } - - private async Task MarkCurrentItem() - { - await AppState.SelectedTab.MarkCurrentItem(); - } - - private async Task Copy() - { - _clipboard.Clear(); - _clipboard.SetCommand(); - - var currentSelectedItems = await AppState.SelectedTab.TabState.GetCurrentMarkedItems(); - if (currentSelectedItems.Count > 0) - { - foreach (var selectedItem in currentSelectedItems) - { - _clipboard.AddContent(selectedItem); - } - await AppState.SelectedTab.TabState.ClearCurrentMarkedItems(); - } - else - { - var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item; - if (currentSelectedItem != null) - { - _clipboard.AddContent(new AbsolutePath(currentSelectedItem)); - } - } - } - - private Task Cut() - { - _clipboard.Clear(); - _clipboard.SetCommand(); - - return Task.CompletedTask; - } - - private async Task SoftDelete() => await Delete(false); - - private async Task HardDelete() => await Delete(true); - - public async Task Delete(bool hardDelete = false) - { - IList? itemsToDelete = null; - var askForDelete = false; - var questionText = ""; - var shouldDelete = false; - var shouldClearMarkedItems = false; - - var currentSelectedItems = await AppState.SelectedTab.TabState.GetCurrentMarkedItems(); - var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item; - if (currentSelectedItems.Count > 0) - { - itemsToDelete = new List(currentSelectedItems); - shouldClearMarkedItems = true; - - //FIXME: check 'is Container' - if (currentSelectedItems.Count == 1) - { - if ((await currentSelectedItems[0].Resolve()) is IContainer container - && (await container.GetItems())?.Count > 0) - { - askForDelete = true; - questionText = $"The container '{container.Name}' is not empty. Proceed with delete?"; - } - else - { - shouldDelete = true; - } - } - else - { - askForDelete = true; - questionText = $"Are you sure you want to delete {itemsToDelete.Count} item?"; - } - } - else if (currentSelectedItem != null) - { - itemsToDelete = new List() - { - new AbsolutePath(currentSelectedItem) - }; - - if (currentSelectedItem is IContainer container && (await container.GetItems())?.Count > 0) - { - askForDelete = true; - questionText = $"The container '{container.Name}' is not empty. Proceed with delete?"; - } - else - { - shouldDelete = true; - } - } - - if (itemsToDelete?.Count > 0) - { - if (askForDelete) - { - ShowMessageBox(questionText, HandleDelete); - } - else if (shouldDelete) - { - await HandleDelete(); - } - } - - async Task HandleDelete() - { - var deleteCommand = new DeleteCommand - { - HardDelete = hardDelete - }; - - foreach (var itemToDelete in itemsToDelete!) - { - deleteCommand.ItemsToDelete.Add(itemToDelete); - } - - await AddCommand(deleteCommand); - _clipboard.Clear(); - if (shouldClearMarkedItems) - { - await AppState.SelectedTab.TabState.ClearCurrentMarkedItems(); - } - } - } - - private async Task PasteMerge() - { - await Paste(TransportMode.Merge); - } - private async Task PasteOverwrite() - { - await Paste(TransportMode.Overwrite); - } - - private async Task PasteSkip() - { - await Paste(TransportMode.Skip); - } - - private async Task Paste(TransportMode transportMode) - { - if (_clipboard.CommandType != null) - { - var command = (ITransportationCommand)Activator.CreateInstance(_clipboard.CommandType!)!; - command.TransportMode = transportMode; - - command.Sources.Clear(); - - foreach (var item in _clipboard.Content) - { - command.Sources.Add(item); - } - - var currentLocation = AppState.SelectedTab.CurrentLocation.Container; - command.Target = currentLocation is VirtualContainer virtualContainer - ? virtualContainer.BaseContainer - : currentLocation; - - await AddCommand(command); - - _clipboard.Clear(); - } - } - - private Task Rename() - { - var selectedItem = AppState.SelectedTab.SelectedItem?.Item; - if (selectedItem != null) - { - var handler = async () => - { - if (Inputs != null) - { - var renameCommand = new RenameCommand(new Core.Models.AbsolutePath(selectedItem), Inputs[0].Value); - await AddCommand(renameCommand); - } - }; - - ReadInputs(new List() { new Core.Interactions.InputElement("New name", InputType.Text, selectedItem.Name) }, handler); - } - return Task.CompletedTask; - } - - private async Task RefreshCurrentLocation() - { - await AppState.SelectedTab.CurrentLocation.Container.RefreshAsync(); - await AppState.SelectedTab.UpdateCurrentSelectedItem(); - } - - private Task PauseTimeline() - { - _timeRunner.EnableRunning = false; - return Task.CompletedTask; - } - - private async Task ContinueTimeline() - { - _timeRunner.EnableRunning = true; - await _timeRunner.TryStartCommandRunner(); - } - - private async Task RefreshTimeline() - { - await _timeRunner.Refresh(); - } - - private Task ChangeTimelineMode() - { - _addCommandToNextBatch = !_addCommandToNextBatch; - var text = "Timeline mode: " + (_addCommandToNextBatch ? "Continuous" : "Parallel"); - _popupTexts.Add(text); - - Task.Run(async () => - { - await Task.Delay(5000); - await Dispatcher.UIThread.InvokeAsync(() => _popupTexts.Remove(text)); - }); - - return Task.CompletedTask; - } - - private Task GoToContainer() - { - var handler = async () => - { - if (Inputs != null) - { - var path = Inputs[0].Value; - foreach (var contentProvider in _contentProviders) - { - if (contentProvider.CanHandlePath(path)) - { - var possibleContainer = await contentProvider.GetByPath(path); - if (possibleContainer is IContainer container) - { - await AppState.SelectedTab.OpenContainer(container); - } - //TODO: multiple possible content provider handler - return; - } - } - } - }; - - ReadInputs(new List() { new Core.Interactions.InputElement("Path", InputType.Text) }, handler); - - return Task.CompletedTask; - } - - private Task ToggleAdvancedIcons() - { - _iconProvider.EnableAdvancedIcons = !_iconProvider.EnableAdvancedIcons; - var text = "Advanced icons are: " + (_iconProvider.EnableAdvancedIcons ? "ON" : "OFF"); - _popupTexts.Add(text); - - Task.Run(async () => - { - await Task.Delay(5000); - await Dispatcher.UIThread.InvokeAsync(() => _popupTexts.Remove(text)); - }); - return Task.CompletedTask; - } - - private Task OpenInDefaultFileExplorer() - { - if (AppState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) - { - var path = localFolder.Directory.FullName; - if (path != null) - { - Process.Start("explorer.exe", "\"" + path + "\""); - } - } - - return Task.CompletedTask; - } - - private async Task CopyPath() - { - string? textToCopy = null; - if (AppState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) - { - textToCopy = localFolder.Directory.FullName; - } - if (AppState.SelectedTab.CurrentLocation.Container is LocalFile localFile) - { - textToCopy = localFile.File.FullName; - } - else if (AppState.SelectedTab.CurrentLocation.Container.FullName is string fullName) - { - textToCopy = fullName; - } - - if (textToCopy != null && global::Avalonia.Application.Current?.Clipboard is not null) - { - await global::Avalonia.Application.Current.Clipboard.SetTextAsync(textToCopy); - } - } - - private Task ShowAllShortcut() - { - IsAllShortcutVisible = true; - return Task.CompletedTask; - } - - private Task RunCommandInContainer() - { - var handler = () => - { - if (Inputs != null) - { - var input = Inputs[0].Value; - string? path = null; - string? arguments = null; - - if (input.StartsWith("\"")) - { - var pathEnd = input.IndexOf('\"', 1); - - path = input.Substring(1, pathEnd); - arguments = input.Substring(pathEnd + 1).Trim(); - } - else - { - var inputParts = input.Split(' '); - path = inputParts[0]; - arguments = inputParts.Length > 1 ? string.Join(' ', inputParts[1..]).Trim() : null; - } - - if (!string.IsNullOrWhiteSpace(path)) - { - var process = new Process(); - process.StartInfo.FileName = path; - - if (!string.IsNullOrWhiteSpace(arguments)) - { - process.StartInfo.Arguments = arguments; - } - if (AppState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder) - { - process.StartInfo.WorkingDirectory = localFolder.Directory.FullName; - } - process.Start(); - } - } - - return Task.CompletedTask; - }; - - ReadInputs(new List() { new Core.Interactions.InputElement("Command", InputType.Text) }, handler); - - return Task.CompletedTask; - } - - private Task SelectPreviousTimelineBlock() - { - var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); - if (currentSelected == null) return Task.CompletedTask; - - ParallelCommandsViewModel? newBlockVM = null; - ParallelCommandsViewModel? previousBlockVM = null; - - foreach (var timelineBlock in TimelineCommands) - { - - foreach (var command in timelineBlock.ParallelCommands) - { - if (command.IsSelected) - { - newBlockVM = previousBlockVM; - break; - } - } - - previousBlockVM = timelineBlock; - } - - if (newBlockVM == null) return Task.CompletedTask; - - foreach (var val in TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t)) - { - val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0; - } - - return Task.CompletedTask; - } - - private Task SelectNextTimelineCommand() - { - var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); - if (currentSelected == null) return Task.CompletedTask; - - ParallelCommandViewModel? lastCommand = null; - var any = false; - foreach (var command in TimelineCommands.SelectMany(t => t.ParallelCommands)) - { - var isSelected = lastCommand == currentSelected; - command.IsSelected = isSelected; - any = any || isSelected; - lastCommand = command; - } - if (!any && lastCommand != null) lastCommand.IsSelected = true; - return Task.CompletedTask; - } - - private Task SelectPreviousTimelineCommand() - { - var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); - if (currentSelected == null) return Task.CompletedTask; - - ParallelCommandViewModel? lastCommand = null; - foreach (var command in TimelineCommands.SelectMany(t => t.ParallelCommands)) - { - if (lastCommand != null) - { - lastCommand.IsSelected = command == currentSelected; - } - lastCommand = command; - } - if (lastCommand != null) lastCommand.IsSelected = false; - return Task.CompletedTask; - } - - private Task SelectNextTimelineBlock() - { - var currentSelected = GetSelectedTimelineCommandOrSelectFirst(); - if (currentSelected == null) return Task.CompletedTask; - - ParallelCommandsViewModel? newBlockVM = null; - var select = false; - foreach (var timelineBlock in TimelineCommands) - { - if (select) - { - newBlockVM = timelineBlock; - break; - } - foreach (var command in timelineBlock.ParallelCommands) - { - if (command.IsSelected) - { - select = true; - break; - } - } - } - - if (newBlockVM == null) return Task.CompletedTask; - - foreach (var val in TimelineCommands.Select(t => t.ParallelCommands.Select((c, i) => (ParalellCommandVM: t, CommandVM: c, Index: i))).SelectMany(t => t)) - { - val.CommandVM.IsSelected = val.ParalellCommandVM == newBlockVM && val.Index == 0; - } - - return Task.CompletedTask; - } - - private ParallelCommandViewModel? GetSelectedTimelineCommandOrSelectFirst() - { - var currentSelected = TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault(c => c.IsSelected); - if (currentSelected != null) return currentSelected; - - var firstCommand = TimelineCommands.SelectMany(t => t.ParallelCommands).FirstOrDefault(); - if (firstCommand != null) - { - firstCommand.IsSelected = true; - } - - return null; - } - - private async Task AddCommand(ICommand command) - { - if (_addCommandToNextBatch) - { - await _timeRunner.AddCommand(command, toNewBatch: true); - } - else - { - ParallelCommandsViewModel? batchToAdd = null; - foreach (var val in TimelineCommands.Select(t => t.ParallelCommands.Select(c => (ParalellCommandVM: t, CommandVM: c))).SelectMany(t => t)) - { - if (val.CommandVM.IsSelected) - { - batchToAdd = val.ParalellCommandVM; - break; - } - } - - if (batchToAdd != null) - { - await _timeRunner.AddCommand(command, batchToAdd.Id); - } - else - { - await _timeRunner.AddCommand(command); - } - } - } - - private Task ToggleAutoRefresh() - { - var tab = AppState.SelectedTab.TabState.Tab; - tab.AutoRefresh = !tab.AutoRefresh; - - var text = "Auto refresh is: " + (tab.AutoRefresh ? "ON" : "OFF"); - _popupTexts.Add(text); - - Task.Run(async () => - { - await Task.Delay(5000); - await Dispatcher.UIThread.InvokeAsync(() => _popupTexts.Remove(text)); - }); - return Task.CompletedTask; - } - [Command] public async void ProcessInputs() { - if (_inputHandler != null) - { - await _inputHandler.Invoke(); - } - - Inputs = null; - _inputHandler = null; + await DialogService.ProcessInputs(); } [Command] public void CancelInputs() { - Inputs = null; - _inputHandler = null; + DialogService.CancelInputs(); } [Command] public void ProcessMessageBox() { - _inputHandler?.Invoke(); - - MessageBoxText = null; - _inputHandler = null; + DialogService.ProcessMessageBox(); } [Command] public void CancelMessageBox() { - MessageBoxText = null; - _inputHandler = null; + DialogService.CancelMessageBox(); } - public async void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action setHandled) + public void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action setHandled) { - if (key == Key.LeftAlt - || key == Key.RightAlt - || key == Key.LeftShift - || key == Key.RightShift - || key == Key.LeftCtrl - || key == Key.RightCtrl) return; - - NoCommandFound = false; - - var isAltPressed = (keyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt; - var isShiftPressed = (keyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift; - var isCtrlPressed = (keyModifiers & KeyModifiers.Control) == KeyModifiers.Control; - - if (AppState.ViewMode == ViewMode.Default) - { - var keyWithModifiers = new KeyWithModifiers(key, isAltPressed, isShiftPressed, isCtrlPressed); - _previousKeys.Add(keyWithModifiers); - - var selectedCommandBinding = _universalCommandBindings.Find(c => AreKeysEqual(c.Keys, _previousKeys)); - selectedCommandBinding ??= _commandBindings.Find(c => AreKeysEqual(c.Keys, _previousKeys)); - - if (key == Key.Escape) - { - IsAllShortcutVisible = false; - MessageBoxText = null; - _previousKeys.Clear(); - PossibleCommands = new(); - setHandled(true); - } - else if (key == Key.Enter - && MessageBoxText != null) - { - _previousKeys.Clear(); - ProcessMessageBox(); - setHandled(true); - } - else if (selectedCommandBinding != null) - { - setHandled(true); - await selectedCommandBinding.InvokeAsync(); - _previousKeys.Clear(); - PossibleCommands = new(); - } - else if (_keysToSkip.Any(k => AreKeysEqual(k, _previousKeys))) - { - _previousKeys.Clear(); - PossibleCommands = new(); - return; - } - else if (_previousKeys.Count == 2) - { - setHandled(true); - NoCommandFound = true; - _previousKeys.Clear(); - PossibleCommands = new(); - } - else - { - var possibleCommands = _universalCommandBindings.Concat(_commandBindings).Where(c => AreKeysEqual(c.Keys[0], keyWithModifiers)).ToList(); - - if (possibleCommands.Count == 0) - { - NoCommandFound = true; - _previousKeys.Clear(); - } - else - { - PossibleCommands = possibleCommands; - } - setHandled(true); - } - } - else - { - var keyString = key.ToString(); - var updateRapidTravelFilter = false; - - if (key == Key.Escape) - { - setHandled(true); - if (IsAllShortcutVisible) - { - IsAllShortcutVisible = false; - } - else if (MessageBoxText != null) - { - MessageBoxText = null; - } - else - { - await ExitRapidTravelMode(); - } - } - else if (key == Key.Back) - { - if (AppState.RapidTravelText.Length > 0) - { - setHandled(true); - AppState.RapidTravelText = AppState.RapidTravelText.Substring(0, AppState.RapidTravelText.Length - 1); - updateRapidTravelFilter = true; - } - } - else if (keyString.Length == 1) - { - setHandled(true); - AppState.RapidTravelText += keyString.ToString().ToLower(); - updateRapidTravelFilter = true; - } - else - { - var currentKeyAsList = new List() { new KeyWithModifiers(key) }; - var selectedCommandBinding = _universalCommandBindings.Find(c => AreKeysEqual(c.Keys, currentKeyAsList)); - if (selectedCommandBinding != null) - { - setHandled(true); - await selectedCommandBinding.InvokeAsync(); - } - } - - if (updateRapidTravelFilter) - { - var currentLocation = await AppState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(RAPIDTRAVEL); - var newLocation = new VirtualContainer( - currentLocation, - new List, IEnumerable>>() - { - container => container.Where(c => c.Name.ToLower().Contains(AppState.RapidTravelText)) - }, - new List, IEnumerable>>() - { - element => element.Where(e => e.Name.ToLower().Contains(AppState.RapidTravelText)) - }, - virtualContainerName: RAPIDTRAVEL - ); - - await newLocation.Init(); - - await AppState.SelectedTab.OpenContainer(newLocation); - - var selectedItemName = AppState.SelectedTab.SelectedItem?.Item.Name; - var currentLocationItems = await AppState.SelectedTab.CurrentLocation.GetItems(); - if (currentLocationItems.FirstOrDefault(i => string.Equals(i.Item.Name, AppState.RapidTravelText, StringComparison.OrdinalIgnoreCase)) is IItemViewModel matchItem) - { - await AppState.SelectedTab.SetCurrentSelectedItem(matchItem.Item); - } - else if (!currentLocationItems.Select(i => i.Item.Name).Any(n => n == selectedItemName)) - { - await AppState.SelectedTab.MoveCursorToFirst(); - } - } - } - } - - private void ReadInputs(List inputs, Action inputHandler) - { - ReadInputs(inputs, () => { inputHandler(); return Task.CompletedTask; }); - } - private void ReadInputs(List inputs, Func inputHandler) - { - Inputs = inputs.ConvertAll(i => new InputElementWrapper(i, i.DefaultValue)); - _inputHandler = inputHandler; - } - - public async Task ReadInputs(IEnumerable fields) - { - var waiting = true; - var result = new string[0]; - ReadInputs(fields.ToList(), () => - { - if (Inputs != null) - { - result = Inputs.Select(i => i.Value).ToArray(); - } - waiting = false; - }); - - while (waiting) await Task.Delay(100); - - return result; - } - - private void ShowMessageBox(string text, Func inputHandler) - { - MessageBoxText = text; - _inputHandler = inputHandler; - } - - private static bool AreKeysEqual(IReadOnlyList collection1, IReadOnlyList collection2) - { - if (collection1.Count != collection2.Count) return false; - - for (var i = 0; i < collection1.Count; i++) - { - if (!AreKeysEqual(collection1[i], collection2[i])) return false; - } - - return true; - } - - private static bool AreKeysEqual(KeyWithModifiers key1, KeyWithModifiers key2) => - key1.Key == key2.Key - && key1.Alt == key2.Alt - && key1.Shift == key2.Shift - && key1.Ctrl == key2.Ctrl; - - private void InitCommandBindings() - { - var commandBindings = new List() - { - new CommandBinding( - "enter rapid travel mode", - FileTime.App.Core.Command.Commands.EnterRapidTravel, - new KeyWithModifiers[]{new KeyWithModifiers(Key.OemComma, shift: true)}, - EnterRapidTravelMode), - new CommandBinding( - "create container", - FileTime.App.Core.Command.Commands.CreateContainer, - new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.C)}, - CreateContainer), - new CommandBinding( - "create container", - FileTime.App.Core.Command.Commands.CreateContainer, - new KeyWithModifiers[]{new KeyWithModifiers(Key.F7)}, - CreateContainer), - new CommandBinding( - "create element", - FileTime.App.Core.Command.Commands.CreateElement, - new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.E)}, - CreateElement), - new CommandBinding( - "move to first", - FileTime.App.Core.Command.Commands.MoveToTop, - new KeyWithModifiers[]{new KeyWithModifiers(Key.G),new KeyWithModifiers(Key.G)}, - MoveToFirst), - new CommandBinding( - "move to last", - FileTime.App.Core.Command.Commands.MoveToBottom, - new KeyWithModifiers[]{new KeyWithModifiers(Key.G, shift: true)}, - MoveToLast), - new CommandBinding( - "go to provider", - FileTime.App.Core.Command.Commands.GoToProvider, - new KeyWithModifiers[]{new KeyWithModifiers(Key.G),new KeyWithModifiers(Key.T)}, - GotToProvider), - new CommandBinding( - "go to root", - FileTime.App.Core.Command.Commands.GoToRoot, - new KeyWithModifiers[]{new KeyWithModifiers(Key.G),new KeyWithModifiers(Key.R)}, - GotToRoot), - new CommandBinding( - "go to home", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.G),new KeyWithModifiers(Key.H)}, - GotToHome), - new CommandBinding( - "switch to tab 1", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D1)}, - async() => await SwitchToTab(1)), - new CommandBinding( - "switch to tab 2", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D2)}, - async() => await SwitchToTab(2)), - new CommandBinding( - "switch to tab 3", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D3)}, - async() => await SwitchToTab(3)), - new CommandBinding( - "switch to tab 4", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D4)}, - async() => await SwitchToTab(4)), - new CommandBinding( - "switch to tab 5", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D5)}, - async() => await SwitchToTab(5)), - new CommandBinding( - "switch to tab 6", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D6)}, - async() => await SwitchToTab(6)), - new CommandBinding( - "switch to tab 7", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D7)}, - async() => await SwitchToTab(7)), - new CommandBinding( - "switch to tab 8", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D8)}, - async() => await SwitchToTab(8)), - new CommandBinding( - "switch to last tab", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D9)}, - async() => await SwitchToTab(-1)), - new CommandBinding( - "close tab", - FileTime.App.Core.Command.Commands.GoToHome, - new KeyWithModifiers[]{new KeyWithModifiers(Key.Q)}, - CloseTab), - new CommandBinding( - "select", - FileTime.App.Core.Command.Commands.Select, - new KeyWithModifiers[]{new KeyWithModifiers(Key.Space)}, - MarkCurrentItem), - new CommandBinding( - "copy", - FileTime.App.Core.Command.Commands.Copy, - new KeyWithModifiers[]{new KeyWithModifiers(Key.Y),new KeyWithModifiers(Key.Y)}, - Copy), - new CommandBinding( - "cut", - FileTime.App.Core.Command.Commands.Cut, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D)}, - Cut), - new CommandBinding( - "delete", - FileTime.App.Core.Command.Commands.Delete, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D, shift: true)}, - SoftDelete), - new CommandBinding( - "hard delete", - FileTime.App.Core.Command.Commands.Delete, - new KeyWithModifiers[]{new KeyWithModifiers(Key.D, shift: true),new KeyWithModifiers(Key.D, shift: true)}, - HardDelete), - new CommandBinding( - "paste merge", - FileTime.App.Core.Command.Commands.PasteMerge, - new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.P)}, - PasteMerge), - new CommandBinding( - "paste (overwrite)", - FileTime.App.Core.Command.Commands.PasteOverwrite, - new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.O)}, - PasteOverwrite), - new CommandBinding( - "paste (skip)", - FileTime.App.Core.Command.Commands.PasteSkip, - new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.S)}, - PasteSkip), - new CommandBinding( - "rename", - FileTime.App.Core.Command.Commands.Rename, - new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.W)}, - Rename), - new CommandBinding( - "rename", - FileTime.App.Core.Command.Commands.Rename, - new KeyWithModifiers[]{new KeyWithModifiers(Key.F2)}, - Rename), - new CommandBinding( - "timeline pause", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.P)}, - PauseTimeline), - new CommandBinding( - "timeline start", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.S)}, - ContinueTimeline), - new CommandBinding( - "refresh timeline", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.R)}, - RefreshTimeline), - new CommandBinding( - "refresh", - FileTime.App.Core.Command.Commands.Refresh, - new KeyWithModifiers[]{new KeyWithModifiers(Key.R)}, - RefreshCurrentLocation), - new CommandBinding( - "refresh", - FileTime.App.Core.Command.Commands.Refresh, - new KeyWithModifiers[]{new KeyWithModifiers(Key.F5)}, - RefreshCurrentLocation), - new CommandBinding( - "go to", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.L, ctrl: true) }, - GoToContainer), - new CommandBinding( - "toggle advanced icons", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.Z), new KeyWithModifiers(Key.I) }, - ToggleAdvancedIcons), - new CommandBinding( - "show all shortcut", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.F1) }, - ShowAllShortcut), - new CommandBinding( - "run command", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.D4, shift: true) }, - RunCommandInContainer), - new CommandBinding( - "copy path", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.C), new KeyWithModifiers(Key.P) }, - CopyPath), - new CommandBinding( - "select previous timeline block", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.H) }, - SelectPreviousTimelineBlock), - new CommandBinding( - "select next timeline command", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.J) }, - SelectNextTimelineCommand), - new CommandBinding( - "select previous timeline command", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.K) }, - SelectPreviousTimelineCommand), - new CommandBinding( - "select next timeline block", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.L) }, - SelectNextTimelineBlock), - new CommandBinding( - "command running mode", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.T), new KeyWithModifiers(Key.M) }, - ChangeTimelineMode), - new CommandBinding( - "toggle auto refresh", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.R, shift: true) }, - ToggleAutoRefresh), - //TODO REMOVE - new CommandBinding( - "open in default file browser", - FileTime.App.Core.Command.Commands.Dummy, - new KeyWithModifiers[] { new KeyWithModifiers(Key.O), new KeyWithModifiers(Key.E) }, - OpenInDefaultFileExplorer), - }; - var universalCommandBindings = new List() - { - new CommandBinding("go up", FileTime.App.Core.Command.Commands.GoUp, new KeyWithModifiers[]{new KeyWithModifiers(Key.Left)}, GoUp), - new CommandBinding("open", FileTime.App.Core.Command.Commands.Open, new KeyWithModifiers[]{new KeyWithModifiers(Key.Right)}, OpenContainer), - new CommandBinding("open or run", FileTime.App.Core.Command.Commands.OpenOrRun, new KeyWithModifiers[]{new KeyWithModifiers(Key.Enter)}, OpenOrRun), - new CommandBinding("cursor up", FileTime.App.Core.Command.Commands.MoveCursorUp, new KeyWithModifiers[]{new KeyWithModifiers(Key.Up)}, MoveCursorUp), - new CommandBinding("cursor down", FileTime.App.Core.Command.Commands.MoveCursorDown, new KeyWithModifiers[]{new KeyWithModifiers(Key.Down)}, MoveCursorDown), - new CommandBinding("cursor page up", FileTime.App.Core.Command.Commands.MoveCursorUpPage, new KeyWithModifiers[]{new KeyWithModifiers(Key.PageUp)}, MoveCursorUpPage), - new CommandBinding("cursor page down", FileTime.App.Core.Command.Commands.MoveCursorDownPage, new KeyWithModifiers[]{new KeyWithModifiers(Key.PageDown)}, MoveCursorDownPage), - }; - - _commandBindings.AddRange(commandBindings); - _universalCommandBindings.AddRange(universalCommandBindings); + KeyInputHandlerService.ProcessKeyDown(key, keyModifiers, setHandled); } } } diff --git a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml index 49ef781..8d30dda 100644 --- a/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml +++ b/src/GuiApp/FileTime.Avalonia/Views/MainWindow.axaml @@ -17,6 +17,8 @@ ExtendClientAreaToDecorationsHint="True" Opened="OnWindowOpened" Closed="OnWindowClosed" + x:DataType="vm:MainPageViewModel" + x:CompileBindings="True" mc:Ignorable="d"> @@ -149,7 +151,7 @@ - + @@ -335,7 +337,7 @@ - +