Repository: microsoft/terminal (language: cpp)

Issue:
Add support for the Mica material
Depends on #10508


Discussion / hints:
Blocked by https://github.com/microsoft/microsoft-ui-xaml/issues/5319
Oh no. Thanks.
https://github.com/microsoft/microsoft-ui-xaml/issues/5319#issuecomment-894008314
Or you can reference WinUI's source code and create a composition brush for Mica.
Have you tried using Compositor.TryCreateBlurredWallpaperBackdropBrush() , it gives the blurred wallpaper brush that is used for creating mica. It is also supported in windows.ui.composition
There's also the "Tabbed" material used by Explorer [on the last few Insider builds], which would be nice to have for the tab bar's background:
![image](https://user-images.githubusercontent.com/24303423/161751714-a7ca4d10-b769-46ab-bbf4-2c011493e0d6.png)

Not sure about its implementation.
Since I'm tracking this here: `dev/migrie/f/no-custom-caption-btns` has the weird version of the terminal with no MinMaxClose control, only the DWM frame. I think that's a dead end though.

Fix patch:
diff --git a/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
index 327be5ad92c..4e3bdacc254 100644
--- a/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
@@ -64,7 +64,8 @@ namespace SettingsModelLocalTests
             },
             "window":
             {
-                "applicationTheme": "light"
+                "applicationTheme": "light",
+                "useMica": true
             }
         })" };
 
@@ -80,6 +81,7 @@ namespace SettingsModelLocalTests
 
         VERIFY_IS_NOT_NULL(theme->Window());
         VERIFY_ARE_EQUAL(winrt::Windows::UI::Xaml::ElementTheme::Light, theme->Window().RequestedTheme());
+        VERIFY_ARE_EQUAL(true, theme->Window().UseMica());
     }
 
     void ThemeTests::ParseEmptyTheme()
@@ -161,7 +163,8 @@ namespace SettingsModelLocalTests
                     },
                     "window":
                     {
-                        "applicationTheme": "light"
+                        "applicationTheme": "light",
+                        "useMica": true
                     }
                 },
                 {
@@ -172,14 +175,16 @@ namespace SettingsModelLocalTests
                     },
                     "window":
                     {
-                        "applicationTheme": "light"
+                        "applicationTheme": "light",
+                        "useMica": true
                     }
                 },
                 {
                     "name": "backgroundOmittedEntirely",
                     "window":
                     {
-                        "applicationTheme": "light"
+                        "applicationTheme": "light",
+                        "useMica": true
                     }
                 }
             ]
@@ -234,7 +239,8 @@ namespace SettingsModelLocalTests
                     "tabRow": {},
                     "window":
                     {
-                        "applicationTheme": "light"
+                        "applicationTheme": "light",
+                        "useMica": true
                     }
                 }
             ]
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 0e0c2a4548e..a5e4b2bde1f 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -630,7 +630,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         //
         // Firing it manually makes sure it does.
         _BackgroundBrush = RootGrid().Background();
-        _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
+        _PropertyChangedHandlers(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
 
         _isBackgroundLight = _isColorLight(bg);
     }
@@ -652,7 +652,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     {
         const auto opacity{ _core.Opacity() };
         const auto useAcrylic{ _core.UseAcrylic() };
-
+        auto changed = false;
         // GH#11743, #11619: If we're changing whether or not acrylic is used,
         // then just entirely reinitialize the brush. The primary way that this
         // happens is on Windows 10, where we need to enable acrylic when the
@@ -666,6 +666,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
                 _InitializeBackgroundBrush();
                 return;
             }
+            changed = acrylic.TintOpacity() != opacity;
             acrylic.TintOpacity(opacity);
         }
         else if (auto solidColor = RootGrid().Background().try_as<Media::SolidColorBrush>())
@@ -675,8 +676,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
                 _InitializeBackgroundBrush();
                 return;
             }
+            changed = solidColor.Opacity() != opacity;
             solidColor.Opacity(opacity);
         }
+        // Send a BG brush changed event, so you can mouse wheel the
+        // transparency of the titlebar too.
+        if (changed)
+        {
+            _PropertyChangedHandlers(*this, Data::PropertyChangedEventArgs{ L"BackgroundBrush" });
+        }
     }
 
     TermControl::~TermControl()
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 91ff1b068a4..335fc7ad864 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -122,8 +122,9 @@ Author(s):
     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)
+#define MTSM_THEME_WINDOW_SETTINGS(X)                                                                                              \
+    X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "applicationTheme", winrt::Windows::UI::Xaml::ElementTheme::Default) \
+    X(bool, UseMica, "useMica", false)
 
 #define MTSM_THEME_TABROW_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 c7236a8463f..67c82ef7045 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.cpp
+++ b/src/cascadia/TerminalSettingsModel/Theme.cpp
@@ -125,10 +125,6 @@ winrt::WUX::Media::Brush ThemeColor::Evaluate(const winrt::WUX::ResourceDictiona
 {
     static const auto accentColorKey{ winrt::box_value(L"SystemAccentColor") };
 
-    // NOTE: Currently, the DWM titlebar is always drawn, underneath our XAML
-    // content. If the opacity is <1.0, then you'll be able to see it, including
-    // the original caption buttons, which we don't want.
-
     switch (ColorType())
     {
     case ThemeColorType::Accent:
@@ -148,51 +144,10 @@ winrt::WUX::Media::Brush ThemeColor::Evaluate(const winrt::WUX::ResourceDictiona
     }
     case ThemeColorType::Color:
     {
-        return winrt::WUX::Media::SolidColorBrush{ forTitlebar ?
-                                                       Color().with_alpha(255) :
-                                                       Color() };
+        return winrt::WUX::Media::SolidColorBrush{ Color() };
     }
     case ThemeColorType::TerminalBackground:
     {
-        // If we're evaluating this color for the tab row, there are some rules
-        // we have to follow, unfortunately. We can't allow a transparent
-        // background, so we have to make sure to fill that in with Opacity(1.0)
-        // manually.
-        //
-        // So for that case, just make a new brush with the relevant properties
-        // set.
-        if (forTitlebar)
-        {
-            if (auto acrylic = terminalBackground.try_as<winrt::WUX::Media::AcrylicBrush>())
-            {
-                winrt::WUX::Media::AcrylicBrush newBrush{};
-                newBrush.TintColor(acrylic.TintColor());
-                newBrush.FallbackColor(acrylic.FallbackColor());
-                newBrush.TintLuminosityOpacity(acrylic.TintLuminosityOpacity());
-
-                // Allow acrylic opacity, but it's gotta be HostBackdrop acrylic.
-                //
-                // For now, just always use 50% opacity for this. If we do ever
-                // figure out how to get rid of our titlebar under the XAML tab
-                // row (GH#10509), we can always get rid of the HostBackdrop
-                // thing, and all this copying, and just return the
-                // terminalBackground brush directly.
-                //
-                // Because we're wholesale copying the brush, we won't be able
-                // to adjust it's opacity with the mouse wheel. This seems like
-                // an acceptable tradeoff for now.
-                newBrush.TintOpacity(.5);
-                newBrush.BackgroundSource(winrt::WUX::Media::AcrylicBackgroundSource::HostBackdrop);
-                return newBrush;
-            }
-            else if (auto solidColor = terminalBackground.try_as<winrt::WUX::Media::SolidColorBrush>())
-            {
-                winrt::WUX::Media::SolidColorBrush newBrush{};
-                newBrush.Color(til::color{ solidColor.Color() }.with_alpha(255));
-                return newBrush;
-            }
-        }
-
         return terminalBackground;
     }
     }
diff --git a/src/cascadia/TerminalSettingsModel/Theme.idl b/src/cascadia/TerminalSettingsModel/Theme.idl
index 39260bb0cf4..7078030fa9b 100644
--- a/src/cascadia/TerminalSettingsModel/Theme.idl
+++ b/src/cascadia/TerminalSettingsModel/Theme.idl
@@ -37,6 +37,7 @@ namespace Microsoft.Terminal.Settings.Model
 
     runtimeclass WindowTheme {
         Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
+        Boolean UseMica { get; };
     }
 
     runtimeclass TabRowTheme {
diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp
index 4ab2d0031bd..95d47906de4 100644
--- a/src/cascadia/WindowsTerminal/AppHost.cpp
+++ b/src/cascadia/WindowsTerminal/AppHost.cpp
@@ -1350,17 +1350,9 @@ void AppHost::_updateTheme()
 
     _window->OnApplicationThemeChanged(theme.RequestedTheme());
 
-    // This block of code enables Mica for our window. By all accounts, this
-    // version of the code will only work on Windows 11, SV2. There's a slightly
-    // different API surface for enabling Mica on Windows 11 22000.0.
-    //
-    // This code is left here, commented out, for future enablement of Mica.
-    // We'll revisit this in GH#10509. Because we can't enable transparent
-    // titlebars for showing Mica currently, we're just gonna disable it
-    // entirely while we sort that out.
-    //
-    // const int attribute = theme.Window().UseMica() ? /*DWMSBT_MAINWINDOW*/ 2 : /*DWMSBT_NONE*/ 1;
-    // DwmSetWindowAttribute(_window->GetHandle(), /* DWMWA_SYSTEMBACKDROP_TYPE */ 38, &attribute, sizeof(attribute));
+    const auto b = _logic.TitlebarBrush();
+    const auto opacity = b ? ThemeColor::ColorFromBrush(b).A / 255.0 : 0.0;
+    _window->UseMica(theme.Window().UseMica(), opacity);
 }
 
 void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/,
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp
index 446d1b46506..4d76921656c 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp
@@ -1894,3 +1894,16 @@ void IslandWindow::RemoveFromSystemMenu(const winrt::hstring& itemLabel)
     }
     _systemMenuItems.erase(it->first);
 }
+
+void IslandWindow::UseMica(const bool newValue, const double /*titlebarOpacity*/)
+{
+    // This block of code enables Mica for our window. By all accounts, this
+    // version of the code will only work on Windows 11, SV2. There's a slightly
+    // different API surface for enabling Mica on Windows 11 22000.0.
+    //
+    // This API was only publicly supported as of Windows 11 SV2, 22621. Before
+    // that version, this API will just return an error and do nothing silently.
+
+    const int attribute = newValue ? DWMSBT_MAINWINDOW : DWMSBT_NONE;
+    std::ignore = DwmSetWindowAttribute(GetHandle(), DWMWA_SYSTEMBACKDROP_TYPE, &attribute, sizeof(attribute));
+}
diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h
index b0d3efff5c5..cfa3e0907d8 100644
--- a/src/cascadia/WindowsTerminal/IslandWindow.h
+++ b/src/cascadia/WindowsTerminal/IslandWindow.h
@@ -65,6 +65,8 @@ class IslandWindow :
     void AddToSystemMenu(const winrt::hstring& itemLabel, winrt::delegate<void()> callback);
     void RemoveFromSystemMenu(const winrt::hstring& itemLabel);
 
+    virtual void UseMica(const bool newValue, const double titlebarOpacity);
+
     WINRT_CALLBACK(DragRegionClicked, winrt::delegate<>);
     WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>);
     WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
index a7e619a2ad4..2ae1b885e93 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
@@ -442,11 +442,21 @@ til::rect NonClientIslandWindow::_GetDragAreaRect() const noexcept
             static_cast<float>(_rootGrid.ActualWidth()),
             static_cast<float>(_dragBar.ActualHeight())
         };
+
         const auto clientDragBarRect = transform.TransformBounds(logicalDragBarRect);
+
+        // Make sure to trim the right side of the rectangle, so that it doesn't
+        // hang off the right side of the root window. This normally wouldn't
+        // matter, but UIA will still think its bounds can extend past the right
+        // of the parent HWND.
+        //
+        // x here is the width of the tabs.
+        const auto x = gsl::narrow_cast<til::CoordType>(clientDragBarRect.X * scale);
+
         return {
-            gsl::narrow_cast<til::CoordType>(clientDragBarRect.X * scale),
+            x,
             gsl::narrow_cast<til::CoordType>(clientDragBarRect.Y * scale),
-            gsl::narrow_cast<til::CoordType>((clientDragBarRect.Width + clientDragBarRect.X) * scale),
+            gsl::narrow_cast<til::CoordType>((clientDragBarRect.Width + clientDragBarRect.X) * scale) - x,
             gsl::narrow_cast<til::CoordType>((clientDragBarRect.Height + clientDragBarRect.Y) * scale),
         };
     }
@@ -879,7 +889,26 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
         //  at the top) in the WM_PAINT handler. This eliminates the transparency
         //  bug and it's what a lot of Win32 apps that customize the title bar do
         //  so it should work fine.
-        margins.cyTopHeight = -frame.top;
+        //
+        // Notes #3 (circa late 2022): We want to make some changes here to
+        // support Mica. This introduces some complications.
+        // - If we leave the titlebar visible AT ALL, then a transparent
+        //   titlebar (theme.tabRow.background:#ff00ff00 for example) will allow
+        //   the DWM titlebar to be visible, underneath our content. EVEN MORE
+        //   SO: Mica + "show accent color on title bars" will _always_ show the
+        //   accent-colored strip of the titlebar, even on top of the Mica.
+        // - It _seems_ like we can just set this to 0, and have it work. You'd
+        //   be wrong. On Windows 10, setting this to 0 will cause the topmost
+        //   pixel of our window to be just a little darker than the rest of the
+        //   frame. So ONLY set this to 0 when the user has explicitly asked for
+        //   Mica. Though it won't do anything on Windows 10, they should be
+        //   able to opt back out of having that weird dark pixel.
+        // - This is LOAD-BEARING. By having the titlebar a totally empty rect,
+        //   DWM will know that we don't have the traditional titlebar, and will
+        //   use NCHITTEST to determine where to place the Snap Flyout. The drag
+        //   rect will handle that.
+
+        margins.cyTopHeight = (_useMica || _titlebarOpacity < 1.0) ? 0 : -frame.top;
     }
 
     // Extend the frame into the client area. microsoft/terminal#2735 - Just log
@@ -1132,3 +1161,16 @@ void NonClientIslandWindow::SetTitlebarBackground(winrt::Windows::UI::Xaml::Medi
 {
     _titlebar.Background(brush);
 }
+
+void NonClientIslandWindow::UseMica(const bool newValue, const double titlebarOpacity)
+{
+    // Stash internally if we're using Mica. If we aren't, we don't want to
+    // totally blow away our titlebar with DwmExtendFrameIntoClientArea,
+    // especially on Windows 10
+    _useMica = newValue;
+    _titlebarOpacity = titlebarOpacity;
+
+    IslandWindow::UseMica(newValue, titlebarOpacity);
+
+    _UpdateFrameMargins();
+}
diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
index 20b9bde43ef..8b3cdfef2e9 100644
--- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
+++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h
@@ -49,6 +49,8 @@ class NonClientIslandWindow : public IslandWindow
 
     void SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush);
 
+    virtual void UseMica(const bool newValue, const double titlebarOpacity) override;
+
 private:
     std::optional<til::point> _oldIslandPos;
 
@@ -63,6 +65,9 @@ class NonClientIslandWindow : public IslandWindow
 
     winrt::Windows::UI::Xaml::ElementTheme _theme;
 
+    bool _useMica{ false };
+    double _titlebarOpacity{ 1.0 };
+
     bool _isMaximized;
     bool _trackingMouse{ false };
 


Test patch: