terminal/src/cascadia/TerminalSettingsEditor/KeyChordListener.cpp
Leonard Hecker 10b12ac90c
Introduce vk() and sc() key chord specifiers (#10666)
This commit introduces an alternative to specifying key bindings as a combination of key modifiers and a character. It allows you to specify an explicit virtual key as `vk(nnn)`.
Additionally this commit makes it possible to bind actions to scan codes. As scan code 41 appears to be the button below the Escape key on virtually all keyboards, we'll be able to bind the quake mode hotkey to `win+sc(41)` and have it work consistently across most if not all keyboard layouts.

## PR Checklist
* [x] Closes #7539, Closes #10203
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

The following was tested both on US and DE keyboard layouts:
* Ctrl+, opens settings ✔️
* Win+` opens quake mode window ✔️
* Ctrl+plus/minus increase/decrease font size ✔️
2021-07-20 22:34:51 +00:00

138 lines
4.8 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "KeyChordListener.h"
#include "KeyChordListener.g.cpp"
#include "LibraryResources.h"
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Data;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Xaml::Input;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
DependencyProperty KeyChordListener::_KeysProperty{ nullptr };
// The ModifierKeys have been sorted by value.
// Not just binary search, but also your CPU likes sorted data.
static constexpr std::array ModifierKeys{
VirtualKey::Shift,
VirtualKey::Control,
VirtualKey::Menu,
VirtualKey::LeftWindows,
VirtualKey::RightWindows,
VirtualKey::LeftShift,
VirtualKey::LeftControl,
VirtualKey::RightControl,
VirtualKey::LeftMenu,
VirtualKey::RightMenu
};
static VirtualKeyModifiers _GetModifiers()
{
const auto window{ CoreWindow::GetForCurrentThread() };
VirtualKeyModifiers flags = VirtualKeyModifiers::None;
for (const auto mod : ModifierKeys)
{
const auto state = window.GetKeyState(mod);
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
if (isDown)
{
switch (mod)
{
case VirtualKey::Control:
case VirtualKey::LeftControl:
case VirtualKey::RightControl:
flags |= VirtualKeyModifiers::Control;
break;
case VirtualKey::Menu:
case VirtualKey::LeftMenu:
case VirtualKey::RightMenu:
flags |= VirtualKeyModifiers::Menu;
break;
case VirtualKey::Shift:
case VirtualKey::LeftShift:
flags |= VirtualKeyModifiers::Shift;
break;
case VirtualKey::LeftWindows:
case VirtualKey::RightWindows:
flags |= VirtualKeyModifiers::Windows;
break;
}
}
}
return flags;
}
KeyChordListener::KeyChordListener()
{
InitializeComponent();
_InitializeProperties();
}
void KeyChordListener::_InitializeProperties()
{
// Initialize any KeyChordListener dependency properties here.
// This performs a lazy load on these properties, instead of
// initializing them when the DLL loads.
if (!_KeysProperty)
{
_KeysProperty =
DependencyProperty::Register(
L"Keys",
xaml_typename<Control::KeyChord>(),
xaml_typename<Editor::KeyChordListener>(),
PropertyMetadata{ nullptr, PropertyChangedCallback{ &KeyChordListener::_OnKeysChanged } });
}
}
void KeyChordListener::_OnKeysChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e)
{
if (auto control{ d.try_as<Editor::KeyChordListener>() })
{
auto controlImpl{ get_self<KeyChordListener>(control) };
TextBox tb{ controlImpl->FindName(L"KeyChordTextBox").as<TextBox>() };
tb.Text(Model::KeyChordSerialization::ToString(unbox_value<Control::KeyChord>(e.NewValue())));
if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(tb) })
{
automationPeer.RaiseNotificationEvent(
Automation::Peers::AutomationNotificationKind::ActionCompleted,
Automation::Peers::AutomationNotificationProcessing::MostRecent,
tb.Text(),
L"KeyChordListenerText");
}
}
}
void KeyChordListener::KeyChordTextBox_KeyDown(IInspectable const& /*sender*/, KeyRoutedEventArgs const& e)
{
const auto key{ e.OriginalKey() };
for (const auto mod : ModifierKeys)
{
if (key == mod)
{
// Ignore modifier keys
return;
}
}
const auto modifiers{ _GetModifiers() };
if (key == VirtualKey::Tab && (modifiers == VirtualKeyModifiers::None || modifiers == VirtualKeyModifiers::Shift))
{
// [Shift]+[Tab] && [Tab] are needed for keyboard navigation
return;
}
// Permitted key events are used to update _Keys
Keys({ modifiers, static_cast<int32_t>(key), 0 });
e.Handled(true);
}
}