diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 2fd76fc9893..3f73b97ee73 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1628,6 +1628,7 @@ sysparams SYSTEMHAND SYSTEMMENU SYSTEMTIME +TABBEDWINDOW tabview taef TARG diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 9296cd1eb6c..0c7b0516e17 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2048,9 +2048,21 @@ "type": "string" }, "useMica": { - "description": "True if the Terminal should use a Mica backdrop for the window. This will apply underneath all controls (including the terminal panes and the titlebar)", - "type": "boolean", - "default": false + "default": "none", + "description": "Sets the backdrop material for the window, applied underneath all controls (including the terminal panes and the titlebar). Possible values:\n -\"mica\" (the Mica material)\n -\"micaAlt\" (the Mica Alt material)\n -\"none\" (no backdrop).\ntrue and false are accepted as synonyms for \"mica\" and \"none\" respectively.", + "oneOf": [ + { + "enum": [ + "none", + "mica", + "micaAlt" + ], + "type": "string" + }, + { + "type": "boolean" + } + ] }, "experimental.rainbowFrame": { "description": "When enabled, the frame of the window will cycle through all the colors. Enabling this will override the `frame` and `unfocusedFrame` settings.", diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index 2f3cd75db0b..fcce429e3dd 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -1169,7 +1169,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // that our theme is different than the app's. const bool actuallyUseMica = isMicaAvailable && (appTheme == requestedTheme); - const auto bgKey = (theme.Window() != nullptr && theme.Window().UseMica()) && actuallyUseMica ? + const auto bgKey = (theme.Window() != nullptr && theme.Window().UseMica() != winrt::Microsoft::Terminal::Settings::Model::MicaStyle::None) && actuallyUseMica ? L"SettingsPageMicaBackground" : L"SettingsPageBackground"; diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 96cd878b800..016b1d37c5f 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -157,12 +157,12 @@ 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) \ - X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Frame, "frame", nullptr) \ - X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedFrame, "unfocusedFrame", nullptr) \ - X(bool, RainbowFrame, "experimental.rainbowFrame", false) \ - X(bool, UseMica, "useMica", false) \ +#define MTSM_THEME_WINDOW_SETTINGS(X) \ + X(winrt::Windows::UI::Xaml::ElementTheme, RequestedTheme, "applicationTheme", winrt::Windows::UI::Xaml::ElementTheme::Default) \ + X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, Frame, "frame", nullptr) \ + X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedFrame, "unfocusedFrame", nullptr) \ + X(bool, RainbowFrame, "experimental.rainbowFrame", false) \ + X(winrt::Microsoft::Terminal::Settings::Model::MicaStyle, UseMica, "useMica", winrt::Microsoft::Terminal::Settings::Model::MicaStyle::None) \ X(bool, ShowWorkspacesButton, "showWorkspacesButton", true) #define MTSM_THEME_SETTINGS_SETTINGS(X) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 878b09f89ca..1d2ac88b32f 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -193,6 +193,31 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode) using EnumMapper::TypeDescription; }; +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::MicaStyle) +{ + JSON_MAPPINGS(3) = { + pair_type{ "none", ValueType::None }, + pair_type{ "mica", ValueType::Mica }, + pair_type{ "micaAlt", ValueType::MicaAlt }, + }; + + // Override mapping parser to add boolean parsing (useMica used to be a bool) + ::winrt::Microsoft::Terminal::Settings::Model::MicaStyle FromJson(const Json::Value& json) + { + if (json.isBool()) + { + return json.asBool() ? ValueType::Mica : ValueType::None; + } + return EnumMapper::FromJson(json); + } + + bool CanConvert(const Json::Value& json) + { + return EnumMapper::CanConvert(json) || json.isBool(); + } + using EnumMapper::TypeDescription; +}; + // This specialization isn't using JSON_ENUM_MAPPER because we need to have a different // value type (unsigned int) and return type (FontWeight struct). JSON_ENUM_MAPPER // expects that the value type _is_ the return type. diff --git a/src/cascadia/TerminalSettingsModel/Theme.idl b/src/cascadia/TerminalSettingsModel/Theme.idl index cb88aae95a1..6833bae76a2 100644 --- a/src/cascadia/TerminalSettingsModel/Theme.idl +++ b/src/cascadia/TerminalSettingsModel/Theme.idl @@ -26,6 +26,13 @@ namespace Microsoft.Terminal.Settings.Model ActiveOnly }; + enum MicaStyle + { + None, + Mica, + MicaAlt + }; + [default_interface] runtimeclass ThemePair { ThemePair(); @@ -60,7 +67,7 @@ namespace Microsoft.Terminal.Settings.Model runtimeclass WindowTheme { Windows.UI.Xaml.ElementTheme RequestedTheme { get; }; - Boolean UseMica { get; }; + MicaStyle UseMica { get; }; Boolean RainbowFrame { get; }; Boolean ShowWorkspacesButton { get; }; ThemeColor Frame { get; }; diff --git a/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp b/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp index 34b4e5eb73d..e8b18560453 100644 --- a/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp +++ b/src/cascadia/UnitTests_SettingsModel/ThemeTests.cpp @@ -23,6 +23,7 @@ namespace SettingsModelUnitTests TEST_CLASS(ThemeTests); TEST_METHOD(ParseSimpleTheme); + TEST_METHOD(ParseMicaStyle); TEST_METHOD(ParseEmptyTheme); TEST_METHOD(ParseNoWindowTheme); TEST_METHOD(ParseNullWindowTheme); @@ -68,7 +69,76 @@ namespace SettingsModelUnitTests 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()); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::Mica, theme->Window().UseMica()); + } + + void ThemeTests::ParseMicaStyle() + { + Log::Comment(L"Verify useMica parses the MicaStyle enum, plus booleans for back-compat."); + + { + static constexpr std::string_view noneTheme{ R"({ + "name": "none", + "window": + { + "useMica": "none" + } + })" }; + const auto schemeObject = VerifyParseSucceeded(noneTheme); + auto theme = Theme::FromJson(schemeObject); + VERIFY_IS_NOT_NULL(theme->Window()); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::None, theme->Window().UseMica()); + } + { + static constexpr std::string_view micaTheme{ R"({ + "name": "mica", + "window": + { + "useMica": "mica" + } + })" }; + const auto schemeObject = VerifyParseSucceeded(micaTheme); + auto theme = Theme::FromJson(schemeObject); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::Mica, theme->Window().UseMica()); + } + { + static constexpr std::string_view micaAltTheme{ R"({ + "name": "micaAlt", + "window": + { + "useMica": "micaAlt" + } + })" }; + const auto schemeObject = VerifyParseSucceeded(micaAltTheme); + auto theme = Theme::FromJson(schemeObject); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::MicaAlt, theme->Window().UseMica()); + } + + Log::Comment(L"Back-compat: booleans map to Mica / None."); + { + static constexpr std::string_view trueTheme{ R"({ + "name": "true", + "window": + { + "useMica": true + } + })" }; + const auto schemeObject = VerifyParseSucceeded(trueTheme); + auto theme = Theme::FromJson(schemeObject); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::Mica, theme->Window().UseMica()); + } + { + static constexpr std::string_view falseTheme{ R"({ + "name": "false", + "window": + { + "useMica": false + } + })" }; + const auto schemeObject = VerifyParseSucceeded(falseTheme); + auto theme = Theme::FromJson(schemeObject); + VERIFY_ARE_EQUAL(Settings::Model::MicaStyle::None, theme->Window().UseMica()); + } } void ThemeTests::ParseEmptyTheme() diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6d22a9a8182..403f3f5d6cd 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1002,7 +1002,7 @@ void AppHost::_updateTheme() const auto colorOpacity = b ? color.A / 255.0 : 0.0; const auto brushOpacity = _opacityFromBrush(b); const auto opacity = std::min(colorOpacity, brushOpacity); - _window->UseMica(windowTheme ? windowTheme.UseMica() : false, opacity); + _window->UseMica(windowTheme ? windowTheme.UseMica() : winrt::Microsoft::Terminal::Settings::Model::MicaStyle::None, opacity); // This is a hack to make the window borders dark instead of light. // It must be done before WM_NCPAINT so that the borders are rendered with diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index e627bd44957..7fe4584ccff 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1844,7 +1844,7 @@ void IslandWindow::UseDarkTheme(const bool v) std::ignore = DwmSetWindowAttribute(GetHandle(), DWMWA_USE_IMMERSIVE_DARK_MODE, &attribute, sizeof(attribute)); } -void IslandWindow::UseMica(const bool newValue, const double /*titlebarOpacity*/) +void IslandWindow::UseMica(const winrt::Microsoft::Terminal::Settings::Model::MicaStyle 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 @@ -1853,7 +1853,19 @@ void IslandWindow::UseMica(const bool newValue, const double /*titlebarOpacity*/ // 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; + int attribute = DWMSBT_NONE; + switch (newValue) + { + case winrt::Microsoft::Terminal::Settings::Model::MicaStyle::Mica: + attribute = DWMSBT_MAINWINDOW; // "Mica" + break; + case winrt::Microsoft::Terminal::Settings::Model::MicaStyle::MicaAlt: + attribute = DWMSBT_TABBEDWINDOW; // "Mica Alt" + break; + default: // None + attribute = DWMSBT_NONE; + break; + } 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 83afeaf44f5..7ea491cc909 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -70,7 +70,7 @@ class IslandWindow : void RemoveFromSystemMenu(const winrt::hstring& itemLabel); void UseDarkTheme(const bool v); - virtual void UseMica(const bool newValue, const double titlebarOpacity); + virtual void UseMica(const winrt::Microsoft::Terminal::Settings::Model::MicaStyle newValue, const double titlebarOpacity); til::event> DragRegionClicked; til::event> WindowCloseButtonClicked; diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 5ab07ef7607..45570d81c50 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -1195,12 +1195,12 @@ void NonClientIslandWindow::SetTitlebarBackground(winrt::Windows::UI::Xaml::Medi _titlebar.Background(brush); } -void NonClientIslandWindow::UseMica(const bool newValue, const double titlebarOpacity) +void NonClientIslandWindow::UseMica(const winrt::Microsoft::Terminal::Settings::Model::MicaStyle 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; + _useMica = newValue != winrt::Microsoft::Terminal::Settings::Model::MicaStyle::None; _titlebarOpacity = titlebarOpacity; IslandWindow::UseMica(newValue, titlebarOpacity); diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 6fc0761a3c6..48a3078c9bc 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -48,7 +48,7 @@ class NonClientIslandWindow : public IslandWindow void SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush); void SetShowTabsFullscreen(const bool newShowTabsFullscreen) override; - virtual void UseMica(const bool newValue, const double titlebarOpacity) override; + virtual void UseMica(const winrt::Microsoft::Terminal::Settings::Model::MicaStyle newValue, const double titlebarOpacity) override; private: std::optional _oldIslandPos;