terminal/src/cascadia/TerminalApp/AppKeyBindings.cpp
Mike Griese dcc2799457
Add support for iterable, nested commands (#6856)
## Summary of the Pull Request

This PR adds support for both _nested_ and _iterable_ commands in the Command palette.
![nested-commands-000](https://user-images.githubusercontent.com/18356694/87072916-2d991c00-c1e2-11ea-8917-a70e8b8b9803.gif)

* **Nested commands**: These are commands that include additional sub-commands. When the user selects on of these, the palette will update to only show the nested commands.
* **Iterable commands**: These are commands what allow the user to define only a single command, which is repeated once for every profile. (in the future, also repeated for color schemes, themes, etc.)

The above gif uses the following json:

```json
        {
            "name": "Split Pane...",
            "commands": [
                {
                    "iterateOn": "profiles",
                    "name": "Split with ${profile.name}...",
                    "commands": [
                        { "command": { "action": "splitPane", "profile": "${profile.name}", "split": "automatic" } },
                        { "command": { "action": "splitPane", "profile": "${profile.name}", "split": "vertical" } },
                        { "command": { "action": "splitPane", "profile": "${profile.name}", "split": "horizontal" } }
                    ]
                }
            ]
        },
```

## References

## PR Checklist
* [x] Closes #3994
* [x] I work here
* [x] Tests added/passed
* [ ] Requires documentation to be updated - Sure does, but we'll finish polishing this first.

## Detailed Description of the Pull Request / Additional comments

We've now gotta keep the original json for a command around, so that once we know what all the profiles will be, we can expand the commands that need it. 

We've also got to parse commands recursively, because they might have any number of child commands.

These together made the command parsing a _lot_ more complicated, but it feels good so far.

## Validation Steps Performed
* wrote a bunch of tests
* Played with it a bunch
2020-08-13 21:22:46 +00:00

116 lines
3.9 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AppKeyBindings.h"
#include "KeyChordSerialization.h"
#include "AppKeyBindings.g.cpp"
using namespace winrt::Microsoft::Terminal;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::TerminalControl;
namespace winrt::TerminalApp::implementation
{
void AppKeyBindings::SetKeyBinding(const TerminalApp::ActionAndArgs& actionAndArgs,
const KeyChord& chord)
{
_keyShortcuts[chord] = actionAndArgs;
}
// Method Description:
// - Remove the action that's bound to a particular KeyChord.
// Arguments:
// - chord: the keystroke to remove the action for.
// Return Value:
// - <none>
void AppKeyBindings::ClearKeyBinding(const KeyChord& chord)
{
_keyShortcuts.erase(chord);
}
KeyChord AppKeyBindings::GetKeyBindingForAction(TerminalApp::ShortcutAction const& action)
{
for (auto& kv : _keyShortcuts)
{
if (kv.second.Action() == action)
{
return kv.first;
}
}
return { nullptr };
}
// Method Description:
// - Lookup the keychord bound to a particular combination of ShortcutAction
// and IActionArgs. This enables searching no only for the binding of a
// particular ShortcutAction, but also a particular set of values for
// arguments to that action.
// Arguments:
// - actionAndArgs: The ActionAndArgs to lookup the keybinding for.
// Return Value:
// - The bound keychord, if this ActionAndArgs is bound to a key, otherwise nullptr.
KeyChord AppKeyBindings::GetKeyBindingForActionWithArgs(TerminalApp::ActionAndArgs const& actionAndArgs)
{
if (actionAndArgs == nullptr)
{
return { nullptr };
}
for (auto& kv : _keyShortcuts)
{
const auto action = kv.second.Action();
const auto args = kv.second.Args();
const auto actionMatched = action == actionAndArgs.Action();
const auto argsMatched = args ? args.Equals(actionAndArgs.Args()) : args == actionAndArgs.Args();
if (actionMatched && argsMatched)
{
return kv.first;
}
}
return { nullptr };
}
bool AppKeyBindings::TryKeyChord(const KeyChord& kc)
{
const auto keyIter = _keyShortcuts.find(kc);
if (keyIter != _keyShortcuts.end())
{
const auto actionAndArgs = keyIter->second;
return _dispatch.DoAction(actionAndArgs);
}
return false;
}
void AppKeyBindings::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
_dispatch = dispatch;
}
// Method Description:
// - Takes the KeyModifier flags from Terminal and maps them to the WinRT types which are used by XAML
// Return Value:
// - a Windows::System::VirtualKeyModifiers object with the flags of which modifiers used.
Windows::System::VirtualKeyModifiers AppKeyBindings::ConvertVKModifiers(KeyModifiers modifiers)
{
Windows::System::VirtualKeyModifiers keyModifiers = Windows::System::VirtualKeyModifiers::None;
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Control;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
{
keyModifiers |= Windows::System::VirtualKeyModifiers::Shift;
}
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
{
// note: Menu is the Alt VK_MENU
keyModifiers |= Windows::System::VirtualKeyModifiers::Menu;
}
return keyModifiers;
}
}