From ff8fdbd2431f1cfd8211833815be481dfdec4420 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 20 May 2021 14:44:04 -0400 Subject: [PATCH] Introduce serialization for actions (#9926) ## Summary of the Pull Request This PR builds on the `ActionMap` PR (#6900) by leveraging `ActionMap` to serialize actions. From the top down, the process is now as follows: - `CascadiaSettings`: remove the hack of copying whatever we had for actions before. - `GlobalAppSettings`: serialize the `ActionMap` to `"actions": []` - `ActionMap`: iterate over the internal `_ActionMap` (list of actions) and serialize each `Command` - `Command`: **THIS IS WHERE THE MAGIC HAPPENS!** For _each_ key mapping, serialize an action. Only the first one needs to include the name and icon. - `ActionAndArgs`: Find the relevant `IActionArgs` parser and serialize the `ActionAndArgs`. - `ActionArgs`: **ANNOYING CHANGE** Serialize any args that are set. We _need_ each setting to be saved as a `std::optional`. As with inheritance, this allows us to distinguish an explicit setting to the default value (sometimes `null`) vs an implicit "give me the default value". This allows us to serialize only the relevant details of each action, rather than writing _all_ of the args. ## References - #8100: Inheritance/Layering for lists - This tracks layering and better serialization for color schemes _and_ actions. This PR resolves half of that issue. The next step is to apply the concepts used in this PR (and #9621) to solve the similar problem for color schemes. - #6900: Actions page ## Validation Steps Performed Tests added! --- .github/actions/spelling/expect/expect.txt | 2 + .../SerializationTests.cpp | 160 ++++++- .../TerminalSettingsModel/ActionAndArgs.cpp | 174 ++++--- .../TerminalSettingsModel/ActionAndArgs.h | 1 + .../TerminalSettingsModel/ActionArgs.cpp | 184 +++---- .../TerminalSettingsModel/ActionArgs.h | 448 +++++++++++++++--- .../TerminalSettingsModel/ActionMap.h | 4 + .../ActionMapSerialization.cpp | 42 ++ .../AllShortcutActions.h | 27 ++ .../CascadiaSettingsSerialization.cpp | 10 - .../TerminalSettingsModel/Command.cpp | 60 +++ src/cascadia/TerminalSettingsModel/Command.h | 2 + .../GlobalAppSettings.cpp | 6 +- .../TerminalSettingsModel/JsonUtils.h | 4 +- 14 files changed, 847 insertions(+), 277 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 4a19919cce9..8107fae626f 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2069,6 +2069,8 @@ selectany SELECTEDFONT SELECTSTRING Selfhosters +serializer +serializers SERVERDLL SETACTIVE SETBUDDYINT diff --git a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp index 022db740092..97251b8b2d8 100644 --- a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp @@ -39,6 +39,7 @@ namespace SettingsModelLocalTests TEST_METHOD(GlobalSettings); TEST_METHOD(Profile); TEST_METHOD(ColorScheme); + TEST_METHOD(Actions); TEST_METHOD(CascadiaSettings); TEST_CLASS_SETUP(ClassSetup) @@ -106,12 +107,15 @@ namespace SettingsModelLocalTests "experimental.input.forceVT": false, "experimental.rendering.forceFullRepaint": false, - "experimental.rendering.software": false + "experimental.rendering.software": false, + + "actions": [] })" }; const std::string smallGlobalsString{ R"( { - "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" + "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + "actions": [] })" }; RoundtripTest(globalsString); @@ -178,7 +182,9 @@ namespace SettingsModelLocalTests // - null should be acceptable even though we're working with colors const std::string weirdProfileString{ R"( { + "guid" : "{8b039d4d-77ca-5a83-88e1-dfc8e895a127}", "name": "Weird Profile", + "hidden": false, "tabColor": null, "foreground": null, "source": "local" @@ -221,6 +227,149 @@ namespace SettingsModelLocalTests RoundtripTest(schemeString); } + void SerializationTests::Actions() + { + const std::string actionsString1{ R"([ + { "command": "paste" } + ])" }; + + const std::string actionsString2A{ R"([ + { "command": { "action": "setTabColor" } } + ])" }; + const std::string actionsString2B{ R"([ + { "command": { "action": "setTabColor", "color": "#112233" } } + ])" }; + const std::string actionsString2C{ R"([ + { "command": { "action": "copy" } }, + { "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } } + ])" }; + + const std::string actionsString3{ R"([ + { "command": "toggleAlwaysOnTop", "keys": "ctrl+a" }, + { "command": "toggleAlwaysOnTop", "keys": "ctrl+b" } + ])" }; + + const std::string actionsString4{ R"([ + { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" }, + { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" } + ])" }; + + const std::string actionsString5{ R"([ + { "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" }, + { "command": "scrollToTop", "keys": "ctrl+f" } + ])" }; + + const std::string actionsString6{ R"([ + { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" }, + ])" }; + + const std::string actionsString7{ R"([ + { "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" } + ])" }; + + const std::string actionsString8{ R"([ + { + "name": "Change font size...", + "commands": [ + { "command": { "action": "adjustFontSize", "delta": 1 } }, + { "command": { "action": "adjustFontSize", "delta": -1 } }, + { "command": "resetFontSize" }, + ] + } + ])" }; + + const std::string actionsString9A{ R"([ + { + "name": "New tab", + "commands": [ + { + "iterateOn": "profiles", + "icon": "${profile.icon}", + "name": "${profile.name}", + "command": { "action": "newTab", "profile": "${profile.name}" } + } + ] + } + ])" }; + const std::string actionsString9B{ R"([ + { + "commands": + [ + { + "command": + { + "action": "sendInput", + "input": "${profile.name}" + }, + "iterateOn": "profiles" + } + ], + "name": "Send Input ..." + } + ])" }; + const std::string actionsString9C{ R""([ + { + "commands": + [ + { + "commands": + [ + { + "command": + { + "action": "sendInput", + "input": "${profile.name} ${scheme.name}" + }, + "iterateOn": "schemes" + } + ], + "iterateOn": "profiles", + "name": "nest level (${profile.name})" + } + ], + "name": "Send Input (Evil) ..." + } + ])"" }; + + const std::string actionsString10{ R"([ + { "command": "unbound", "keys": "ctrl+c" } + ])" }; + + Log::Comment(L"simple command"); + RoundtripTest(actionsString1); + + Log::Comment(L"complex commands"); + RoundtripTest(actionsString2A); + RoundtripTest(actionsString2B); + RoundtripTest(actionsString2C); + + Log::Comment(L"simple command with key chords"); + RoundtripTest(actionsString3); + + Log::Comment(L"complex commands with key chords"); + RoundtripTest(actionsString4); + + Log::Comment(L"command with name and icon and multiple key chords"); + RoundtripTest(actionsString5); + + Log::Comment(L"complex command with new terminal args"); + RoundtripTest(actionsString6); + + Log::Comment(L"complex command with meaningful null arg"); + RoundtripTest(actionsString7); + + Log::Comment(L"nested command"); + RoundtripTest(actionsString8); + + Log::Comment(L"iterable command"); + RoundtripTest(actionsString9A); + RoundtripTest(actionsString9B); + RoundtripTest(actionsString9C); + + Log::Comment(L"unbound command"); + RoundtripTest(actionsString10); + } + void SerializationTests::CascadiaSettings() { const std::string settingsString{ R"({ @@ -277,10 +426,9 @@ namespace SettingsModelLocalTests } ], "actions": [ - {"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" } - ], - "keybindings": [ - { "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" } + { "command": { "action": "renameTab", "title": "Liang Tab" }, "keys": "ctrl+t" }, + { "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" }, + { "command": { "action": "renameWindow", "name": "Hecker Window" }, "keys": "ctrl+l" } ] })" }; diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index a7508374df3..ae1fbaae2c9 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#include "AllShortcutActions.h" #include "ActionArgs.h" #include "ActionAndArgs.h" #include "ActionAndArgs.g.cpp" @@ -29,10 +30,10 @@ static constexpr std::string_view RenameTabKey{ "renameTab" }; static constexpr std::string_view OpenTabRenamerKey{ "openTabRenamer" }; static constexpr std::string_view ResetFontSizeKey{ "resetFontSize" }; static constexpr std::string_view ResizePaneKey{ "resizePane" }; -static constexpr std::string_view ScrolldownKey{ "scrollDown" }; -static constexpr std::string_view ScrolldownpageKey{ "scrollDownPage" }; -static constexpr std::string_view ScrollupKey{ "scrollUp" }; -static constexpr std::string_view ScrolluppageKey{ "scrollUpPage" }; +static constexpr std::string_view ScrollDownKey{ "scrollDown" }; +static constexpr std::string_view ScrollDownPageKey{ "scrollDownPage" }; +static constexpr std::string_view ScrollUpKey{ "scrollUp" }; +static constexpr std::string_view ScrollUpPageKey{ "scrollUpPage" }; static constexpr std::string_view ScrollToTopKey{ "scrollToTop" }; static constexpr std::string_view ScrollToBottomKey{ "scrollToBottom" }; static constexpr std::string_view SendInputKey{ "sendInput" }; @@ -65,6 +66,10 @@ static constexpr std::string_view ActionKey{ "action" }; // This key is reserved to remove a keybinding, instead of mapping it to an action. static constexpr std::string_view UnboundKey{ "unbound" }; +#define KEY_TO_ACTION_PAIR(action) { action##Key, ShortcutAction::action }, +#define ACTION_TO_KEY_PAIR(action) { ShortcutAction::action, action##Key }, +#define ACTION_TO_SERIALIZERS_PAIR(action) { ShortcutAction::action, { action##Args::FromJson, action##Args::ToJson } }, + namespace winrt::Microsoft::Terminal::Settings::Model::implementation { using namespace ::Microsoft::Terminal::Settings::Model; @@ -78,98 +83,38 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // the map are all const for the lifetime of the app, we have nothing to worry // about here. const std::map> ActionAndArgs::ActionKeyNamesMap{ - { AdjustFontSizeKey, ShortcutAction::AdjustFontSize }, - { CloseOtherTabsKey, ShortcutAction::CloseOtherTabs }, - { ClosePaneKey, ShortcutAction::ClosePane }, - { CloseTabKey, ShortcutAction::CloseTab }, - { CloseTabsAfterKey, ShortcutAction::CloseTabsAfter }, - { CloseWindowKey, ShortcutAction::CloseWindow }, - { CopyTextKey, ShortcutAction::CopyText }, - { DuplicateTabKey, ShortcutAction::DuplicateTab }, - { ExecuteCommandlineKey, ShortcutAction::ExecuteCommandline }, - { FindKey, ShortcutAction::Find }, - { MoveFocusKey, ShortcutAction::MoveFocus }, - { NewTabKey, ShortcutAction::NewTab }, - { NextTabKey, ShortcutAction::NextTab }, - { OpenNewTabDropdownKey, ShortcutAction::OpenNewTabDropdown }, - { OpenSettingsKey, ShortcutAction::OpenSettings }, - { OpenTabColorPickerKey, ShortcutAction::OpenTabColorPicker }, - { PasteTextKey, ShortcutAction::PasteText }, - { PrevTabKey, ShortcutAction::PrevTab }, - { RenameTabKey, ShortcutAction::RenameTab }, - { OpenTabRenamerKey, ShortcutAction::OpenTabRenamer }, - { ResetFontSizeKey, ShortcutAction::ResetFontSize }, - { ResizePaneKey, ShortcutAction::ResizePane }, - { ScrolldownKey, ShortcutAction::ScrollDown }, - { ScrolldownpageKey, ShortcutAction::ScrollDownPage }, - { ScrollupKey, ShortcutAction::ScrollUp }, - { ScrolluppageKey, ShortcutAction::ScrollUpPage }, - { ScrollToTopKey, ShortcutAction::ScrollToTop }, - { ScrollToBottomKey, ShortcutAction::ScrollToBottom }, - { SendInputKey, ShortcutAction::SendInput }, - { SetColorSchemeKey, ShortcutAction::SetColorScheme }, - { SetTabColorKey, ShortcutAction::SetTabColor }, - { SplitPaneKey, ShortcutAction::SplitPane }, - { SwitchToTabKey, ShortcutAction::SwitchToTab }, - { TabSearchKey, ShortcutAction::TabSearch }, - { ToggleAlwaysOnTopKey, ShortcutAction::ToggleAlwaysOnTop }, - { ToggleCommandPaletteKey, ShortcutAction::ToggleCommandPalette }, - { ToggleFocusModeKey, ShortcutAction::ToggleFocusMode }, - { ToggleFullscreenKey, ShortcutAction::ToggleFullscreen }, - { TogglePaneZoomKey, ShortcutAction::TogglePaneZoom }, - { LegacyToggleRetroEffectKey, ShortcutAction::ToggleShaderEffects }, - { ToggleShaderEffectsKey, ShortcutAction::ToggleShaderEffects }, - { MoveTabKey, ShortcutAction::MoveTab }, - { BreakIntoDebuggerKey, ShortcutAction::BreakIntoDebugger }, - { UnboundKey, ShortcutAction::Invalid }, - { FindMatchKey, ShortcutAction::FindMatch }, - { TogglePaneReadOnlyKey, ShortcutAction::TogglePaneReadOnly }, - { NewWindowKey, ShortcutAction::NewWindow }, - { IdentifyWindowKey, ShortcutAction::IdentifyWindow }, - { IdentifyWindowsKey, ShortcutAction::IdentifyWindows }, - { RenameWindowKey, ShortcutAction::RenameWindow }, - { OpenWindowRenamerKey, ShortcutAction::OpenWindowRenamer }, - { GlobalSummonKey, ShortcutAction::GlobalSummon }, - { QuakeModeKey, ShortcutAction::QuakeMode }, + +#define ON_ALL_ACTIONS(action) KEY_TO_ACTION_PAIR(action) + ALL_SHORTCUT_ACTIONS +#undef ON_ALL_ACTIONS + }; + + static const std::map> ActionToStringMap{ +#define ON_ALL_ACTIONS(action) ACTION_TO_KEY_PAIR(action) + ALL_SHORTCUT_ACTIONS +#undef ON_ALL_ACTIONS }; using ParseResult = std::tuple>; using ParseActionFunction = std::function; + using SerializeActionFunction = std::function; - // This is a map of ShortcutAction->function. It holds - // a set of deserializer functions that can be used to deserialize a IActionArgs + // This is a map of ShortcutAction->{function, function. It holds + // a set of (de)serializer functions that can be used to (de)serialize an IActionArgs // from json. Each type of IActionArgs that can accept arbitrary args should be // placed into this map, with the corresponding deserializer function as the // value. - static const std::map> argParsers{ - { ShortcutAction::AdjustFontSize, AdjustFontSizeArgs::FromJson }, - { ShortcutAction::CloseOtherTabs, CloseOtherTabsArgs::FromJson }, - { ShortcutAction::CloseTabsAfter, CloseTabsAfterArgs::FromJson }, - { ShortcutAction::CopyText, CopyTextArgs::FromJson }, - { ShortcutAction::ExecuteCommandline, ExecuteCommandlineArgs::FromJson }, - { ShortcutAction::MoveFocus, MoveFocusArgs::FromJson }, - { ShortcutAction::NewTab, NewTabArgs::FromJson }, - { ShortcutAction::OpenSettings, OpenSettingsArgs::FromJson }, - { ShortcutAction::RenameTab, RenameTabArgs::FromJson }, - { ShortcutAction::ResizePane, ResizePaneArgs::FromJson }, - { ShortcutAction::SendInput, SendInputArgs::FromJson }, - { ShortcutAction::SetColorScheme, SetColorSchemeArgs::FromJson }, - { ShortcutAction::SetTabColor, SetTabColorArgs::FromJson }, - { ShortcutAction::SplitPane, SplitPaneArgs::FromJson }, - { ShortcutAction::SwitchToTab, SwitchToTabArgs::FromJson }, - { ShortcutAction::ScrollUp, ScrollUpArgs::FromJson }, - { ShortcutAction::ScrollDown, ScrollDownArgs::FromJson }, - { ShortcutAction::MoveTab, MoveTabArgs::FromJson }, - { ShortcutAction::ToggleCommandPalette, ToggleCommandPaletteArgs::FromJson }, - { ShortcutAction::FindMatch, FindMatchArgs::FromJson }, - { ShortcutAction::NewWindow, NewWindowArgs::FromJson }, - { ShortcutAction::PrevTab, PrevTabArgs::FromJson }, - { ShortcutAction::NextTab, NextTabArgs::FromJson }, - { ShortcutAction::RenameWindow, RenameWindowArgs::FromJson }, - { ShortcutAction::GlobalSummon, GlobalSummonArgs::FromJson }, - { ShortcutAction::QuakeMode, GlobalSummonArgs::QuakeModeFromJson }, - - { ShortcutAction::Invalid, nullptr }, + static const std::unordered_map> argSerializerMap{ + + // These are special cases. + // - QuakeMode: deserializes into a GlobalSummon, so we don't need a serializer + // - Invalid: has no args + { ShortcutAction::QuakeMode, { GlobalSummonArgs::QuakeModeFromJson, nullptr } }, + { ShortcutAction::Invalid, { nullptr, nullptr } }, + +#define ON_ALL_ACTIONS_WITH_ARGS(action) ACTION_TO_SERIALIZERS_PAIR(action) + ALL_SHORTCUT_ACTIONS_WITH_ARGS +#undef ON_ALL_ACTIONS_WITH_ARGS }; // Function Description: @@ -248,10 +193,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // the binding. IActionArgs args{ nullptr }; std::vector parseWarnings; - const auto deserializersIter = argParsers.find(action); - if (deserializersIter != argParsers.end()) + const auto deserializersIter = argSerializerMap.find(action); + if (deserializersIter != argSerializerMap.end()) { - auto pfn = deserializersIter->second; + auto pfn = deserializersIter->second.first; if (pfn) { std::tie(args, parseWarnings) = pfn(argsVal); @@ -271,6 +216,53 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return make_self(action, args); } + Json::Value ActionAndArgs::ToJson(const Model::ActionAndArgs& val) + { + if (val) + { + // Search for the ShortcutAction + const auto shortcutActionIter{ ActionToStringMap.find(val.Action()) }; + if (shortcutActionIter == ActionToStringMap.end()) + { + // Couldn't find the ShortcutAction, + // return... "command": "unbound" + return static_cast(UnboundKey); + } + + if (!val.Args()) + { + // No args to serialize, + // output something like... "command": "copy" + return static_cast(shortcutActionIter->second); + } + else + { + // Serialize any set args, + // output something like... "command": { "action": "copy", "singleLine": false } + Json::Value result{ Json::ValueType::objectValue }; + + // Set the action args, if any + const auto actionArgSerializerIter{ argSerializerMap.find(val.Action()) }; + if (actionArgSerializerIter != argSerializerMap.end()) + { + auto pfn{ actionArgSerializerIter->second.second }; + if (pfn) + { + result = pfn(val.Args()); + } + } + + // Set the "action" part + result[static_cast(ActionKey)] = static_cast(shortcutActionIter->second); + + return result; + } + } + + // "command": "unbound" + return static_cast(UnboundKey); + } + com_ptr ActionAndArgs::Copy() const { auto copy{ winrt::make_self() }; diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h index 5817f595ab8..b0f07080095 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h @@ -15,6 +15,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static const std::map> ActionKeyNamesMap; static winrt::com_ptr FromJson(const Json::Value& json, std::vector& warnings); + static Json::Value ToJson(const Model::ActionAndArgs& val); ActionAndArgs() = default; ActionAndArgs(ShortcutAction action, IActionArgs args) : diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 9565d3d8132..c778bb511d6 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -41,43 +41,43 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { std::wstringstream ss; - if (!_Profile.empty()) + if (!Profile().empty()) { - ss << fmt::format(L"profile: {}, ", _Profile); + ss << fmt::format(L"profile: {}, ", Profile()); } - else if (_ProfileIndex) + else if (ProfileIndex()) { - ss << fmt::format(L"profile index: {}, ", _ProfileIndex.Value()); + ss << fmt::format(L"profile index: {}, ", ProfileIndex().Value()); } - if (!_Commandline.empty()) + if (!Commandline().empty()) { - ss << fmt::format(L"commandline: {}, ", _Commandline); + ss << fmt::format(L"commandline: {}, ", Commandline()); } - if (!_StartingDirectory.empty()) + if (!StartingDirectory().empty()) { - ss << fmt::format(L"directory: {}, ", _StartingDirectory); + ss << fmt::format(L"directory: {}, ", StartingDirectory()); } - if (!_TabTitle.empty()) + if (!TabTitle().empty()) { - ss << fmt::format(L"title: {}, ", _TabTitle); + ss << fmt::format(L"title: {}, ", TabTitle()); } - if (_TabColor) + if (TabColor()) { - const til::color tabColor{ _TabColor.Value() }; + const til::color tabColor{ TabColor().Value() }; ss << fmt::format(L"tabColor: {}, ", tabColor.ToHexString(true)); } - if (!_ColorScheme.empty()) + if (!ColorScheme().empty()) { - ss << fmt::format(L"colorScheme: {}, ", _ColorScheme); + ss << fmt::format(L"colorScheme: {}, ", ColorScheme()); } - if (_SuppressApplicationTitle) + if (SuppressApplicationTitle()) { - if (_SuppressApplicationTitle.Value()) + if (SuppressApplicationTitle().Value()) { ss << fmt::format(L"suppress application title, "); } @@ -101,37 +101,37 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { std::wstringstream ss; - if (!_Profile.empty()) + if (!Profile().empty()) { - ss << fmt::format(L"--profile \"{}\" ", _Profile); + ss << fmt::format(L"--profile \"{}\" ", Profile()); } // The caller is always expected to provide the evaluated profile in the // NewTerminalArgs, not the index // - // else if (_ProfileIndex) + // else if (ProfileIndex()) // { - // ss << fmt::format(L"profile index: {}, ", _ProfileIndex.Value()); + // ss << fmt::format(L"profile index: {}, ", ProfileIndex().Value()); // } - if (!_StartingDirectory.empty()) + if (!StartingDirectory().empty()) { - ss << fmt::format(L"--startingDirectory \"{}\" ", _StartingDirectory); + ss << fmt::format(L"--startingDirectory \"{}\" ", StartingDirectory()); } - if (!_TabTitle.empty()) + if (!TabTitle().empty()) { - ss << fmt::format(L"--title \"{}\" ", _TabTitle); + ss << fmt::format(L"--title \"{}\" ", TabTitle()); } - if (_TabColor) + if (TabColor()) { - const til::color tabColor{ _TabColor.Value() }; + const til::color tabColor{ TabColor().Value() }; ss << fmt::format(L"--tabColor \"{}\" ", tabColor.ToHexString(true)); } - if (_SuppressApplicationTitle) + if (SuppressApplicationTitle()) { - if (_SuppressApplicationTitle.Value()) + if (SuppressApplicationTitle().Value()) { ss << fmt::format(L"--suppressApplicationTitle "); } @@ -141,14 +141,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } - if (!_ColorScheme.empty()) + if (!ColorScheme().empty()) { - ss << fmt::format(L"--colorScheme \"{}\" ", _ColorScheme); + ss << fmt::format(L"--colorScheme \"{}\" ", ColorScheme()); } - if (!_Commandline.empty()) + if (!Commandline().empty()) { - ss << fmt::format(L"-- \"{}\" ", _Commandline); + ss << fmt::format(L"-- \"{}\" ", Commandline()); } auto s = ss.str(); @@ -165,7 +165,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { std::wstringstream ss; - if (_SingleLine) + if (SingleLine()) { ss << RS_(L"CopyTextAsSingleLineCommandKey").c_str(); } @@ -174,25 +174,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ss << RS_(L"CopyTextCommandKey").c_str(); } - if (_CopyFormatting != nullptr) + if (CopyFormatting()) { ss << L", copyFormatting: "; - if (_CopyFormatting.Value() == CopyFormat::All) + if (CopyFormatting().Value() == CopyFormat::All) { ss << L"all, "; } - else if (_CopyFormatting.Value() == static_cast(0)) + else if (CopyFormatting().Value() == static_cast(0)) { ss << L"none, "; } else { - if (WI_IsFlagSet(_CopyFormatting.Value(), CopyFormat::HTML)) + if (WI_IsFlagSet(CopyFormatting().Value(), CopyFormat::HTML)) { ss << L"html, "; } - if (WI_IsFlagSet(_CopyFormatting.Value(), CopyFormat::RTF)) + if (WI_IsFlagSet(CopyFormatting().Value(), CopyFormat::RTF)) { ss << L"rtf, "; } @@ -209,9 +209,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring NewTabArgs::GenerateName() const { winrt::hstring newTerminalArgsStr; - if (_TerminalArgs) + if (TerminalArgs()) { - newTerminalArgsStr = _TerminalArgs.GenerateName(); + newTerminalArgsStr = TerminalArgs().GenerateName(); } if (newTerminalArgsStr.empty()) @@ -226,14 +226,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring SwitchToTabArgs::GenerateName() const { return winrt::hstring{ - fmt::format(L"{}, index:{}", RS_(L"SwitchToTabCommandKey"), _TabIndex) + fmt::format(L"{}, index:{}", RS_(L"SwitchToTabCommandKey"), TabIndex()) }; } winrt::hstring ResizePaneArgs::GenerateName() const { winrt::hstring directionString; - switch (_ResizeDirection) + switch (ResizeDirection()) { case ResizeDirection::Left: directionString = RS_(L"DirectionLeft"); @@ -257,7 +257,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring MoveFocusArgs::GenerateName() const { winrt::hstring directionString; - switch (_FocusDirection) + switch (FocusDirection()) { case FocusDirection::Left: directionString = RS_(L"DirectionLeft"); @@ -286,21 +286,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // size" (or "Decrease font size"). If the amount delta has a greater // absolute value, we'll include it like" // * Decrease font size, amount: {delta}" - if (_Delta < 0) + if (Delta() < 0) { - return _Delta == -1 ? RS_(L"DecreaseFontSizeCommandKey") : - winrt::hstring{ - fmt::format(std::wstring_view(RS_(L"DecreaseFontSizeWithAmountCommandKey")), - -_Delta) - }; + return Delta() == -1 ? RS_(L"DecreaseFontSizeCommandKey") : + winrt::hstring{ + fmt::format(std::wstring_view(RS_(L"DecreaseFontSizeWithAmountCommandKey")), + -Delta()) + }; } else { - return _Delta == 1 ? RS_(L"IncreaseFontSizeCommandKey") : - winrt::hstring{ - fmt::format(std::wstring_view(RS_(L"IncreaseFontSizeWithAmountCommandKey")), - _Delta) - }; + return Delta() == 1 ? RS_(L"IncreaseFontSizeCommandKey") : + winrt::hstring{ + fmt::format(std::wstring_view(RS_(L"IncreaseFontSizeWithAmountCommandKey")), + Delta()) + }; } } @@ -309,7 +309,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // The string will be similar to the following: // * "Send Input: ...input..." - auto escapedInput = til::visualize_control_codes(_Input); + auto escapedInput = til::visualize_control_codes(Input()); auto name = fmt::format(std::wstring_view(RS_(L"SendInputCommandKey")), escapedInput); return winrt::hstring{ name }; } @@ -326,7 +326,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // will be omitted (as they're unused) std::wstringstream ss; - if (_SplitMode == SplitType::Duplicate) + if (SplitMode() == SplitType::Duplicate) { ss << std::wstring_view(RS_(L"DuplicatePaneCommandKey")); } @@ -338,7 +338,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // This text is intentionally _not_ localized, to attempt to mirror the // exact syntax that the property would have in JSON. - switch (_SplitStyle) + switch (SplitStyle()) { case SplitState::Vertical: ss << L"split: vertical, "; @@ -348,18 +348,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation break; } - if (_SplitSize != .5f) + if (SplitSize() != .5f) { - ss << L"size: " << (_SplitSize * 100) << L"%, "; + ss << L"size: " << (SplitSize() * 100) << L"%, "; } winrt::hstring newTerminalArgsStr; - if (_TerminalArgs) + if (TerminalArgs()) { - newTerminalArgsStr = _TerminalArgs.GenerateName(); + newTerminalArgsStr = TerminalArgs().GenerateName(); } - if (_SplitMode != SplitType::Duplicate && !newTerminalArgsStr.empty()) + if (SplitMode() != SplitType::Duplicate && !newTerminalArgsStr.empty()) { ss << newTerminalArgsStr.c_str(); ss << L", "; @@ -372,7 +372,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring OpenSettingsArgs::GenerateName() const { - switch (_Target) + switch (Target()) { case SettingsTarget::DefaultsFile: return RS_(L"OpenDefaultSettingsCommandKey"); @@ -389,11 +389,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring SetColorSchemeArgs::GenerateName() const { // "Set color scheme to "{_SchemeName}"" - if (!_SchemeName.empty()) + if (!SchemeName().empty()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"SetColorSchemeCommandKey")), - _SchemeName.c_str()) + SchemeName().c_str()) }; } return L""; @@ -403,9 +403,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // "Set tab color to #RRGGBB" // "Reset tab color" - if (_TabColor) + if (TabColor()) { - til::color tabColor{ _TabColor.Value() }; + til::color tabColor{ TabColor().Value() }; return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"SetTabColorCommandKey")), tabColor.ToHexString(true)) @@ -419,11 +419,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // "Rename tab to \"{_Title}\"" // "Reset tab title" - if (!_Title.empty()) + if (!Title().empty()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"RenameTabCommandKey")), - _Title.c_str()) + Title().c_str()) }; } return RS_(L"ResetTabNameCommandKey"); @@ -432,11 +432,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ExecuteCommandlineArgs::GenerateName() const { // "Run commandline "{_Commandline}" in this window" - if (!_Commandline.empty()) + if (!Commandline().empty()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"ExecuteCommandlineCommandKey")), - _Commandline.c_str()) + Commandline().c_str()) }; } return L""; @@ -444,12 +444,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring CloseOtherTabsArgs::GenerateName() const { - if (_Index) + if (Index()) { // "Close tabs other than index {0}" return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"CloseOtherTabsCommandKey")), - _Index.Value()) + Index().Value()) }; } return RS_(L"CloseOtherTabsDefaultCommandKey"); @@ -457,12 +457,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring CloseTabsAfterArgs::GenerateName() const { - if (_Index) + if (Index()) { // "Close tabs after index {0}" return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"CloseTabsAfterCommandKey")), - _Index.Value()) + Index().Value()) }; } return RS_(L"CloseTabsAfterDefaultCommandKey"); @@ -470,11 +470,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ScrollUpArgs::GenerateName() const { - if (_RowsToScroll) + if (RowsToScroll()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"ScrollUpSeveralRowsCommandKey")), - _RowsToScroll.Value()) + RowsToScroll().Value()) }; } return RS_(L"ScrollUpCommandKey"); @@ -482,11 +482,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ScrollDownArgs::GenerateName() const { - if (_RowsToScroll) + if (RowsToScroll()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"ScrollDownSeveralRowsCommandKey")), - _RowsToScroll.Value()) + RowsToScroll().Value()) }; } return RS_(L"ScrollDownCommandKey"); @@ -495,7 +495,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring MoveTabArgs::GenerateName() const { winrt::hstring directionString; - switch (_Direction) + switch (Direction()) { case MoveTabDirection::Forward: directionString = RS_(L"MoveTabDirectionForward"); @@ -512,7 +512,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring ToggleCommandPaletteArgs::GenerateName() const { - if (_LaunchMode == CommandPaletteLaunchMode::CommandLine) + if (LaunchMode() == CommandPaletteLaunchMode::CommandLine) { return RS_(L"ToggleCommandPaletteCommandLineModeCommandKey"); } @@ -521,7 +521,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring FindMatchArgs::GenerateName() const { - switch (_Direction) + switch (Direction()) { case FindMatchDirection::Next: return winrt::hstring{ RS_(L"FindNextCommandKey") }; @@ -534,9 +534,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring NewWindowArgs::GenerateName() const { winrt::hstring newTerminalArgsStr; - if (_TerminalArgs) + if (TerminalArgs()) { - newTerminalArgsStr = _TerminalArgs.GenerateName(); + newTerminalArgsStr = TerminalArgs().GenerateName(); } if (newTerminalArgsStr.empty()) @@ -550,23 +550,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring PrevTabArgs::GenerateName() const { - if (!_SwitcherMode) + if (!SwitcherMode()) { return RS_(L"PrevTabCommandKey"); } - const auto mode = _SwitcherMode.Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order"; + const auto mode = SwitcherMode().Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order"; return winrt::hstring(fmt::format(L"{}, {}", RS_(L"PrevTabCommandKey"), mode)); } winrt::hstring NextTabArgs::GenerateName() const { - if (!_SwitcherMode) + if (!SwitcherMode()) { return RS_(L"NextTabCommandKey"); } - const auto mode = _SwitcherMode.Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order"; + const auto mode = SwitcherMode().Value() == TabSwitcherMode::MostRecentlyUsed ? L"most recently used" : L"in order"; return winrt::hstring(fmt::format(L"{}, {}", RS_(L"NextTabCommandKey"), mode)); } @@ -574,11 +574,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // "Rename window to \"{_Name}\"" // "Clear window name" - if (!_Name.empty()) + if (!Name().empty()) { return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"RenameWindowCommandKey")), - _Name.c_str()) + Name().c_str()) }; } return RS_(L"ResetWindowNameCommandKey"); @@ -591,10 +591,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // "Summon the Terminal window" // "Summon the Terminal window, name:\"{_Name}\"" - if (!_Name.empty()) + if (!Name().empty()) { ss << L", name: "; - ss << std::wstring_view(_Name); + ss << std::wstring_view(Name()); } return winrt::hstring{ ss.str() }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 7d89207e882..bd93e815509 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -41,6 +41,14 @@ #include "TerminalSettingsSerializationHelpers.h" +#define ACTION_ARG(type, name, ...) \ +public: \ + type name() const noexcept { return _##name.has_value() ? _##name.value() : type{ __VA_ARGS__ }; } \ + void name(const type& value) noexcept { _##name = value; } \ + \ +private: \ + std::optional _##name{ std::nullopt }; + // Notes on defining ActionArgs and ActionEventArgs: // * All properties specific to an action should be defined as an ActionArgs // class that implements IActionArgs @@ -79,14 +87,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation NewTerminalArgs() = default; NewTerminalArgs(int32_t& profileIndex) : _ProfileIndex{ profileIndex } {}; - WINRT_PROPERTY(winrt::hstring, Commandline, L""); - WINRT_PROPERTY(winrt::hstring, StartingDirectory, L""); - WINRT_PROPERTY(winrt::hstring, TabTitle, L""); - WINRT_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); - WINRT_PROPERTY(Windows::Foundation::IReference, ProfileIndex, nullptr); - WINRT_PROPERTY(winrt::hstring, Profile, L""); - WINRT_PROPERTY(Windows::Foundation::IReference, SuppressApplicationTitle, nullptr); - WINRT_PROPERTY(winrt::hstring, ColorScheme); + ACTION_ARG(winrt::hstring, Commandline, L""); + ACTION_ARG(winrt::hstring, StartingDirectory, L""); + ACTION_ARG(winrt::hstring, TabTitle, L""); + ACTION_ARG(Windows::Foundation::IReference, TabColor, nullptr); + ACTION_ARG(Windows::Foundation::IReference, ProfileIndex, nullptr); + ACTION_ARG(winrt::hstring, Profile, L""); + ACTION_ARG(Windows::Foundation::IReference, SuppressApplicationTitle, nullptr); + ACTION_ARG(winrt::hstring, ColorScheme); static constexpr std::string_view CommandlineKey{ "commandline" }; static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; @@ -126,6 +134,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme); return *args; } + static Json::Value ToJson(const Model::NewTerminalArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, CommandlineKey, args->_Commandline); + JsonUtils::SetValueForKey(json, StartingDirectoryKey, args->_StartingDirectory); + JsonUtils::SetValueForKey(json, TabTitleKey, args->_TabTitle); + JsonUtils::SetValueForKey(json, ProfileIndexKey, args->_ProfileIndex); + JsonUtils::SetValueForKey(json, ProfileKey, args->_Profile); + JsonUtils::SetValueForKey(json, TabColorKey, args->_TabColor); + JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); + JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme); + return json; + } Model::NewTerminalArgs Copy() const { auto copy{ winrt::make_self() }; @@ -141,15 +167,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Commandline, _StartingDirectory, _TabTitle, _TabColor, _ProfileIndex, _Profile, _SuppressApplicationTitle, _ColorScheme); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Commandline(), StartingDirectory(), TabTitle(), TabColor(), ProfileIndex(), Profile(), SuppressApplicationTitle(), ColorScheme()); } }; struct CopyTextArgs : public CopyTextArgsT { CopyTextArgs() = default; - WINRT_PROPERTY(bool, SingleLine, false); - WINRT_PROPERTY(Windows::Foundation::IReference, CopyFormatting, nullptr); + ACTION_ARG(bool, SingleLine, false); + ACTION_ARG(Windows::Foundation::IReference, CopyFormatting, nullptr); static constexpr std::string_view SingleLineKey{ "singleLine" }; static constexpr std::string_view CopyFormattingKey{ "copyFormatting" }; @@ -175,6 +201,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, CopyFormattingKey, args->_CopyFormatting); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, SingleLineKey, args->_SingleLine); + JsonUtils::SetValueForKey(json, CopyFormattingKey, args->_CopyFormatting); + return json; + } IActionArgs Copy() const { @@ -185,7 +223,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SingleLine, _CopyFormatting); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SingleLine(), CopyFormatting()); } }; @@ -215,6 +253,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation args->_TerminalArgs = NewTerminalArgs::FromJson(json); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + const auto args{ get_self(val) }; + return NewTerminalArgs::ToJson(args->_TerminalArgs); + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -223,7 +270,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TerminalArgs); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TerminalArgs()); } }; @@ -232,7 +279,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation SwitchToTabArgs() = default; SwitchToTabArgs(uint32_t& tabIndex) : _TabIndex{ tabIndex } {}; - WINRT_PROPERTY(uint32_t, TabIndex, 0); + ACTION_ARG(uint32_t, TabIndex, 0); static constexpr std::string_view TabIndexKey{ "index" }; @@ -255,6 +302,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, TabIndexKey, args->_TabIndex); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, TabIndexKey, args->_TabIndex); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -263,14 +321,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TabIndex); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TabIndex()); } }; struct ResizePaneArgs : public ResizePaneArgsT { ResizePaneArgs() = default; - WINRT_PROPERTY(Model::ResizeDirection, ResizeDirection, ResizeDirection::None); + ACTION_ARG(Model::ResizeDirection, ResizeDirection, ResizeDirection::None); static constexpr std::string_view DirectionKey{ "direction" }; @@ -291,7 +349,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, DirectionKey, args->_ResizeDirection); - if (args->_ResizeDirection == ResizeDirection::None) + if (args->ResizeDirection() == ResizeDirection::None) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } @@ -300,6 +358,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { *args, {} }; } } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, DirectionKey, args->_ResizeDirection); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -308,7 +377,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_ResizeDirection); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(ResizeDirection()); } }; @@ -318,7 +387,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation MoveFocusArgs(Model::FocusDirection direction) : _FocusDirection{ direction } {}; - WINRT_PROPERTY(Model::FocusDirection, FocusDirection, FocusDirection::None); + ACTION_ARG(Model::FocusDirection, FocusDirection, FocusDirection::None); static constexpr std::string_view DirectionKey{ "direction" }; @@ -339,7 +408,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, DirectionKey, args->_FocusDirection); - if (args->_FocusDirection == FocusDirection::None) + if (args->FocusDirection() == FocusDirection::None) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } @@ -348,6 +417,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { *args, {} }; } } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, DirectionKey, args->_FocusDirection); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -356,14 +436,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_FocusDirection); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(FocusDirection()); } }; struct AdjustFontSizeArgs : public AdjustFontSizeArgsT { AdjustFontSizeArgs() = default; - WINRT_PROPERTY(int32_t, Delta, 0); + ACTION_ARG(int32_t, Delta, 0); static constexpr std::string_view AdjustFontSizeDelta{ "delta" }; @@ -386,6 +466,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, AdjustFontSizeDelta, args->_Delta); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, AdjustFontSizeDelta, args->_Delta); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -394,14 +485,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Delta); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Delta()); } }; struct SendInputArgs : public SendInputArgsT { SendInputArgs() = default; - WINRT_PROPERTY(winrt::hstring, Input, L""); + ACTION_ARG(winrt::hstring, Input, L""); static constexpr std::string_view InputKey{ "input" }; @@ -421,12 +512,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, InputKey, args->_Input); - if (args->_Input.empty()) + if (args->Input().empty()) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, InputKey, args->_Input); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -435,7 +537,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Input); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Input()); } }; @@ -456,10 +558,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _TerminalArgs{ terminalArgs } {}; SplitPaneArgs(SplitType splitMode) : _SplitMode{ splitMode } {}; - WINRT_PROPERTY(SplitState, SplitStyle, SplitState::Automatic); + ACTION_ARG(SplitState, SplitStyle, SplitState::Automatic); WINRT_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr); - WINRT_PROPERTY(SplitType, SplitMode, SplitType::Manual); - WINRT_PROPERTY(double, SplitSize, .5); + ACTION_ARG(SplitType, SplitMode, SplitType::Manual); + ACTION_ARG(double, SplitSize, .5); static constexpr std::string_view SplitKey{ "split" }; static constexpr std::string_view SplitModeKey{ "splitMode" }; @@ -489,12 +591,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle); JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode); JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize); - if (args->_SplitSize >= 1 || args->_SplitSize <= 0) + if (args->SplitSize() >= 1 || args->SplitSize() <= 0) { return { nullptr, { SettingsLoadWarnings::InvalidSplitSize } }; } return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + const auto args{ get_self(val) }; + auto json{ NewTerminalArgs::ToJson(args->_TerminalArgs) }; + JsonUtils::SetValueForKey(json, SplitKey, args->_SplitStyle); + JsonUtils::SetValueForKey(json, SplitModeKey, args->_SplitMode); + JsonUtils::SetValueForKey(json, SplitSizeKey, args->_SplitSize); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -506,7 +621,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SplitStyle, _TerminalArgs, _SplitMode, _SplitSize); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitStyle(), TerminalArgs(), SplitMode(), SplitSize()); } }; @@ -515,7 +630,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation OpenSettingsArgs() = default; OpenSettingsArgs(const SettingsTarget& target) : _Target{ target } {} - WINRT_PROPERTY(SettingsTarget, Target, SettingsTarget::SettingsFile); + ACTION_ARG(SettingsTarget, Target, SettingsTarget::SettingsFile); static constexpr std::string_view TargetKey{ "target" }; @@ -538,6 +653,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, TargetKey, args->_Target); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, TargetKey, args->_Target); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -546,7 +672,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Target); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Target()); } }; @@ -555,7 +681,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation SetColorSchemeArgs() = default; SetColorSchemeArgs(winrt::hstring name) : _SchemeName{ name } {}; - WINRT_PROPERTY(winrt::hstring, SchemeName, L""); + ACTION_ARG(winrt::hstring, SchemeName, L""); static constexpr std::string_view NameKey{ "colorScheme" }; @@ -576,12 +702,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, args->_SchemeName); - if (args->_SchemeName.empty()) + if (args->SchemeName().empty()) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, NameKey, args->_SchemeName); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -590,14 +727,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SchemeName); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SchemeName()); } }; struct SetTabColorArgs : public SetTabColorArgsT { SetTabColorArgs() = default; - WINRT_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); + ACTION_ARG(Windows::Foundation::IReference, TabColor, nullptr); static constexpr std::string_view ColorKey{ "color" }; @@ -620,6 +757,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, ColorKey, args->_TabColor); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, ColorKey, args->_TabColor); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -628,14 +776,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TabColor); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TabColor()); } }; struct RenameTabArgs : public RenameTabArgsT { RenameTabArgs() = default; - WINRT_PROPERTY(winrt::hstring, Title, L""); + ACTION_ARG(winrt::hstring, Title, L""); static constexpr std::string_view TitleKey{ "title" }; @@ -658,6 +806,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, TitleKey, args->_Title); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, TitleKey, args->_Title); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -666,7 +825,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Title); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Title()); } }; @@ -675,7 +834,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ExecuteCommandlineArgs() = default; ExecuteCommandlineArgs(winrt::hstring commandline) : _Commandline{ commandline } {}; - WINRT_PROPERTY(winrt::hstring, Commandline, L""); + ACTION_ARG(winrt::hstring, Commandline, L""); static constexpr std::string_view CommandlineKey{ "commandline" }; @@ -696,12 +855,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline); - if (args->_Commandline.empty()) + if (args->Commandline().empty()) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, CommandlineKey, args->_Commandline); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -710,7 +880,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Commandline); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Commandline()); } }; @@ -719,7 +889,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation CloseOtherTabsArgs() = default; CloseOtherTabsArgs(uint32_t& tabIndex) : _Index{ tabIndex } {}; - WINRT_PROPERTY(Windows::Foundation::IReference, Index, nullptr); + ACTION_ARG(Windows::Foundation::IReference, Index, nullptr); static constexpr std::string_view IndexKey{ "index" }; @@ -742,6 +912,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, IndexKey, args->_Index); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, IndexKey, args->_Index); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -750,7 +931,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Index); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Index()); } }; @@ -759,7 +940,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation CloseTabsAfterArgs() = default; CloseTabsAfterArgs(uint32_t& tabIndex) : _Index{ tabIndex } {}; - WINRT_PROPERTY(Windows::Foundation::IReference, Index, nullptr); + ACTION_ARG(Windows::Foundation::IReference, Index, nullptr); static constexpr std::string_view IndexKey{ "index" }; @@ -782,6 +963,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, IndexKey, args->_Index); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, IndexKey, args->_Index); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -790,7 +982,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Index); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Index()); } }; @@ -799,7 +991,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation MoveTabArgs() = default; MoveTabArgs(MoveTabDirection direction) : _Direction{ direction } {}; - WINRT_PROPERTY(MoveTabDirection, Direction, MoveTabDirection::None); + ACTION_ARG(MoveTabDirection, Direction, MoveTabDirection::None); static constexpr std::string_view DirectionKey{ "direction" }; @@ -820,7 +1012,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction); - if (args->_Direction == MoveTabDirection::None) + if (args->Direction() == MoveTabDirection::None) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } @@ -829,6 +1021,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { *args, {} }; } } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, DirectionKey, args->_Direction); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -837,14 +1040,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Direction); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Direction()); } }; struct ScrollUpArgs : public ScrollUpArgsT { ScrollUpArgs() = default; - WINRT_PROPERTY(Windows::Foundation::IReference, RowsToScroll, nullptr); + ACTION_ARG(Windows::Foundation::IReference, RowsToScroll, nullptr); static constexpr std::string_view RowsToScrollKey{ "rowsToScroll" }; @@ -867,6 +1070,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, RowsToScrollKey, args->_RowsToScroll); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, RowsToScrollKey, args->_RowsToScroll); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -875,14 +1089,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_RowsToScroll); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(RowsToScroll()); } }; struct ScrollDownArgs : public ScrollDownArgsT { ScrollDownArgs() = default; - WINRT_PROPERTY(Windows::Foundation::IReference, RowsToScroll, nullptr); + ACTION_ARG(Windows::Foundation::IReference, RowsToScroll, nullptr); static constexpr std::string_view RowsToScrollKey{ "rowsToScroll" }; @@ -905,6 +1119,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, RowsToScrollKey, args->_RowsToScroll); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, RowsToScrollKey, args->_RowsToScroll); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -913,7 +1138,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_RowsToScroll); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(RowsToScroll()); } }; @@ -922,7 +1147,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ToggleCommandPaletteArgs() = default; // To preserve backward compatibility the default is Action. - WINRT_PROPERTY(CommandPaletteLaunchMode, LaunchMode, CommandPaletteLaunchMode::Action); + ACTION_ARG(CommandPaletteLaunchMode, LaunchMode, CommandPaletteLaunchMode::Action); static constexpr std::string_view LaunchModeKey{ "launchMode" }; @@ -945,6 +1170,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, LaunchModeKey, args->_LaunchMode); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, LaunchModeKey, args->_LaunchMode); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -953,7 +1189,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_LaunchMode); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(LaunchMode()); } }; @@ -962,7 +1198,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation FindMatchArgs() = default; FindMatchArgs(FindMatchDirection direction) : _Direction{ direction } {}; - WINRT_PROPERTY(FindMatchDirection, Direction, FindMatchDirection::None); + ACTION_ARG(FindMatchDirection, Direction, FindMatchDirection::None); static constexpr std::string_view DirectionKey{ "direction" }; @@ -983,7 +1219,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction); - if (args->_Direction == FindMatchDirection::None) + if (args->Direction() == FindMatchDirection::None) { return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; } @@ -992,6 +1228,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return { *args, {} }; } } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1000,7 +1247,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Direction); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Direction()); } }; @@ -1030,6 +1277,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation args->_TerminalArgs = NewTerminalArgs::FromJson(json); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + const auto args{ get_self(val) }; + return NewTerminalArgs::ToJson(args->_TerminalArgs); + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1038,14 +1294,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_TerminalArgs); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TerminalArgs()); } }; struct PrevTabArgs : public PrevTabArgsT { PrevTabArgs() = default; - WINRT_PROPERTY(Windows::Foundation::IReference, SwitcherMode, nullptr); + ACTION_ARG(Windows::Foundation::IReference, SwitcherMode, nullptr); static constexpr std::string_view SwitcherModeKey{ "tabSwitcherMode" }; public: @@ -1067,6 +1323,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SwitcherModeKey, args->_SwitcherMode); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, SwitcherModeKey, args->_SwitcherMode); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1075,14 +1342,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SwitcherMode); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SwitcherMode()); } }; struct NextTabArgs : public NextTabArgsT { NextTabArgs() = default; - WINRT_PROPERTY(Windows::Foundation::IReference, SwitcherMode, nullptr); + ACTION_ARG(Windows::Foundation::IReference, SwitcherMode, nullptr); static constexpr std::string_view SwitcherModeKey{ "tabSwitcherMode" }; public: @@ -1104,6 +1371,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SwitcherModeKey, args->_SwitcherMode); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, SwitcherModeKey, args->_SwitcherMode); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1112,14 +1390,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_SwitcherMode); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SwitcherMode()); } }; struct RenameWindowArgs : public RenameWindowArgsT { RenameWindowArgs() = default; - WINRT_PROPERTY(winrt::hstring, Name); + ACTION_ARG(winrt::hstring, Name); static constexpr std::string_view NameKey{ "name" }; public: @@ -1141,6 +1419,17 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, NameKey, args->_Name); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, NameKey, args->_Name); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1149,18 +1438,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Name()); } }; struct GlobalSummonArgs : public GlobalSummonArgsT { GlobalSummonArgs() = default; - WINRT_PROPERTY(winrt::hstring, Name, L""); - WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); - WINRT_PROPERTY(Model::MonitorBehavior, Monitor, Model::MonitorBehavior::ToMouse); - WINRT_PROPERTY(bool, ToggleVisibility, true); - WINRT_PROPERTY(uint32_t, DropdownDuration, 0); + ACTION_ARG(winrt::hstring, Name, L""); + ACTION_ARG(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); + ACTION_ARG(Model::MonitorBehavior, Monitor, Model::MonitorBehavior::ToMouse); + ACTION_ARG(bool, ToggleVisibility, true); + ACTION_ARG(uint32_t, DropdownDuration, 0); static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view DesktopKey{ "desktop" }; @@ -1194,6 +1483,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); return { *args, {} }; } + static Json::Value ToJson(const IActionArgs& val) + { + if (!val) + { + return {}; + } + Json::Value json{ Json::ValueType::objectValue }; + const auto args{ get_self(val) }; + JsonUtils::SetValueForKey(json, NameKey, args->_Name); + JsonUtils::SetValueForKey(json, DesktopKey, args->_Desktop); + JsonUtils::SetValueForKey(json, MonitorKey, args->_Monitor); + JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration); + JsonUtils::SetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); + return json; + } IActionArgs Copy() const { auto copy{ winrt::make_self() }; @@ -1218,7 +1522,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } size_t Hash() const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Name, _Desktop, _Monitor, _DropdownDuration, _ToggleVisibility); + return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Name(), Desktop(), Monitor(), DropdownDuration(), ToggleVisibility()); } }; diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index 9aef9db5110..e33638cffd0 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -65,7 +65,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // population void AddAction(const Model::Command& cmd); + + // JSON + static com_ptr FromJson(const Json::Value& json); std::vector LayerJson(const Json::Value& json); + Json::Value ToJson() const; // modification bool RebindKeys(Control::KeyChord const& oldKeys, Control::KeyChord const& newKeys); diff --git a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp index cda5f929689..1358c786590 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp @@ -19,6 +19,13 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { + com_ptr ActionMap::FromJson(const Json::Value& json) + { + auto result = make_self(); + result->LayerJson(json); + return result; + } + // Method Description: // - Deserialize an ActionMap from the array `json`. The json array should contain // an array of serialized `Command` objects. @@ -49,6 +56,41 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return warnings; } + Json::Value ActionMap::ToJson() const + { + Json::Value actionList{ Json::ValueType::arrayValue }; + + // Serialize all standard Command objects in the current layer + for (const auto& [_, cmd] : _ActionMap) + { + // Command serializes to an array of JSON objects. + // This is because a Command may have multiple key chords associated with it. + // The name and icon are only serialized in the first object. + // Example: + // { "name": "Custom Copy", "command": "copy", "keys": "ctrl+c" } + // { "command": "copy", "keys": "ctrl+shift+c" } + // { "command": "copy", "keys": "ctrl+ins" } + const auto cmdImpl{ winrt::get_self(cmd) }; + const auto& cmdJsonArray{ cmdImpl->ToJson() }; + for (const auto& cmdJson : cmdJsonArray) + { + actionList.append(cmdJson); + } + } + + // Serialize all nested Command objects added in the current layer + for (const auto& [_, cmd] : _NestedCommands) + { + const auto cmdImpl{ winrt::get_self(cmd) }; + const auto& cmdJsonArray{ cmdImpl->ToJson() }; + for (const auto& cmdJson : cmdJsonArray) + { + actionList.append(cmdJson); + } + } + return actionList; + } + // Method Description: // - Takes the KeyModifier flags from Terminal and maps them to the Windows WinRT types // Return Value: diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index bc710a756b9..1f518817b04 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -75,3 +75,30 @@ ON_ALL_ACTIONS(OpenWindowRenamer) \ ON_ALL_ACTIONS(GlobalSummon) \ ON_ALL_ACTIONS(QuakeMode) + +#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \ + ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \ + ON_ALL_ACTIONS_WITH_ARGS(CloseOtherTabs) \ + ON_ALL_ACTIONS_WITH_ARGS(CloseTabsAfter) \ + ON_ALL_ACTIONS_WITH_ARGS(CopyText) \ + ON_ALL_ACTIONS_WITH_ARGS(ExecuteCommandline) \ + ON_ALL_ACTIONS_WITH_ARGS(FindMatch) \ + ON_ALL_ACTIONS_WITH_ARGS(GlobalSummon) \ + ON_ALL_ACTIONS_WITH_ARGS(MoveFocus) \ + ON_ALL_ACTIONS_WITH_ARGS(MoveTab) \ + ON_ALL_ACTIONS_WITH_ARGS(NewTab) \ + ON_ALL_ACTIONS_WITH_ARGS(NewWindow) \ + ON_ALL_ACTIONS_WITH_ARGS(NextTab) \ + ON_ALL_ACTIONS_WITH_ARGS(OpenSettings) \ + ON_ALL_ACTIONS_WITH_ARGS(PrevTab) \ + ON_ALL_ACTIONS_WITH_ARGS(RenameTab) \ + ON_ALL_ACTIONS_WITH_ARGS(RenameWindow) \ + ON_ALL_ACTIONS_WITH_ARGS(ResizePane) \ + ON_ALL_ACTIONS_WITH_ARGS(ScrollDown) \ + ON_ALL_ACTIONS_WITH_ARGS(ScrollUp) \ + ON_ALL_ACTIONS_WITH_ARGS(SendInput) \ + ON_ALL_ACTIONS_WITH_ARGS(SetColorScheme) \ + ON_ALL_ACTIONS_WITH_ARGS(SetTabColor) \ + ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \ + ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \ + ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index bd83fd44970..65b6d0acfde 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -1345,15 +1345,5 @@ Json::Value CascadiaSettings::ToJson() const } json[JsonKey(SchemesKey)] = schemes; - // "actions"/"keybindings" will be whatever blob we had in the file - if (_userSettings.isMember(JsonKey(LegacyKeybindingsKey))) - { - json[JsonKey(LegacyKeybindingsKey)] = _userSettings[JsonKey(LegacyKeybindingsKey)]; - } - if (_userSettings.isMember(JsonKey(ActionsKey))) - { - json[JsonKey(ActionsKey)] = _userSettings[JsonKey(ActionsKey)]; - } - return json; } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 0f954225977..ee560b78d42 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -48,6 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation command->_IterateOn = _IterateOn; command->_originalJson = _originalJson; + command->_nestedCommand = _nestedCommand; if (HasNestedCommands()) { command->_subcommands = winrt::single_threaded_map(); @@ -383,6 +384,65 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return warnings; } + // Function Description: + // - Serialize the Command into an array of json actions + // Arguments: + // - + // Return Value: + // - an array of serialized actions + Json::Value Command::ToJson() const + { + Json::Value cmdList{ Json::ValueType::arrayValue }; + + if (_nestedCommand) + { + // handle nested command + // For nested commands, we can trust _originalJson to be correct. + // In fact, we _need_ to use it here because we don't actually deserialize `iterateOn` + // until we expand the command. + cmdList.append(_originalJson); + } + else if (_keyMappings.empty()) + { + // only write out one command + Json::Value cmdJson{ Json::ValueType::objectValue }; + JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath); + JsonUtils::SetValueForKey(cmdJson, NameKey, _name); + + if (_ActionAndArgs) + { + cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs); + } + + cmdList.append(cmdJson); + } + else + { + // we'll write out one command per key mapping + for (auto keys{ _keyMappings.begin() }; keys != _keyMappings.end(); ++keys) + { + Json::Value cmdJson{ Json::ValueType::objectValue }; + + if (keys == _keyMappings.begin()) + { + // First iteration also writes icon and name + JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath); + JsonUtils::SetValueForKey(cmdJson, NameKey, _name); + } + + if (_ActionAndArgs) + { + cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs); + } + + JsonUtils::SetValueForKey(cmdJson, KeysKey, *keys); + cmdList.append(cmdJson); + } + } + + return cmdList; + } + // Function Description: // - Helper to escape a string as a json string. This function will also // trim off the leading and trailing double-quotes, so the output string diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 41125b73a20..f3cc751e308 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -49,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static std::vector LayerJson(Windows::Foundation::Collections::IMap& commands, const Json::Value& json); + Json::Value ToJson() const; + bool HasNestedCommands() const; bool IsNestedCommand() const noexcept; Windows::Foundation::Collections::IMapView NestedCommands() const; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 63672d31292..39c370228d0 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -411,10 +411,6 @@ Json::Value GlobalAppSettings::ToJson() const JsonUtils::SetValueForKey(json, DetectURLsKey, _DetectURLs); // clang-format on - // TODO GH#8100: keymap needs to be serialized here - // For deserialization, we iterate over each action in the Json and interpret it as a keybinding, then as a command. - // Converting this back to JSON is a problem because we have no way to know if a Command and Keybinding come from - // the same entry. - + json[JsonKey(ActionsKey)] = _actionMap->ToJson(); return json; } diff --git a/src/cascadia/TerminalSettingsModel/JsonUtils.h b/src/cascadia/TerminalSettingsModel/JsonUtils.h index 1e2f5193bb2..591688ba4c0 100644 --- a/src/cascadia/TerminalSettingsModel/JsonUtils.h +++ b/src/cascadia/TerminalSettingsModel/JsonUtils.h @@ -77,6 +77,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils { static constexpr std::optional EmptyV() { return std::nullopt; } static constexpr bool HasValue(const std::optional& o) { return o.has_value(); } + // We can return a reference here because the original value is stored inside an std::optional static constexpr auto&& Value(const std::optional& o) { return *o; } }; @@ -85,7 +86,8 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils { static constexpr ::winrt::Windows::Foundation::IReference EmptyV() { return nullptr; } static constexpr bool HasValue(const ::winrt::Windows::Foundation::IReference& o) { return static_cast(o); } - static constexpr auto&& Value(const ::winrt::Windows::Foundation::IReference& o) { return o.Value(); } + // We CANNOT return a reference here because IReference does NOT return a reference to its internal memory + static constexpr auto Value(const ::winrt::Windows::Foundation::IReference& o) { return o.Value(); } }; class SerializationError : public std::runtime_error