Repository: microsoft/terminal (language: cpp)

Issue:
UI Suggestion: Visually link tab to console view
When work begins on polishing the user interface, it could be nice to make the tab and the console view feel connected, in a similar way to the teaser/trailer video shown at build tries to portray.

![image](https://user-images.githubusercontent.com/7389110/57567803-9fa38480-73d6-11e9-827a-549de5d65a62.png)

![image](https://user-images.githubusercontent.com/7389110/57567807-a500cf00-73d6-11e9-806f-39cc316e916b.png)

The window frame respects the app mode so Light or Dark.  But each profile's tab, will match the customised settings.


Discussion / hints:
So I'm currently hacking around with this but I found one issue that most likely makes this kinda hard to implement: The tab bar is not using the acrylic effect (yet). This makes the opacity of the tab (thus the color) not match the rest of the window. But there is hope! They seem to have this on the radar, I found this comment:

https://github.com/microsoft/Terminal/blob/6088134832b851c553af791ee17a9b5c0d808385/src/cascadia/TerminalApp/App.cpp#L643-L644

Here is how I did it (acrylic needs to be off else it looks bad): https://github.com/Ghosty141/Terminal/commit/2cc95d5b360653f0766534a6205d0b6b99e85796
I believe it's using a XAML Island, but there are other surfaces that can use Acrylic, and I'm sure as WinRT's presentation platform becomes open source in WinUI 3.0 - this will become more possible.

But thinking about where the app is heading now, will hopefully drive things forward.
@mdtauk What I'm saying is that changing the tab color "can" only be done once the tab bar is using the acrylic effect. One big problem is that the tabbar currently gets style according to the theme the user uses. The use of acrylic is done per profile while the tabbar exists only once. So unless the UI gets changed quite a bit, this will probably take a while.
Yea I get that, but the window itself is a Win32 window, so XAML acrylic brushes are a bit difficult.

The app would have to extend into the titlebar area and the window colour would need to be the Host Acrylic matching the RequestedTheme.

But yes, matching tab colour to the console is a start, and removing the tab background colour, and ensuring the inactive tabs show up well using ThemeResources
Funny enough, the code @ghosty141 changed ("how to set a brush maybe?") is code I wrote when testing this exact feature.

If you get the _actual brush_ from the background of the terminal control, you can copy the acrylic effect directly into the tab. Use the same acrylic setup as is in `TerminalControl` -- create a `HostBackdropAcrylicBrush`, give it a tint and opacity, and set that as the background brush.

Ideally, we'd be using a `.xaml` file and xaml "theme resources" instead of pushing brushes directly into controls at runtime. :smile:
ThemeResources would automatically refresh upon the user setting the theme.  Until Win32 gets official support, you'll have to redraw and detect the change
I think this would be a really cool setting.

If someone wanted to to go ahead an implement this, they'd probably need to:
* add a `GetBackgroundBrush` method to `TermControl`, in the `.idl`, `.h` and `.cpp` files. 
* Then when the control is focused (when the tab changes), you'd need to change the background of the tab to control's brush. When it's unfocused, you'd need to remove this brush (somehow).
* Then, I'd add a setting to enable this feature, since not everyone's going to want their terminal to look this way. I'd make it a global setting, so see `GlobalAppSettings`. I'm okay defaulting this behavior to on, but if people have problems with that, we can discuss it on the PR :)
@zadjii-msft Thanks for the help! I'm currently working on it and trying to get the changing of the background to work. 
This is another department where @cinnamon-msft might want to weigh in on what this should look like relative to the design mockups we've already prepared.
Daaaanng i like that mockup.  When is this coming to the version in the Store??
For those who enabled Acrylic effects, can we also have it on the active tab BG?

Fix patch:
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index d88b7a7b4fe..e1631e1a229 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -154,7 +154,7 @@ namespace winrt::TerminalApp::implementation
                 // Possibly update the icon of the tab.
                 page->_UpdateTabIcon(*tab);
 
-                page->_updateTabRowColors();
+                page->_updateThemeColors();
 
                 // Update the taskbar progress as well. We'll raise our own
                 // SetTaskbarProgress event here, to get tell the hosting
@@ -927,7 +927,7 @@ namespace winrt::TerminalApp::implementation
                 _TitleChangedHandlers(*this, tab.Title());
             }
 
-            _updateTabRowColors();
+            _updateThemeColors();
 
             auto tab_impl = _GetTerminalTabImpl(tab);
             if (tab_impl)
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index a975a7f6149..e9d5324bcb3 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -223,7 +223,7 @@ namespace winrt::TerminalApp::implementation
             transparent.Color(Windows::UI::Colors::Transparent());
             _tabRow.Background(transparent);
         }
-        _updateTabRowColors();
+        _updateThemeColors();
 
         // Hookup our event handlers to the ShortcutActionDispatch
         _RegisterActionCallbacks();
@@ -1440,7 +1440,7 @@ namespace winrt::TerminalApp::implementation
             {
                 if (e.PropertyName() == L"BackgroundBrush")
                 {
-                    page->_updateTabRowColors();
+                    page->_updateThemeColors();
                 }
             }
         });
@@ -2702,7 +2702,7 @@ namespace winrt::TerminalApp::implementation
 
         ////////////////////////////////////////////////////////////////////////
         // Begin Theme handling
-        _updateTabRowColors();
+        _updateThemeColors();
     }
 
     // This is a helper to aid in sorting commands by their `Name`s, alphabetically.
@@ -4004,7 +4004,7 @@ namespace winrt::TerminalApp::implementation
         applicationState.DismissedMessages(std::move(messages));
     }
 
-    void TerminalPage::_updateTabRowColors()
+    void TerminalPage::_updateThemeColors()
     {
         if (_settings == nullptr)
         {
@@ -4012,7 +4012,19 @@ namespace winrt::TerminalApp::implementation
         }
 
         const auto theme = _settings.GlobalSettings().CurrentTheme();
-        const auto requestedTheme{ theme.RequestedTheme() };
+        auto requestedTheme{ theme.RequestedTheme() };
+
+        // First: Update the colors of our individual TabViewItems. This applies tab.background to the tabs via TerminalTab::ThemeColor
+        {
+            auto tabBackground = theme.Tab() ? theme.Tab().Background() : nullptr;
+            for (const auto& tab : _tabs)
+            {
+                if (const auto& terminalTabImpl{ _GetTerminalTabImpl(tab) })
+                {
+                    terminalTabImpl->ThemeColor(tabBackground);
+                }
+            }
+        }
 
         const auto res = Application::Current().Resources();
 
@@ -4130,6 +4142,6 @@ namespace winrt::TerminalApp::implementation
         // Stash if we're activated. Use that when we reload
         // the settings, change active panes, etc.
         _activated = activated;
-        _updateTabRowColors();
+        _updateThemeColors();
     }
 }
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index f4e8091dd93..9a4e0a13d55 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -446,7 +446,7 @@ namespace winrt::TerminalApp::implementation
         static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
         static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
 
-        void _updateTabRowColors();
+        void _updateThemeColors();
 
         winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
 
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index a5508e91739..3b1307c9cab 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -1343,7 +1343,7 @@ namespace winrt::TerminalApp::implementation
         // -------------------- | --          | --
         // Runtime Color        | _optional_  | Color Picker / `setTabColor` action
         // Control Tab Color    | _optional_  | Profile's `tabColor`, or a color set by VT
-        // Theme Tab Background | _optional_  | `tab.backgroundColor` in the theme
+        // Theme Tab Background | _optional_  | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
         // Tab Default Color    | **default** | TabView in XAML
         //
         // coalesce will get us the first of these values that's
@@ -1352,7 +1352,6 @@ namespace winrt::TerminalApp::implementation
 
         return til::coalesce(_runtimeTabColor,
                              controlTabColor,
-                             _themeTabColor,
                              std::optional<Windows::UI::Color>(std::nullopt));
     }
 
@@ -1370,6 +1369,12 @@ namespace winrt::TerminalApp::implementation
         _RecalculateAndApplyTabColor();
     }
 
+    void TerminalTab::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color)
+    {
+        _themeColor = 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,
@@ -1390,11 +1395,37 @@ namespace winrt::TerminalApp::implementation
 
             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.
             auto currentColor = tab->GetTabColor();
             if (currentColor.has_value())
             {
                 tab->_ApplyTabColor(currentColor.value());
             }
+            else if (tab->_themeColor != nullptr)
+            {
+                // One-liner to safely get the active control's brush.
+                Media::Brush terminalBrush{ nullptr };
+                if (const auto& c{ tab->GetActiveTerminalControl() })
+                {
+                    terminalBrush = c.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->_ApplyTabColor(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
+                }
+                else
+                {
+                    tab->_ClearTabBackgroundColor();
+                }
+            }
             else
             {
                 tab->_ClearTabBackgroundColor();
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index f5ea3b0cd41..d5e45ca0a0b 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -71,6 +71,7 @@ namespace winrt::TerminalApp::implementation
 
         std::optional<winrt::Windows::UI::Color> GetTabColor();
 
+        void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& color);
         void SetRuntimeTabColor(const winrt::Windows::UI::Color& color);
         void ResetRuntimeTabColor();
         void ActivateColorPicker();
@@ -113,10 +114,10 @@ namespace winrt::TerminalApp::implementation
 
         winrt::hstring _lastIconPath{};
         winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{};
-        std::optional<winrt::Windows::UI::Color> _themeTabColor{};
         std::optional<winrt::Windows::UI::Color> _runtimeTabColor{};
         winrt::TerminalApp::TabHeaderControl _headerControl{};
         winrt::TerminalApp::TerminalTabStatus _tabStatus{};
+        winrt::Microsoft::Terminal::Settings::Model::ThemeColor _themeColor{ nullptr };
 
         struct ControlEventTokens
         {
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 259c22951b4..b131cf59ad7 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -117,7 +117,8 @@ Author(s):
 
 #define MTSM_THEME_SETTINGS(X)                                                             \
     X(winrt::Microsoft::Terminal::Settings::Model::WindowTheme, Window, "window", nullptr) \
-    X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr)
+    X(winrt::Microsoft::Terminal::Settings::Model::TabRowTheme, TabRow, "tabRow", nullptr) \
+    X(winrt::Microsoft::Terminal::Settings::Model::TabTheme, Tab, "tab", nullptr)
 
 #define MTSM_THEME_WINDOW_SETTINGS(X)                                                                                              \
     X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "applicationTheme", winrt::Windows::UI::Xaml::ElementTheme::Default) \
@@ -125,3 +126,6 @@ Author(s):
 
 #define MTSM_THEME_TABROW_SETTINGS(X) \
     X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr)
+
+#define MTSM_THEME_TAB_SETTINGS(X) \
+    X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Background, "background", nullptr)
diff --git a/src/cascadia/TerminalSettingsModel/Theme.cpp b/src/cascadia/TerminalSettingsModel/Theme.cpp
index e4b770a6ced..27826d6bb4e 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.cpp
+++ b/src/cascadia/TerminalSettingsModel/Theme.cpp
@@ -12,6 +12,7 @@
 #include "ThemeColor.g.cpp"
 #include "WindowTheme.g.cpp"
 #include "TabRowTheme.g.cpp"
+#include "TabTheme.g.cpp"
 #include "Theme.g.cpp"
 
 using namespace ::Microsoft::Console;
@@ -211,6 +212,7 @@ winrt::WUX::Media::Brush ThemeColor::Evaluate(const winrt::WUX::ResourceDictiona
 
 THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
 THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
+THEME_OBJECT_CONVERTER(winrt::Microsoft::Terminal::Settings::Model, TabTheme, MTSM_THEME_TAB_SETTINGS);
 
 #undef THEME_SETTINGS_FROM_JSON
 #undef THEME_SETTINGS_TO_JSON
@@ -242,6 +244,10 @@ winrt::com_ptr<Theme> Theme::Copy() const
     {
         theme->_TabRow = *winrt::get_self<implementation::TabRowTheme>(_TabRow)->Copy();
     }
+    if (_Tab)
+    {
+        theme->_Tab = *winrt::get_self<implementation::TabTheme>(_Tab)->Copy();
+    }
 
     return theme;
 }
diff --git a/src/cascadia/TerminalSettingsModel/Theme.h b/src/cascadia/TerminalSettingsModel/Theme.h
index 4e4ba8e66be..27fb8cc3e32 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.h
+++ b/src/cascadia/TerminalSettingsModel/Theme.h
@@ -21,6 +21,7 @@ Author(s):
 #include "ThemeColor.g.h"
 #include "WindowTheme.g.h"
 #include "TabRowTheme.g.h"
+#include "TabTheme.g.h"
 #include "Theme.g.h"
 
 namespace winrt::Microsoft::Terminal::Settings::Model::implementation
@@ -57,21 +58,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         return result;                        \
     }
 
-    struct WindowTheme : WindowThemeT<WindowTheme>
-    {
-        MTSM_THEME_WINDOW_SETTINGS(THEME_SETTINGS_INITIALIZE);
-
-    public:
-        COPY_THEME_OBJECT(WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
+#define THEME_OBJECT(className, macro)         \
+    struct className : className##T<className> \
+    {                                          \
+        macro(THEME_SETTINGS_INITIALIZE);      \
+                                               \
+    public:                                    \
+        COPY_THEME_OBJECT(className, macro);   \
     };
 
-    struct TabRowTheme : TabRowThemeT<TabRowTheme>
-    {
-        MTSM_THEME_TABROW_SETTINGS(THEME_SETTINGS_INITIALIZE);
-
-    public:
-        COPY_THEME_OBJECT(TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
-    };
+    THEME_OBJECT(WindowTheme, MTSM_THEME_WINDOW_SETTINGS);
+    THEME_OBJECT(TabRowTheme, MTSM_THEME_TABROW_SETTINGS);
+    THEME_OBJECT(TabTheme, MTSM_THEME_TAB_SETTINGS);
 
     struct Theme : ThemeT<Theme>
     {
@@ -98,6 +96,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
 #undef THEME_SETTINGS_INITIALIZE
 #undef THEME_SETTINGS_COPY
+#undef THEME_OBJECT
 }
 
 namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
diff --git a/src/cascadia/TerminalSettingsModel/Theme.idl b/src/cascadia/TerminalSettingsModel/Theme.idl
index f2932a5e1fd..5c295f7df6a 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.idl
+++ b/src/cascadia/TerminalSettingsModel/Theme.idl
@@ -36,6 +36,10 @@ namespace Microsoft.Terminal.Settings.Model
         ThemeColor Background { get; };
     }
 
+    runtimeclass TabTheme {
+        ThemeColor Background { get; };
+    }
+
     [default_interface] runtimeclass Theme : Windows.Foundation.IStringable {
         Theme();
         Theme(Windows.UI.Xaml.ElementTheme requestedTheme);
@@ -48,6 +52,9 @@ namespace Microsoft.Terminal.Settings.Model
         // tabRow.* Namespace
         TabRowTheme TabRow { get; };
 
+        // tab.* Namespace
+        TabTheme Tab { get; };
+
         // A helper for retrieving the RequestedTheme out of the window property
         Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
 


Test patch: