Files
FluentTerminal/FluentTerminal.SystemTray/Services/ToggleWindowService.cs
T
Mike 8acf618fd4 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<AppCommand>()` 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
2018-12-29 13:20:24 +01:00

166 lines
5.3 KiB
C#

using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GlobalHotKey;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.Windows.Threading;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
namespace FluentTerminal.SystemTray.Services
{
public class ToggleWindowService : IDisposable
{
private const int SW_MINIMIZE = 6;
private readonly HotKeyManager _hotKeyManager;
private readonly INotificationService _notificationService;
private readonly Dispatcher _dispatcher;
private bool _disposedValue;
private readonly List<HotKey> _hotKeys;
public ToggleWindowService(Dispatcher dispatcher, HotKeyManager hotKeyManager, INotificationService notificationService, ISettingsService settingsService)
{
_dispatcher = dispatcher;
_notificationService = notificationService;
_hotKeyManager = hotKeyManager;
_hotKeys = new List<HotKey>();
_hotKeyManager.KeyPressed += OnKeyPressed;
var keyBindings = settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
SetHotKeys(keyBindings);
}
public void Dispose()
{
Dispose(true);
}
public void SetHotKeys(IEnumerable<KeyBinding> keyBindings)
{
foreach (var hotKey in _hotKeys)
{
_dispatcher.Invoke(() =>
{
_hotKeyManager.Unregister(hotKey);
});
}
_hotKeys.Clear();
foreach (var keyBinding in keyBindings)
{
try
{
var key = Utilities.ExtendVirtualKeyToInputKey((ExtendedVirtualKey)keyBinding.Key);
var modifiers = ModifierKeys.None;
if (keyBinding.Alt)
{
modifiers |= ModifierKeys.Alt;
}
if (keyBinding.Ctrl)
{
modifiers |= ModifierKeys.Control;
}
if (keyBinding.Shift)
{
modifiers |= ModifierKeys.Shift;
}
if (keyBinding.Meta)
{
modifiers |= ModifierKeys.Windows;
}
var hotKey = new HotKey(key, modifiers);
_dispatcher.Invoke(() =>
{
_hotKeyManager.Register(hotKey);
});
_hotKeys.Add(hotKey);
}
catch (Exception)
{
_notificationService.ShowNotification("Error", "Failed to register the following hotkey: " + GetKeyBindingRepresentation(keyBinding));
}
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_hotKeyManager?.Dispose();
}
_disposedValue = true;
}
}
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
private static string GetKeyBindingRepresentation(KeyBinding keyBinding)
{
var keys = new List<string>();
if (keyBinding.Ctrl)
{
keys.Add("Ctrl");
}
if (keyBinding.Alt)
{
keys.Add("Alt");
}
if (keyBinding.Shift)
{
keys.Add("Shift");
}
keys.Add(((ExtendedVirtualKey)keyBinding.Key).ToString());
return string.Join(" + ", keys);
}
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private string GetActiveProcessFileName()
{
try
{
var hwnd = GetForegroundWindow();
GetWindowThreadProcessId(hwnd, out uint pid);
var process = Process.GetProcessById((int)pid);
return process.MainWindowTitle;
}
catch (Exception)
{
return null;
}
}
private async void OnKeyPressed(object sender, KeyPressedEventArgs e)
{
if (GetActiveProcessFileName().EndsWith("Fluent Terminal"))
{
var hwnd = GetForegroundWindow();
ShowWindow(hwnd, SW_MINIMIZE);
}
else
{
IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
await appListEntries.First().LaunchAsync();
}
}
}
}