Skip to content

Commit

Permalink
Re-add support for Mica, transparent titlebars (#13935)
Browse files Browse the repository at this point in the history
This reverts commit 19b6d35.

This re-enables support for Mica, and transparent titlebars in general. It also syncs the titlebar opacity with the control opacity for `terminalBackground`. It also somehow fixes the bug where the bottom few pixels of the max btn doesn't work to trigger the snap flyout.

Closes #10509 

Does nothing for #13631

### To-done's

* [x] Check the mica API on 22000, windows 11 RTM
  - this works on 10.0.22621.674, but that's not 22000
* [x] Check how this behaves on windows 10. 
  - For both, this API just no-ops. That's fine! we can just say "Mica is only supported on >=22621"
  • Loading branch information
zadjii-msft authored Dec 9, 2022
1 parent a5c5b8a commit 031271f
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 69 deletions.
16 changes: 11 additions & 5 deletions src/cascadia/LocalTests_SettingsModel/ThemeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ namespace SettingsModelLocalTests
},
"window":
{
"applicationTheme": "light"
"applicationTheme": "light",
"useMica": true
}
})" };

Expand All @@ -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()
Expand Down Expand Up @@ -161,7 +163,8 @@ namespace SettingsModelLocalTests
},
"window":
{
"applicationTheme": "light"
"applicationTheme": "light",
"useMica": true
}
},
{
Expand All @@ -172,14 +175,16 @@ namespace SettingsModelLocalTests
},
"window":
{
"applicationTheme": "light"
"applicationTheme": "light",
"useMica": true
}
},
{
"name": "backgroundOmittedEntirely",
"window":
{
"applicationTheme": "light"
"applicationTheme": "light",
"useMica": true
}
}
]
Expand Down Expand Up @@ -234,7 +239,8 @@ namespace SettingsModelLocalTests
"tabRow": {},
"window":
{
"applicationTheme": "light"
"applicationTheme": "light",
"useMica": true
}
}
]
Expand Down
12 changes: 10 additions & 2 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand All @@ -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>())
Expand All @@ -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()
Expand Down
5 changes: 3 additions & 2 deletions src/cascadia/TerminalSettingsModel/MTSMSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
47 changes: 1 addition & 46 deletions src/cascadia/TerminalSettingsModel/Theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/Theme.idl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace Microsoft.Terminal.Settings.Model

runtimeclass WindowTheme {
Windows.UI.Xaml.ElementTheme RequestedTheme { get; };
Boolean UseMica { get; };
}

runtimeclass TabRowTheme {
Expand Down
14 changes: 3 additions & 11 deletions src/cascadia/WindowsTerminal/AppHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*/,
Expand Down
13 changes: 13 additions & 0 deletions src/cascadia/WindowsTerminal/IslandWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
2 changes: 2 additions & 0 deletions src/cascadia/WindowsTerminal/IslandWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)>);
Expand Down
48 changes: 45 additions & 3 deletions src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
5 changes: 5 additions & 0 deletions src/cascadia/WindowsTerminal/NonClientIslandWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 };

Expand Down

0 comments on commit 031271f

Please sign in to comment.