terminal/src/cascadia/TerminalSettingsModel/ActionMap.h
Leonard Hecker 70d44c84c8
Make ActionMap compatible with ScanCode-only KeyChords (#10945)
This commit partially reverts d465a47 and introduces an alternative approach by adding Hash and Equals methods to the KeyChords class. Those methods will now favor any existing Vkeys over ScanCodes.

## PR Checklist
* [x] Closes #10933
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed

* Added a new test, which is ✔️
* Various standard commands still work ✔️
* Hash() returns the same value for all KeyChords that are Equals() ✔️
2021-08-20 00:21:33 +00:00

122 lines
5.5 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ActionMap.h
Abstract:
- A mapping of key chords to actions. Includes (de)serialization logic.
Author(s):
- Carlos Zamora - September 2020
--*/
#pragma once
#include "ActionMap.g.h"
#include "IInheritable.h"
#include "Command.h"
#include "../inc/cppwinrt_utils.h"
// fwdecl unittest classes
namespace SettingsModelLocalTests
{
class KeyBindingsTests;
class DeserializationTests;
class TerminalSettingsTests;
}
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
using InternalActionID = size_t;
struct KeyChordHash
{
inline std::size_t operator()(const Control::KeyChord& key) const
{
return static_cast<size_t>(key.Hash());
}
};
struct KeyChordEquality
{
inline bool operator()(const Control::KeyChord& lhs, const Control::KeyChord& rhs) const
{
return lhs.Equals(rhs);
}
};
struct ActionMap : ActionMapT<ActionMap>, IInheritable<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();
com_ptr<ActionMap> Copy() const;
// queries
Model::Command GetActionByKeyChord(Control::KeyChord const& keys) const;
bool IsKeyChordExplicitlyUnbound(Control::KeyChord const& keys) const;
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action) const;
Control::KeyChord GetKeyBindingForAction(ShortcutAction const& action, IActionArgs const& actionArgs) const;
// population
void AddAction(const Model::Command& cmd);
// JSON
static com_ptr<ActionMap> FromJson(const Json::Value& json);
std::vector<SettingsLoadWarnings> LayerJson(const Json::Value& json);
Json::Value ToJson() const;
// 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);
private:
std::optional<Model::Command> _GetActionByID(const InternalActionID actionID) const;
std::optional<Model::Command> _GetActionByKeyChordInternal(const Control::KeyChord& keys) const;
void _RefreshKeyBindingCaches();
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;
std::vector<Model::Command> _GetCumulativeActions() const noexcept;
void _TryUpdateActionMap(const Model::Command& cmd, Model::Command& oldCmd, Model::Command& consolidatedCmd);
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 };
std::unordered_map<winrt::hstring, Model::Command> _NestedCommands;
std::vector<Model::Command> _IterableCommands;
std::unordered_map<Control::KeyChord, InternalActionID, KeyChordHash, KeyChordEquality> _KeyMap;
std::unordered_map<InternalActionID, Model::Command> _ActionMap;
// Masking Actions:
// These are actions that were introduced in an ancestor,
// but were edited (or unbound) in the current layer.
// _ActionMap shows a Command with keys that were added in this layer,
// whereas _MaskingActions provides a view that encompasses all of
// the valid associated key chords.
// Maintaining this map allows us to return a valid Command
// in GetKeyBindingForAction.
// Additionally, these commands to not need to be serialized,
// whereas those in _ActionMap do. These actions provide more data
// than is necessary to be serialized.
std::unordered_map<InternalActionID, Model::Command> _MaskingActions;
friend class SettingsModelLocalTests::KeyBindingsTests;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::TerminalSettingsTests;
};
}