Support remapping keybindings (#748)
* Add support for serializing keybindings
This commit is contained in:
parent
acabbe0459
commit
29e380824f
|
@ -405,7 +405,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void App::_ReloadSettings()
|
||||
{
|
||||
_settings = CascadiaSettings::LoadAll();
|
||||
_settings = CascadiaSettings::LoadAll(false);
|
||||
// Re-wire the keybindings to their handlers, as we'll have created a
|
||||
// new AppKeyBindings object.
|
||||
_HookupKeyBindings(_settings->GetKeybindings());
|
||||
|
|
|
@ -1,152 +1,347 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppKeyBindings.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void AppKeyBindings::SetKeyBinding(TerminalApp::ShortcutAction const& action,
|
||||
Settings::KeyChord const& chord)
|
||||
{
|
||||
_keyShortcuts[chord] = action;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::TryKeyChord(Settings::KeyChord const& kc)
|
||||
{
|
||||
const auto keyIter = _keyShortcuts.find(kc);
|
||||
if (keyIter != _keyShortcuts.end())
|
||||
{
|
||||
const auto action = keyIter->second;
|
||||
return _DoAction(action);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::_DoAction(ShortcutAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ShortcutAction::CopyText:
|
||||
_CopyTextHandlers();
|
||||
return true;
|
||||
case ShortcutAction::PasteText:
|
||||
_PasteTextHandlers();
|
||||
return true;
|
||||
case ShortcutAction::NewTab:
|
||||
_NewTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::OpenSettings:
|
||||
_OpenSettingsHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::NewTabProfile0:
|
||||
_NewTabWithProfileHandlers(0);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile1:
|
||||
_NewTabWithProfileHandlers(1);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile2:
|
||||
_NewTabWithProfileHandlers(2);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile3:
|
||||
_NewTabWithProfileHandlers(3);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile4:
|
||||
_NewTabWithProfileHandlers(4);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile5:
|
||||
_NewTabWithProfileHandlers(5);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile6:
|
||||
_NewTabWithProfileHandlers(6);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile7:
|
||||
_NewTabWithProfileHandlers(7);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile8:
|
||||
_NewTabWithProfileHandlers(8);
|
||||
return true;
|
||||
|
||||
case ShortcutAction::NewWindow:
|
||||
_NewWindowHandlers();
|
||||
return true;
|
||||
case ShortcutAction::CloseWindow:
|
||||
_CloseWindowHandlers();
|
||||
return true;
|
||||
case ShortcutAction::CloseTab:
|
||||
_CloseTabHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::ScrollUp:
|
||||
_ScrollUpHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollDown:
|
||||
_ScrollDownHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollUpPage:
|
||||
_ScrollUpPageHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollDownPage:
|
||||
_ScrollDownPageHandlers();
|
||||
return true;
|
||||
case ShortcutAction::NextTab:
|
||||
_NextTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::PrevTab:
|
||||
_PrevTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
_SwitchToTabHandlers(0);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab1:
|
||||
_SwitchToTabHandlers(1);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab2:
|
||||
_SwitchToTabHandlers(2);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab3:
|
||||
_SwitchToTabHandlers(3);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab4:
|
||||
_SwitchToTabHandlers(4);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab5:
|
||||
_SwitchToTabHandlers(5);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab6:
|
||||
_SwitchToTabHandlers(6);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab7:
|
||||
_SwitchToTabHandlers(7);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab8:
|
||||
_SwitchToTabHandlers(8);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------- Events ---------------------------------
|
||||
DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
|
||||
|
||||
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Windows::Data::Json;
|
||||
|
||||
static constexpr std::wstring_view KEYS_KEY{ L"keys" };
|
||||
static constexpr std::wstring_view COMMAND_KEY{ L"command" };
|
||||
|
||||
static constexpr std::wstring_view COPYTEXT_KEY{ L"copy" };
|
||||
static constexpr std::wstring_view PASTETEXT_KEY{ L"paste" };
|
||||
static constexpr std::wstring_view NEWTAB_KEY{ L"newTab" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE0_KEY{ L"newTabProfile0" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE1_KEY{ L"newTabProfile1" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE2_KEY{ L"newTabProfile2" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE3_KEY{ L"newTabProfile3" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE4_KEY{ L"newTabProfile4" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE5_KEY{ L"newTabProfile5" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE6_KEY{ L"newTabProfile6" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE7_KEY{ L"newTabProfile7" };
|
||||
static constexpr std::wstring_view NEWTABWITHPROFILE8_KEY{ L"newTabProfile8" };
|
||||
static constexpr std::wstring_view NEWWINDOW_KEY{ L"newWindow" };
|
||||
static constexpr std::wstring_view CLOSEWINDOW_KEY{ L"closeWindow" };
|
||||
static constexpr std::wstring_view CLOSETAB_KEY{ L"closeTab" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB_KEY{ L"switchToTab" };
|
||||
static constexpr std::wstring_view NEXTTAB_KEY{ L"nextTab" };
|
||||
static constexpr std::wstring_view PREVTAB_KEY{ L"prevTab" };
|
||||
static constexpr std::wstring_view INCREASEFONTSIZE_KEY{ L"increaseFontSize" };
|
||||
static constexpr std::wstring_view DECREASEFONTSIZE_KEY{ L"decreaseFontSize" };
|
||||
static constexpr std::wstring_view SCROLLUP_KEY{ L"scrollUp" };
|
||||
static constexpr std::wstring_view SCROLLDOWN_KEY{ L"scrollDown" };
|
||||
static constexpr std::wstring_view SCROLLUPPAGE_KEY{ L"scrollUpPage" };
|
||||
static constexpr std::wstring_view SCROLLDOWNPAGE_KEY{ L"scrollDownPage" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB0_KEY{ L"switchToTab0" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB1_KEY{ L"switchToTab1" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB2_KEY{ L"switchToTab2" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB3_KEY{ L"switchToTab3" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB4_KEY{ L"switchToTab4" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB5_KEY{ L"switchToTab5" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB6_KEY{ L"switchToTab6" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB7_KEY{ L"switchToTab7" };
|
||||
static constexpr std::wstring_view SWITCHTOTAB8_KEY{ L"switchToTab8" };
|
||||
static constexpr std::wstring_view OPENSETTINGS_KEY{ L"openSettings" };
|
||||
|
||||
// Specifically use a map here over an unordered_map. We want to be able to
|
||||
// iterate over these entries in-order when we're serializing the keybindings.
|
||||
static const std::map<std::wstring_view, ShortcutAction> commandNames {
|
||||
{ COPYTEXT_KEY, ShortcutAction::CopyText },
|
||||
{ PASTETEXT_KEY, ShortcutAction::PasteText },
|
||||
{ NEWTAB_KEY, ShortcutAction::NewTab },
|
||||
{ NEWTABWITHPROFILE0_KEY, ShortcutAction::NewTabProfile0 },
|
||||
{ NEWTABWITHPROFILE1_KEY, ShortcutAction::NewTabProfile1 },
|
||||
{ NEWTABWITHPROFILE2_KEY, ShortcutAction::NewTabProfile2 },
|
||||
{ NEWTABWITHPROFILE3_KEY, ShortcutAction::NewTabProfile3 },
|
||||
{ NEWTABWITHPROFILE4_KEY, ShortcutAction::NewTabProfile4 },
|
||||
{ NEWTABWITHPROFILE5_KEY, ShortcutAction::NewTabProfile5 },
|
||||
{ NEWTABWITHPROFILE6_KEY, ShortcutAction::NewTabProfile6 },
|
||||
{ NEWTABWITHPROFILE7_KEY, ShortcutAction::NewTabProfile7 },
|
||||
{ NEWTABWITHPROFILE8_KEY, ShortcutAction::NewTabProfile8 },
|
||||
{ NEWWINDOW_KEY, ShortcutAction::NewWindow },
|
||||
{ CLOSEWINDOW_KEY, ShortcutAction::CloseWindow },
|
||||
{ CLOSETAB_KEY, ShortcutAction::CloseTab },
|
||||
{ NEXTTAB_KEY, ShortcutAction::NextTab },
|
||||
{ PREVTAB_KEY, ShortcutAction::PrevTab },
|
||||
{ INCREASEFONTSIZE_KEY, ShortcutAction::IncreaseFontSize },
|
||||
{ DECREASEFONTSIZE_KEY, ShortcutAction::DecreaseFontSize },
|
||||
{ SCROLLUP_KEY, ShortcutAction::ScrollUp },
|
||||
{ SCROLLDOWN_KEY, ShortcutAction::ScrollDown },
|
||||
{ SCROLLUPPAGE_KEY, ShortcutAction::ScrollUpPage },
|
||||
{ SCROLLDOWNPAGE_KEY, ShortcutAction::ScrollDownPage },
|
||||
{ SWITCHTOTAB0_KEY, ShortcutAction::SwitchToTab0 },
|
||||
{ SWITCHTOTAB1_KEY, ShortcutAction::SwitchToTab1 },
|
||||
{ SWITCHTOTAB2_KEY, ShortcutAction::SwitchToTab2 },
|
||||
{ SWITCHTOTAB3_KEY, ShortcutAction::SwitchToTab3 },
|
||||
{ SWITCHTOTAB4_KEY, ShortcutAction::SwitchToTab4 },
|
||||
{ SWITCHTOTAB5_KEY, ShortcutAction::SwitchToTab5 },
|
||||
{ SWITCHTOTAB6_KEY, ShortcutAction::SwitchToTab6 },
|
||||
{ SWITCHTOTAB7_KEY, ShortcutAction::SwitchToTab7 },
|
||||
{ SWITCHTOTAB8_KEY, ShortcutAction::SwitchToTab8 },
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
void AppKeyBindings::SetKeyBinding(const TerminalApp::ShortcutAction& action,
|
||||
const Settings::KeyChord& chord)
|
||||
{
|
||||
_keyShortcuts[chord] = action;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::TryKeyChord(const Settings::KeyChord& kc)
|
||||
{
|
||||
const auto keyIter = _keyShortcuts.find(kc);
|
||||
if (keyIter != _keyShortcuts.end())
|
||||
{
|
||||
const auto action = keyIter->second;
|
||||
return _DoAction(action);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AppKeyBindings::_DoAction(ShortcutAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ShortcutAction::CopyText:
|
||||
_CopyTextHandlers();
|
||||
return true;
|
||||
case ShortcutAction::PasteText:
|
||||
_PasteTextHandlers();
|
||||
return true;
|
||||
case ShortcutAction::NewTab:
|
||||
_NewTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::OpenSettings:
|
||||
_OpenSettingsHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::NewTabProfile0:
|
||||
_NewTabWithProfileHandlers(0);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile1:
|
||||
_NewTabWithProfileHandlers(1);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile2:
|
||||
_NewTabWithProfileHandlers(2);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile3:
|
||||
_NewTabWithProfileHandlers(3);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile4:
|
||||
_NewTabWithProfileHandlers(4);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile5:
|
||||
_NewTabWithProfileHandlers(5);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile6:
|
||||
_NewTabWithProfileHandlers(6);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile7:
|
||||
_NewTabWithProfileHandlers(7);
|
||||
return true;
|
||||
case ShortcutAction::NewTabProfile8:
|
||||
_NewTabWithProfileHandlers(8);
|
||||
return true;
|
||||
|
||||
case ShortcutAction::NewWindow:
|
||||
_NewWindowHandlers();
|
||||
return true;
|
||||
case ShortcutAction::CloseWindow:
|
||||
_CloseWindowHandlers();
|
||||
return true;
|
||||
case ShortcutAction::CloseTab:
|
||||
_CloseTabHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::ScrollUp:
|
||||
_ScrollUpHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollDown:
|
||||
_ScrollDownHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollUpPage:
|
||||
_ScrollUpPageHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ScrollDownPage:
|
||||
_ScrollDownPageHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::NextTab:
|
||||
_NextTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::PrevTab:
|
||||
_PrevTabHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::SwitchToTab0:
|
||||
_SwitchToTabHandlers(0);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab1:
|
||||
_SwitchToTabHandlers(1);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab2:
|
||||
_SwitchToTabHandlers(2);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab3:
|
||||
_SwitchToTabHandlers(3);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab4:
|
||||
_SwitchToTabHandlers(4);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab5:
|
||||
_SwitchToTabHandlers(5);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab6:
|
||||
_SwitchToTabHandlers(6);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab7:
|
||||
_SwitchToTabHandlers(7);
|
||||
return true;
|
||||
case ShortcutAction::SwitchToTab8:
|
||||
_SwitchToTabHandlers(8);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------- Events ---------------------------------
|
||||
DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
|
||||
|
||||
// Method Description:
|
||||
// - Deserialize an AppKeyBindings from the key mappings that are in the
|
||||
// array `json`. The json array should contain an array of objects with
|
||||
// both a `command` string and a `keys` array, where `command` is one of
|
||||
// the names listed in `commandNames`, and `keys` is an array of
|
||||
// keypresses. Currently, the array should contain a single string, which
|
||||
// can be deserialized into a KeyChord.
|
||||
// Arguments:
|
||||
// - json: and array of JsonObject's to deserialize into our _keyShortcuts mapping.
|
||||
// Return Value:
|
||||
// - the newly constructed AppKeyBindings object.
|
||||
TerminalApp::AppKeyBindings AppKeyBindings::FromJson(const JsonArray& json)
|
||||
{
|
||||
TerminalApp::AppKeyBindings newBindings{};
|
||||
|
||||
for (const auto& value : json)
|
||||
{
|
||||
if (value.ValueType() == JsonValueType::Object)
|
||||
{
|
||||
JsonObject obj = value.GetObjectW();
|
||||
if (obj.HasKey(COMMAND_KEY) && obj.HasKey(KEYS_KEY))
|
||||
{
|
||||
const auto commandString = obj.GetNamedString(COMMAND_KEY);
|
||||
const auto keys = obj.GetNamedArray(KEYS_KEY);
|
||||
if (keys.Size() != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto keyChordString = keys.GetAt(0).GetString();
|
||||
ShortcutAction action;
|
||||
|
||||
// Try matching the command to one we have
|
||||
auto found = commandNames.find(commandString);
|
||||
if (found != commandNames.end())
|
||||
{
|
||||
action = found->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try parsing the chord
|
||||
try
|
||||
{
|
||||
auto chord = KeyChordSerialization::FromString(keyChordString);
|
||||
newBindings.SetKeyBinding(action, chord);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return newBindings;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Small helper to insert a given KeyChord, ShortcutAction pair into the
|
||||
// given json array
|
||||
// Arguments:
|
||||
// - bindingsArray: The JsonArray to insert the object into.
|
||||
// - chord: The KeyChord to serailize and place in the json array
|
||||
// - actionName: the name of the ShortcutAction to use with this KeyChord
|
||||
static void _AddShortcutToJsonArray(const JsonArray& bindingsArray,
|
||||
const Settings::KeyChord& chord,
|
||||
const std::wstring_view& actionName)
|
||||
{
|
||||
const auto keyString = KeyChordSerialization::ToString(chord);
|
||||
if (keyString == L"")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
winrt::Windows::Data::Json::JsonObject jsonObject;
|
||||
winrt::Windows::Data::Json::JsonArray keysArray;
|
||||
keysArray.Append(JsonValue::CreateStringValue(keyString));
|
||||
jsonObject.Insert(KEYS_KEY, keysArray);
|
||||
jsonObject.Insert(COMMAND_KEY, JsonValue::CreateStringValue(actionName));
|
||||
|
||||
bindingsArray.Append(jsonObject);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Serialize this AppKeyBindings to a json array of objects. Each object
|
||||
// in the array represents a single keybinding, mapping a KeyChord to a
|
||||
// ShortcutAction.
|
||||
// Return Value:
|
||||
// - a JsonArray which is an equivalent serialization of this object.
|
||||
Windows::Data::Json::JsonArray AppKeyBindings::ToJson()
|
||||
{
|
||||
winrt::Windows::Data::Json::JsonArray bindingsArray;
|
||||
|
||||
// Iterate over all the possible actions in the names list, and see if
|
||||
// it has a binding.
|
||||
for (auto& actionName : commandNames)
|
||||
{
|
||||
const auto searchedForName = actionName.first;
|
||||
const auto searchedForAction = actionName.second;
|
||||
for (const auto& kv : _keyShortcuts)
|
||||
{
|
||||
const auto chord = kv.first;
|
||||
const auto command = kv.second;
|
||||
if (command == searchedForAction)
|
||||
{
|
||||
_AddShortcutToJsonArray(bindingsArray, chord, searchedForName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bindingsArray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
AppKeyBindings() = default;
|
||||
|
||||
static TerminalApp::AppKeyBindings FromJson(Windows::Data::Json::JsonArray const& json);
|
||||
Windows::Data::Json::JsonArray ToJson();
|
||||
|
||||
bool TryKeyChord(winrt::Microsoft::Terminal::Settings::KeyChord const& kc);
|
||||
void SetKeyBinding(TerminalApp::ShortcutAction const& action, winrt::Microsoft::Terminal::Settings::KeyChord const& chord);
|
||||
|
||||
|
|
|
@ -63,6 +63,9 @@ namespace TerminalApp
|
|||
{
|
||||
AppKeyBindings();
|
||||
|
||||
Windows.Data.Json.JsonArray ToJson();
|
||||
static AppKeyBindings FromJson(Windows.Data.Json.JsonArray json);
|
||||
|
||||
void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord);
|
||||
|
||||
event CopyTextEventArgs CopyText;
|
||||
|
|
|
@ -137,6 +137,9 @@ JsonObject CascadiaSettings::ToJson() const
|
|||
jsonObject.Insert(PROFILES_KEY, profilesArray);
|
||||
jsonObject.Insert(SCHEMES_KEY, schemesArray);
|
||||
|
||||
jsonObject.Insert(KEYBINDINGS_KEY,
|
||||
_globals.GetKeybindings().ToJson());
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
|
@ -190,9 +193,18 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::FromJson(JsonObject json)
|
|||
}
|
||||
}
|
||||
|
||||
// TODO:MSFT:20700157
|
||||
// Load the keybindings from the file as well
|
||||
resultPtr->_CreateDefaultKeybindings();
|
||||
if (json.HasKey(KEYBINDINGS_KEY))
|
||||
{
|
||||
const auto keybindingsObj = json.GetNamedArray(KEYBINDINGS_KEY);
|
||||
auto loadedBindings = AppKeyBindings::FromJson(keybindingsObj);
|
||||
resultPtr->_globals.SetKeybindings(loadedBindings);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create the default keybindings if we couldn't find any keybindings.
|
||||
resultPtr->_CreateDefaultKeybindings();
|
||||
}
|
||||
|
||||
return resultPtr;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,11 @@ AppKeyBindings GlobalAppSettings::GetKeybindings() const noexcept
|
|||
return _keybindings;
|
||||
}
|
||||
|
||||
void GlobalAppSettings::SetKeybindings(winrt::TerminalApp::AppKeyBindings newBindings) noexcept
|
||||
{
|
||||
_keybindings = newBindings;
|
||||
}
|
||||
|
||||
bool GlobalAppSettings::GetAlwaysShowTabs() const noexcept
|
||||
{
|
||||
return _alwaysShowTabs;
|
||||
|
@ -154,6 +159,9 @@ JsonObject GlobalAppSettings::ToJson() const
|
|||
jsonObject.Insert(REQUESTED_THEME_KEY,
|
||||
JsonValue::CreateStringValue(_SerializeTheme(_requestedTheme)));
|
||||
|
||||
// We'll add the keybindings later in CascadiaSettings, because if we do it
|
||||
// here, they'll appear before the profiles.
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
GUID GetDefaultProfile() const noexcept;
|
||||
|
||||
winrt::TerminalApp::AppKeyBindings GetKeybindings() const noexcept;
|
||||
void SetKeybindings(winrt::TerminalApp::AppKeyBindings newBindings) noexcept;
|
||||
|
||||
bool GetAlwaysShowTabs() const noexcept;
|
||||
void SetAlwaysShowTabs(const bool showTabs) noexcept;
|
||||
|
|
249
src/cascadia/TerminalApp/KeyChordSerialization.cpp
Normal file
249
src/cascadia/TerminalApp/KeyChordSerialization.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
|
||||
static constexpr std::wstring_view CTRL_KEY{ L"ctrl" };
|
||||
static constexpr std::wstring_view SHIFT_KEY{ L"shift" };
|
||||
static constexpr std::wstring_view ALT_KEY{ L"alt" };
|
||||
|
||||
static constexpr int MAX_CHORD_PARTS = 4;
|
||||
|
||||
static const std::unordered_map<int32_t, std::wstring_view> vkeyNamePairs {
|
||||
{ VK_BACK , L"backspace"},
|
||||
{ VK_TAB , L"tab"},
|
||||
{ VK_RETURN , L"enter" },
|
||||
{ VK_ESCAPE , L"esc" },
|
||||
{ VK_SPACE , L"space" },
|
||||
{ VK_PRIOR , L"pgup" },
|
||||
{ VK_NEXT , L"pgdn" },
|
||||
{ VK_END , L"end" },
|
||||
{ VK_HOME , L"home" },
|
||||
{ VK_LEFT , L"left" },
|
||||
{ VK_UP , L"up" },
|
||||
{ VK_RIGHT , L"right" },
|
||||
{ VK_DOWN , L"down" },
|
||||
{ VK_INSERT , L"insert" },
|
||||
{ VK_DELETE , L"delete" },
|
||||
{ VK_NUMPAD0 , L"numpad_0" },
|
||||
{ VK_NUMPAD1 , L"numpad_1" },
|
||||
{ VK_NUMPAD2 , L"numpad_2" },
|
||||
{ VK_NUMPAD3 , L"numpad_3" },
|
||||
{ VK_NUMPAD4 , L"numpad_4" },
|
||||
{ VK_NUMPAD5 , L"numpad_5" },
|
||||
{ VK_NUMPAD6 , L"numpad_6" },
|
||||
{ VK_NUMPAD7 , L"numpad_7" },
|
||||
{ VK_NUMPAD8 , L"numpad_8" },
|
||||
{ VK_NUMPAD9 , L"numpad_9" },
|
||||
{ VK_MULTIPLY , L"numpad_multiply" },
|
||||
{ VK_ADD , L"numpad_plus" },
|
||||
{ VK_SUBTRACT , L"numpad_minus" },
|
||||
{ VK_DECIMAL , L"numpad_period" },
|
||||
{ VK_DIVIDE , L"numpad_divide" },
|
||||
{ VK_F1 , L"f1" },
|
||||
{ VK_F2 , L"f2" },
|
||||
{ VK_F3 , L"f3" },
|
||||
{ VK_F4 , L"f4" },
|
||||
{ VK_F5 , L"f5" },
|
||||
{ VK_F6 , L"f6" },
|
||||
{ VK_F7 , L"f7" },
|
||||
{ VK_F8 , L"f8" },
|
||||
{ VK_F9 , L"f9" },
|
||||
{ VK_F10 , L"f10" },
|
||||
{ VK_F11 , L"f11" },
|
||||
{ VK_F12 , L"f12" },
|
||||
{ VK_F13 , L"f13" },
|
||||
{ VK_F14 , L"f14" },
|
||||
{ VK_F15 , L"f15" },
|
||||
{ VK_F16 , L"f16" },
|
||||
{ VK_F17 , L"f17" },
|
||||
{ VK_F18 , L"f18" },
|
||||
{ VK_F19 , L"f19" },
|
||||
{ VK_F20 , L"f20" },
|
||||
{ VK_F21 , L"f21" },
|
||||
{ VK_F22 , L"f22" },
|
||||
{ VK_F23 , L"f23" },
|
||||
{ VK_F24 , L"f24" },
|
||||
{ VK_OEM_PLUS , L"plus" },
|
||||
{ VK_OEM_COMMA , L"," },
|
||||
{ VK_OEM_MINUS , L"-" },
|
||||
{ VK_OEM_PERIOD , L"." }
|
||||
// TODO:
|
||||
// These all look like they'd be good keybindings, but change based on keyboard
|
||||
// layout. How do we deal with that?
|
||||
// #define VK_OEM_NEC_EQUAL 0x92 // '=' key on numpad
|
||||
// #define VK_OEM_1 0xBA // ';:' for US
|
||||
// #define VK_OEM_2 0xBF // '/?' for US
|
||||
// #define VK_OEM_3 0xC0 // '`~' for US
|
||||
// #define VK_OEM_4 0xDB // '[{' for US
|
||||
// #define VK_OEM_5 0xDC // '\|' for US
|
||||
// #define VK_OEM_6 0xDD // ']}' for US
|
||||
// #define VK_OEM_7 0xDE // ''"' for US
|
||||
};
|
||||
|
||||
// Function Description:
|
||||
// - Deserializes the given string into a new KeyChord instance. If this
|
||||
// fails to translate the string into a keychord, it will throw a
|
||||
// hresult_invalid_argument exception.
|
||||
// - The string should fit the format "[ctrl+][alt+][shift+]<keyName>",
|
||||
// where each modifier is optional, and keyName is either one of the
|
||||
// names listed in the vkeyNamePairs vector above, or is one of 0-9a-zA-Z.
|
||||
// Arguments:
|
||||
// - hstr: the string to parse into a keychord.
|
||||
// Return Value:
|
||||
// - a newly constructed KeyChord
|
||||
winrt::Microsoft::Terminal::Settings::KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
|
||||
{
|
||||
std::wstring wstr{ hstr };
|
||||
|
||||
// Split the string on '+'
|
||||
std::wstring temp;
|
||||
std::vector<std::wstring> parts;
|
||||
std::wstringstream wss(wstr);
|
||||
|
||||
while(std::getline(wss, temp, L'+'))
|
||||
{
|
||||
parts.push_back(temp);
|
||||
|
||||
// If we have > 4, something's wrong.
|
||||
if (parts.size() > MAX_CHORD_PARTS)
|
||||
{
|
||||
throw winrt::hresult_invalid_argument();
|
||||
}
|
||||
}
|
||||
|
||||
KeyModifiers modifiers = KeyModifiers::None;
|
||||
int32_t vkey = 0;
|
||||
|
||||
// Look for ctrl, shift, alt. Anything else might be a key
|
||||
for (const auto& part : parts)
|
||||
{
|
||||
std::wstring lowercase = part;
|
||||
std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), std::towlower);
|
||||
if (lowercase == CTRL_KEY)
|
||||
{
|
||||
modifiers |= KeyModifiers::Ctrl;
|
||||
}
|
||||
else if (lowercase == ALT_KEY)
|
||||
{
|
||||
modifiers |= KeyModifiers::Alt;
|
||||
}
|
||||
else if (lowercase == SHIFT_KEY)
|
||||
{
|
||||
modifiers |= KeyModifiers::Shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool foundKey = false;
|
||||
// For potential keys, look through the pairs of strings and vkeys
|
||||
if (part.size() == 1)
|
||||
{
|
||||
const wchar_t wch = part.at(0);
|
||||
// Quick lookup: ranges of vkeys that correlate directly to a key.
|
||||
if (wch >= L'0' && wch <= L'9')
|
||||
{
|
||||
vkey = static_cast<int32_t>(wch);
|
||||
foundKey = true;
|
||||
}
|
||||
else if (wch >= L'a' && wch <= L'z')
|
||||
{
|
||||
// subtract 0x20 to shift to uppercase
|
||||
vkey = static_cast<int32_t>(wch - 0x20);
|
||||
foundKey = true;
|
||||
}
|
||||
else if (wch >= L'A' && wch <= L'Z')
|
||||
{
|
||||
vkey = static_cast<int32_t>(wch);
|
||||
foundKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find the key with a quick lookup, search the
|
||||
// table to see if we have a matching name.
|
||||
if (!foundKey)
|
||||
{
|
||||
for (const auto& pair : vkeyNamePairs)
|
||||
{
|
||||
if (pair.second == part)
|
||||
{
|
||||
vkey = pair.first;
|
||||
foundKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we weren't able to find a match, throw an exception.
|
||||
if (!foundKey)
|
||||
{
|
||||
throw winrt::hresult_invalid_argument();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return winrt::Microsoft::Terminal::Settings::KeyChord{ modifiers, vkey };
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Serialize this keychord into a string represenation.
|
||||
// - The string will fit the format "[ctrl+][alt+][shift+]<keyName>",
|
||||
// where each modifier is optional, and keyName is either one of the
|
||||
// names listed in the vkeyNamePairs vector above, or is one of 0-9a-z.
|
||||
// Return Value:
|
||||
// - a string which is an equivalent serialization of this object.
|
||||
winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
|
||||
{
|
||||
bool serializedSuccessfully = false;
|
||||
const auto modifiers = chord.Modifiers();
|
||||
const auto vkey = chord.Vkey();
|
||||
|
||||
std::wstring buffer{ L"" };
|
||||
|
||||
// Add modifiers
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
|
||||
{
|
||||
buffer += CTRL_KEY;
|
||||
buffer += L"+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
|
||||
{
|
||||
buffer += ALT_KEY;
|
||||
buffer += L"+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
|
||||
{
|
||||
buffer += SHIFT_KEY;
|
||||
buffer += L"+";
|
||||
}
|
||||
|
||||
// Quick lookup: ranges of vkeys that correlate directly to a key.
|
||||
if (vkey >= L'0' && vkey <= L'9')
|
||||
{
|
||||
buffer += std::wstring(1, static_cast<wchar_t>(vkey));
|
||||
serializedSuccessfully = true;
|
||||
}
|
||||
else if (vkey >= L'A' && vkey <= L'Z')
|
||||
{
|
||||
// add 0x20 to shift to lowercase
|
||||
buffer += std::wstring(1, static_cast<wchar_t>(vkey + 0x20));
|
||||
serializedSuccessfully = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vkeyNamePairs.find(vkey) != vkeyNamePairs.end())
|
||||
{
|
||||
buffer += vkeyNamePairs.at(vkey);
|
||||
serializedSuccessfully = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!serializedSuccessfully)
|
||||
{
|
||||
buffer = L"";
|
||||
}
|
||||
|
||||
return winrt::hstring{ buffer };
|
||||
}
|
12
src/cascadia/TerminalApp/KeyChordSerialization.h
Normal file
12
src/cascadia/TerminalApp/KeyChordSerialization.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include <winrt/Microsoft.Terminal.Settings.h>
|
||||
|
||||
class KeyChordSerialization final
|
||||
{
|
||||
public:
|
||||
static winrt::Microsoft::Terminal::Settings::KeyChord FromString(const winrt::hstring& str);
|
||||
static winrt::hstring ToString(const winrt::Microsoft::Terminal::Settings::KeyChord& chord);
|
||||
};
|
|
@ -41,6 +41,7 @@
|
|||
<ClInclude Include="GlobalAppSettings.h" />
|
||||
<ClInclude Include="Profile.h" />
|
||||
<ClInclude Include="CascadiaSettings.h" />
|
||||
<ClInclude Include="KeyChordSerialization.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="AppKeyBindings.h">
|
||||
<DependentUpon>AppKeyBindings.idl</DependentUpon>
|
||||
|
@ -58,6 +59,7 @@
|
|||
<ClCompile Include="Profile.cpp" />
|
||||
<ClCompile Include="CascadiaSettings.cpp" />
|
||||
<ClCompile Include="CascadiaSettingsSerialization.cpp" />
|
||||
<ClCompile Include="KeyChordSerialization.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
@ -128,4 +130,4 @@
|
|||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.1.190405001-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.1.190405001-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in a new issue