Allow setting the action on Actions page (#10220)

This introduces the ability to set the action for a key binding. A combo box is used to let the user select a new action.

## References
#6900 - Actions page Epic
#9427 - Actions page design doc
#9949 - Actions page PR

## Detailed Description of the Pull Request / Additional comments
### Settings Model Changes
- `ActionAndArgs`
   - new ctor that just takes a `ShortcutAction`
- `ActionMap`
   - `AvailableActions` provides a map of all the "acceptable" actions to choose from. This is a merged list of (1) all `{ "command": X }` style actions and (2) any actions with args that are already defined in the ActionMap (or any parents).
   - `RegisterKeyBinding` introduces a new unnamed key binding to the action map.

### Editor Changes
- XAML
   - Pretty straightforward, when in edit mode, we replace the text block with a combo box. This combo box just presents the actions you can choose from.
- `RebindKeysEventArgs` --> `ModifyKeyBindingEventArgs`
- `AvailableActionAndArgs`
   - stores the list of actions to choose from in the combo box
   - _Unfortunately_, `KeyBindingViewModel` needs this so that we can populate the combo box
   - `Actions` stores and maintains this though. We populate this from the settings model on navigation.
- `ProposedAction` vs `CurrentAction`
   - similar to `ProposedKeys` and `Keys`, we need a way to distinguish the value from the settings model and the value of the control (i.e. combo box).
   - `CurrentAction` --> settings model
   - `ProposedAction` --> combo box selected item

## Validation Steps Performed
- Cancel:
   - ✔️ change action --> cancel button --> begin editing action again --> original action is selected
- Accept:
   - ✔️ don't change anything
   - ✔️ change action --> OK! --> Save!
      - NOTE: The original action is still left as a stub `{ "command": "closePane" }`. This is intentional because we want to prevent all modifications to the command palette.
   - ✔️ change action & change key chord --> OK! --> Save!
   - ✔️ change action & change key chord (conflicting key chord) --> OK! --> click ok on flyout --> Save!
      - NOTE: original action is left as a stub; original key chord explicitly unbound; new command/keys combo added.
This commit is contained in:
Carlos Zamora 2021-07-02 15:35:55 -07:00 committed by GitHub
parent 9b9b0738c8
commit d3b9a780d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 257 additions and 52 deletions

View file

@ -7,6 +7,7 @@
#include "KeyBindingViewModel.g.cpp"
#include "ActionsPageNavigationState.g.cpp"
#include "LibraryResources.h"
#include "../TerminalSettingsModel/AllShortcutActions.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
@ -20,10 +21,12 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
KeyBindingViewModel::KeyBindingViewModel(const Control::KeyChord& keys, const Model::Command& cmd) :
KeyBindingViewModel::KeyBindingViewModel(const Control::KeyChord& keys, const hstring& actionName, const IObservableVector<hstring>& availableActions) :
_Keys{ keys },
_KeyChordText{ Model::KeyChordSerialization::ToString(keys) },
_Command{ cmd }
_CurrentAction{ actionName },
_ProposedAction{ box_value(actionName) },
_AvailableActions{ availableActions }
{
// Add a property changed handler to our own property changed event.
// This propagates changes from the settings model to anybody listening to our
@ -43,6 +46,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
_NotifyChanges(L"ShowEditButton");
}
else if (viewModelProperty == L"CurrentAction")
{
_NotifyChanges(L"Name");
}
});
}
@ -63,8 +70,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (_IsInEditMode)
{
// if we're in edit mode,
// pre-populate the text box with the current keys
// - pre-populate the text box with the current keys
// - reset the combo box with the current action
ProposedKeys(KeyChordText());
ProposedAction(box_value(CurrentAction()));
}
}
@ -75,13 +84,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void KeyBindingViewModel::AttemptAcceptChanges(hstring newKeyChordText)
{
auto args{ make_self<RebindKeysEventArgs>(_Keys, _Keys) };
auto args{ make_self<ModifyKeyBindingEventArgs>(_Keys, _Keys, _CurrentAction, unbox_value<hstring>(_ProposedAction)) };
// Key Chord Text
try
{
// Attempt to convert the provided key chord text
const auto newKeyChord{ KeyChordSerialization::FromString(newKeyChordText) };
args->NewKeys(newKeyChord);
_RebindKeysRequestedHandlers(*this, *args);
}
catch (hresult_invalid_argument)
{
@ -94,6 +104,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Alternatively, we want a full key chord editor/listener.
// If we implement that, we won't need this validation or error message.
}
_ModifyKeyBindingRequestedHandlers(*this, *args);
}
Actions::Actions()
@ -118,16 +130,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
_State = e.Parameter().as<Editor::ActionsPageNavigationState>();
// Populate AvailableActionAndArgs
_AvailableActionMap = single_threaded_map<hstring, Model::ActionAndArgs>();
std::vector<hstring> availableActionAndArgs;
for (const auto& [name, actionAndArgs] : _State.Settings().ActionMap().AvailableActions())
{
availableActionAndArgs.push_back(name);
_AvailableActionMap.Insert(name, actionAndArgs);
}
std::sort(begin(availableActionAndArgs), end(availableActionAndArgs));
_AvailableActionAndArgs = single_threaded_observable_vector(std::move(availableActionAndArgs));
// Convert the key bindings from our settings into a view model representation
const auto& keyBindingMap{ _State.Settings().ActionMap().KeyBindings() };
std::vector<Editor::KeyBindingViewModel> keyBindingList;
keyBindingList.reserve(keyBindingMap.Size());
for (const auto& [keys, cmd] : keyBindingMap)
{
auto container{ make_self<KeyBindingViewModel>(keys, cmd) };
// convert the cmd into a KeyBindingViewModel
auto container{ make_self<KeyBindingViewModel>(keys, cmd.Name(), _AvailableActionAndArgs) };
container->PropertyChanged({ this, &Actions::_ViewModelPropertyChangedHandler });
container->DeleteKeyBindingRequested({ this, &Actions::_ViewModelDeleteKeyBindingHandler });
container->RebindKeysRequested({ this, &Actions::_ViewModelRebindKeysHandler });
container->ModifyKeyBindingRequested({ this, &Actions::_ViewModelModifyKeyBindingHandler });
container->IsAutomationPeerAttached(_AutomationPeerAttached);
keyBindingList.push_back(*container);
}
@ -228,12 +252,42 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
void Actions::_ViewModelRebindKeysHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::RebindKeysEventArgs& args)
void Actions::_ViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args)
{
auto applyChangesToSettingsModel = [=]() {
// If the key chord was changed,
// update the settings model and view model appropriately
if (args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
{
// update settings model
_State.Settings().ActionMap().RebindKeys(args.OldKeys(), args.NewKeys());
// update view model
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
senderVMImpl->Keys(args.NewKeys());
}
// If the action was changed,
// update the settings model and view model appropriately
if (args.OldActionName() != args.NewActionName())
{
// convert the action's name into a view model.
const auto& newAction{ _AvailableActionMap.Lookup(args.NewActionName()) };
// update settings model
_State.Settings().ActionMap().RegisterKeyBinding(args.NewKeys(), newAction);
// update view model
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
senderVMImpl->CurrentAction(args.NewActionName());
}
};
// Check for this special case:
// we're changing the key chord,
// but the new key chord is already in use
if (args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
{
// We're actually changing the key chord
const auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
const auto& conflictingCmd{ _State.Settings().ActionMap().GetActionByKeyChord(args.NewKeys()) };
if (conflictingCmd)
{
@ -262,8 +316,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
senderVM.AcceptChangesFlyout(nullptr);
// update settings model and view model
_State.Settings().ActionMap().RebindKeys(args.OldKeys(), args.NewKeys());
senderVMImpl->Keys(args.NewKeys());
applyChangesToSettingsModel();
senderVM.ToggleEditMode();
});
@ -278,17 +331,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
senderVM.AcceptChangesFlyout(acceptChangesFlyout);
return;
}
else
{
// update settings model
_State.Settings().ActionMap().RebindKeys(args.OldKeys(), args.NewKeys());
// update view model (keys)
senderVMImpl->Keys(args.NewKeys());
}
}
// update view model (exit edit mode)
// update settings model and view model
applyChangesToSettingsModel();
// We NEED to toggle the edit mode here,
// so that if nothing changed, we still exit
// edit mode.
senderVM.ToggleEditMode();
}

View file

@ -6,7 +6,7 @@
#include "Actions.g.h"
#include "KeyBindingViewModel.g.h"
#include "ActionsPageNavigationState.g.h"
#include "RebindKeysEventArgs.g.h"
#include "ModifyKeyBindingEventArgs.g.h"
#include "Utils.h"
#include "ViewModelHelpers.h"
@ -20,25 +20,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
};
struct RebindKeysEventArgs : RebindKeysEventArgsT<RebindKeysEventArgs>
struct ModifyKeyBindingEventArgs : ModifyKeyBindingEventArgsT<ModifyKeyBindingEventArgs>
{
public:
RebindKeysEventArgs(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys) :
ModifyKeyBindingEventArgs(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys, const hstring oldActionName, const hstring newActionName) :
_OldKeys{ oldKeys },
_NewKeys{ newKeys } {}
_NewKeys{ newKeys },
_OldActionName{ std::move(oldActionName) },
_NewActionName{ std::move(newActionName) } {}
WINRT_PROPERTY(Control::KeyChord, OldKeys, nullptr);
WINRT_PROPERTY(Control::KeyChord, NewKeys, nullptr);
WINRT_PROPERTY(hstring, OldActionName);
WINRT_PROPERTY(hstring, NewActionName);
};
struct KeyBindingViewModel : KeyBindingViewModelT<KeyBindingViewModel>, ViewModelHelper<KeyBindingViewModel>
{
public:
KeyBindingViewModel(const Control::KeyChord& keys, const Settings::Model::Command& cmd);
KeyBindingViewModel(const Control::KeyChord& keys, const hstring& name, const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions);
hstring Name() const { return _Command.Name(); }
hstring Name() const { return _CurrentAction; }
hstring KeyChordText() const { return _KeyChordText; }
Settings::Model::Command Command() const { return _Command; };
// UIA Text
hstring EditButtonName() const noexcept;
@ -59,20 +62,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void AttemptAcceptChanges(hstring newKeyChordText);
void DeleteKeyBinding() { _DeleteKeyBindingRequestedHandlers(*this, _Keys); }
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsInEditMode, false);
// ProposedAction: the entry selected by the combo box; may disagree with the settings model.
// CurrentAction: the combo box item that maps to the settings model value.
// AvailableActions: the list of options in the combo box; both actions above must be in this list.
// NOTE: ProposedAction and CurrentAction may disagree mainly due to the "edit mode" system in place.
// Current Action serves as...
// 1 - a record of what to set ProposedAction to on a cancellation
// 2 - a form of translation between ProposedAction and the settings model
// We would also need an ActionMap reference to remove this, but this is a better separation
// of responsibilities.
VIEW_MODEL_OBSERVABLE_PROPERTY(IInspectable, ProposedAction);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentAction);
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<hstring>, AvailableActions, nullptr);
// ProposedKeys: the text shown in the text box; may disagree with the settings model.
// Keys: the key chord bound in the settings model.
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, ProposedKeys);
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, Keys, nullptr);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsInEditMode, false);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Controls::Flyout, AcceptChangesFlyout, nullptr);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsAutomationPeerAttached, false);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsHovered, false);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsContainerFocused, false);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsEditButtonFocused, false);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Media::Brush, ContainerBackground, nullptr);
TYPED_EVENT(RebindKeysRequested, Editor::KeyBindingViewModel, Editor::RebindKeysEventArgs);
TYPED_EVENT(ModifyKeyBindingRequested, Editor::KeyBindingViewModel, Editor::ModifyKeyBindingEventArgs);
TYPED_EVENT(DeleteKeyBindingRequested, Editor::KeyBindingViewModel, Terminal::Control::KeyChord);
private:
Settings::Model::Command _Command{ nullptr };
hstring _KeyChordText{};
};
@ -101,11 +119,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
private:
void _ViewModelPropertyChangedHandler(const Windows::Foundation::IInspectable& senderVM, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
void _ViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& args);
void _ViewModelRebindKeysHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::RebindKeysEventArgs& args);
void _ViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args);
std::optional<uint32_t> _GetContainerIndexByKeyChord(const Control::KeyChord& keys);
bool _AutomationPeerAttached{ false };
Windows::Foundation::Collections::IObservableVector<hstring> _AvailableActionAndArgs;
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionMap;
};
}

View file

@ -5,10 +5,12 @@ import "EnumEntry.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass RebindKeysEventArgs
runtimeclass ModifyKeyBindingEventArgs
{
Microsoft.Terminal.Control.KeyChord OldKeys { get; };
Microsoft.Terminal.Control.KeyChord NewKeys { get; };
String OldActionName { get; };
String NewActionName { get; };
}
runtimeclass KeyBindingViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
@ -21,6 +23,7 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean ShowEditButton { get; };
Boolean IsInEditMode { get; };
String ProposedKeys;
Object ProposedAction;
Windows.UI.Xaml.Controls.Flyout AcceptChangesFlyout;
String EditButtonName { get; };
String CancelButtonName { get; };
@ -34,11 +37,12 @@ namespace Microsoft.Terminal.Settings.Editor
void ActionLostFocus();
void EditButtonGettingFocus();
void EditButtonLosingFocus();
IObservableVector<String> AvailableActions { get; };
void ToggleEditMode();
void AttemptAcceptChanges();
void DeleteKeyBinding();
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, RebindKeysEventArgs> RebindKeysRequested;
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, ModifyKeyBindingEventArgs> ModifyKeyBindingRequested;
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, Microsoft.Terminal.Control.KeyChord> DeleteKeyBindingRequested;
}

View file

@ -198,7 +198,15 @@
<!-- Command Name -->
<TextBlock Grid.Column="0"
Style="{StaticResource KeyBindingNameTextBlockStyle}"
Text="{x:Bind Name, Mode=OneWay}" />
Text="{x:Bind Name, Mode=OneWay}"
Visibility="{x:Bind IsInEditMode, Mode=OneWay, Converter={StaticResource InvertedBooleanToVisibilityConverter}}" />
<!-- Edit Mode: Action Combo-box -->
<ComboBox Grid.Column="0"
VerticalAlignment="Center"
ItemsSource="{x:Bind AvailableActions, Mode=OneWay}"
SelectedItem="{x:Bind ProposedAction, Mode=TwoWay}"
Visibility="{x:Bind IsInEditMode, Mode=OneWay}" />
<!-- Key Chord Text -->
<Border Grid.Column="1"

View file

@ -5,6 +5,7 @@
#include "ActionAndArgs.g.cpp"
#include "JsonUtils.h"
#include "HashUtils.h"
#include <LibraryResources.h>
@ -117,6 +118,35 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
#undef ON_ALL_ACTIONS_WITH_ARGS
};
ActionAndArgs::ActionAndArgs(ShortcutAction action)
{
// Find the deserializer
const auto deserializersIter = argSerializerMap.find(action);
if (deserializersIter != argSerializerMap.end())
{
auto pfn = deserializersIter->second.first;
if (pfn)
{
// Call the deserializer on an empty JSON object.
// This ensures that we have a valid ActionArgs
std::vector<Microsoft::Terminal::Settings::Model::SettingsLoadWarnings> parseWarnings;
std::tie(_Args, parseWarnings) = pfn({});
}
// if an arg parser was registered, but failed,
// return the invalid ActionAndArgs we started with.
if (pfn && _Args == nullptr)
{
return;
}
}
// Either...
// (1) we don't have a deserializer, so it's ok for _Args to be null, or
// (2) we had one AND it worked, so _Args is set up properly
_Action = action;
}
// Function Description:
// - Attempts to match a string to a ShortcutAction. If there's no match, then
// returns ShortcutAction::Invalid

View file

@ -18,6 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static Json::Value ToJson(const Model::ActionAndArgs& val);
ActionAndArgs() = default;
ActionAndArgs(ShortcutAction action);
ActionAndArgs(ShortcutAction action, IActionArgs args) :
_Action{ action },
_Args{ args } {};

View file

@ -4,6 +4,7 @@
#include "pch.h"
#include "AllShortcutActions.h"
#include "ActionMap.h"
#include "AllShortcutActions.h"
#include "ActionMap.g.cpp"
@ -96,6 +97,75 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
return std::nullopt;
}
static void RegisterShortcutAction(ShortcutAction shortcutAction, std::unordered_map<hstring, Model::ActionAndArgs>& list, std::unordered_set<InternalActionID>& visited)
{
const auto actionAndArgs{ make_self<ActionAndArgs>(shortcutAction) };
if (actionAndArgs->Action() != ShortcutAction::Invalid)
{
/*We have a valid action.*/
/*Check if the action was already added.*/
if (visited.find(Hash(*actionAndArgs)) == visited.end())
{
/*This is an action that wasn't added!*/
/*Let's add it.*/
const auto name{ actionAndArgs->GenerateName() };
list.insert({ name, *actionAndArgs });
}
}
}
// Method Description:
// - Retrieves a map of actions that can be bound to a key
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> ActionMap::AvailableActions()
{
if (!_AvailableActionsCache)
{
// populate _AvailableActionsCache
std::unordered_map<hstring, Model::ActionAndArgs> availableActions;
std::unordered_set<InternalActionID> visitedActionIDs;
_PopulateAvailableActionsWithStandardCommands(availableActions, visitedActionIDs);
// now add any ShortcutActions that we might have missed
#define ON_ALL_ACTIONS(action) RegisterShortcutAction(ShortcutAction::action, availableActions, visitedActionIDs);
ALL_SHORTCUT_ACTIONS
#undef ON_ALL_ACTIONS
_AvailableActionsCache = single_threaded_map<hstring, Model::ActionAndArgs>(std::move(availableActions));
}
return _AvailableActionsCache.GetView();
}
void ActionMap::_PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const
{
// Update AvailableActions and visitedActionIDs with our current layer
for (const auto& [actionID, cmd] : _ActionMap)
{
if (cmd.ActionAndArgs().Action() != ShortcutAction::Invalid)
{
// Only populate AvailableActions with actions that haven't been visited already.
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
{
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Update AvailableActions.
const auto actionAndArgsImpl{ get_self<ActionAndArgs>(cmd.ActionAndArgs()) };
availableActions.insert_or_assign(name, *actionAndArgsImpl->Copy());
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.insert(actionID);
}
}
}
// Update NameMap and visitedActionIDs with our parents
for (const auto& parent : _parents)
{
parent->_PopulateAvailableActionsWithStandardCommands(availableActions, visitedActionIDs);
}
}
// Method Description:
// - Retrieves a map of command names to the commands themselves
// - These commands should not be modified directly because they may result in
@ -164,25 +234,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::unordered_set<InternalActionID> visitedActionIDs;
for (const auto& cmd : _GetCumulativeActions())
{
// skip over all invalid actions
if (cmd.ActionAndArgs().Action() == ShortcutAction::Invalid)
// only populate with valid commands
if (cmd.ActionAndArgs().Action() != ShortcutAction::Invalid)
{
continue;
}
// Only populate NameMap with actions that haven't been visited already.
const auto actionID{ Hash(cmd.ActionAndArgs()) };
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
{
const auto& name{ cmd.Name() };
if (!name.empty())
// Only populate NameMap with actions that haven't been visited already.
const auto actionID{ Hash(cmd.ActionAndArgs()) };
if (visitedActionIDs.find(actionID) == visitedActionIDs.end())
{
// Update NameMap.
nameMap.insert_or_assign(name, cmd);
}
const auto& name{ cmd.Name() };
if (!name.empty())
{
// Update NameMap.
nameMap.insert_or_assign(name, cmd);
}
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.emplace(actionID);
// Record that we already handled adding this action to the NameMap.
visitedActionIDs.emplace(actionID);
}
}
}
}
@ -759,4 +827,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
cmd->RegisterKey(keys);
AddAction(*cmd);
}
// Method Description:
// - Add a new key binding
// - If the key chord is already in use, the conflicting command is overwritten.
// Arguments:
// - keys: the key chord that is being bound
// - action: the action that the keys are being bound to
// Return Value:
// - <none>
void ActionMap::RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action)
{
auto cmd{ make_self<Command>() };
cmd->RegisterKey(keys);
cmd->ActionAndArgs(action);
AddAction(*cmd);
}
}

View file

@ -53,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ActionMap();
// views
Windows::Foundation::Collections::IMapView<hstring, Model::ActionAndArgs> AvailableActions();
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> GlobalHotkeys();
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> KeyBindings();
@ -74,6 +75,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// modification
bool RebindKeys(Control::KeyChord const& oldKeys, Control::KeyChord const& newKeys);
void DeleteKeyBinding(Control::KeyChord const& keys);
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(Control::KeyModifiers modifiers);
@ -81,6 +83,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::optional<Model::Command> _GetActionByID(const InternalActionID actionID) const;
std::optional<Model::Command> _GetActionByKeyChordInternal(Control::KeyChord const& keys) const;
void _PopulateAvailableActionsWithStandardCommands(std::unordered_map<hstring, Model::ActionAndArgs>& availableActions, std::unordered_set<InternalActionID>& visitedActionIDs) const;
void _PopulateNameMapWithSpecialCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
void _PopulateNameMapWithStandardCommands(std::unordered_map<hstring, Model::Command>& nameMap) const;
void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map<Control::KeyChord, Model::Command, KeyChordHash, KeyChordEquality>& keyBindingsMap, std::unordered_set<Control::KeyChord, KeyChordHash, KeyChordEquality>& unboundKeys) const;
@ -90,6 +93,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void _TryUpdateName(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
void _TryUpdateKeyChord(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd);
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionsCache{ nullptr };
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _GlobalHotkeysCache{ nullptr };
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _KeyBindingMapCache{ nullptr };

View file

@ -13,6 +13,8 @@ namespace Microsoft.Terminal.Settings.Model
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
[method_name("GetKeyBindingForActionWithArgs")] Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action, IActionArgs actionArgs);
Windows.Foundation.Collections.IMapView<String, ActionAndArgs> AvailableActions { get; };
Windows.Foundation.Collections.IMapView<String, Command> NameMap { get; };
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Command> KeyBindings { get; };
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Command> GlobalHotkeys { get; };
@ -20,7 +22,9 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass ActionMap : IActionMapView
{
Boolean RebindKeys(Microsoft.Terminal.Control.KeyChord oldKeys, Microsoft.Terminal.Control.KeyChord newKeys);
void RebindKeys(Microsoft.Terminal.Control.KeyChord oldKeys, Microsoft.Terminal.Control.KeyChord newKeys);
void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys);
void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action);
}
}