Add setTabColor and openTabColorPicker actions (#6567)

## Summary of the Pull Request

Adds a pair of `ShortcutAction`s for setting the tab color.
* `setTabColor`: This changes the color of the current tab to the provided color, or can be used to clear the color.
* `openTabColorPicker`: This keybinding immediately activates the tab color picker for the currently focused tab.

## References

## PR Checklist
* [x] scratches my own itch
* [x] I work here
* [x] Tests added/passed
* [x] https://github.com/MicrosoftDocs/terminal/pull/69

## Detailed Description of the Pull Request / Additional comments

## Validation Steps Performed
* hey look there are tests
* Tested with the following:
```json

        // { "command": "setTabColor", "keys": [ "alt+c" ] },
        { "keys": "ctrl+alt+c", "command": { "action": "setTabColor", "color": "#123456" } },
        { "keys": "alt+shift+c", "command": { "action": "setTabColor", "color": null} },
        { "keys": "alt+c", "command": "openTabColorPicker" },
```
This commit is contained in:
Mike Griese 2020-06-25 08:06:21 -05:00 committed by GitHub
parent 9215b5282d
commit a3a9df82b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 207 additions and 13 deletions

View File

@ -8,6 +8,7 @@ abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST ABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
ABE ABE
BBGGRR
BBBBBBBBBBBBBBDDDD BBBBBBBBBBBBBBDDDD
QQQQQQQQQQABCDEFGHIJ QQQQQQQQQQABCDEFGHIJ
QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ

View File

@ -56,6 +56,8 @@
"switchToTab", "switchToTab",
"toggleFullscreen", "toggleFullscreen",
"find", "find",
"setTabColor",
"openTabColorPicker",
"unbound" "unbound"
], ],
"type": "string" "type": "string"
@ -257,6 +259,22 @@
} }
] ]
}, },
"SetTabColorAction": {
"description": "Arguments corresponding to a Set Tab Color Action",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "setTabColor" },
"color": {
"$ref": "#/definitions/Color",
"default": null,
"description": "If provided, will set the tab's color to the given value. If omitted, will reset the tab's color."
}
}
}
]
},
"Keybinding": { "Keybinding": {
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
@ -272,6 +290,7 @@
{ "$ref": "#/definitions/ResizePaneAction" }, { "$ref": "#/definitions/ResizePaneAction" },
{ "$ref": "#/definitions/SplitPaneAction" }, { "$ref": "#/definitions/SplitPaneAction" },
{ "$ref": "#/definitions/OpenSettingsAction" }, { "$ref": "#/definitions/OpenSettingsAction" },
{ "$ref": "#/definitions/SetTabColorAction" },
{ "type": "null" } { "type": "null" }
] ]
}, },

View File

@ -44,6 +44,8 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestStringOverload); TEST_METHOD(TestStringOverload);
TEST_METHOD(TestSetTabColorArgs);
TEST_CLASS_SETUP(ClassSetup) TEST_CLASS_SETUP(ClassSetup)
{ {
InitializeJsonReader(); InitializeJsonReader();
@ -400,6 +402,63 @@ namespace TerminalAppLocalTests
} }
} }
void KeyBindingsTests::TestSetTabColorArgs()
{
const std::string bindings0String{ R"([
{ "keys": ["ctrl+c"], "command": { "action": "setTabColor", "color": null } },
{ "keys": ["ctrl+d"], "command": { "action": "setTabColor", "color": "#123456" } },
{ "keys": ["ctrl+e"], "command": { "action": "setTabColor", "color": "thisStringObviouslyWontWork" } },
{ "keys": ["ctrl+f"], "command": "setTabColor" },
])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto appKeyBindings = winrt::make_self<implementation::AppKeyBindings>();
VERIFY_IS_NOT_NULL(appKeyBindings);
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
appKeyBindings->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(4u, appKeyBindings->_keyShortcuts.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_IS_NULL(realArgs.TabColor());
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('D') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// 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<uint32_t>(til::color(0x563412)), realArgs.TabColor().Value());
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_IS_NULL(realArgs.TabColor());
}
{
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_IS_NULL(realArgs.TabColor());
}
}
void KeyBindingsTests::TestStringOverload() void KeyBindingsTests::TestStringOverload()
{ {
const std::string bindings0String{ R"([ const std::string bindings0String{ R"([

View File

@ -33,6 +33,8 @@ static constexpr std::string_view ResizePaneKey{ "resizePane" };
static constexpr std::string_view MoveFocusKey{ "moveFocus" }; static constexpr std::string_view MoveFocusKey{ "moveFocus" };
static constexpr std::string_view FindKey{ "find" }; static constexpr std::string_view FindKey{ "find" };
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" }; static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
static constexpr std::string_view SetTabColorKey{ "setTabColor" };
static constexpr std::string_view OpenTabColorPickerKey{ "openTabColorPicker" };
static constexpr std::string_view RenameTabKey{ "renameTab" }; static constexpr std::string_view RenameTabKey{ "renameTab" };
namespace winrt::TerminalApp::implementation namespace winrt::TerminalApp::implementation
@ -69,6 +71,8 @@ namespace winrt::TerminalApp::implementation
{ OpenSettingsKey, ShortcutAction::OpenSettings }, { OpenSettingsKey, ShortcutAction::OpenSettings },
{ ToggleFullscreenKey, ShortcutAction::ToggleFullscreen }, { ToggleFullscreenKey, ShortcutAction::ToggleFullscreen },
{ SplitPaneKey, ShortcutAction::SplitPane }, { SplitPaneKey, ShortcutAction::SplitPane },
{ SetTabColorKey, ShortcutAction::SetTabColor },
{ OpenTabColorPickerKey, ShortcutAction::OpenTabColorPicker },
{ UnboundKey, ShortcutAction::Invalid }, { UnboundKey, ShortcutAction::Invalid },
{ FindKey, ShortcutAction::Find }, { FindKey, ShortcutAction::Find },
{ RenameTabKey, ShortcutAction::RenameTab } { RenameTabKey, ShortcutAction::RenameTab }
@ -99,6 +103,8 @@ namespace winrt::TerminalApp::implementation
{ ShortcutAction::OpenSettings, winrt::TerminalApp::implementation::OpenSettingsArgs::FromJson }, { ShortcutAction::OpenSettings, winrt::TerminalApp::implementation::OpenSettingsArgs::FromJson },
{ ShortcutAction::SetTabColor, winrt::TerminalApp::implementation::SetTabColorArgs::FromJson },
{ ShortcutAction::RenameTab, winrt::TerminalApp::implementation::RenameTabArgs::FromJson }, { ShortcutAction::RenameTab, winrt::TerminalApp::implementation::RenameTabArgs::FromJson },
{ ShortcutAction::Invalid, nullptr }, { ShortcutAction::Invalid, nullptr },

View File

@ -15,4 +15,5 @@
#include "AdjustFontSizeArgs.g.cpp" #include "AdjustFontSizeArgs.g.cpp"
#include "SplitPaneArgs.g.cpp" #include "SplitPaneArgs.g.cpp"
#include "OpenSettingsArgs.g.cpp" #include "OpenSettingsArgs.g.cpp"
#include "SetTabColorArgs.g.cpp"
#include "RenameTabArgs.g.cpp" #include "RenameTabArgs.g.cpp"

View File

@ -15,10 +15,12 @@
#include "AdjustFontSizeArgs.g.h" #include "AdjustFontSizeArgs.g.h"
#include "SplitPaneArgs.g.h" #include "SplitPaneArgs.g.h"
#include "OpenSettingsArgs.g.h" #include "OpenSettingsArgs.g.h"
#include "SetTabColorArgs.g.h"
#include "RenameTabArgs.g.h" #include "RenameTabArgs.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h" #include "../../cascadia/inc/cppwinrt_utils.h"
#include "Utils.h" #include "Utils.h"
#include "JsonUtils.h"
#include "TerminalWarnings.h" #include "TerminalWarnings.h"
// Notes on defining ActionArgs and ActionEventArgs: // Notes on defining ActionArgs and ActionEventArgs:
@ -443,6 +445,41 @@ namespace winrt::TerminalApp::implementation
} }
}; };
struct SetTabColorArgs : public SetTabColorArgsT<SetTabColorArgs>
{
SetTabColorArgs() = default;
GETSET_PROPERTY(Windows::Foundation::IReference<uint32_t>, TabColor, nullptr);
static constexpr std::string_view ColorKey{ "color" };
public:
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<SetTabColorArgs>();
if (otherAsUs)
{
return otherAsUs->_TabColor == _TabColor;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<SetTabColorArgs>();
std::optional<til::color> temp;
try
{
::TerminalApp::JsonUtils::GetOptionalColor(json, ColorKey, temp);
if (temp.has_value())
{
args->_TabColor = static_cast<uint32_t>(temp.value());
}
}
CATCH_LOG();
return { *args, {} };
}
};
struct RenameTabArgs : public RenameTabArgsT<RenameTabArgs> struct RenameTabArgs : public RenameTabArgsT<RenameTabArgs>
{ {
RenameTabArgs() = default; RenameTabArgs() = default;

View File

@ -103,6 +103,11 @@ namespace TerminalApp
SettingsTarget Target { get; }; SettingsTarget Target { get; };
}; };
[default_interface] runtimeclass SetTabColorArgs : IActionArgs
{
Windows.Foundation.IReference<UInt32> TabColor { get; };
};
[default_interface] runtimeclass RenameTabArgs : IActionArgs [default_interface] runtimeclass RenameTabArgs : IActionArgs
{ {
String Title { get; }; String Title { get; };

View File

@ -238,6 +238,45 @@ namespace winrt::TerminalApp::implementation
args.Handled(true); args.Handled(true);
} }
void TerminalPage::_HandleSetTabColor(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
std::optional<til::color> tabColor;
if (const auto& realArgs = args.ActionArgs().try_as<TerminalApp::SetTabColorArgs>())
{
if (realArgs.TabColor() != nullptr)
{
tabColor = realArgs.TabColor().Value();
}
}
auto activeTab = _GetFocusedTab();
if (activeTab)
{
if (tabColor.has_value())
{
activeTab->SetTabColor(tabColor.value());
}
else
{
activeTab->ResetTabColor();
}
}
args.Handled(true);
}
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
auto activeTab = _GetFocusedTab();
if (activeTab)
{
activeTab->ActivateColorPicker();
}
args.Handled(true);
}
void TerminalPage::_HandleRenameTab(const IInspectable& /*sender*/, void TerminalPage::_HandleRenameTab(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args) const TerminalApp::ActionEventArgs& args)
{ {
@ -262,5 +301,4 @@ namespace winrt::TerminalApp::implementation
} }
args.Handled(true); args.Handled(true);
} }
} }

View File

@ -159,6 +159,16 @@ namespace winrt::TerminalApp::implementation
_ToggleFullscreenHandlers(*this, *eventArgs); _ToggleFullscreenHandlers(*this, *eventArgs);
break; break;
} }
case ShortcutAction::SetTabColor:
{
_SetTabColorHandlers(*this, *eventArgs);
break;
}
case ShortcutAction::OpenTabColorPicker:
{
_OpenTabColorPickerHandlers(*this, *eventArgs);
break;
}
case ShortcutAction::RenameTab: case ShortcutAction::RenameTab:
{ {
_RenameTabHandlers(*this, *eventArgs); _RenameTabHandlers(*this, *eventArgs);

View File

@ -47,6 +47,8 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(Find, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs); TYPED_EVENT(Find, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(MoveFocus, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs); TYPED_EVENT(MoveFocus, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(ToggleFullscreen, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs); TYPED_EVENT(ToggleFullscreen, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(SetTabColor, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(OpenTabColorPicker,TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
TYPED_EVENT(RenameTab, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs); TYPED_EVENT(RenameTab, TerminalApp::ShortcutActionDispatch, TerminalApp::ActionEventArgs);
// clang-format on // clang-format on

View File

@ -4,9 +4,6 @@ import "../ActionArgs.idl";
namespace TerminalApp namespace TerminalApp
{ {
// TODO: GH#1069 - Many of these shortcut actions are "legacy" now that we
// have support for arbitrary args (#1142). We should remove them, and our
// legacy deserializers.
enum ShortcutAction enum ShortcutAction
{ {
Invalid = 0, Invalid = 0,
@ -35,6 +32,8 @@ namespace TerminalApp
MoveFocus, MoveFocus,
Find, Find,
ToggleFullscreen, ToggleFullscreen,
SetTabColor,
OpenTabColorPicker,
OpenSettings, OpenSettings,
RenameTab RenameTab
}; };
@ -74,7 +73,8 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> Find; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> Find;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> MoveFocus; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> MoveFocus;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> ToggleFullscreen; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> ToggleFullscreen;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> SetTabColor;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> OpenTabColorPicker;
event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> RenameTab; event Windows.Foundation.TypedEventHandler<ShortcutActionDispatch, ActionEventArgs> RenameTab;
} }
} }

View File

@ -520,7 +520,7 @@ namespace winrt::TerminalApp::implementation
chooseColorMenuItem.Click([weakThis](auto&&, auto&&) { chooseColorMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() }) if (auto tab{ weakThis.get() })
{ {
tab->_tabColorPickup.ShowAt(tab->_tabViewItem); tab->ActivateColorPicker();
} }
}); });
chooseColorMenuItem.Text(RS_(L"TabColorChoose")); chooseColorMenuItem.Text(RS_(L"TabColorChoose"));
@ -530,14 +530,14 @@ namespace winrt::TerminalApp::implementation
_tabColorPickup.ColorSelected([weakThis](auto newTabColor) { _tabColorPickup.ColorSelected([weakThis](auto newTabColor) {
if (auto tab{ weakThis.get() }) if (auto tab{ weakThis.get() })
{ {
tab->_SetTabColor(newTabColor); tab->SetTabColor(newTabColor);
} }
}); });
_tabColorPickup.ColorCleared([weakThis]() { _tabColorPickup.ColorCleared([weakThis]() {
if (auto tab{ weakThis.get() }) if (auto tab{ weakThis.get() })
{ {
tab->_ResetTabColor(); tab->ResetTabColor();
} }
}); });
@ -718,7 +718,7 @@ namespace winrt::TerminalApp::implementation
// - color: the shiny color the user picked for their tab // - color: the shiny color the user picked for their tab
// Return Value: // Return Value:
// - <none> // - <none>
void Tab::_SetTabColor(const winrt::Windows::UI::Color& color) void Tab::SetTabColor(const winrt::Windows::UI::Color& color)
{ {
auto weakThis{ get_weak() }; auto weakThis{ get_weak() };
@ -779,7 +779,7 @@ namespace winrt::TerminalApp::implementation
// - <none> // - <none>
// Return Value: // Return Value:
// - <none> // - <none>
void Tab::_ResetTabColor() void Tab::ResetTabColor()
{ {
auto weakThis{ get_weak() }; auto weakThis{ get_weak() };
@ -817,6 +817,17 @@ namespace winrt::TerminalApp::implementation
}); });
} }
// Method Description:
// - Display the tab color picker at the location of the TabViewItem for this tab.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::ActivateColorPicker()
{
_tabColorPickup.ShowAt(_tabViewItem);
}
// Method Description: // Method Description:
// Toggles the visual state of the tab view item, // Toggles the visual state of the tab view item,
// so that changes to the tab color are reflected immediately // so that changes to the tab color are reflected immediately

View File

@ -56,6 +56,10 @@ namespace winrt::TerminalApp::implementation
std::optional<winrt::Windows::UI::Color> GetTabColor(); std::optional<winrt::Windows::UI::Color> GetTabColor();
void SetTabColor(const winrt::Windows::UI::Color& color);
void ResetTabColor();
void ActivateColorPicker();
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>); WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>); DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
@ -83,8 +87,6 @@ namespace winrt::TerminalApp::implementation
void _Focus(); void _Focus();
void _CreateContextMenu(); void _CreateContextMenu();
void _SetTabColor(const winrt::Windows::UI::Color& color);
void _ResetTabColor();
void _RefreshVisualState(); void _RefreshVisualState();
void _BindEventHandlers(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control) noexcept; void _BindEventHandlers(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control) noexcept;

View File

@ -755,6 +755,8 @@ namespace winrt::TerminalApp::implementation
_actionDispatch->Find({ this, &TerminalPage::_HandleFind }); _actionDispatch->Find({ this, &TerminalPage::_HandleFind });
_actionDispatch->ResetFontSize({ this, &TerminalPage::_HandleResetFontSize }); _actionDispatch->ResetFontSize({ this, &TerminalPage::_HandleResetFontSize });
_actionDispatch->ToggleFullscreen({ this, &TerminalPage::_HandleToggleFullscreen }); _actionDispatch->ToggleFullscreen({ this, &TerminalPage::_HandleToggleFullscreen });
_actionDispatch->SetTabColor({ this, &TerminalPage::_HandleSetTabColor });
_actionDispatch->OpenTabColorPicker({ this, &TerminalPage::_HandleOpenTabColorPicker });
_actionDispatch->RenameTab({ this, &TerminalPage::_HandleRenameTab }); _actionDispatch->RenameTab({ this, &TerminalPage::_HandleRenameTab });
} }

View File

@ -198,8 +198,9 @@ namespace winrt::TerminalApp::implementation
void _HandleFind(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); void _HandleFind(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleResetFontSize(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); void _HandleResetFontSize(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleToggleFullscreen(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); void _HandleToggleFullscreen(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleSetTabColor(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleOpenTabColorPicker(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleRenameTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); void _HandleRenameTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
#pragma endregion #pragma endregion
friend class TerminalAppLocalTests::TabTests; friend class TerminalAppLocalTests::TabTests;