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 tab.unfocusedBackground #13689

Merged
merged 24 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
57e02b8
sometimes I do the code good
zadjii-msft Aug 5, 2022
fe5a86d
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 11, 2022
bdfb015
Allow the settings tab to also have a color set by the theme
zadjii-msft Aug 11, 2022
4146d71
default settings changes
zadjii-msft Aug 11, 2022
f911929
transparent inactive tabs on light tab row, when the tabs have a dark…
zadjii-msft Aug 11, 2022
2964ebe
comments
zadjii-msft Aug 11, 2022
37ce107
I can't tell you how much I hate this
zadjii-msft Aug 11, 2022
25e09d6
comments galore
zadjii-msft Aug 11, 2022
b38b704
Merge branch 'dev/migrie/b/13554-new-default-theme' into dev/migrie/b…
zadjii-msft Aug 12, 2022
48deba0
Revert the changes I made to the default themes
zadjii-msft Aug 12, 2022
ff90631
runformat
zadjii-msft Aug 12, 2022
dfe40fa
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 15, 2022
b040192
Thanks austin mode
zadjii-msft Aug 15, 2022
17262f7
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 18, 2022
60d28f3
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 24, 2022
4c7cfee
Merge branch 'main' into dev/migrie/b/13684-inactiveTabBg
zadjii-msft Aug 25, 2022
9f7cbdc
fixes the settings tab getting initialized with the wrong theme
zadjii-msft Aug 26, 2022
c5e0445
This fixes the unfocused tab color weirdness
zadjii-msft Aug 26, 2022
2e8e802
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 29, 2022
da56d48
Merge remote-tracking branch 'origin/main' into dev/migrie/b/13684-in…
zadjii-msft Aug 30, 2022
951ab2c
nits from review
zadjii-msft Aug 30, 2022
b92da58
okay
zadjii-msft Aug 30, 2022
81ef597
Update src/inc/til/color.h
zadjii-msft Aug 31, 2022
ec3ebb9
ugh "aai is not a word"
zadjii-msft Aug 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@

<StaticResource x:Key="UnfocusedBorderBrush"
ResourceKey="ApplicationPageBackgroundThemeBrush" />

<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#0c0c0c" />
</ResourceDictionary>

<ResourceDictionary x:Key="Light">
Expand All @@ -166,6 +169,9 @@

<StaticResource x:Key="UnfocusedBorderBrush"
ResourceKey="ApplicationPageBackgroundThemeBrush" />

<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#ffffff" />
</ResourceDictionary>

<ResourceDictionary x:Key="HighContrast">
Expand All @@ -183,6 +189,9 @@
-->
<StaticResource x:Key="TabViewBackground"
ResourceKey="SystemColorButtonFaceColorBrush" />

<StaticResource x:Key="SettingsUiTabBrush"
ResourceKey="SystemControlBackgroundBaseLowBrush" />
</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>
Expand Down
20 changes: 19 additions & 1 deletion src/cascadia/TerminalApp/SettingsTab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ namespace winrt

namespace winrt::TerminalApp::implementation
{
SettingsTab::SettingsTab(MainPage settingsUI)
SettingsTab::SettingsTab(MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme)
{
Content(settingsUI);
_requestedTheme = requestedTheme;
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

_MakeTabViewItem();
_CreateContextMenu();
Expand All @@ -36,6 +38,10 @@ namespace winrt::TerminalApp::implementation
{
auto settingsUI{ Content().as<MainPage>() };
settingsUI.UpdateSettings(settings);

// Stash away the current requested theme of the app. We'll need that in
// _BackgroundBrush() to do a theme-aware resource lookup
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
}

// Method Description:
Expand Down Expand Up @@ -105,4 +111,16 @@ namespace winrt::TerminalApp::implementation
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
}
}

winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
{
// Look up the color we should use for the settings tab item from our
// resources. This should only be used for when "terminalBackground" is
// requested.
static const auto key = winrt::box_value(L"SettingsUiTabBrush");
// You can't just do a Application::Current().Resources().TryLookup
// lookup, cause the app theme never changes! Do the hacky version
// instead.
return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
}
}
7 changes: 6 additions & 1 deletion src/cascadia/TerminalApp/SettingsTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@ namespace winrt::TerminalApp::implementation
struct SettingsTab : SettingsTabT<SettingsTab, TabBase>
{
public:
SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI);
SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme);

void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;

std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;

private:
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;

void _MakeTabViewItem() override;
winrt::fire_and_forget _CreateIcon();

virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
};
}
289 changes: 289 additions & 0 deletions src/cascadia/TerminalApp/TabBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <LibraryResources.h>
#include "TabBase.h"
#include "TabBase.g.cpp"
#include "Utils.h"
#include "ColorHelper.h"

using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
Expand Down Expand Up @@ -252,4 +254,291 @@ namespace winrt::TerminalApp::implementation
});
}

std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()
{
return std::nullopt;
}

void TabBase::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
Copy link
Member Author

Choose a reason for hiding this comment

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

note to reviewers: This mostly just came from TerminalTab.cpp so go ahead and hit that . and open the two SxS
image

const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
const til::color& tabRowColor)
{
_themeColor = focused;
_unfocusedThemeColor = unfocused;
_tabRowColor = tabRowColor;
_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 TabBase::_RecalculateAndApplyTabColor()
{
auto weakThis{ get_weak() };

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

auto tab{ ptrTab };

// GetTabColor will return the color set by the color picker, or the
// color specified in the profile. If neither of those were set,
// then look to _themeColor to see if there's a value there.
// Otherwise, clear our color, falling back to the TabView defaults.
const auto currentColor = tab->GetTabColor();
if (currentColor.has_value())
{
tab->_ApplyTabColorOnUIThread(currentColor.value());
}
else if (tab->_themeColor != nullptr)
{
// Safely get the active control's brush.
const Media::Brush terminalBrush{ tab->_BackgroundBrush() };

if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
{
// ThemeColor.Evaluate will get us a Brush (because the
// TermControl could have an acrylic BG, for example). Take
// that brush, and get the color out of it. We don't really
// want to have the tab items themselves be acrylic.
tab->_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
}
else
{
tab->_ClearTabBackgroundColor();
}
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}
else
{
tab->_ClearTabBackgroundColor();
}
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
});
}

// Method Description:
// - 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.
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// Arguments:
// - color: the color the user picked for their tab
// Return Value:
// - <none>
void TabBase::_ApplyTabColorOnUIThread(const winrt::Windows::UI::Color& color)
{
Media::SolidColorBrush selectedTabBrush{};
Media::SolidColorBrush deselectedTabBrush{};
Media::SolidColorBrush fontBrush{};
Media::SolidColorBrush deselectedFontBrush{};
Media::SolidColorBrush secondaryFontBrush{};
Media::SolidColorBrush hoverTabBrush{};
Media::SolidColorBrush subtleFillColorSecondaryBrush;
Media::SolidColorBrush subtleFillColorTertiaryBrush;
// 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());
auto secondaryFontColor = winrt::Windows::UI::Colors::Black();
// For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L269
secondaryFontColor.A = 0x9E;
secondaryFontBrush.Color(secondaryFontColor);
auto subtleFillColorSecondary = winrt::Windows::UI::Colors::Black();
subtleFillColorSecondary.A = 0x09;
subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
auto subtleFillColorTertiary = winrt::Windows::UI::Colors::Black();
subtleFillColorTertiary.A = 0x06;
subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
}
else
{
fontBrush.Color(winrt::Windows::UI::Colors::White());
auto secondaryFontColor = winrt::Windows::UI::Colors::White();
// For alpha value see: https://github.com/microsoft/microsoft-ui-xaml/blob/7a33ad772d77d908aa6b316ec24e6d2eb3ebf571/dev/CommonStyles/Common_themeresources_any.xaml#L14
secondaryFontColor.A = 0xC5;
secondaryFontBrush.Color(secondaryFontColor);
auto subtleFillColorSecondary = winrt::Windows::UI::Colors::White();
subtleFillColorSecondary.A = 0x0F;
subtleFillColorSecondaryBrush.Color(subtleFillColorSecondary);
auto subtleFillColorTertiary = winrt::Windows::UI::Colors::White();
subtleFillColorTertiary.A = 0x0A;
subtleFillColorTertiaryBrush.Color(subtleFillColorTertiary);
}

selectedTabBrush.Color(color);

// Start with the current tab color, set to Opacity=.3
til::color deselectedTabColor{ color };
deselectedTabColor = deselectedTabColor.with_alpha(77); // 255 * .3 = 77

// If we DON'T have a color set from the color picker, or the profile's
// tabColor, but we do have a unfocused color in the theme, use the
// unfocused theme color here instead.
if (!GetTabColor().has_value() &&
_unfocusedThemeColor != nullptr)
{
// Safely get the active control's brush.
const Media::Brush terminalBrush{ _BackgroundBrush() };

// Get the color of the brush.
if (const auto themeBrush{ _unfocusedThemeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
{
// We did figure out the brush. Get the color out of it. If it
// was "accent" or "terminalBackground", then we're gonna set
// the alpha to .3 manually here.
// (ThemeColor::UnfocusedTabOpacity will do this for us). If the
// user sets both unfocused and focused tab.background to
// terminalBackground, this will allow for some differentiation
// (and is generally just sensible).
deselectedTabColor = til::color{ ThemeColor::ColorFromBrush(themeBrush) }.with_alpha(_unfocusedThemeColor.UnfocusedTabOpacity());
}
}

// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit of transparency
deselectedTabBrush.Color(deselectedTabColor.with_alpha(255));
deselectedTabBrush.Opacity(deselectedTabColor.a / 255.f);

hoverTabBrush.Color(color);
hoverTabBrush.Opacity(0.6);
Copy link
Member

Choose a reason for hiding this comment

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

might be worth mentioning where this 0.6 came from (and if we should change this in the future)


// Account for the color of the tab row when setting the color of text
// on inactive tabs. Consider:
// * black active tabs
// * on a white tab row
// * with a transparent inactive tab color
//
// We don't want that to result in white text on a white tab row for
// inactive tabs.
const auto deselectedActualColor = deselectedTabColor.blend_with(_tabRowColor);
if (TerminalApp::ColorHelper::IsBrightColor(deselectedActualColor))
{
deselectedFontBrush.Color(winrt::Windows::UI::Colors::Black());
}
else
{
deselectedFontBrush.Color(winrt::Windows::UI::Colors::White());
}

// Prior to MUX 2.7, we set TabViewItemHeaderBackground, but now we can
// use TabViewItem().Background() for that. HOWEVER,
// TabViewItem().Background() only sets the color of the tab background
// when the TabViewItem is unselected. So we still need to set the other
// properties ourselves.
//
// In GH#11294 we thought we'd still need to set
// TabViewItemHeaderBackground manually, but GH#11382 discovered that
// Background() was actually okay after all.
TabViewItem().Background(deselectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);

// Similarly, TabViewItem().Foreground() sets the color for the text
// when the TabViewItem isn't selected, but not when it is hovered,
// pressed, dragged, or selected, so we'll need to just set them all
// anyways.
TabViewItem().Foreground(deselectedFontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), deselectedFontBrush);
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"TabViewItemHeaderCloseButtonForeground"), deselectedFontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPressed"), secondaryFontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonForegroundPointerOver"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPressedCloseButtonForeground"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderPointerOverCloseButtonForeground"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderSelectedCloseButtonForeground"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPressed"), subtleFillColorTertiaryBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderCloseButtonBackgroundPointerOver"), subtleFillColorSecondaryBrush);

TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPressed"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundPointerOver"), fontBrush);
Comment on lines +444 to +469
Copy link
Member

Choose a reason for hiding this comment

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

Would there be enough benefit to magic static-ify all of these winrt::box_value(L"<resource name>")?

Copy link
Member

Choose a reason for hiding this comment

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

hmm... The strings also seem to be used below in _ClearTabBackgroundColor. Maybe we should static-ify the strings themselves? Idk if/when it's worth it though 🤷

Copy link
Member Author

Choose a reason for hiding this comment

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

meh we maybe could. This is all copypasta from TerminalTab and this hasn't ever seemed like an issue before.


_RefreshVisualState();
}

// 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 TabBase::_ClearTabBackgroundColor()
{
winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
L"TabViewItemHeaderBackgroundSelected",
L"TabViewItemHeaderBackgroundPointerOver",
L"TabViewItemHeaderBackgroundPressed",
L"TabViewItemHeaderForeground",
L"TabViewItemHeaderForegroundSelected",
L"TabViewItemHeaderForegroundPointerOver",
L"TabViewItemHeaderForegroundPressed",
L"TabViewItemHeaderCloseButtonForeground",
L"TabViewItemHeaderCloseButtonForegroundPressed",
L"TabViewItemHeaderCloseButtonForegroundPointerOver",
L"TabViewItemHeaderPressedCloseButtonForeground",
L"TabViewItemHeaderPointerOverCloseButtonForeground",
L"TabViewItemHeaderSelectedCloseButtonForeground",
L"TabViewItemHeaderCloseButtonBackgroundPressed",
L"TabViewItemHeaderCloseButtonBackgroundPointerOver",
L"TabViewButtonForegroundActiveTab",
L"TabViewButtonForegroundPressed",
L"TabViewButtonForegroundPointerOver"
};

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

// GH#11382 DON'T set the background to null. If you do that, then the
// tab won't be hit testable at all. Transparent, however, is a totally
// valid hit test target. That makes sense.
TabViewItem().Background(WUX::Media::SolidColorBrush{ Windows::UI::Colors::Transparent() });

_RefreshVisualState();
}

// Method Description:
// Toggles the visual state of the tab view item,
// so that changes to the tab color are reflected immediately
// Arguments:
// - <none>
// Return Value:
// - <none>
void TabBase::_RefreshVisualState()
{
if (TabViewItem().IsSelected())
{
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
}
else
{
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
}
}

}
Loading