diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 21211778fb95..aa5d18e44b39 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -180,6 +180,11 @@ "index": { "type": "integer", "description": "The index of the profile in the new tab dropdown (starting at 0)" + }, + "tabColor": { + "$ref": "#/definitions/Color", + "default": null, + "description": "If provided, will set the tab's color to the given value" } }, "type": "object" diff --git a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp index 6f74fed4d6b5..e70e69f1b2ca 100644 --- a/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/KeyBindingsTests.cpp @@ -408,7 +408,7 @@ namespace SettingsModelLocalTests // Verify the args have the expected value VERIFY_IS_NOT_NULL(realArgs.TabColor()); // Remember that COLORREFs are actually BBGGRR order, while the string is in #RRGGBB order - VERIFY_ARE_EQUAL(static_cast(til::color(0x563412)), realArgs.TabColor().Value()); + VERIFY_ARE_EQUAL(til::color(0x563412), til::color(realArgs.TabColor().Value())); } { KeyChord kc{ true, false, false, static_cast('F') }; diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index 57c4b4f0730f..b9261c1c6833 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include +#include "../types/inc/utils.hpp" #include "../TerminalApp/TerminalPage.h" #include "../TerminalApp/AppCommandlineArgs.h" @@ -335,7 +336,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); auto myCommand = myArgs.TerminalArgs().Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg \"", myCommand); @@ -350,7 +352,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); auto myCommand = myArgs.TerminalArgs().Commandline(); VERIFY_ARE_EQUAL(L"\" with spaces\"", myCommand); @@ -373,7 +376,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); auto myCommand = myArgs.TerminalArgs().Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg ; with spaces\"", myCommand); @@ -419,7 +423,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); } { @@ -438,7 +443,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile()); } @@ -458,7 +464,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_FALSE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory()); } @@ -478,7 +485,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline()); } @@ -498,7 +506,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); auto myCommand = myArgs.TerminalArgs().Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myCommand); @@ -519,7 +528,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); auto myCommand = myArgs.TerminalArgs().Commandline(); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\" another-arg \"more spaces in this one\"", myCommand); @@ -540,7 +550,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"Windows PowerShell", myArgs.TerminalArgs().Profile()); } @@ -560,7 +571,7 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); } @@ -580,11 +591,35 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); } + { + AppCommandlineArgs appArgs{}; + std::vector rawCommands{ L"wt.exe", subcommand, L"--tabColor", L"#009999" }; + const auto expectedColor = Microsoft::Console::Utils::ColorFromHexString("#009999"); + + _buildCommandlinesHelper(appArgs, 1u, rawCommands); + + VERIFY_ARE_EQUAL(1u, appArgs._startupActions.size()); + + auto actionAndArgs = appArgs._startupActions.at(0); + VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action()); + VERIFY_IS_NOT_NULL(actionAndArgs.Args()); + auto myArgs = actionAndArgs.Args().try_as(); + VERIFY_IS_NOT_NULL(myArgs); + VERIFY_IS_NOT_NULL(myArgs.TerminalArgs()); + VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); + VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); + VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); + VERIFY_IS_NOT_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_ARE_EQUAL(til::color(myArgs.TerminalArgs().TabColor().Value()), expectedColor); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); + VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); + } } void CommandlineTest::ParseSplitPaneIntoArgs() @@ -677,7 +712,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); @@ -704,7 +740,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"wsl -d Alpine", myArgs.TerminalArgs().Commandline()); VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); @@ -731,7 +768,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"wsl -d Alpine -H", myArgs.TerminalArgs().Commandline()); VERIFY_ARE_EQUAL(L"1", myArgs.TerminalArgs().Profile()); @@ -779,7 +817,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); } { @@ -798,7 +837,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_FALSE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"cmd", myArgs.TerminalArgs().Profile()); } @@ -818,7 +858,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_FALSE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"c:\\Foo", myArgs.TerminalArgs().StartingDirectory()); } @@ -838,7 +879,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"powershell.exe", myArgs.TerminalArgs().Commandline()); } @@ -858,7 +900,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"powershell.exe \"This is an arg with spaces\"", myArgs.TerminalArgs().Commandline()); } @@ -988,7 +1031,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); actionAndArgs = appArgs._startupActions.at(1); @@ -1000,7 +1044,8 @@ namespace TerminalAppLocalTests VERIFY_IS_FALSE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); VERIFY_ARE_EQUAL(L"slpit-pane", myArgs.TerminalArgs().Commandline()); } @@ -1090,7 +1135,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); } @@ -1109,7 +1155,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); } { @@ -1122,7 +1169,8 @@ namespace TerminalAppLocalTests VERIFY_IS_TRUE(myArgs.TerminalArgs().Commandline().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().StartingDirectory().empty()); VERIFY_IS_TRUE(myArgs.TerminalArgs().TabTitle().empty()); - VERIFY_IS_TRUE(myArgs.TerminalArgs().ProfileIndex() == nullptr); + VERIFY_IS_NULL(myArgs.TerminalArgs().TabColor()); + VERIFY_IS_NULL(myArgs.TerminalArgs().ProfileIndex()); VERIFY_IS_TRUE(myArgs.TerminalArgs().Profile().empty()); } } diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 306aa87ffbd6..5ac7c4193406 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -350,23 +350,20 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleSetTabColor(const IInspectable& /*sender*/, const ActionEventArgs& args) { - std::optional tabColor; + Windows::Foundation::IReference tabColor; if (const auto& realArgs = args.ActionArgs().try_as()) { - if (realArgs.TabColor() != nullptr) - { - tabColor = realArgs.TabColor().Value(); - } + tabColor = realArgs.TabColor(); } if (auto focusedTab = _GetFocusedTab()) { if (auto activeTab = _GetTerminalTabImpl(focusedTab)) { - if (tabColor.has_value()) + if (tabColor) { - activeTab->SetRuntimeTabColor(tabColor.value()); + activeTab->SetRuntimeTabColor(tabColor.Value()); } else { diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 684ee12cf7e2..7b7e21382655 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "AppLogic.h" #include "AppCommandlineArgs.h" +#include "../types/inc/utils.hpp" #include using namespace winrt::TerminalApp; @@ -355,6 +356,10 @@ void AppCommandlineArgs::_addNewTerminalArgs(AppCommandlineArgs::NewTerminalSubc _startingTitle, RS_A(L"CmdTitleArgDesc")); + subcommand.tabColorOption = subcommand.subcommand->add_option("--tabColor", + _startingTabColor, + RS_A(L"CmdTabColorArgDesc")); + // Using positionals_at_end allows us to support "wt new-tab -d wsl -d Ubuntu" // without CLI11 thinking that we've specified -d twice. // There's an alternate construction where we make all subcommands "prefix commands", @@ -416,6 +421,12 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT args.TabTitle(winrt::to_hstring(_startingTitle)); } + if (*subcommand.tabColorOption) + { + const auto tabColor = Microsoft::Console::Utils::ColorFromHexString(_startingTabColor); + args.TabColor(static_cast(tabColor)); + } + return args; } @@ -450,6 +461,7 @@ void AppCommandlineArgs::_resetStateToDefault() _profileName.clear(); _startingDirectory.clear(); _startingTitle.clear(); + _startingTabColor.clear(); _commandline.clear(); _splitVertical = false; diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 35f3c301cbf9..6a7d08b0e7de 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -54,6 +54,7 @@ class TerminalApp::AppCommandlineArgs final CLI::Option* profileNameOption; CLI::Option* startingDirectoryOption; CLI::Option* titleOption; + CLI::Option* tabColorOption; }; struct NewPaneSubcommand : public NewTerminalSubcommand @@ -74,6 +75,7 @@ class TerminalApp::AppCommandlineArgs final std::string _profileName; std::string _startingDirectory; std::string _startingTitle; + std::string _startingTabColor; // _commandline will contain the command line with which we'll be spawning a new terminal std::vector _commandline; diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 7690b12d0857..f978dd9991c7 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -295,6 +295,9 @@ Open the terminal with the provided title instead of the profile's set "title" {Locked="\"title\""} + + Open the tab with the specified color, in #rrggbb format + Display the application version diff --git a/src/cascadia/TerminalApp/TerminalSettings.cpp b/src/cascadia/TerminalApp/TerminalSettings.cpp index 662079d7dcb7..babcf02078ed 100644 --- a/src/cascadia/TerminalApp/TerminalSettings.cpp +++ b/src/cascadia/TerminalApp/TerminalSettings.cpp @@ -61,6 +61,10 @@ namespace winrt::TerminalApp::implementation { settings.StartingTitle(newTerminalArgs.TabTitle()); } + if (newTerminalArgs.TabColor()) + { + settings.StartingTabColor(static_cast(til::color(newTerminalArgs.TabColor().Value()))); + } } return { profileGuid, settings }; diff --git a/src/cascadia/TerminalApp/TerminalSettings.h b/src/cascadia/TerminalApp/TerminalSettings.h index 41c65e0c48d7..db4db524220e 100644 --- a/src/cascadia/TerminalApp/TerminalSettings.h +++ b/src/cascadia/TerminalApp/TerminalSettings.h @@ -70,6 +70,16 @@ namespace winrt::TerminalApp::implementation GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); + // When set, StartingTabColor allows to create a terminal with a "sticky" tab color. + // This color is prioritized above the TabColor (that is usually initialized based on profile settings). + // Due to this prioritization, the tab color will be preserved upon settings reload + // (even if the profile's tab color gets altered or removed). + // This property is expected to be passed only once upon terminal creation. + // TODO: to ensure that this property is not populated during settings reload, + // we should consider moving this property to a separate interface, + // passed to the terminal only upon creation. + GETSET_PROPERTY(Windows::Foundation::IReference, StartingTabColor, nullptr); + // ------------------------ End of Core Settings ----------------------- GETSET_PROPERTY(hstring, ProfileName); diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index be4321a31ce1..a41bac07bdaf 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -35,6 +35,7 @@ namespace Microsoft.Terminal.TerminalControl Boolean ForceVTInput; Windows.Foundation.IReference TabColor; + Windows.Foundation.IReference StartingTabColor; }; } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 05e4da5fdd61..01ef57bbd365 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -166,9 +166,15 @@ void Terminal::UpdateSettings(ICoreSettings settings) { _tabColor = til::color(settings.TabColor().Value() | 0xff000000); } + + if (!_startingTabColor && settings.StartingTabColor()) + { + _startingTabColor = til::color(settings.StartingTabColor().Value() | 0xff000000); + } + if (_pfnTabColorChanged) { - _pfnTabColorChanged(_tabColor); + _pfnTabColorChanged(GetTabColor()); } // TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we @@ -1171,9 +1177,12 @@ void Terminal::ClearPatternTree() noexcept _InvalidatePatternTree(oldTree); } +// Method Description: +// - Returns the tab color +// If the starting color exits, it's value is preferred const std::optional Terminal::GetTabColor() const noexcept { - return _tabColor; + return _startingTabColor.has_value() ? _startingTabColor : _tabColor; } BlinkingState& Terminal::GetBlinkingState() const noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index c691fc99f06d..395293f58737 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -235,6 +235,7 @@ class Microsoft::Terminal::Core::Terminal final : std::optional _title; std::wstring _startingTitle; std::optional _tabColor; + std::optional _startingTabColor; std::array _colorTable; COLORREF _defaultFg; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 4271ba8099f0..94d85549cc0f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -56,6 +56,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ss << fmt::format(L"title: {}, ", _TabTitle); } + + if (_TabColor) + { + const til::color tabColor{ _TabColor.Value() }; + ss << fmt::format(L"tabColor: {}, ", tabColor.ToHexString(true)); + } + auto s = ss.str(); if (s.empty()) { @@ -300,10 +307,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // "Reset tab color" if (_TabColor) { - til::color c{ _TabColor.Value() }; + til::color tabColor{ _TabColor.Value() }; return winrt::hstring{ fmt::format(std::wstring_view(RS_(L"SetTabColorCommandKey")), - c.ToHexString(true)) + tabColor.ToHexString(true)) }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 8562f587063f..7d23d9e93914 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -60,12 +60,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_PROPERTY(winrt::hstring, Commandline, L""); GETSET_PROPERTY(winrt::hstring, StartingDirectory, L""); GETSET_PROPERTY(winrt::hstring, TabTitle, L""); + GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); GETSET_PROPERTY(Windows::Foundation::IReference, ProfileIndex, nullptr); GETSET_PROPERTY(winrt::hstring, Profile, L""); static constexpr std::string_view CommandlineKey{ "commandline" }; static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; static constexpr std::string_view TabTitleKey{ "tabTitle" }; + static constexpr std::string_view TabColorKey{ "tabColor" }; static constexpr std::string_view ProfileIndexKey{ "index" }; static constexpr std::string_view ProfileKey{ "profile" }; @@ -77,6 +79,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return other.Commandline() == _Commandline && other.StartingDirectory() == _StartingDirectory && other.TabTitle() == _TabTitle && + other.TabColor() == _TabColor && other.ProfileIndex() == _ProfileIndex && other.Profile() == _Profile; }; @@ -89,6 +92,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, TabTitleKey, args->_TabTitle); JsonUtils::GetValueForKey(json, ProfileIndexKey, args->_ProfileIndex); JsonUtils::GetValueForKey(json, ProfileKey, args->_Profile); + JsonUtils::GetValueForKey(json, TabColorKey, args->_TabColor); return *args; } Model::NewTerminalArgs Copy() const @@ -97,6 +101,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_Commandline = _Commandline; copy->_StartingDirectory = _StartingDirectory; copy->_TabTitle = _TabTitle; + copy->_TabColor = _TabColor; copy->_ProfileIndex = _ProfileIndex; copy->_Profile = _Profile; return *copy; @@ -493,7 +498,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct SetTabColorArgs : public SetTabColorArgsT { SetTabColorArgs() = default; - GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); + GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); static constexpr std::string_view ColorKey{ "color" }; @@ -513,10 +518,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(); - if (const auto temp{ JsonUtils::GetValueForKey>(json, ColorKey) }) - { - args->_TabColor = static_cast(*temp); - } + JsonUtils::GetValueForKey(json, ColorKey, args->_TabColor); return { *args, {} }; } IActionArgs Copy() const diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 08fccbddae8b..5e93449d0b86 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -54,6 +54,7 @@ namespace Microsoft.Terminal.Settings.Model String Commandline; String StartingDirectory; String TabTitle; + Windows.Foundation.IReference TabColor; String Profile; // Either a GUID or a profile's name if the GUID isn't a match // ProfileIndex can be null (for "use the default"), so this needs to be // a IReference, so it's nullable @@ -130,7 +131,7 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass SetTabColorArgs : IActionArgs { - Windows.Foundation.IReference TabColor { get; }; + Windows.Foundation.IReference TabColor { get; }; }; [default_interface] runtimeclass RenameTabArgs : IActionArgs diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h index 237e8e1b3f63..e55e17b96125 100644 --- a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -62,6 +62,7 @@ namespace TerminalCoreUnitTests void ForceVTInput(bool) {} GETSET_PROPERTY(winrt::Windows::Foundation::IReference, TabColor, nullptr); + GETSET_PROPERTY(winrt::Windows::Foundation::IReference, StartingTabColor, nullptr); private: int32_t _historySize;