Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
"movePane",
"swapPane",
"moveTab",
"multipleActions",
"newTab",
"newWindow",
"nextTab",
Expand Down Expand Up @@ -808,6 +809,25 @@
],
"required": [ "direction" ]
},
"MultipleActionsAction": {
"description": "Arguments for the multiple actions command",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "multipleActions" },
"name": { "type": "string"},
"actions" : {
"$ref": "#/definitions/ShortcutAction"
"type": "array",
"minItems": 1,
"description": "A list of other actions."
}
}
}
],
"required": [ "name", "actions" ]
},
"CommandPaletteAction": {
"description": "Arguments for a commandPalette action",
"allOf": [
Expand Down
17 changes: 17 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" };

Expand Down Expand Up @@ -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
};
}();

Expand Down
31 changes: 31 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionAndArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
};
}
11 changes: 11 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "RenameWindowArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
#include "MultipleActionsArgs.g.cpp"

#include <LibraryResources.h>

Expand Down Expand Up @@ -681,4 +682,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Id())
};
}

winrt::hstring MultipleActionsArgs::GenerateName() const
{
if (!_Name.empty())
{
return _Name;
}

return RS_(L"MultipleActionsMissingName");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This raises a good point - this is another example of an action that doesn't really make sense unless the user gives it a manual name. I think the precedent we have here is the sendInput action, we just leave the name as the empty string if it wasn't given one. What that should do is force the user to give it a name if they want to see it in the command palette, or they can leave it nameless and only use it via a keybinding. (Granted, this has also resulted in #10981).

I'd think we could just follow that precedent here too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, I should remove this entirely and always just return _Name and force the user to deal with it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I might remove the _Name member entirely. Command already has a name, so the command palette can just use that one (if the user provides one). Granted, the json then becomes:

      {
        "command": {
          "action": "multipleActions",
          "actions": [
            {"action":  "moveFocus", "direction": "right" },
            {"action":  "moveFocus", "direction": "down" },
          ]
        },
        "name": "Move right->down",
      }

instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the Name property.

}
}
54 changes: 54 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1754,6 +1755,58 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
};

struct MultipleActionsArgs : public MultipleActionsArgsT<MultipleActionsArgs>
{
MultipleActionsArgs() = default;
WINRT_PROPERTY(hstring, Name, L"");
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<ActionAndArgs>, Actions);
static constexpr std::string_view NameKey{ "name" };
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->_Name == _Name && 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, NameKey, args->_Name);
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, NameKey, args->_Name);
JsonUtils::SetValueForKey(json, ActionsKey, args->_Actions);
return json;
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<MultipleActionsArgs>() };
copy->_Name = _Name;
copy->_Actions = _Actions;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name, _Actions);
}
};
}

namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
Expand All @@ -1778,4 +1831,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(FocusPaneArgs);
BASIC_FACTORY(PrevTabArgs);
BASIC_FACTORY(NextTabArgs);
BASIC_FACTORY(MultipleActionsArgs);
}
9 changes: 9 additions & 0 deletions src/cascadia/TerminalSettingsModel/ActionArgs.idl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import "Command.idl";

namespace Microsoft.Terminal.Settings.Model
{
interface IActionArgs
Expand Down Expand Up @@ -307,4 +309,11 @@ namespace Microsoft.Terminal.Settings.Model
FocusPaneArgs(UInt32 Id);
UInt32 Id { get; };
};

[default_interface] runtimeclass MultipleActionsArgs : IActionArgs
{
MultipleActionsArgs();
String Name;
Windows.Foundation.Collections.IVector<ActionAndArgs> Actions;
}
}
6 changes: 4 additions & 2 deletions src/cascadia/TerminalSettingsModel/AllShortcutActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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)
77 changes: 77 additions & 0 deletions src/cascadia/TerminalSettingsModel/JsonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>>
{
Expand Down Expand Up @@ -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>>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,7 @@
<data name="MinimizeToTrayCommandKey" xml:space="preserve">
<value>Minimize current window to tray</value>
</data>
<data name="MultipleActionsMissingName" xml:space="preserve">
<value>Multiple Actions (give this a name)</value>
</data>
</root>