2019-06-04 23:55:27 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
#include "AppKeyBindingsSerialization.h"
|
|
|
|
#include "KeyChordSerialization.h"
|
|
|
|
#include "Utils.h"
|
|
|
|
#include <winrt/Microsoft.Terminal.Settings.h>
|
|
|
|
|
|
|
|
using namespace winrt::Microsoft::Terminal::Settings;
|
|
|
|
using namespace winrt::TerminalApp;
|
|
|
|
|
|
|
|
static constexpr std::string_view KeysKey{ "keys" };
|
|
|
|
static constexpr std::string_view CommandKey{ "command" };
|
|
|
|
|
|
|
|
static constexpr std::string_view CopyTextKey{ "copy" };
|
2019-06-25 21:17:02 +02:00
|
|
|
static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" };
|
2019-06-04 23:55:27 +02:00
|
|
|
static constexpr std::string_view PasteTextKey{ "paste" };
|
|
|
|
static constexpr std::string_view NewTabKey{ "newTab" };
|
2019-08-16 23:29:13 +02:00
|
|
|
static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" };
|
2019-07-02 19:45:51 +02:00
|
|
|
static constexpr std::string_view DuplicateTabKey{ "duplicateTab" };
|
2019-06-04 23:55:27 +02:00
|
|
|
static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile2Key{ "newTabProfile2" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile3Key{ "newTabProfile3" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile4Key{ "newTabProfile4" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile5Key{ "newTabProfile5" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile6Key{ "newTabProfile6" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile7Key{ "newTabProfile7" };
|
|
|
|
static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" };
|
|
|
|
static constexpr std::string_view NewWindowKey{ "newWindow" };
|
|
|
|
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
|
|
|
|
static constexpr std::string_view CloseTabKey{ "closeTab" };
|
2019-07-19 02:23:40 +02:00
|
|
|
static constexpr std::string_view ClosePaneKey{ "closePane" };
|
2019-06-04 23:55:27 +02:00
|
|
|
static constexpr std::string_view SwitchtoTabKey{ "switchToTab" };
|
|
|
|
static constexpr std::string_view NextTabKey{ "nextTab" };
|
|
|
|
static constexpr std::string_view PrevTabKey{ "prevTab" };
|
|
|
|
static constexpr std::string_view IncreaseFontSizeKey{ "increaseFontSize" };
|
|
|
|
static constexpr std::string_view DecreaseFontSizeKey{ "decreaseFontSize" };
|
|
|
|
static constexpr std::string_view ScrollupKey{ "scrollUp" };
|
|
|
|
static constexpr std::string_view ScrolldownKey{ "scrollDown" };
|
|
|
|
static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" };
|
|
|
|
static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" };
|
|
|
|
static constexpr std::string_view SwitchToTab0Key{ "switchToTab0" };
|
|
|
|
static constexpr std::string_view SwitchToTab1Key{ "switchToTab1" };
|
|
|
|
static constexpr std::string_view SwitchToTab2Key{ "switchToTab2" };
|
|
|
|
static constexpr std::string_view SwitchToTab3Key{ "switchToTab3" };
|
|
|
|
static constexpr std::string_view SwitchToTab4Key{ "switchToTab4" };
|
|
|
|
static constexpr std::string_view SwitchToTab5Key{ "switchToTab5" };
|
|
|
|
static constexpr std::string_view SwitchToTab6Key{ "switchToTab6" };
|
|
|
|
static constexpr std::string_view SwitchToTab7Key{ "switchToTab7" };
|
|
|
|
static constexpr std::string_view SwitchToTab8Key{ "switchToTab8" };
|
|
|
|
static constexpr std::string_view OpenSettingsKey{ "openSettings" };
|
2019-06-10 18:38:35 +02:00
|
|
|
static constexpr std::string_view SplitHorizontalKey{ "splitHorizontal" };
|
|
|
|
static constexpr std::string_view SplitVerticalKey{ "splitVertical" };
|
Enable resizing the panes with the keyboard. (#1207)
Adds the ability to resize panes with the keyboard.
This is accomplished by making the Column/RowDefinitions for a Pane use `GridLengthHelper::FromPixels` to set their size. We store a pair of floats that represents the relative amount that each pane takes out of the parent pane. When the window is resized, we use that percentage to figure out the new size of each child in pixels, and manually size each column.
Then, when the user presses the keybindings for resizePane{Left/Right/Up/Down}, we'll adjust those percentages, and resize the rows/cols as appropriate.
Currently, each pane adjusts the width/height by 5% of the total size at a time. I am not in love with this, but it works for now. I think when we get support for keybindings with arbitrary arg blobs, then we could do either a percent movement, or a number of characters at a time. The number of characters one would be trickier, because we'd have to get the focused control, and get the number of pixels per character, as adjacent panes might not have the same font sizes.
2019-07-10 15:27:12 +02:00
|
|
|
static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" };
|
|
|
|
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" };
|
|
|
|
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" };
|
|
|
|
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" };
|
2019-07-17 16:30:15 +02:00
|
|
|
static constexpr std::string_view MoveFocusLeftKey{ "moveFocusLeft" };
|
|
|
|
static constexpr std::string_view MoveFocusRightKey{ "moveFocusRight" };
|
|
|
|
static constexpr std::string_view MoveFocusUpKey{ "moveFocusUp" };
|
|
|
|
static constexpr std::string_view MoveFocusDownKey{ "moveFocusDown" };
|
2019-06-04 23:55:27 +02:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
// HERE BE DRAGONS:
|
|
|
|
// These are string_views that are being used as keys. These string_views are
|
|
|
|
// just pointers to other strings. This could be dangerous, if the map outlived
|
|
|
|
// the actual strings being pointed to. However, since both these strings and
|
|
|
|
// the map are all const for the lifetime of the app, we have nothing to worry
|
|
|
|
// about here.
|
2019-06-11 22:27:09 +02:00
|
|
|
static const std::map<std::string_view, ShortcutAction, std::less<>> commandNames{
|
2019-06-04 23:55:27 +02:00
|
|
|
{ CopyTextKey, ShortcutAction::CopyText },
|
2019-06-25 21:17:02 +02:00
|
|
|
{ CopyTextWithoutNewlinesKey, ShortcutAction::CopyTextWithoutNewlines },
|
2019-06-04 23:55:27 +02:00
|
|
|
{ PasteTextKey, ShortcutAction::PasteText },
|
|
|
|
{ NewTabKey, ShortcutAction::NewTab },
|
2019-08-16 23:29:13 +02:00
|
|
|
{ OpenNewTabDropdownKey, ShortcutAction::OpenNewTabDropdown },
|
2019-07-02 19:45:51 +02:00
|
|
|
{ DuplicateTabKey, ShortcutAction::DuplicateTab },
|
2019-06-04 23:55:27 +02:00
|
|
|
{ NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 },
|
|
|
|
{ NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 },
|
|
|
|
{ NewTabWithProfile2Key, ShortcutAction::NewTabProfile2 },
|
|
|
|
{ NewTabWithProfile3Key, ShortcutAction::NewTabProfile3 },
|
|
|
|
{ NewTabWithProfile4Key, ShortcutAction::NewTabProfile4 },
|
|
|
|
{ NewTabWithProfile5Key, ShortcutAction::NewTabProfile5 },
|
|
|
|
{ NewTabWithProfile6Key, ShortcutAction::NewTabProfile6 },
|
|
|
|
{ NewTabWithProfile7Key, ShortcutAction::NewTabProfile7 },
|
|
|
|
{ NewTabWithProfile8Key, ShortcutAction::NewTabProfile8 },
|
|
|
|
{ NewWindowKey, ShortcutAction::NewWindow },
|
|
|
|
{ CloseWindowKey, ShortcutAction::CloseWindow },
|
|
|
|
{ CloseTabKey, ShortcutAction::CloseTab },
|
2019-07-19 02:23:40 +02:00
|
|
|
{ ClosePaneKey, ShortcutAction::ClosePane },
|
2019-06-04 23:55:27 +02:00
|
|
|
{ NextTabKey, ShortcutAction::NextTab },
|
|
|
|
{ PrevTabKey, ShortcutAction::PrevTab },
|
|
|
|
{ IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize },
|
|
|
|
{ DecreaseFontSizeKey, ShortcutAction::DecreaseFontSize },
|
|
|
|
{ ScrollupKey, ShortcutAction::ScrollUp },
|
|
|
|
{ ScrolldownKey, ShortcutAction::ScrollDown },
|
|
|
|
{ ScrolluppageKey, ShortcutAction::ScrollUpPage },
|
|
|
|
{ ScrolldownpageKey, ShortcutAction::ScrollDownPage },
|
|
|
|
{ SwitchToTab0Key, ShortcutAction::SwitchToTab0 },
|
|
|
|
{ SwitchToTab1Key, ShortcutAction::SwitchToTab1 },
|
|
|
|
{ SwitchToTab2Key, ShortcutAction::SwitchToTab2 },
|
|
|
|
{ SwitchToTab3Key, ShortcutAction::SwitchToTab3 },
|
|
|
|
{ SwitchToTab4Key, ShortcutAction::SwitchToTab4 },
|
|
|
|
{ SwitchToTab5Key, ShortcutAction::SwitchToTab5 },
|
|
|
|
{ SwitchToTab6Key, ShortcutAction::SwitchToTab6 },
|
|
|
|
{ SwitchToTab7Key, ShortcutAction::SwitchToTab7 },
|
|
|
|
{ SwitchToTab8Key, ShortcutAction::SwitchToTab8 },
|
2019-06-10 18:38:35 +02:00
|
|
|
{ SplitHorizontalKey, ShortcutAction::SplitHorizontal },
|
|
|
|
{ SplitVerticalKey, ShortcutAction::SplitVertical },
|
Enable resizing the panes with the keyboard. (#1207)
Adds the ability to resize panes with the keyboard.
This is accomplished by making the Column/RowDefinitions for a Pane use `GridLengthHelper::FromPixels` to set their size. We store a pair of floats that represents the relative amount that each pane takes out of the parent pane. When the window is resized, we use that percentage to figure out the new size of each child in pixels, and manually size each column.
Then, when the user presses the keybindings for resizePane{Left/Right/Up/Down}, we'll adjust those percentages, and resize the rows/cols as appropriate.
Currently, each pane adjusts the width/height by 5% of the total size at a time. I am not in love with this, but it works for now. I think when we get support for keybindings with arbitrary arg blobs, then we could do either a percent movement, or a number of characters at a time. The number of characters one would be trickier, because we'd have to get the focused control, and get the number of pixels per character, as adjacent panes might not have the same font sizes.
2019-07-10 15:27:12 +02:00
|
|
|
{ ResizePaneLeftKey, ShortcutAction::ResizePaneLeft },
|
|
|
|
{ ResizePaneRightKey, ShortcutAction::ResizePaneRight },
|
|
|
|
{ ResizePaneUpKey, ShortcutAction::ResizePaneUp },
|
|
|
|
{ ResizePaneDownKey, ShortcutAction::ResizePaneDown },
|
2019-07-17 16:30:15 +02:00
|
|
|
{ MoveFocusLeftKey, ShortcutAction::MoveFocusLeft },
|
|
|
|
{ MoveFocusRightKey, ShortcutAction::MoveFocusRight },
|
|
|
|
{ MoveFocusUpKey, ShortcutAction::MoveFocusUp },
|
|
|
|
{ MoveFocusDownKey, ShortcutAction::MoveFocusDown },
|
2019-06-12 14:21:44 +02:00
|
|
|
{ OpenSettingsKey, ShortcutAction::OpenSettings },
|
2019-06-04 23:55:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// Function Description:
|
|
|
|
// - Small helper to create a json value serialization of a single
|
|
|
|
// KeyBinding->Action maping. The created object is of schema:
|
|
|
|
// {
|
|
|
|
// keys:[String],
|
|
|
|
// command:String
|
|
|
|
// }
|
|
|
|
// Arguments:
|
|
|
|
// - chord: The KeyChord to serialize
|
|
|
|
// - actionName: the name of the ShortcutAction to use with this KeyChord
|
|
|
|
// Return Value:
|
|
|
|
// - a Json::Value which is an equivalent serialization of this object.
|
|
|
|
static Json::Value _ShortcutAsJsonObject(const KeyChord& chord,
|
|
|
|
const std::string_view actionName)
|
|
|
|
{
|
|
|
|
const auto keyString = KeyChordSerialization::ToString(chord);
|
|
|
|
if (keyString == L"")
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value jsonObject;
|
|
|
|
Json::Value keysArray;
|
|
|
|
keysArray.append(winrt::to_string(keyString));
|
|
|
|
|
|
|
|
jsonObject[JsonKey(KeysKey)] = keysArray;
|
|
|
|
jsonObject[JsonKey(CommandKey)] = actionName.data();
|
|
|
|
|
|
|
|
return 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 Json::Value which is an equivalent serialization of this object.
|
|
|
|
Json::Value AppKeyBindingsSerialization::ToJson(const winrt::TerminalApp::AppKeyBindings& bindings)
|
|
|
|
{
|
|
|
|
Json::Value 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;
|
|
|
|
|
|
|
|
if (const auto chord{ bindings.GetKeyBinding(searchedForAction) })
|
|
|
|
{
|
|
|
|
if (const auto serialization{ _ShortcutAsJsonObject(chord, searchedForName) })
|
|
|
|
{
|
|
|
|
bindingsArray.append(serialization);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bindingsArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
winrt::TerminalApp::AppKeyBindings AppKeyBindingsSerialization::FromJson(const Json::Value& json)
|
|
|
|
{
|
|
|
|
winrt::TerminalApp::AppKeyBindings newBindings{};
|
|
|
|
|
|
|
|
for (const auto& value : json)
|
|
|
|
{
|
|
|
|
if (value.isObject())
|
|
|
|
{
|
|
|
|
const auto commandString = value[JsonKey(CommandKey)];
|
|
|
|
const auto keys = value[JsonKey(KeysKey)];
|
|
|
|
|
|
|
|
if (commandString && keys)
|
|
|
|
{
|
|
|
|
if (!keys.isArray() || keys.size() != 1)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const auto keyChordString = winrt::to_hstring(keys[0].asString());
|
|
|
|
ShortcutAction action;
|
|
|
|
|
|
|
|
// Try matching the command to one we have
|
|
|
|
const auto found = commandNames.find(commandString.asString());
|
|
|
|
if (found != commandNames.end())
|
|
|
|
{
|
|
|
|
action = found->second;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try parsing the chord
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const auto chord = KeyChordSerialization::FromString(keyChordString);
|
|
|
|
newBindings.SetKeyBinding(action, chord);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newBindings;
|
|
|
|
}
|