Add action to run multiple actions. (#11045)

<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Add a new action that can contain multiple other actions.

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> 
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #3992
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [x] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Creates a shortcut action that allows a list of actions to be specified as arguments. Steals a bunch of the serialization code from my other pr. Overall, because I had the serialization code written already, this was remarkably easy.

I can't think of any combined action to be added to the defaults, so I think this is just a thing for the documentation unless someone else has a good example. I know there are lot of times when the recommended workaround is "make an action with commandline wt.exe ..." and this could be a good replacement for that, but that is all personalized.

I didn't add this to the command line parsing, since the command line is already a way to run multiple actions.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Created a new command, confirmed that "Move right->down" showed up in the command palette, and that running it did the correct behavior (moving right one pane, then down one pane).
```
      {
        "command": {
          "action": "multipleActions",
          "name": "Move right->down",
          "actions": [
            {"action":  "moveFocus", "direction": "right" },
            {"action":  "moveFocus", "direction": "down" },
          ]
        }
      }
```
This commit is contained in:
Schuyler Rosefield 2021-08-31 15:35:51 -04:00 committed by GitHub
parent 717ea85c9f
commit 8d81497eb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 213 additions and 44 deletions

View File

@ -268,6 +268,7 @@
"movePane",
"swapPane",
"moveTab",
"multipleActions",
"newTab",
"newWindow",
"nextTab",
@ -825,6 +826,24 @@
],
"required": [ "direction" ]
},
"MultipleActionsAction": {
"description": "Arguments for the multiple actions command",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "multipleActions" },
"actions" : {
"$ref": "#/definitions/ShortcutAction",
"type": "array",
"minItems": 1,
"description": "A list of other actions."
}
}
}
],
"required": [ "actions" ]
},
"CommandPaletteAction": {
"description": "Arguments for a commandPalette action",
"allOf": [

View File

@ -874,4 +874,21 @@ namespace winrt::TerminalApp::implementation
}
}
}
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
{
for (const auto& action : realArgs.Actions())
{
_actionDispatch->DoAction(action);
}
args.Handled(true);
}
}
}
}

View File

@ -65,6 +65,7 @@ static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view ActionKey{ "action" };
@ -366,6 +367,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
};
}();

View File

@ -35,3 +35,34 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ActionAndArgs);
}
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
using namespace winrt::Microsoft::Terminal::Settings::Model;
template<>
struct ConversionTrait<ActionAndArgs>
{
ActionAndArgs FromJson(const Json::Value& json)
{
std::vector<SettingsLoadWarnings> v;
return *implementation::ActionAndArgs::FromJson(json, v);
}
bool CanConvert(const Json::Value& json) const
{
// commands without args might just be a string
return json.isString() || json.isObject();
}
Json::Value ToJson(const ActionAndArgs& val)
{
return implementation::ActionAndArgs::ToJson(val);
}
std::string TypeDescription() const
{
return "ActionAndArgs";
}
};
}

View File

@ -34,6 +34,7 @@
#include "RenameWindowArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
#include "MultipleActionsArgs.g.cpp"
#include <LibraryResources.h>
@ -687,4 +688,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Id())
};
}
winrt::hstring MultipleActionsArgs::GenerateName() const
{
return L"";
}
}

View File

@ -36,6 +36,7 @@
#include "RenameWindowArgs.g.h"
#include "GlobalSummonArgs.g.h"
#include "FocusPaneArgs.g.h"
#include "MultipleActionsArgs.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "JsonUtils.h"
@ -1754,6 +1755,53 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
};
struct MultipleActionsArgs : public MultipleActionsArgsT<MultipleActionsArgs>
{
MultipleActionsArgs() = default;
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<ActionAndArgs>, Actions);
static constexpr std::string_view ActionsKey{ "actions" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<MultipleActionsArgs>();
if (otherAsUs)
{
return otherAsUs->_Actions == _Actions;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<MultipleActionsArgs>();
JsonUtils::GetValueForKey(json, ActionsKey, args->_Actions);
return { *args, {} };
}
static Json::Value ToJson(const IActionArgs& val)
{
if (!val)
{
return {};
}
Json::Value json{ Json::ValueType::objectValue };
const auto args{ get_self<MultipleActionsArgs>(val) };
JsonUtils::SetValueForKey(json, ActionsKey, args->_Actions);
return json;
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<MultipleActionsArgs>() };
copy->_Actions = _Actions;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Actions);
}
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
@ -1778,4 +1826,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(FocusPaneArgs);
BASIC_FACTORY(PrevTabArgs);
BASIC_FACTORY(NextTabArgs);
BASIC_FACTORY(MultipleActionsArgs);
}

View File

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Command.idl";
namespace Microsoft.Terminal.Settings.Model
{
interface IActionArgs
@ -308,4 +310,10 @@ namespace Microsoft.Terminal.Settings.Model
FocusPaneArgs(UInt32 Id);
UInt32 Id { get; };
};
[default_interface] runtimeclass MultipleActionsArgs : IActionArgs
{
MultipleActionsArgs();
Windows.Foundation.Collections.IVector<ActionAndArgs> Actions;
}
}

View File

@ -78,7 +78,8 @@
ON_ALL_ACTIONS(OpenWindowRenamer) \
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane)
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(MultipleActions)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
@ -109,4 +110,5 @@
ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
ON_ALL_ACTIONS_WITH_ARGS(FocusPane)
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
ON_ALL_ACTIONS_WITH_ARGS(MultipleActions)

View File

@ -55,48 +55,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::vector<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return winrt::single_threaded_vector(move(val));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("vector ({})", ConversionTrait<T>{}.TypeDescription());
}
};
}
using namespace ::Microsoft::Terminal::Settings::Model;

View File

@ -177,6 +177,47 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
template<typename T>
struct ConversionTrait<std::vector<T>>
{
std::vector<T> FromJson(const Json::Value& json)
{
std::vector<T> val;
val.reserve(json.size());
ConversionTrait<T> trait;
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return val;
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const std::vector<T>& val)
{
Json::Value json{ Json::arrayValue };
ConversionTrait<T> trait;
for (const auto& v : val)
{
json.append(trait.ToJson(v));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<std::unordered_map<std::string, T>>
{
@ -259,6 +300,42 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json)
{
ConversionTrait<std::vector<T>> trait;
return winrt::single_threaded_vector<T>(std::move(trait.FromJson(json)));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<std::vector<T>> trait;
return trait.CanConvert(json);
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
Json::Value json{ Json::arrayValue };
if (val)
{
ConversionTrait<T> trait;
for (const auto& v : val)
{
json.append(trait.ToJson(v));
}
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>>
{