Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for per-profile tab colors #7162

Merged
merged 10 commits into from
Aug 7, 2020
4 changes: 2 additions & 2 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,11 @@ namespace winrt::TerminalApp::implementation
{
if (tabColor.has_value())
{
activeTab->SetTabColor(tabColor.value());
activeTab->SetRuntimeTabColor(tabColor.value());
}
else
{
activeTab->ResetTabColor();
activeTab->ResetRuntimeTabColor();
}
}
args.Handled(true);
Expand Down
10 changes: 10 additions & 0 deletions src/cascadia/TerminalApp/Profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImag
static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" };
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
static constexpr std::string_view TabColorKey{ "tabColor" };

Profile::Profile() :
Profile(std::nullopt)
Expand Down Expand Up @@ -231,6 +232,13 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w

terminalSettings.AntialiasingMode(_antialiasingMode);

if (_tabColor)
{
uint32_t c = _tabColor.value();
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
winrt::Windows::Foundation::IReference<uint32_t> cr{ c };
terminalSettings.TabColor(cr);
}

return terminalSettings;
}

Expand Down Expand Up @@ -404,6 +412,8 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _backgroundImageAlignment);
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _retroTerminalEffect);
JsonUtils::GetValueForKey(json, AntialiasingModeKey, _antialiasingMode);

JsonUtils::GetValueForKey(json, TabColorKey, _tabColor);
}

void Profile::SetFontFace(std::wstring fontFace) noexcept
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/Profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class TerminalApp::Profile final
std::optional<til::color> _selectionBackground;
std::optional<til::color> _cursorColor;
std::optional<std::wstring> _tabTitle;
std::optional<til::color> _tabColor;
bool _suppressApplicationTitle;
int32_t _historySize;
bool _snapOnInput;
Expand Down
212 changes: 138 additions & 74 deletions src/cascadia/TerminalApp/Tab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
#include "Tab.g.cpp"
#include "Utils.h"
#include "ColorHelper.h"
#include "../../types/inc/utils.hpp"
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Settings;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Windows::System;
using namespace ::Microsoft::Console;

namespace winrt
{
Expand Down Expand Up @@ -55,6 +57,7 @@ namespace winrt::TerminalApp::implementation
});

_UpdateTitle();
_RecalculateAndApplyTabColor();
}

// Method Description:
Expand Down Expand Up @@ -436,6 +439,16 @@ namespace winrt::TerminalApp::implementation
_rootPane->Relayout();
}
});

control.TabColorChanged([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
// The control's tabColor changed, but it is not necessarily the
// active control in this tab. We'll just recalculate the
// current color anyways.
tab->_RecalculateAndApplyTabColor();
}
});
}

// Method Description:
Expand Down Expand Up @@ -480,6 +493,7 @@ namespace winrt::TerminalApp::implementation
if (tab && sender != tab->_activePane)
{
tab->_UpdateActivePane(sender);
tab->_RecalculateAndApplyTabColor();
}
});
}
Expand Down Expand Up @@ -530,14 +544,14 @@ namespace winrt::TerminalApp::implementation
_tabColorPickup.ColorSelected([weakThis](auto newTabColor) {
if (auto tab{ weakThis.get() })
{
tab->SetTabColor(newTabColor);
tab->SetRuntimeTabColor(newTabColor);
}
});

_tabColorPickup.ColorCleared([weakThis]() {
if (auto tab{ weakThis.get() })
{
tab->ResetTabColor();
tab->ResetRuntimeTabColor();
}
});

Expand Down Expand Up @@ -707,114 +721,164 @@ namespace winrt::TerminalApp::implementation
// - The tab's color, if any
std::optional<winrt::Windows::UI::Color> Tab::GetTabColor()
{
return _tabColor;
const auto currControlColor{ GetActiveTerminalControl().TabColor() };
std::optional<winrt::Windows::UI::Color> controlTabColor;
if (currControlColor != nullptr)
{
controlTabColor = currControlColor.Value();
}
return Utils::CoalesceOptionalsOrNot(_runtimeTabColor,
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
controlTabColor,
_themeTabColor,
std::optional<Windows::UI::Color>(std::nullopt));
}

// Method Description:
// - Sets the tab background color to the color chosen by the user
// - Sets the runtime tab background color to the color chosen by the user
// - Sets the tab foreground color depending on the luminance of
// the background color
// Arguments:
// - color: the shiny color the user picked for their tab
// - color: the color the user picked for their tab
// Return Value:
// - <none>
void Tab::SetTabColor(const winrt::Windows::UI::Color& color)
void Tab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
{
_runtimeTabColor.emplace(color);
_RecalculateAndApplyTabColor();
}

// Method Description:
// - This function dispatches a function to the UI thread to recalculate
// what this tab's current background color should be. If a color is set,
// it will apply the given color to the tab's background. Otherwise, it
// will clear the tab's background color.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_RecalculateAndApplyTabColor()
{
auto weakThis{ get_weak() };

_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis, color]() {
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
auto ptrTab = weakThis.get();
if (!ptrTab)
return;

auto tab{ ptrTab };
Media::SolidColorBrush selectedTabBrush{};
Media::SolidColorBrush deselectedTabBrush{};
Media::SolidColorBrush fontBrush{};
Media::SolidColorBrush hoverTabBrush{};
// calculate the luminance of the current color and select a font
// color based on that
// see https://www.w3.org/TR/WCAG20/#relativeluminancedef
if (TerminalApp::ColorHelper::IsBrightColor(color))

std::optional<winrt::Windows::UI::Color> currentColor = tab->GetTabColor();
if (currentColor.has_value())
{
fontBrush.Color(winrt::Windows::UI::Colors::Black());
tab->_ApplyTabColor(currentColor.value());
}
else
{
fontBrush.Color(winrt::Windows::UI::Colors::White());
tab->_ClearTabBackgroundColor();
}

hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color));
selectedTabBrush.Color(color);

// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit ot transparency
auto deselectedTabColor = color;
deselectedTabColor.A = 64;
deselectedTabBrush.Color(deselectedTabColor);

// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit ot transparency
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);

tab->_RefreshVisualState();

tab->_tabColor.emplace(color);
tab->_colorSelected(color);
});
}

// Method Description:
// Clear the custom color of the tab, if any
// - Applies the given color to the background of this tab's TabViewItem.
// - Sets the tab foreground color depending on the luminance of
// the background color
// - This method should only be called on the UI thread.
// Arguments:
// - color: the color the user picked for their tab
// Return Value:
// - <none>
void Tab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
{
Media::SolidColorBrush selectedTabBrush{};
Media::SolidColorBrush deselectedTabBrush{};
Media::SolidColorBrush fontBrush{};
Media::SolidColorBrush hoverTabBrush{};
// calculate the luminance of the current color and select a font
// color based on that
// see https://www.w3.org/TR/WCAG20/#relativeluminancedef
if (TerminalApp::ColorHelper::IsBrightColor(color))
{
fontBrush.Color(winrt::Windows::UI::Colors::Black());
}
else
{
fontBrush.Color(winrt::Windows::UI::Colors::White());
}

hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color));
selectedTabBrush.Color(color);

// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit ot transparency
auto deselectedTabColor = color;
deselectedTabColor.A = 64;
deselectedTabBrush.Color(deselectedTabColor);

// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit ot transparency
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);

_RefreshVisualState();

_colorSelected(color);
}

// Method Description:
// - Clear the custom runtime color of the tab, if any color is set. This
// will re-apply whatever the tab's base color should be (either the color
// from the control, the theme, or the default tab color.)
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::ResetTabColor()
void Tab::ResetRuntimeTabColor()
{
auto weakThis{ get_weak() };
_runtimeTabColor.reset();
_RecalculateAndApplyTabColor();
}

_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
auto ptrTab = weakThis.get();
if (!ptrTab)
return;
// Method Description:
// - Clear out any color we've set for the TabViewItem.
// - This method should only be called on the UI thread.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_ClearTabBackgroundColor()
{
winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
L"TabViewItemHeaderBackgroundSelected",
L"TabViewItemHeaderBackgroundPointerOver",
L"TabViewItemHeaderForeground",
L"TabViewItemHeaderForegroundSelected",
L"TabViewItemHeaderForegroundPointerOver",
L"TabViewItemHeaderBackgroundPressed",
L"TabViewItemHeaderForegroundPressed",
L"TabViewButtonForegroundActiveTab"
};

auto tab{ ptrTab };
winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
L"TabViewItemHeaderBackgroundSelected",
L"TabViewItemHeaderBackgroundPointerOver",
L"TabViewItemHeaderForeground",
L"TabViewItemHeaderForegroundSelected",
L"TabViewItemHeaderForegroundPointerOver",
L"TabViewItemHeaderBackgroundPressed",
L"TabViewItemHeaderForegroundPressed",
L"TabViewButtonForegroundActiveTab"
};

// simply clear any of the colors in the tab's dict
for (auto keyString : keys)
// simply clear any of the colors in the tab's dict
for (auto keyString : keys)
{
auto key = winrt::box_value(keyString);
if (_tabViewItem.Resources().HasKey(key))
{
auto key = winrt::box_value(keyString);
if (tab->_tabViewItem.Resources().HasKey(key))
{
tab->_tabViewItem.Resources().Remove(key);
}
_tabViewItem.Resources().Remove(key);
}
}

tab->_RefreshVisualState();
tab->_tabColor.reset();
tab->_colorCleared();
});
_RefreshVisualState();
_colorCleared();
}

// Method Description:
Expand Down
11 changes: 8 additions & 3 deletions src/cascadia/TerminalApp/Tab.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ namespace winrt::TerminalApp::implementation

std::optional<winrt::Windows::UI::Color> GetTabColor();

void SetTabColor(const winrt::Windows::UI::Color& color);
void ResetTabColor();
void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
void ResetRuntimeTabColor();
void ActivateColorPicker();

WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
Expand All @@ -75,7 +75,8 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> _activePane{ nullptr };
winrt::hstring _lastIconPath{};
winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
std::optional<winrt::Windows::UI::Color> _tabColor{};
std::optional<winrt::Windows::UI::Color> _themeTabColor{};
std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};

bool _focused{ false };
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr };
Expand All @@ -102,6 +103,10 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget _UpdateTitle();
void _ConstructTabRenameBox(const winrt::hstring& tabText);

void _RecalculateAndApplyTabColor();
void _ApplyTabColor(const winrt::Windows::UI::Color& color);
void _ClearTabBackgroundColor();

friend class ::TerminalAppLocalTests::TabTests;
};
}
Loading