From 8acf618fd45b37d64879b2785f15ef96f9a7e515 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 29 Dec 2018 06:20:24 -0600 Subject: [PATCH] Per-shell-profile keyboard shortcuts (#85) * Added new command wrapper, abstracts static and dynamic commands - All uses of the `Command` are replaced with either `AppCommand`, for a command that is static to the scope of the application, or a reference to an ICommand (specifically, an `EnumCommand()` instance), which provides the abstract wrapper. - Functionality seems intact. - Next step is to have the MainViewModel add the per-shell commands via SettingsService.CreateShellKeyboardShortcuts(), and have the SettingsService.ParseCommandString() function parse the command strings passed around properly. - Finally, add the keyboard chord input to each shell's settings page. * First kick at adding a new terminal keyboard command. - It isn't presented anywhere, but references to keybindings should be using the ICommand key as their identifier for recognizing anything, and when xterm passes back a keyboard command event, the SettingsService is asked to convert the string into an ICommand. * Renaming ICommand to Command. It's not longer an interface, so this makes more sense. * Renaming back to Command, and new class is AbstractCommand. Going to simplify the PR diff considerably. * Shortcuts for default shells work. Need to get the shell profile edit UI elements working. * Added keyboard shortcut UI elements to shell profile pages. * New and deleted shell profiles propagate to all terminal views. * A bit of cleanup. * Camel-casing command text to fix Ctrl+C and copy behaviour. * Fixed failing test. The new on disk store uses a .ToString(), not the Enum description, since the command isn't an Enum, it's a class now in general, and those semantics make more sense. * Adding default value for null keybindings in shell profiles. * Merge branch 'shell_commands' of https://github.com/Riebart/FluentTerminal into pr/85 Improved default value for keybinding property * Renamed KeyBinding collection to KeyBindings * Use strings as Command parameter of KeyBinding * Added missing Commands to keyBindings of default shellProfiles * fix build and unit tests after merge with master * improve UI * Split OnShellProfileCollectionChanged event --- .../KeyboardCommandServiceTests.cs | 10 +- .../SettingsServiceTests.cs | 29 +- .../TrayProcessCommunicationServiceTests.cs | 24 +- .../IKeyboardCommandService.cs | 8 +- .../ISettingsService.cs | 9 +- FluentTerminal.App.Services/ITerminalView.cs | 3 +- .../Implementation/DefaultValueProvider.cs | 72 +++- .../Implementation/KeyboardCommandService.cs | 76 +++-- .../Implementation/SettingsService.cs | 31 +- .../TrayProcessCommunicationService.cs | 2 +- .../MainViewModel.cs | 54 ++- .../Settings/KeyBindingViewModel.cs | 15 +- .../Settings/KeyBindingsPageViewModel.cs | 21 +- .../Settings/KeyBindingsViewModel.cs | 44 ++- .../Settings/ProfilesPageViewModel.cs | 6 +- .../ShellProfileViewModel.cs | 25 +- .../TerminalViewModel.cs | 24 +- FluentTerminal.App/Views/KeyBindingsView.xaml | 12 +- .../Views/KeyBindingsView.xaml.cs | 11 +- .../SettingsPages/KeyBindingSettings.xaml.cs | 8 +- .../SettingsPages/ShellProfileSettings.xaml | 27 +- FluentTerminal.App/Views/TerminalView.xaml.cs | 11 +- FluentTerminal.Models/KeyBinding.cs | 21 +- FluentTerminal.Models/ShellProfile.cs | 2 + .../Services/TerminalSession.cs | 2 +- .../Services/ToggleWindowService.cs | 308 +++++++++--------- 26 files changed, 523 insertions(+), 332 deletions(-) diff --git a/FluentTerminal.App.Services.Test/KeyboardCommandServiceTests.cs b/FluentTerminal.App.Services.Test/KeyboardCommandServiceTests.cs index 9616c50..304fbdc 100644 --- a/FluentTerminal.App.Services.Test/KeyboardCommandServiceTests.cs +++ b/FluentTerminal.App.Services.Test/KeyboardCommandServiceTests.cs @@ -20,7 +20,7 @@ namespace FluentTerminal.App.Services.Test [Fact] public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException() { - var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length); + var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString(); Action handler = null; var keyboardCommandService = new KeyboardCommandService(); @@ -32,7 +32,7 @@ namespace FluentTerminal.App.Services.Test [Fact] public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException() { - var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length); + var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString(); var handler = _fixture.Create(); var keyboardCommandService = new KeyboardCommandService(); keyboardCommandService.RegisterCommandHandler(command, handler); @@ -45,7 +45,7 @@ namespace FluentTerminal.App.Services.Test [Fact] public void SendCommand_CommandIsRegistered_HandlerGetsInvoked() { - var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length); + var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString(); var handlerCalled = false; var keyboardCommandService = new KeyboardCommandService(); keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true); @@ -58,7 +58,7 @@ namespace FluentTerminal.App.Services.Test [Fact] public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown() { - var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length); + var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString(); var keyboardCommandService = new KeyboardCommandService(); Action invoke = () => keyboardCommandService.SendCommand(command); @@ -69,7 +69,7 @@ namespace FluentTerminal.App.Services.Test [Fact] public void SendCommand_CommandIsToggleWindow_ShouldNotThrow() { - var command = Command.ToggleWindow; + var command = nameof(Command.ToggleWindow); var keyboardCommandService = new KeyboardCommandService(); Action invoke = () => keyboardCommandService.SendCommand(command); diff --git a/FluentTerminal.App.Services.Test/SettingsServiceTests.cs b/FluentTerminal.App.Services.Test/SettingsServiceTests.cs index c479f0c..009dbda 100644 --- a/FluentTerminal.App.Services.Test/SettingsServiceTests.cs +++ b/FluentTerminal.App.Services.Test/SettingsServiceTests.cs @@ -1,6 +1,7 @@ using AutoFixture; using FluentAssertions; using FluentTerminal.App.Services.Implementation; +using FluentTerminal.App.Services.Utilities; using FluentTerminal.Models; using FluentTerminal.Models.Enums; using Moq; @@ -439,12 +440,12 @@ namespace FluentTerminal.App.Services.Test [Fact] public void GetKeyBindings_KeyBindingsInKeyBindingsContainer_ReturnsKeyBindingsFromKeyBindingsContainer() { - var keyBindings = _fixture.CreateMany(5); + var commands = _fixture.CreateMany(5); var defaultValueProvider = Mock.Of(); var keyBindingsContainer = new Mock(); - foreach (var keyBinding in keyBindings) + foreach (var command in commands) { - keyBindingsContainer.Setup(x => x.ReadValueFromJson>(keyBinding.Command.ToString(), null)).Returns(new Collection { keyBinding }); + keyBindingsContainer.Setup(x => x.ReadValueFromJson>(command.ToString(), null)).Returns(new Collection { }); } var applicationDataContainers = new ApplicationDataContainers { @@ -456,23 +457,23 @@ namespace FluentTerminal.App.Services.Test }; var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers); - var result = settingsService.GetKeyBindings(); + var result = settingsService.GetCommandKeyBindings(); - foreach (var keyBinding in keyBindings) + foreach (var command in commands) { - keyBindingsContainer.Verify(x => x.ReadValueFromJson>(keyBinding.Command.ToString(), null), Times.Once); + keyBindingsContainer.Verify(x => x.ReadValueFromJson>(command.ToString(), null), Times.Once); } } [Fact] public void GetKeyBindings_KeyBindingsNotInKeyBindingsContainer_ReturnsKeyBindingsFromDefaultValueProvider() { - var keyBindings = _fixture.CreateMany(5); + var commands = _fixture.CreateMany(5); var defaultValueProvider = new Mock(); var keyBindingsContainer = new Mock(); - foreach (var keyBinding in keyBindings) + foreach (var command in commands) { - keyBindingsContainer.Setup(x => x.ReadValueFromJson>(keyBinding.Command.ToString(), null)).Returns(new Collection { keyBinding }); + keyBindingsContainer.Setup(x => x.ReadValueFromJson>(EnumHelper.GetEnumDescription(command), null)).Returns(new Collection { }); } var applicationDataContainers = new ApplicationDataContainers { @@ -484,14 +485,10 @@ namespace FluentTerminal.App.Services.Test }; var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers); - var result = settingsService.GetKeyBindings(); + var result = settingsService.GetCommandKeyBindings(); foreach (Command command in Enum.GetValues(typeof(Command))) { - if (keyBindings.Select(x => x.Command).Contains(command)) - { - continue; - } defaultValueProvider.Verify(x => x.GetDefaultKeyBindings(command), Times.Once); } } @@ -513,7 +510,7 @@ namespace FluentTerminal.App.Services.Test }; var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers); - settingsService.SaveKeyBindings(command, keyBindings); + settingsService.SaveKeyBindings(command.ToString(), keyBindings); keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), keyBindings), Times.Once); } @@ -537,7 +534,7 @@ namespace FluentTerminal.App.Services.Test var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers); settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true; - settingsService.SaveKeyBindings(command, keyBindings); + settingsService.SaveKeyBindings(command.ToString(), keyBindings); keyBindingsChangedEventInvoked.Should().BeTrue(); } diff --git a/FluentTerminal.App.Services.Test/TrayProcessCommunicationServiceTests.cs b/FluentTerminal.App.Services.Test/TrayProcessCommunicationServiceTests.cs index ebc627e..dc6fb09 100644 --- a/FluentTerminal.App.Services.Test/TrayProcessCommunicationServiceTests.cs +++ b/FluentTerminal.App.Services.Test/TrayProcessCommunicationServiceTests.cs @@ -28,9 +28,9 @@ namespace FluentTerminal.App.Services.Test { var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var appServiceConnection = new Mock(); var response = _fixture.Create(); @@ -57,9 +57,9 @@ namespace FluentTerminal.App.Services.Test var terminalSize = _fixture.Create(); var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var appServiceConnection = new Mock(); var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object); @@ -77,9 +77,9 @@ namespace FluentTerminal.App.Services.Test var text = _fixture.Create(); var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var appServiceConnection = new Mock(); var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object); @@ -96,9 +96,9 @@ namespace FluentTerminal.App.Services.Test var terminalId = _fixture.Create(); var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var appServiceConnection = new Mock(); var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object); @@ -126,9 +126,9 @@ namespace FluentTerminal.App.Services.Test }; var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object); var appServiceConnection = new Mock(); @@ -163,9 +163,9 @@ namespace FluentTerminal.App.Services.Test }; var settingsService = new Mock(); var keyBindings = _fixture.CreateMany(3); - settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary> + settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary> { - [Command.ToggleWindow] = keyBindings.ToList() + [nameof(Command.ToggleWindow)] = keyBindings.ToList() }); var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object); var appServiceConnection = new Mock(); diff --git a/FluentTerminal.App.Services/IKeyboardCommandService.cs b/FluentTerminal.App.Services/IKeyboardCommandService.cs index 3c51df5..256e21b 100644 --- a/FluentTerminal.App.Services/IKeyboardCommandService.cs +++ b/FluentTerminal.App.Services/IKeyboardCommandService.cs @@ -1,12 +1,12 @@ -using FluentTerminal.Models.Enums; -using System; +using System; namespace FluentTerminal.App.Services { public interface IKeyboardCommandService { - void RegisterCommandHandler(Command command, Action handler); + void RegisterCommandHandler(string command, Action handler); - void SendCommand(Command command); + void SendCommand(string command); + void DeregisterCommandHandler(string command); } } \ No newline at end of file diff --git a/FluentTerminal.App.Services/ISettingsService.cs b/FluentTerminal.App.Services/ISettingsService.cs index 538702c..319c318 100644 --- a/FluentTerminal.App.Services/ISettingsService.cs +++ b/FluentTerminal.App.Services/ISettingsService.cs @@ -1,5 +1,4 @@ using FluentTerminal.Models; -using FluentTerminal.Models.Enums; using System; using System.Collections.Generic; @@ -10,6 +9,8 @@ namespace FluentTerminal.App.Services event EventHandler ApplicationSettingsChanged; event EventHandler CurrentThemeChanged; event EventHandler KeyBindingsChanged; + event EventHandler ShellProfileAdded; + event EventHandler ShellProfileDeleted; event EventHandler TerminalOptionsChanged; void DeleteShellProfile(Guid id); void DeleteTheme(Guid id); @@ -18,7 +19,7 @@ namespace FluentTerminal.App.Services Guid GetCurrentThemeId(); ShellProfile GetDefaultShellProfile(); Guid GetDefaultShellProfileId(); - IDictionary> GetKeyBindings(); + IDictionary> GetCommandKeyBindings(); IEnumerable GetShellProfiles(); IEnumerable GetTabThemes(); TerminalOptions GetTerminalOptions(); @@ -28,8 +29,8 @@ namespace FluentTerminal.App.Services void SaveApplicationSettings(ApplicationSettings applicationSettings); void SaveCurrentThemeId(Guid id); void SaveDefaultShellProfileId(Guid id); - void SaveKeyBindings(Command command, ICollection keyBindings); - void SaveShellProfile(ShellProfile shellProfile); + void SaveKeyBindings(string command, ICollection keyBindings); + void SaveShellProfile(ShellProfile shellProfile, bool newShell = false); void SaveTerminalOptions(TerminalOptions terminalOptions); void SaveTheme(TerminalTheme theme); } diff --git a/FluentTerminal.App.Services/ITerminalView.cs b/FluentTerminal.App.Services/ITerminalView.cs index 5abcdbc..c407bd6 100644 --- a/FluentTerminal.App.Services/ITerminalView.cs +++ b/FluentTerminal.App.Services/ITerminalView.cs @@ -1,5 +1,4 @@ using FluentTerminal.Models; -using FluentTerminal.Models.Enums; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -12,7 +11,7 @@ namespace FluentTerminal.App.Services event EventHandler TerminalTitleChanged; - event EventHandler KeyboardCommandReceived; + event EventHandler KeyboardCommandReceived; Task ChangeTheme(TerminalColors theme); diff --git a/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs b/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs index 6c3a6b9..ebdcf1f 100644 --- a/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs +++ b/FluentTerminal.App.Services/Implementation/DefaultValueProvider.cs @@ -35,7 +35,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.ToggleWindow, + Command = nameof(Command.ToggleWindow), Ctrl = false, Alt = false, Shift = false, @@ -48,7 +48,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.NextTab, + Command = nameof(Command.NextTab), Ctrl = true, Alt = false, Shift = false, @@ -61,7 +61,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.PreviousTab, + Command = nameof(Command.PreviousTab), Ctrl = true, Alt = false, Shift = true, @@ -82,7 +82,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = command, + Command = command.ToString(), Ctrl = true, Alt = false, Shift = false, @@ -95,7 +95,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.NewTab, + Command = nameof(Command.NewTab), Ctrl = true, Alt = false, Shift = false, @@ -108,7 +108,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.ConfigurableNewTab, + Command = nameof(Command.ConfigurableNewTab), Ctrl = true, Alt = false, Shift = true, @@ -121,7 +121,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.CloseTab, + Command = nameof(Command.CloseTab), Ctrl = true, Alt = false, Shift = false, @@ -134,7 +134,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.NewWindow, + Command = nameof(Command.NewWindow), Ctrl = true, Alt = false, Shift = false, @@ -147,7 +147,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.ShowSettings, + Command = nameof(Command.ShowSettings), Ctrl = true, Alt = false, Shift = false, @@ -160,7 +160,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.Copy, + Command = nameof(Command.Copy), Ctrl = true, Alt = false, Shift = false, @@ -173,7 +173,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.Paste, + Command = nameof(Command.Paste), Ctrl = true, Alt = false, Shift = false, @@ -186,7 +186,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.Search, + Command = nameof(Command.Search), Ctrl = true, Alt = false, Shift = false, @@ -199,7 +199,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.ToggleFullScreen, + Command = nameof(Command.ToggleFullScreen), Ctrl = false, Alt = true, Shift = false, @@ -212,7 +212,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.SelectAll, + Command = nameof(Command.SelectAll), Ctrl = true, Alt = false, Shift = false, @@ -225,7 +225,7 @@ namespace FluentTerminal.App.Services.Implementation { new KeyBinding { - Command = Command.Clear, + Command = nameof(Command.Clear), Ctrl = true, Alt = false, Shift = false, @@ -327,7 +327,19 @@ namespace FluentTerminal.App.Services.Implementation Arguments = string.Empty, Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe", PreInstalled = true, - WorkingDirectory = string.Empty + WorkingDirectory = string.Empty, + KeyBindings = new [] + { + new KeyBinding + { + Command = GetDefaultShellProfileId().ToString(), + Ctrl=true, + Alt=true, + Shift=false, + Meta=false, + Key=(int)ExtendedVirtualKey.Number1 + } + } }, new ShellProfile { @@ -336,7 +348,19 @@ namespace FluentTerminal.App.Services.Implementation Arguments = string.Empty, Location = @"C:\Windows\System32\cmd.exe", PreInstalled = true, - WorkingDirectory = string.Empty + WorkingDirectory = string.Empty, + KeyBindings = new [] + { + new KeyBinding + { + Command = "ab942a61-7673-4755-9bd8-765aff91d9a3", + Ctrl=true, + Alt=true, + Shift=false, + Meta=false, + Key=(int)ExtendedVirtualKey.Number2 + } + } }, new ShellProfile { @@ -345,7 +369,19 @@ namespace FluentTerminal.App.Services.Implementation Arguments = string.Empty, Location = @"C:\windows\system32\wsl.exe", PreInstalled = true, - WorkingDirectory = string.Empty + WorkingDirectory = string.Empty, + KeyBindings = new [] + { + new KeyBinding + { + Command = "e5785ad6-584f-40cb-bdcd-d5b3b3953e7f", + Ctrl=true, + Alt=true, + Shift=false, + Meta=false, + Key=(int)ExtendedVirtualKey.Number3 + } + } } }; } diff --git a/FluentTerminal.App.Services/Implementation/KeyboardCommandService.cs b/FluentTerminal.App.Services/Implementation/KeyboardCommandService.cs index 2f23fb6..38b102d 100644 --- a/FluentTerminal.App.Services/Implementation/KeyboardCommandService.cs +++ b/FluentTerminal.App.Services/Implementation/KeyboardCommandService.cs @@ -1,38 +1,46 @@ -using FluentTerminal.Models.Enums; -using System; -using System.Collections.Generic; - -namespace FluentTerminal.App.Services.Implementation -{ - public class KeyboardCommandService : IKeyboardCommandService - { - private readonly Dictionary _commandHandlers = new Dictionary(); - - public void RegisterCommandHandler(Command command, Action handler) - { - if (_commandHandlers.ContainsKey(command)) - { - throw new InvalidOperationException("command already registered"); - } - - _commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler)); - } - - public void SendCommand(Command command) - { - if (_commandHandlers.TryGetValue(command, out Action handler)) - { - handler.Invoke(); - return; - } - +using FluentTerminal.Models.Enums; +using System; +using System.Collections.Generic; + +namespace FluentTerminal.App.Services.Implementation +{ + public class KeyboardCommandService : IKeyboardCommandService + { + private readonly Dictionary _commandHandlers = new Dictionary(); + + public void RegisterCommandHandler(string command, Action handler) + { + if (_commandHandlers.ContainsKey(command)) + { + throw new InvalidOperationException("command already registered"); + } + + _commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler)); + } + + public void DeregisterCommandHandler(string command) + { + if (_commandHandlers.ContainsKey(command)) + { + _commandHandlers.Remove(command); + } + } + + public void SendCommand(string command) + { + if (_commandHandlers.TryGetValue(command, out Action handler)) + { + handler.Invoke(); + return; + } + // already registered by SystemTray's ToggleWindowService - if (command == Command.ToggleWindow) + if (command == nameof(Command.ToggleWindow)) { return; - } - - throw new KeyNotFoundException($"No handler registered for command: {command}"); - } - } + } + + throw new KeyNotFoundException($"No handler registered for command: {command}"); + } + } } \ No newline at end of file diff --git a/FluentTerminal.App.Services/Implementation/SettingsService.cs b/FluentTerminal.App.Services/Implementation/SettingsService.cs index 8455759..ad6dc39 100644 --- a/FluentTerminal.App.Services/Implementation/SettingsService.cs +++ b/FluentTerminal.App.Services/Implementation/SettingsService.cs @@ -19,6 +19,7 @@ namespace FluentTerminal.App.Services.Implementation private readonly IApplicationDataContainer _roamingSettings; private readonly IApplicationDataContainer _shellProfiles; private readonly IApplicationDataContainer _themes; + public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers) { _defaultValueProvider = defaultValueProvider; @@ -52,10 +53,15 @@ namespace FluentTerminal.App.Services.Implementation public event EventHandler KeyBindingsChanged; + public event EventHandler ShellProfileAdded; + public event EventHandler ShellProfileDeleted; + public event EventHandler TerminalOptionsChanged; + public void DeleteShellProfile(Guid id) { _shellProfiles.Delete(id.ToString()); + ShellProfileDeleted?.Invoke(this, id); } public void DeleteTheme(Guid id) @@ -117,14 +123,14 @@ namespace FluentTerminal.App.Services.Implementation return _defaultValueProvider.GetDefaultShellProfileId(); } - public IDictionary> GetKeyBindings() + public IDictionary> GetCommandKeyBindings() { - var keyBindings = new Dictionary>(); + var keyBindings = new Dictionary>(); + foreach (Command command in Enum.GetValues(typeof(Command))) { - keyBindings.Add(command, _keyBindings.ReadValueFromJson>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command)); + keyBindings.Add(command.ToString(), _keyBindings.ReadValueFromJson>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command)); } - return keyBindings; } @@ -181,15 +187,28 @@ namespace FluentTerminal.App.Services.Implementation _localSettings.SetValue(DefaultShellProfileKey, id); } - public void SaveKeyBindings(Command command, ICollection keyBindings) + public void SaveKeyBindings(string command, ICollection keyBindings) { + if (!Enum.IsDefined(typeof(Command), command)) + { + throw new InvalidOperationException(); + } + _keyBindings.WriteValueAsJson(command.ToString(), keyBindings); KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty); } - public void SaveShellProfile(ShellProfile shellProfile) + public void SaveShellProfile(ShellProfile shellProfile, bool newShell = false) { _shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile); + + // When saving the shell profile, we also need to update keybindings for everywhere. + KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty); + + if (newShell) + { + ShellProfileAdded?.Invoke(this, shellProfile); + } } public void SaveTerminalOptions(TerminalOptions terminalOptions) diff --git a/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs b/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs index 8086d60..e4e90e2 100644 --- a/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs +++ b/FluentTerminal.App.Services/Implementation/TrayProcessCommunicationService.cs @@ -87,7 +87,7 @@ namespace FluentTerminal.App.Services.Implementation public Task UpdateToggleWindowKeyBindings() { - var keyBindings = _settingsService.GetKeyBindings()[Command.ToggleWindow]; + var keyBindings = _settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)]; var request = new SetToggleWindowKeyBindingsRequest { diff --git a/FluentTerminal.App.ViewModels/MainViewModel.cs b/FluentTerminal.App.ViewModels/MainViewModel.cs index fba7828..0c149b3 100644 --- a/FluentTerminal.App.ViewModels/MainViewModel.cs +++ b/FluentTerminal.App.ViewModels/MainViewModel.cs @@ -34,30 +34,40 @@ namespace FluentTerminal.App.ViewModels _settingsService.CurrentThemeChanged += OnCurrentThemeChanged; _settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged; _settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged; + _settingsService.ShellProfileAdded += OnShellProfileAdded; + _settingsService.ShellProfileDeleted += OnShellProfileDeleted; + _trayProcessCommunicationService = trayProcessCommunicationService; _dialogService = dialogService; _applicationView = applicationView; _dispatcherTimer = dispatcherTimer; _clipboardService = clipboardService; _keyboardCommandService = keyboardCommandService; - _keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null, false)); - _keyboardCommandService.RegisterCommandHandler(Command.ConfigurableNewTab, () => AddTerminal(null, true)); - _keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.NewTab), () => AddTerminal(null, false)); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewTab), () => AddTerminal(null, true)); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.CloseTab), CloseCurrentTab); // Add all of the commands for switching to a tab of a given ID, if there's one open there for (int i = 0; i < 9; i++) { - Command switchCmd = Command.SwitchToTerm1 + i; + var switchCmd = Command.SwitchToTerm1 + i; int tabNumber = i; - Action handler = () => SelectTabNumber(tabNumber); - _keyboardCommandService.RegisterCommandHandler(switchCmd, handler); + void handler() => SelectTabNumber(tabNumber); + _keyboardCommandService.RegisterCommandHandler(switchCmd.ToString(), handler); + } + + _keyboardCommandService.RegisterCommandHandler(nameof(Command.NextTab), SelectNextTab); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.PreviousTab), SelectPreviousTab); + + _keyboardCommandService.RegisterCommandHandler(nameof(Command.NewWindow), NewWindow); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.ShowSettings), ShowSettings); + _keyboardCommandService.RegisterCommandHandler(nameof(Command.ToggleFullScreen), ToggleFullScreen); + + foreach (ShellProfile profile in _settingsService.GetShellProfiles()) + { + _keyboardCommandService.RegisterCommandHandler(profile.Id.ToString(), () => AddTerminal(profile.WorkingDirectory, false, profile)); } - _keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab); - _keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab); - _keyboardCommandService.RegisterCommandHandler(Command.NewWindow, NewWindow); - _keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, ShowSettings); - _keyboardCommandService.RegisterCommandHandler(Command.ToggleFullScreen, ToggleFullScreen); var currentTheme = _settingsService.GetCurrentTheme(); var options = _settingsService.GetTerminalOptions(); Background = currentTheme.Colors.Background; @@ -73,6 +83,16 @@ namespace FluentTerminal.App.ViewModels Terminals.CollectionChanged += OnTerminalsCollectionChanged; } + private void OnShellProfileDeleted(object sender, Guid e) + { + _keyboardCommandService.DeregisterCommandHandler(e.ToString()); + } + + private void OnShellProfileAdded(object sender, ShellProfile e) + { + _keyboardCommandService.RegisterCommandHandler(e.Id.ToString(), () => AddTerminal(e.WorkingDirectory, false, e)); + } + private void OnTerminalsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { RaisePropertyChanged(nameof(ShowTabsOnTop)); @@ -143,11 +163,17 @@ namespace FluentTerminal.App.ViewModels public ObservableCollection Terminals { get; } = new ObservableCollection(); - public Task AddTerminal(string startupDirectory, bool showProfileSelection) + /// + /// Add a new terminal window, either with a specified terminal profile, with the default, or by showing the profile selection dialog. + /// + /// + /// + /// + /// + public Task AddTerminal(string startupDirectory, bool showProfileSelection, ShellProfile profile = null) { return _applicationView.RunOnDispatcherThread(async () => { - ShellProfile profile = null; if (showProfileSelection) { profile = await _dialogService.ShowProfileSelectionDialogAsync(); @@ -157,7 +183,7 @@ namespace FluentTerminal.App.ViewModels return; } } - else + else if (profile == null) { profile = _settingsService.GetDefaultShellProfile(); } diff --git a/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs b/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs index 105469f..92bad7b 100644 --- a/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/KeyBindingViewModel.cs @@ -1,7 +1,7 @@ using FluentTerminal.App.Services; +using FluentTerminal.App.ViewModels.Infrastructure; using FluentTerminal.Models; using GalaSoft.MvvmLight; -using GalaSoft.MvvmLight.Command; using System; using System.Threading.Tasks; @@ -11,18 +11,21 @@ namespace FluentTerminal.App.ViewModels.Settings { private readonly IDialogService _dialogService; - public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService) + public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService, KeyBindingsViewModel parent) { Model = keyBinding; + Parent = parent; _dialogService = dialogService; - EditCommand = new RelayCommand(async () => await Edit().ConfigureAwait(false)); - DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false)); + EditCommand = new AsyncCommand(Edit); + DeleteCommand = new AsyncCommand(Delete); } public event EventHandler Deleted; public event EventHandler Edited; + public KeyBindingsViewModel Parent { get; } + public bool Meta { get => Model.Meta; @@ -62,8 +65,8 @@ namespace FluentTerminal.App.ViewModels.Settings } } - public RelayCommand DeleteCommand { get; } - public RelayCommand EditCommand { get; } + public IAsyncCommand DeleteCommand { get; } + public IAsyncCommand EditCommand { get; } public int Key { diff --git a/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs index 90123ac..356bd82 100644 --- a/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/KeyBindingsPageViewModel.cs @@ -1,4 +1,5 @@ using FluentTerminal.App.Services; +using FluentTerminal.App.Services.Utilities; using FluentTerminal.Models; using FluentTerminal.Models.Enums; using GalaSoft.MvvmLight; @@ -17,7 +18,7 @@ namespace FluentTerminal.App.ViewModels.Settings private readonly IDialogService _dialogService; private readonly ISettingsService _settingsService; private readonly ITrayProcessCommunicationService _trayProcessCommunicationService; - private IDictionary> _keyBindings; + private IDictionary> _keyBindings; public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService) { @@ -26,19 +27,19 @@ namespace FluentTerminal.App.ViewModels.Settings _defaultValueProvider = defaultValueProvider; _trayProcessCommunicationService = trayProcessCommunicationService; RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false)); - AddCommand = new RelayCommand(async command => await Add(command).ConfigureAwait(false)); + AddCommand = new RelayCommand(async command => await Add(command).ConfigureAwait(false)); - Initialize(_settingsService.GetKeyBindings()); + Initialize(_settingsService.GetCommandKeyBindings()); } - public RelayCommand AddCommand { get; } + public RelayCommand AddCommand { get; } public ObservableCollection KeyBindings { get; } = new ObservableCollection(); public RelayCommand RestoreDefaultsCommand { get; } - private async Task Add(Command command) + private async Task Add(string command) { var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command); - await keyBinding?.Add(); + await keyBinding?.ShowAddKeyBindingDialog(); } private void ClearKeyBindings() @@ -51,7 +52,7 @@ namespace FluentTerminal.App.ViewModels.Settings KeyBindings.Clear(); } - private void Initialize(IDictionary> keyBindings) + private void Initialize(IDictionary> keyBindings) { _keyBindings = keyBindings; @@ -60,13 +61,13 @@ namespace FluentTerminal.App.ViewModels.Settings foreach (var value in Enum.GetValues(typeof(Command))) { var command = (Command)value; - var viewModel = new KeyBindingsViewModel(command, _keyBindings[command], _dialogService); + var viewModel = new KeyBindingsViewModel(command.ToString(), _keyBindings[command.ToString()], _dialogService, EnumHelper.GetEnumDescription(command), true); viewModel.Edited += OnEdited; KeyBindings.Add(viewModel); } } - private void OnEdited(Command command, ICollection keyBindings) + private void OnEdited(string command, ICollection keyBindings) { _settingsService.SaveKeyBindings(command, keyBindings); _trayProcessCommunicationService.UpdateToggleWindowKeyBindings(); @@ -79,7 +80,7 @@ namespace FluentTerminal.App.ViewModels.Settings if (result == DialogButton.OK) { _settingsService.ResetKeyBindings(); - Initialize(_settingsService.GetKeyBindings()); + Initialize(_settingsService.GetCommandKeyBindings()); } } } diff --git a/FluentTerminal.App.ViewModels/Settings/KeyBindingsViewModel.cs b/FluentTerminal.App.ViewModels/Settings/KeyBindingsViewModel.cs index 7e8dfab..ec01926 100644 --- a/FluentTerminal.App.ViewModels/Settings/KeyBindingsViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/KeyBindingsViewModel.cs @@ -1,7 +1,5 @@ using FluentTerminal.App.Services; -using FluentTerminal.App.Services.Utilities; using FluentTerminal.Models; -using FluentTerminal.Models.Enums; using GalaSoft.MvvmLight; using System; using System.Collections.Generic; @@ -14,48 +12,66 @@ namespace FluentTerminal.App.ViewModels.Settings { private readonly IDialogService _dialogService; private readonly ICollection _keyBindings; + private bool _editable; - public KeyBindingsViewModel(Command command, ICollection keyBindings, IDialogService dialogService) + public KeyBindingsViewModel(string command, ICollection keyBindings, IDialogService dialogService, string commandName, bool editable) { Command = command; _keyBindings = keyBindings; _dialogService = dialogService; - CommandName = EnumHelper.GetEnumDescription(command); + CommandName = commandName; + _editable = editable; foreach (var binding in keyBindings) { - var viewModel = new KeyBindingViewModel(binding, _dialogService); + var viewModel = new KeyBindingViewModel(binding, _dialogService, this); viewModel.Deleted += ViewModel_Deleted; viewModel.Edited += ViewModel_Edited; KeyBindings.Add(viewModel); } } - public delegate void EditedEvent(Command command, ICollection keyBindings); + public delegate void EditedEvent(string command, ICollection keyBindings); public event EditedEvent Edited; - public Command Command { get; } + public string Command { get; } public string CommandName { get; } + /// + /// Whether or not the "Edit" and "Delete" links appear next to the key binding list. + /// + public bool Editable + { + get => _editable; + set => Set(ref _editable, value); + } + public ObservableCollection KeyBindings { get; } = new ObservableCollection(); - public async Task Add() + public async Task ShowAddKeyBindingDialog() { - var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService); + var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService, this); if (await newKeyBinding.Edit().ConfigureAwait(true)) { - newKeyBinding.Deleted += ViewModel_Deleted; - newKeyBinding.Edited += ViewModel_Edited; - KeyBindings.Add(newKeyBinding); - _keyBindings.Add(newKeyBinding.Model); - Edited?.Invoke(Command, _keyBindings); + Add(newKeyBinding.Model); } } + public void Add(KeyBinding keyBinding) + { + var viewModel = new KeyBindingViewModel(keyBinding, _dialogService, this); + + viewModel.Deleted += ViewModel_Deleted; + viewModel.Edited += ViewModel_Edited; + KeyBindings.Add(viewModel); + _keyBindings.Add(viewModel.Model); + Edited?.Invoke(Command, _keyBindings); + } + private void ViewModel_Deleted(object sender, EventArgs e) { if (sender is KeyBindingViewModel keyBinding) diff --git a/FluentTerminal.App.ViewModels/Settings/ProfilesPageViewModel.cs b/FluentTerminal.App.ViewModels/Settings/ProfilesPageViewModel.cs index d30c69c..242d0e4 100644 --- a/FluentTerminal.App.ViewModels/Settings/ProfilesPageViewModel.cs +++ b/FluentTerminal.App.ViewModels/Settings/ProfilesPageViewModel.cs @@ -4,6 +4,7 @@ using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using System; using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Linq; namespace FluentTerminal.App.ViewModels.Settings @@ -82,10 +83,11 @@ namespace FluentTerminal.App.ViewModels.Settings { Id = Guid.NewGuid(), PreInstalled = false, - Name = "New profile" + Name = "New profile", + KeyBindings = new List() }; - _settingsService.SaveShellProfile(shellProfile); + _settingsService.SaveShellProfile(shellProfile, true); var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService); viewModel.EditCommand.Execute(null); diff --git a/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs b/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs index e2bb4a7..c0b0a79 100644 --- a/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs +++ b/FluentTerminal.App.ViewModels/ShellProfileViewModel.cs @@ -1,5 +1,6 @@ using FluentTerminal.App.Services; using FluentTerminal.App.ViewModels.Infrastructure; +using FluentTerminal.App.ViewModels.Settings; using FluentTerminal.Models; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; @@ -46,11 +47,15 @@ namespace FluentTerminal.App.ViewModels SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == shellProfile.TabThemeId); PreInstalled = shellProfile.PreInstalled; + var keyBindings = shellProfile.KeyBindings.Select(x => new KeyBinding(x)).ToList(); + KeyBindings = new KeyBindingsViewModel(shellProfile.Id.ToString(), keyBindings, _dialogService, string.Empty, false); + SetDefaultCommand = new RelayCommand(SetDefault); DeleteCommand = new AsyncCommand(Delete, CanDelete); EditCommand = new RelayCommand(Edit); CancelEditCommand = new AsyncCommand(CancelEdit); SaveChangesCommand = new RelayCommand(SaveChanges); + AddKeyboardShortcutCommand = new AsyncCommand(AddKeyboardShortcut); BrowseForCustomShellCommand = new AsyncCommand(BrowseForCustomShell); BrowseForWorkingDirectoryCommand = new AsyncCommand(BrowseForWorkingDirectory); } @@ -65,8 +70,9 @@ namespace FluentTerminal.App.ViewModels public RelayCommand EditCommand { get; } public RelayCommand SaveChangesCommand { get; } public RelayCommand SetDefaultCommand { get; } - + public IAsyncCommand AddKeyboardShortcutCommand { get; } public ObservableCollection TabThemes { get; } + public KeyBindingsViewModel KeyBindings { get; } public bool PreInstalled { get; } @@ -127,12 +133,18 @@ namespace FluentTerminal.App.ViewModels _shellProfile.Name = Name; _shellProfile.WorkingDirectory = WorkingDirectory; _shellProfile.TabThemeId = SelectedTabTheme.Id; - + _shellProfile.KeyBindings = KeyBindings.KeyBindings.Select(x => x.Model).ToList(); _settingsService.SaveShellProfile(_shellProfile); + KeyBindings.Editable = false; InEditMode = false; } + public Task AddKeyboardShortcut() + { + return KeyBindings.ShowAddKeyBindingDialog(); + } + private async Task BrowseForCustomShell() { var file = await _fileSystemService.OpenFile(new[] { ".exe" }).ConfigureAwait(true); @@ -163,6 +175,13 @@ namespace FluentTerminal.App.ViewModels WorkingDirectory = _fallbackWorkingDirectory; SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == _fallbackTabThemeId); + KeyBindings.KeyBindings.Clear(); + foreach (var keyBinding in _shellProfile.KeyBindings.Select(x => new KeyBinding(x)).ToList()) + { + KeyBindings.Add(keyBinding); + } + + KeyBindings.Editable = false; InEditMode = false; } } @@ -189,6 +208,8 @@ namespace FluentTerminal.App.ViewModels _fallbackName = _shellProfile.Name; _fallbackWorkingDirectory = _shellProfile.WorkingDirectory; _fallbackTabThemeId = _shellProfile.TabThemeId; + + KeyBindings.Editable = true; InEditMode = true; } diff --git a/FluentTerminal.App.ViewModels/TerminalViewModel.cs b/FluentTerminal.App.ViewModels/TerminalViewModel.cs index 79bc3df..2edf751 100644 --- a/FluentTerminal.App.ViewModels/TerminalViewModel.cs +++ b/FluentTerminal.App.ViewModels/TerminalViewModel.cs @@ -214,9 +214,10 @@ namespace FluentTerminal.App.ViewModels var options = _settingsService.GetTerminalOptions(); var theme = _settingsService.GetCurrentTheme(); - var keyBindings = _settingsService.GetKeyBindings(); + var keyBindings = _settingsService.GetCommandKeyBindings(); + var profiles = _settingsService.GetShellProfiles(); - var size = await _terminalView.CreateTerminal(options, theme.Colors, FlattenKeyBindings(keyBindings)).ConfigureAwait(true); + var size = await _terminalView.CreateTerminal(options, theme.Colors, FlattenKeyBindings(keyBindings, profiles)).ConfigureAwait(true); if (!string.IsNullOrWhiteSpace(_startupDirectory)) { @@ -282,9 +283,9 @@ namespace FluentTerminal.App.ViewModels return _terminalView.FindPrevious(SearchText); } - private IEnumerable FlattenKeyBindings(IDictionary> keyBindings) + private IEnumerable FlattenKeyBindings(IDictionary> commandKeyBindings, IEnumerable profiles) { - return keyBindings.Values.SelectMany(k => k); + return commandKeyBindings.Values.SelectMany(k => k).Concat(profiles.SelectMany(x => x.KeyBindings)); } private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e) @@ -311,21 +312,22 @@ namespace FluentTerminal.App.ViewModels private async void OnKeyBindingsChanged(object sender, EventArgs e) { - var keyBindings = _settingsService.GetKeyBindings(); + var keyBindings = _settingsService.GetCommandKeyBindings(); + var profiles = _settingsService.GetShellProfiles(); await _applicationView.RunOnDispatcherThread(async () => { - await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings)).ConfigureAwait(false); + await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings, profiles)).ConfigureAwait(false); }); } - private async void OnKeyboardCommandReceived(object sender, Command e) + private async void OnKeyboardCommandReceived(object sender, string command) { - if (e == Command.Copy) + if (command == nameof(Command.Copy)) { var selection = await _terminalView.GetSelection().ConfigureAwait(true); _clipboardService.SetText(selection); } - else if (e == Command.Paste) + else if (command == nameof(Command.Paste)) { var content = await _clipboardService.GetText().ConfigureAwait(true); if (content != null) @@ -333,7 +335,7 @@ namespace FluentTerminal.App.ViewModels await _trayProcessCommunicationService.WriteText(_terminalId, content).ConfigureAwait(true); } } - else if (e == Command.Search) + else if (command == nameof(Command.Search)) { ShowSearchPanel = !ShowSearchPanel; if (ShowSearchPanel) @@ -343,7 +345,7 @@ namespace FluentTerminal.App.ViewModels } else { - _keyboardCommandService.SendCommand(e); + _keyboardCommandService.SendCommand(command); } } diff --git a/FluentTerminal.App/Views/KeyBindingsView.xaml b/FluentTerminal.App/Views/KeyBindingsView.xaml index 1fbd949..6ec484b 100644 --- a/FluentTerminal.App/Views/KeyBindingsView.xaml +++ b/FluentTerminal.App/Views/KeyBindingsView.xaml @@ -10,13 +10,15 @@ mc:Ignorable="d"> - + + Text="{x:Bind ViewModel.CommandName, Mode=OneWay}" + Visibility="{x:Bind ShowCommandName, Mode=OneWay}" /> @@ -82,11 +84,13 @@ + Content="Edit" + Visibility="{x:Bind Parent.Editable, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}" /> + Content="Delete" + Visibility="{x:Bind Parent.Editable, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}" /> diff --git a/FluentTerminal.App/Views/KeyBindingsView.xaml.cs b/FluentTerminal.App/Views/KeyBindingsView.xaml.cs index 69cce44..50fec23 100644 --- a/FluentTerminal.App/Views/KeyBindingsView.xaml.cs +++ b/FluentTerminal.App/Views/KeyBindingsView.xaml.cs @@ -6,14 +6,23 @@ namespace FluentTerminal.App.Views { public sealed partial class KeyBindingsView : UserControl { + public static readonly DependencyProperty ShowCommandNameProperty = + DependencyProperty.Register(nameof(ShowCommandName), typeof(bool), typeof(KeyBindingsView), new PropertyMetadata(true)); + public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register(nameof(ViewModel), typeof(KeyBindingsViewModel), typeof(KeyBindingsView), new PropertyMetadata(null)); + DependencyProperty.Register(nameof(ViewModel), typeof(KeyBindingsViewModel), typeof(KeyBindingsView), new PropertyMetadata(null)); public KeyBindingsView() { InitializeComponent(); } + public bool ShowCommandName + { + get { return (bool)GetValue(ShowCommandNameProperty); } + set { SetValue(ShowCommandNameProperty, value); } + } + public KeyBindingsViewModel ViewModel { get { return (KeyBindingsViewModel)GetValue(ViewModelProperty); } diff --git a/FluentTerminal.App/Views/SettingsPages/KeyBindingSettings.xaml.cs b/FluentTerminal.App/Views/SettingsPages/KeyBindingSettings.xaml.cs index 9f7b127..5e8f4c0 100644 --- a/FluentTerminal.App/Views/SettingsPages/KeyBindingSettings.xaml.cs +++ b/FluentTerminal.App/Views/SettingsPages/KeyBindingSettings.xaml.cs @@ -1,6 +1,7 @@ using FluentTerminal.App.Services.Utilities; using FluentTerminal.App.ViewModels.Settings; using FluentTerminal.Models.Enums; +using FluentTerminal.Models; using System; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; @@ -22,17 +23,18 @@ namespace FluentTerminal.App.Views.SettingsPages { ViewModel = viewModel; + // Add the commands corresponding to all of the application's static commands foreach (var value in Enum.GetValues(typeof(Command))) { - var command = (Command)value; + Command command = (Command)value; AddCommandMenu.Items.Add(new MenuFlyoutItem { Text = EnumHelper.GetEnumDescription(command), Command = ViewModel.AddCommand, - CommandParameter = command + CommandParameter = command.ToString() }); } } } } -} \ No newline at end of file +} diff --git a/FluentTerminal.App/Views/SettingsPages/ShellProfileSettings.xaml b/FluentTerminal.App/Views/SettingsPages/ShellProfileSettings.xaml index 8fde3ea..b5f2eaf 100644 --- a/FluentTerminal.App/Views/SettingsPages/ShellProfileSettings.xaml +++ b/FluentTerminal.App/Views/SettingsPages/ShellProfileSettings.xaml @@ -6,6 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:FluentTerminal.Models" + xmlns:settings="using:FluentTerminal.App.ViewModels.Settings" xmlns:viewmodels="using:FluentTerminal.App.ViewModels" xmlns:views="using:FluentTerminal.App.Views" mc:Ignorable="d"> @@ -177,15 +178,15 @@