From da2b80bc0a38f5df49a4b987527decb302d16b7b Mon Sep 17 00:00:00 2001 From: Nicholas Bennett Date: Tue, 6 Dec 2022 09:33:22 -0800 Subject: [PATCH] Add support for switching the scheme based on the app's theme (#14064) ## Summary of the Pull Request This pull request solved the problem of users not being able to set color schemes specifically for dark or light mode. Now the code has been updated to accept a dark and light color scheme in the json. The old setting is still compatible. Keep in mind if you update your color scheme through the settings UI, it will set both dark and light to the color scheme selected. This is because the settings UI update for selecting both Dark and Light color schemes is not supported yet. This also solves the problem of the UI not using the system OS theme. Now you can select system theme and your color scheme will be selected based on if the system theme is dark or light. ## References #4066 ## PR Checklist * [x] Closes #4066 * [x] Closes #14050 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA. * [x] Tests added/passed I believe so, added one test to ColorSchemeTests.cpp and I believe it passed. Also had to modify TerminalSettingsTests.cpp to accept the new ApplyAppearanceSettings function template * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [x] Schema updated. * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #4066 and also teams messages with @carlos-zamora ## Detailed Description of the Pull Request / Additional comments -Removed ColorSchemeName from MTSMSettings.h in order to process the setting for both string and object. -Added DarkColorSchemeName and LightColorSchemeName properties to the AppearanceConfig to replace ColorSchemeName. -Hacked a few processes to play nice with all 3 properties listed above as in some cases around the UI, we need to still use the ColorSchemeName. Once we change the UI I believe we can go back to just Dark and LightColorSchemeName -Added and Updated Test to align to the new code. Acceptable Json values, "colorScheme": { "dark": "Campbell", "light": "Campbell" } or "colorScheme": "Campbell" ## Validation Steps Performed Individual testing along with the test case added. --- doc/cascadia/profiles.schema.json | 33 ++++++- .../ColorSchemeTests.cpp | 98 +++++++++++++++++-- .../DeserializationTests.cpp | 12 ++- .../SerializationTests.cpp | 16 +-- .../TerminalSettingsTests.cpp | 17 ++-- src/cascadia/LocalTests_SettingsModel/pch.h | 1 + src/cascadia/TerminalApp/App.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 90 ++++++++--------- src/cascadia/TerminalApp/AppLogic.h | 4 +- src/cascadia/TerminalApp/AppLogic.idl | 2 +- .../TerminalSettingsEditor/Appearances.cpp | 7 +- .../TerminalSettingsEditor/Appearances.h | 3 +- .../TerminalSettingsEditor/Appearances.idl | 3 +- .../TerminalSettingsEditor/Appearances.xaml | 7 +- .../ColorSchemeViewModel.cpp | 4 +- .../ColorSchemesPageViewModel.cpp | 3 +- .../AppearanceConfig.cpp | 29 ++++++ .../TerminalSettingsModel/AppearanceConfig.h | 3 + .../AzureCloudShellGenerator.cpp | 3 +- .../CascadiaSettings.cpp | 70 +++++++------ .../TerminalSettingsModel/CascadiaSettings.h | 1 - .../CascadiaSettings.idl | 1 - .../IAppearanceConfig.idl | 11 ++- .../TerminalSettingsModel/MTSMSettings.h | 1 - .../PowershellCoreProfileGenerator.cpp | 3 +- .../TerminalSettings.cpp | 43 +++++++- .../TerminalSettingsModel/TerminalSettings.h | 3 +- .../WslDistroGenerator.cpp | 3 +- src/cascadia/TerminalSettingsModel/pch.h | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 1 + src/cascadia/WindowsTerminal/IslandWindow.cpp | 12 +++ src/cascadia/WindowsTerminal/IslandWindow.h | 1 + src/cascadia/inc/ControlProperties.h | 2 +- 33 files changed, 347 insertions(+), 143 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 3cef5d1946e..e20f8545681 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -82,7 +82,14 @@ "properties": { "colorScheme": { "description": "The name of a color scheme to use when unfocused.", - "type": "string" + "oneOf": [ + { + "$ref": "#/$defs/SchemePair" + }, + { + "type": "string" + } + ] }, "foreground": { "$ref": "#/$defs/Color", @@ -233,6 +240,21 @@ }, "type": "object" }, + "SchemePair": { + "description": "Contains both a light and dark color scheme for the Terminal to use, depending on the theme of the application.", + "properties": { + "light": { + "default": "Campbell", + "description": "Name of the scheme to use when the app is using light theme", + "type": "string" + }, + "dark": { + "default": "Campbell", + "description": "Name of the scheme to use when the app is using dark theme", + "type": "string" + }, + } + }, "FontConfig": { "properties": { "face": { @@ -2170,7 +2192,14 @@ "colorScheme": { "default": "Campbell", "description": "Name of the terminal color scheme to use. Color schemes are defined under \"schemes\".", - "type": "string" + "oneOf": [ + { + "$ref": "#/$defs/SchemePair" + }, + { + "type": "string" + } + ] }, "commandline": { "description": "Executable used in the profile.", diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp index 721e426761b..b266064977a 100644 --- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp @@ -217,6 +217,30 @@ namespace SettingsModelLocalTests { "name": "Different reference", "colorScheme": "One Half Dark" + }, + { + "name": "rename neither", + "colorScheme": + { + "dark": "One Half Dark", + "light": "One Half Light" + } + }, + { + "name": "rename only light", + "colorScheme": + { + "dark": "One Half Dark", + "light": "Campbell" + } + }, + { + "name": "rename only dark", + "colorScheme": + { + "dark": "Campbell", + "light": "One Half Light" + } } ] }, @@ -289,6 +313,28 @@ namespace SettingsModelLocalTests "selectionBackground": "#FFFFFF", "white": "#DCDFE4", "yellow": "#E5C07B" + }, + { + "name": "One Half Light", + "foreground": "#383A42", + "background": "#FAFAFA", + "cursorColor": "#4F525D", + "black": "#383A42", + "red": "#E45649", + "green": "#50A14F", + "yellow": "#C18301", + "blue": "#0184BC", + "purple": "#A626A4", + "cyan": "#0997B3", + "white": "#FAFAFA", + "brightBlack": "#4F525D", + "brightRed": "#DF6C75", + "brightGreen": "#98C379", + "brightYellow": "#E4C07A", + "brightBlue": "#61AFEF", + "brightPurple": "#C577DD", + "brightCyan": "#56B5C1", + "brightWhite": "#FFFFFF" } ] })json" }; @@ -296,31 +342,63 @@ namespace SettingsModelLocalTests const auto settings{ winrt::make_self(settingsString) }; const auto newName{ L"Campbell (renamed)" }; + settings->UpdateColorSchemeReferences(L"Campbell", newName); - VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName()); - VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().DarkColorSchemeName()); + VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasLightColorSchemeName()); const auto& profiles{ settings->AllProfiles() }; { const auto& prof{ profiles.GetAt(0) }; - VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); - VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); } { const auto& prof{ profiles.GetAt(1) }; - VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); - VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); } { const auto& prof{ profiles.GetAt(2) }; - VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); - VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_IS_FALSE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_FALSE(prof.DefaultAppearance().HasLightColorSchemeName()); } { const auto& prof{ profiles.GetAt(3) }; - VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().ColorSchemeName()); - VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); + VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); + } + { + const auto& prof{ profiles.GetAt(4) }; + VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_ARE_EQUAL(L"One Half Light", prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); + } + { + const auto& prof{ profiles.GetAt(5) }; + VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); + } + { + const auto& prof{ profiles.GetAt(6) }; + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().DarkColorSchemeName()); + VERIFY_ARE_EQUAL(L"One Half Light", prof.DefaultAppearance().LightColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasDarkColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasLightColorSchemeName()); } } } diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp index 7008feed586..b2eaed29014 100644 --- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp @@ -744,7 +744,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size()); for (const auto& profile : settings->AllProfiles()) { - VERIFY_ARE_EQUAL(L"Campbell", profile.DefaultAppearance().ColorSchemeName()); + VERIFY_ARE_EQUAL(L"Campbell", profile.DefaultAppearance().DarkColorSchemeName()); + VERIFY_ARE_EQUAL(L"Campbell", profile.DefaultAppearance().LightColorSchemeName()); } } @@ -959,6 +960,10 @@ namespace SettingsModelLocalTests }, { "name": "profile3", + "closeOnExit": "automatic" + }, + { + "name": "profile4", "closeOnExit": null } ] @@ -968,9 +973,10 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->AllProfiles().GetAt(0).CloseOnExit()); VERIFY_ARE_EQUAL(CloseOnExitMode::Always, settings->AllProfiles().GetAt(1).CloseOnExit()); VERIFY_ARE_EQUAL(CloseOnExitMode::Never, settings->AllProfiles().GetAt(2).CloseOnExit()); + VERIFY_ARE_EQUAL(CloseOnExitMode::Automatic, settings->AllProfiles().GetAt(3).CloseOnExit()); - // Unknown modes parse as "Graceful" - VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings->AllProfiles().GetAt(3).CloseOnExit()); + // Unknown modes parse as "Automatic" + VERIFY_ARE_EQUAL(CloseOnExitMode::Automatic, settings->AllProfiles().GetAt(4).CloseOnExit()); } void DeserializationTests::TestCloseOnExitCompatibilityShim() diff --git a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp index f0a3ed3d02a..4f9fb528f99 100644 --- a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp @@ -134,7 +134,7 @@ namespace SettingsModelLocalTests "font": { "face": "Cascadia Mono", - "size": 12, + "size": 12.0, "weight": "normal" }, "padding": "8, 8, 8, 8", @@ -250,8 +250,8 @@ namespace SettingsModelLocalTests // complex command with key chords static constexpr std::string_view actionsString4A{ R"([ - { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" }, - { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" } + { "command": { "action": "adjustFontSize", "delta": 1.0 }, "keys": "ctrl+c" }, + { "command": { "action": "adjustFontSize", "delta": 1.0 }, "keys": "ctrl+d" } ])" }; // GH#13323 - these can be fragile. In the past, the order these get // re-serialized as has been not entirely stable. We don't really care @@ -260,7 +260,7 @@ namespace SettingsModelLocalTests // itself. Feel free to change as needed. static constexpr std::string_view actionsString4B{ R"([ { "command": { "action": "findMatch", "direction": "prev" }, "keys": "ctrl+shift+r" }, - { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" } + { "command": { "action": "adjustFontSize", "delta": 1.0 }, "keys": "ctrl+d" } ])" }; // command with name and icon and multiple key chords @@ -284,8 +284,8 @@ namespace SettingsModelLocalTests { "name": "Change font size...", "commands": [ - { "command": { "action": "adjustFontSize", "delta": 1 } }, - { "command": { "action": "adjustFontSize", "delta": -1 } }, + { "command": { "action": "adjustFontSize", "delta": 1.0 } }, + { "command": { "action": "adjustFontSize", "delta": -1.0 } }, { "command": "resetFontSize" }, ] } @@ -478,7 +478,7 @@ namespace SettingsModelLocalTests "name": "Profile with legacy font settings", "fontFace": "Cascadia Mono", - "fontSize": 12, + "fontSize": 12.0, "fontWeight": "normal" })" }; @@ -488,7 +488,7 @@ namespace SettingsModelLocalTests "font": { "face": "Cascadia Mono", - "size": 12, + "size": 12.0, "weight": "normal" } })" }; diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index 8680e7f9cd2..0ae0bcc164e 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -779,21 +779,22 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(6u, settings->ActiveProfiles().Size()); VERIFY_ARE_EQUAL(2u, settings->GlobalSettings().ColorSchemes().Size()); - auto createTerminalSettings = [&](const auto& profile, const auto& schemes) { + auto createTerminalSettings = [&](const auto& profile, const auto& schemes, const auto& Theme) { auto terminalSettings{ winrt::make_self() }; terminalSettings->_ApplyProfileSettings(profile); - terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes); + terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes, Theme); return terminalSettings; }; const auto activeProfiles = settings->ActiveProfiles(); const auto colorSchemes = settings->GlobalSettings().ColorSchemes(); - const auto terminalSettings0 = createTerminalSettings(activeProfiles.GetAt(0), colorSchemes); - const auto terminalSettings1 = createTerminalSettings(activeProfiles.GetAt(1), colorSchemes); - const auto terminalSettings2 = createTerminalSettings(activeProfiles.GetAt(2), colorSchemes); - const auto terminalSettings3 = createTerminalSettings(activeProfiles.GetAt(3), colorSchemes); - const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes); - const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes); + const auto currentTheme = settings->GlobalSettings().CurrentTheme(); + const auto terminalSettings0 = createTerminalSettings(activeProfiles.GetAt(0), colorSchemes, currentTheme); + const auto terminalSettings1 = createTerminalSettings(activeProfiles.GetAt(1), colorSchemes, currentTheme); + const auto terminalSettings2 = createTerminalSettings(activeProfiles.GetAt(2), colorSchemes, currentTheme); + const auto terminalSettings3 = createTerminalSettings(activeProfiles.GetAt(3), colorSchemes, currentTheme); + const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes, currentTheme); + const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes, currentTheme); VERIFY_ARE_EQUAL(til::color(0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default diff --git a/src/cascadia/LocalTests_SettingsModel/pch.h b/src/cascadia/LocalTests_SettingsModel/pch.h index b018bdaa4a6..b2e15b5a7b1 100644 --- a/src/cascadia/LocalTests_SettingsModel/pch.h +++ b/src/cascadia/LocalTests_SettingsModel/pch.h @@ -44,6 +44,7 @@ Author(s): #include #include #include +#include #include #include #include diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index c143f2eceda..4d7e5f93763 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -54,7 +54,7 @@ namespace winrt::TerminalApp::implementation { auto logic = Logic(); logic.RunAsUwp(); // Must set UWP status first, settings might change based on it. - logic.LoadSettings(); + logic.ReloadSettings(); logic.Create(); auto page = logic.GetRoot().as(); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 7062bfd73a9..040c39d29d1 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -191,7 +191,7 @@ namespace winrt::TerminalApp::implementation _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { if (auto self{ weakSelf.get() }) { - self->_ReloadSettings(); + self->ReloadSettings(); } }); @@ -615,7 +615,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } winrt::Windows::Foundation::Size proposedSize{}; @@ -696,7 +696,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } // GH#4620/#5801 - If the user passed --maximized or --fullscreen on the @@ -730,7 +730,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; @@ -760,7 +760,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } // If the position has been specified on the commandline, don't center on launch return _settings.GlobalSettings().CenterOnLaunch() && !_appArgs.GetPosition().has_value(); @@ -776,7 +776,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().ShowTabsInTitlebar(); @@ -787,7 +787,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().AlwaysOnTop(); @@ -867,36 +867,6 @@ namespace winrt::TerminalApp::implementation return hr; } - // Method Description: - // - Initialized our settings. See CascadiaSettings for more details. - // Additionally hooks up our callbacks for keybinding events to the - // keybindings object. - // NOTE: This must be called from a MTA if we're running as a packaged - // application. The Windows.Storage APIs require a MTA. If this isn't - // happening during startup, it'll need to happen on a background thread. - void AppLogic::LoadSettings() - { - // Attempt to load the settings. - // If it fails, - // - use Default settings, - // - don't persist them (LoadAll won't save them in this case). - // - _settingsLoadedResult will be set to an error, indicating that - // we should display the loading error. - // * We can't display the error now, because we might not have a - // UI yet. We'll display the error in _OnLoaded. - _settingsLoadedResult = _TryLoadSettings(); - - if (FAILED(_settingsLoadedResult)) - { - _settings = CascadiaSettings::LoadDefaults(); - } - - _loadedInitialSettings = true; - - // Register for directory change notification. - _RegisterSettingsChange(); - } - // Call this function after loading your _settings. // It handles any CPU intensive settings updates (like updating the Jumplist) // which should thus only occur if the settings file actually changed. @@ -920,7 +890,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Registers for changes to the settings folder and upon a updated settings - // profile calls _ReloadSettings(). + // profile calls ReloadSettings(). // Arguments: // - // Return Value: @@ -1055,7 +1025,14 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Reloads the settings from the settings.json file. - void AppLogic::_ReloadSettings() + // - When this is called the first time, this initializes our settings. See + // CascadiaSettings for more details. Additionally hooks up our callbacks + // for keybinding events to the keybindings object. + // - NOTE: when called initially, this must be called from a MTA if we're + // running as a packaged application. The Windows.Storage APIs require a + // MTA. If this isn't happening during startup, it'll need to happen on + // a background thread. + void AppLogic::ReloadSettings() { // Attempt to load our settings. // If it fails, @@ -1064,11 +1041,28 @@ namespace winrt::TerminalApp::implementation // - display a loading error _settingsLoadedResult = _TryLoadSettings(); + const auto initialLoad = !_loadedInitialSettings; + _loadedInitialSettings = true; + if (FAILED(_settingsLoadedResult)) { - const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); + if (initialLoad) + { + _settings = CascadiaSettings::LoadDefaults(); + } + else + { + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); + return; + } + } + + if (initialLoad) + { + // Register for directory change notification. + _RegisterSettingsChange(); return; } @@ -1375,7 +1369,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior()); @@ -1532,7 +1526,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().AutoHideWindow(); @@ -1553,7 +1547,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; @@ -1666,7 +1660,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().MinimizeToNotificationArea(); @@ -1677,7 +1671,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().AlwaysShowNotificationIcon(); @@ -1693,7 +1687,7 @@ namespace winrt::TerminalApp::implementation if (!_loadedInitialSettings) { // Load settings if we haven't already - LoadSettings(); + ReloadSettings(); } return _settings.GlobalSettings().CurrentTheme(); } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index f4abe8254f2..5534979b973 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -62,7 +62,8 @@ namespace winrt::TerminalApp::implementation bool IsUwp() const noexcept; void RunAsUwp(); bool IsElevated() const noexcept; - void LoadSettings(); + void ReloadSettings(); + [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; void Quit(); @@ -195,7 +196,6 @@ namespace winrt::TerminalApp::implementation void _ProcessLazySettingsChanges(); void _RegisterSettingsChange(); fire_and_forget _DispatchReloadSettings(); - void _ReloadSettings(); void _OpenSettingsUI(); bool _hasCommandLineArguments{ false }; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 0f77af0c8cd..b7a92ea1103 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -59,7 +59,7 @@ namespace TerminalApp void Quit(); - void LoadSettings(); + void ReloadSettings(); Windows.UI.Xaml.UIElement GetRoot(); void SetInboundListener(); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index 4b4b5a5a361..ef77b5f6f73 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -243,7 +243,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentCursorShape" }); _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"IsVintageCursor" }); } - else if (settingName == L"ColorSchemeName") + else if (settingName == L"DarkColorSchemeName" || settingName == L"LightColorSchemeName") { _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentColorScheme" }); } @@ -355,7 +355,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation ColorScheme Appearances::CurrentColorScheme() { - const auto schemeName{ Appearance().ColorSchemeName() }; + const auto schemeName{ Appearance().DarkColorSchemeName() }; if (const auto scheme{ Appearance().Schemes().TryLookup(schemeName) }) { return scheme; @@ -370,7 +370,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void Appearances::CurrentColorScheme(const ColorScheme& val) { - Appearance().ColorSchemeName(val.Name()); + Appearance().DarkColorSchemeName(val.Name()); + Appearance().LightColorSchemeName(val.Name()); } bool Appearances::IsVintageCursor() const diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index 207a1f8846a..2f67b03a9b2 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -87,7 +87,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_appearance, RetroTerminalEffect); OBSERVABLE_PROJECTED_SETTING(_appearance, CursorShape); OBSERVABLE_PROJECTED_SETTING(_appearance, CursorHeight); - OBSERVABLE_PROJECTED_SETTING(_appearance, ColorSchemeName); + OBSERVABLE_PROJECTED_SETTING(_appearance, DarkColorSchemeName); + OBSERVABLE_PROJECTED_SETTING(_appearance, LightColorSchemeName); OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImagePath); OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageOpacity); OBSERVABLE_PROJECTED_SETTING(_appearance, BackgroundImageStretchMode); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 303e94b0d4a..e31f7e41703 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -37,7 +37,8 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Windows.UI.Text.FontWeight, FontWeight); - OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, ColorSchemeName); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, DarkColorSchemeName); + OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, LightColorSchemeName); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Boolean, RetroTerminalEffect); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Microsoft.Terminal.Core.CursorStyle, CursorShape); OBSERVABLE_PROJECTED_APPEARANCE_SETTING(UInt32, CursorHeight); diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 8a6a5d45aa9..9bed48c7ef5 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -39,10 +39,11 @@ Style="{StaticResource TextBlockSubHeaderStyle}" /> + + ClearSettingValue="{x:Bind Appearance.ClearDarkColorSchemeName}" + HasSettingValue="{x:Bind Appearance.HasDarkColorSchemeName, Mode=OneWay}" + SettingOverrideSource="{x:Bind Appearance.DarkColorSchemeNameOverrideSource, Mode=OneWay}"> diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp index 62491f7418d..160f5a3839b 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemeViewModel.cpp @@ -52,7 +52,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool ColorSchemeViewModel::IsDefaultScheme() { - return _Name == _settings.ProfileDefaults().DefaultAppearance().ColorSchemeName(); + const auto defaultAppearance = _settings.ProfileDefaults().DefaultAppearance(); + return defaultAppearance.LightColorSchemeName() == defaultAppearance.DarkColorSchemeName() && + _Name == defaultAppearance.LightColorSchemeName(); } void ColorSchemeViewModel::RefreshIsDefault() diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemesPageViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ColorSchemesPageViewModel.cpp index d5c1280f437..badc52b4ac3 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemesPageViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemesPageViewModel.cpp @@ -189,7 +189,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (_CurrentScheme) { - _settings.ProfileDefaults().DefaultAppearance().ColorSchemeName(_CurrentScheme.Name()); + _settings.ProfileDefaults().DefaultAppearance().LightColorSchemeName(_CurrentScheme.Name()); + _settings.ProfileDefaults().DefaultAppearance().DarkColorSchemeName(_CurrentScheme.Name()); for (const auto scheme : _AllColorSchemes) { auto schemeImpl{ get_self(scheme) }; diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp index d48763eca1c..90513b146ca 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp @@ -18,6 +18,7 @@ static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" static constexpr std::string_view CursorColorKey{ "cursorColor" }; static constexpr std::string_view LegacyAcrylicTransparencyKey{ "acrylicOpacity" }; static constexpr std::string_view OpacityKey{ "opacity" }; +static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; AppearanceConfig::AppearanceConfig(winrt::weak_ref sourceProfile) : _sourceProfile(std::move(sourceProfile)) @@ -33,6 +34,9 @@ winrt::com_ptr AppearanceConfig::CopyAppearance(const Appearan appearance->_CursorColor = source->_CursorColor; appearance->_Opacity = source->_Opacity; + appearance->_DarkColorSchemeName = source->_DarkColorSchemeName; + appearance->_LightColorSchemeName = source->_LightColorSchemeName; + #define APPEARANCE_SETTINGS_COPY(type, name, jsonKey, ...) \ appearance->_##name = source->_##name; MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_COPY) @@ -50,6 +54,19 @@ Json::Value AppearanceConfig::ToJson() const JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor); JsonUtils::SetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); + if (HasDarkColorSchemeName() || HasLightColorSchemeName()) + { + // check if the setting is coming from the UI, if so grab the ColorSchemeName until the settings UI is fixed. + if (_LightColorSchemeName != _DarkColorSchemeName) + { + JsonUtils::SetValueForKey(json["colorScheme"], "dark", _DarkColorSchemeName); + JsonUtils::SetValueForKey(json["colorScheme"], "light", _LightColorSchemeName); + } + else + { + JsonUtils::SetValueForKey(json, "colorScheme", _DarkColorSchemeName); + } + } #define APPEARANCE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \ JsonUtils::SetValueForKey(json, jsonKey, _##name); @@ -79,6 +96,18 @@ void AppearanceConfig::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, LegacyAcrylicTransparencyKey, _Opacity); JsonUtils::GetValueForKey(json, OpacityKey, _Opacity, JsonUtils::OptionalConverter{}); + if (json["colorScheme"].isString()) + { + // to make the UI happy, set ColorSchemeName. + JsonUtils::GetValueForKey(json, ColorSchemeKey, _DarkColorSchemeName); + _LightColorSchemeName = _DarkColorSchemeName; + } + else if (json["colorScheme"].isObject()) + { + // to make the UI happy, set ColorSchemeName to whatever the dark value is. + JsonUtils::GetValueForKey(json["colorScheme"], "dark", _DarkColorSchemeName); + JsonUtils::GetValueForKey(json["colorScheme"], "light", _LightColorSchemeName); + } #define APPEARANCE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \ JsonUtils::GetValueForKey(json, jsonKey, _##name); diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h index bbc30927eb7..7e8f35e6633 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -42,6 +42,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, CursorColor, nullptr); INHERITABLE_SETTING(Model::IAppearanceConfig, double, Opacity, 1.0); + INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, DarkColorSchemeName, L"Campbell"); + INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, LightColorSchemeName, L"Campbell"); + #define APPEARANCE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ INHERITABLE_SETTING(Model::IAppearanceConfig, type, name, ##__VA_ARGS__) MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_INITIALIZE) diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp index a69f44eaecf..948b1822f8c 100644 --- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp @@ -31,7 +31,8 @@ void AzureCloudShellGenerator::GenerateProfiles(std::vectorStartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY }); - azureCloudShellProfile->DefaultAppearance().ColorSchemeName(L"Vintage"); + azureCloudShellProfile->DefaultAppearance().DarkColorSchemeName(L"Vintage"); + azureCloudShellProfile->DefaultAppearance().LightColorSchemeName(L"Vintage"); azureCloudShellProfile->ConnectionType(AzureConnection::ConnectionType()); profiles.emplace_back(std::move(azureCloudShellProfile)); } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 9712c6e51cf..c9aa9ff8e81 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -326,6 +326,8 @@ Model::Profile CascadiaSettings::DuplicateProfile(const Model::Profile& source) DUPLICATE_SETTING_MACRO_SUB(appearance, target, SelectionBackground); DUPLICATE_SETTING_MACRO_SUB(appearance, target, CursorColor); DUPLICATE_SETTING_MACRO_SUB(appearance, target, Opacity); + DUPLICATE_SETTING_MACRO_SUB(appearance, target, DarkColorSchemeName); + DUPLICATE_SETTING_MACRO_SUB(appearance, target, LightColorSchemeName); } // UnfocusedAppearance is treated as a single setting, @@ -430,22 +432,29 @@ void CascadiaSettings::_validateSettings() void CascadiaSettings::_validateAllSchemesExist() { const auto colorSchemes = _globals->ColorSchemes(); - auto foundInvalidScheme = false; + auto foundInvalidDarkScheme = false; + auto foundInvalidLightScheme = false; for (const auto& profile : _allProfiles) { for (const auto& appearance : std::array{ profile.DefaultAppearance(), profile.UnfocusedAppearance() }) { - if (appearance && !colorSchemes.HasKey(appearance.ColorSchemeName())) + if (appearance && !colorSchemes.HasKey(appearance.DarkColorSchemeName())) { - // Clear the user set color scheme. We'll just fallback instead. - appearance.ClearColorSchemeName(); - foundInvalidScheme = true; + // Clear the user set dark color scheme. We'll just fallback instead. + appearance.ClearDarkColorSchemeName(); + foundInvalidDarkScheme = true; + } + if (appearance && !colorSchemes.HasKey(appearance.LightColorSchemeName())) + { + // Clear the user set light color scheme. We'll just fallback instead. + appearance.ClearLightColorSchemeName(); + foundInvalidLightScheme = true; } } } - if (foundInvalidScheme) + if (foundInvalidDarkScheme || foundInvalidLightScheme) { _warnings.Append(SettingsLoadWarnings::UnknownColorScheme); } @@ -937,24 +946,6 @@ bool CascadiaSettings::_hasInvalidColorScheme(const Model::Command& command) con return invalid; } -// Method Description: -// - Lookup the color scheme for a given profile. If the profile doesn't exist, -// or the scheme name listed in the profile doesn't correspond to a scheme, -// this will return `nullptr`. -// Arguments: -// - profileGuid: the GUID of the profile to find the scheme for. -// Return Value: -// - a non-owning pointer to the scheme. -Model::ColorScheme CascadiaSettings::GetColorSchemeForProfile(const Model::Profile& profile) const -{ - if (!profile) - { - return nullptr; - } - const auto schemeName = profile.DefaultAppearance().ColorSchemeName(); - return _globals->ColorSchemes().TryLookup(schemeName); -} - // Method Description: // - updates all references to that color scheme with the new name // Arguments: @@ -966,26 +957,41 @@ void CascadiaSettings::UpdateColorSchemeReferences(const winrt::hstring& oldName { // update profiles.defaults, if necessary if (_baseLayerProfile && - _baseLayerProfile->DefaultAppearance().HasColorSchemeName() && - _baseLayerProfile->DefaultAppearance().ColorSchemeName() == oldName) + _baseLayerProfile->DefaultAppearance().HasDarkColorSchemeName() && + _baseLayerProfile->DefaultAppearance().DarkColorSchemeName() == oldName) + { + _baseLayerProfile->DefaultAppearance().DarkColorSchemeName(newName); + } + // NOT else-if, because both could match + if (_baseLayerProfile && + _baseLayerProfile->DefaultAppearance().HasLightColorSchemeName() && + _baseLayerProfile->DefaultAppearance().LightColorSchemeName() == oldName) { - _baseLayerProfile->DefaultAppearance().ColorSchemeName(newName); + _baseLayerProfile->DefaultAppearance().LightColorSchemeName(newName); } // update all profiles referencing this color scheme for (const auto& profile : _allProfiles) { const auto defaultAppearance = profile.DefaultAppearance(); - if (defaultAppearance.HasColorSchemeName() && defaultAppearance.ColorSchemeName() == oldName) + if (defaultAppearance.HasLightColorSchemeName() && defaultAppearance.LightColorSchemeName() == oldName) { - defaultAppearance.ColorSchemeName(newName); + defaultAppearance.LightColorSchemeName(newName); + } + if (defaultAppearance.HasDarkColorSchemeName() && defaultAppearance.DarkColorSchemeName() == oldName) + { + defaultAppearance.DarkColorSchemeName(newName); } - if (profile.UnfocusedAppearance()) + if (auto unfocused{ profile.UnfocusedAppearance() }) { - if (profile.UnfocusedAppearance().HasColorSchemeName() && profile.UnfocusedAppearance().ColorSchemeName() == oldName) + if (unfocused.HasLightColorSchemeName() && unfocused.LightColorSchemeName() == oldName) + { + unfocused.LightColorSchemeName(newName); + } + if (unfocused.HasDarkColorSchemeName() && unfocused.DarkColorSchemeName() == oldName) { - profile.UnfocusedAppearance().ColorSchemeName(newName); + unfocused.DarkColorSchemeName(newName); } } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index dead1629ff2..d8543e74b5a 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -124,7 +124,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::Profile ProfileDefaults() const; Model::Profile CreateNewProfile(); Model::Profile FindProfile(const winrt::guid& guid) const noexcept; - Model::ColorScheme GetColorSchemeForProfile(const Model::Profile& profile) const; void UpdateColorSchemeReferences(const winrt::hstring& oldName, const winrt::hstring& newName); Model::Profile GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const; Model::Profile GetProfileByName(const winrt::hstring& name) const; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index fb69e58ffc8..5a1987dcf3e 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -45,7 +45,6 @@ namespace Microsoft.Terminal.Settings.Model Profile CreateNewProfile(); Profile FindProfile(Guid profileGuid); - ColorScheme GetColorSchemeForProfile(Profile profile); void UpdateColorSchemeReferences(String oldName, String newName); Profile GetProfileForArgs(NewTerminalArgs newTerminalArgs); diff --git a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl index 2cfa305dec4..6352de64b08 100644 --- a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl +++ b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl @@ -5,7 +5,7 @@ import "Profile.idl"; #include "IInheritable.idl.h" #define INHERITABLE_APPEARANCE_SETTING(Type, Name) \ - _BASE_INHERITABLE_SETTING(Type, Name); \ + _BASE_INHERITABLE_SETTING(Type, Name); \ Microsoft.Terminal.Settings.Model.IAppearanceConfig Name##OverrideSource { get; } namespace Microsoft.Terminal.Settings.Model @@ -22,9 +22,7 @@ namespace Microsoft.Terminal.Settings.Model Vertical_Bottom = 0x20 }; - [flags] - enum IntenseStyle - { + [flags] enum IntenseStyle { Bold = 0x1, Bright = 0x2, All = 0xffffffff @@ -33,7 +31,10 @@ namespace Microsoft.Terminal.Settings.Model interface IAppearanceConfig { Microsoft.Terminal.Settings.Model.Profile SourceProfile { get; }; - INHERITABLE_APPEARANCE_SETTING(String, ColorSchemeName); + + INHERITABLE_APPEARANCE_SETTING(String, DarkColorSchemeName); + INHERITABLE_APPEARANCE_SETTING(String, LightColorSchemeName); + INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, Foreground); INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, Background); INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, SelectionBackground); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 3cae1919883..91ff1b068a4 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -109,7 +109,6 @@ Author(s): X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \ X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \ X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \ - X(hstring, ColorSchemeName, "colorScheme", L"Campbell") \ X(hstring, BackgroundImagePath, "backgroundImage") \ X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \ X(Core::AdjustTextMode, AdjustIndistinguishableColors, "adjustIndistinguishableColors", Core::AdjustTextMode::Never) diff --git a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp index 5db52c810b6..ae04753a73c 100644 --- a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp @@ -319,7 +319,8 @@ void PowershellCoreProfileGenerator::GenerateProfiles(std::vectorCommandline(winrt::hstring{ quotedCommandline }); profile->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY }); - profile->DefaultAppearance().ColorSchemeName(L"Campbell"); + profile->DefaultAppearance().DarkColorSchemeName(L"Campbell"); + profile->DefaultAppearance().LightColorSchemeName(L"Campbell"); profile->Icon(winrt::hstring{ WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON }); if (first) diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 0ba57857303..39510e1b951 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -57,7 +57,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const auto globals = appSettings.GlobalSettings(); settings->_ApplyProfileSettings(profile); settings->_ApplyGlobalSettings(globals); - settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes()); + settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes(), globals.CurrentTheme()); return settings; } @@ -91,7 +91,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { const auto globals = appSettings.GlobalSettings(); auto childImpl = settings->CreateChild(); - childImpl->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes()); + childImpl->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes(), globals.CurrentTheme()); child = *childImpl; } @@ -183,17 +183,50 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return settingsPair; } - void TerminalSettings::_ApplyAppearanceSettings(const IAppearanceConfig& appearance, const Windows::Foundation::Collections::IMapView& schemes) + // I'm not even joking, this is the recommended way to do this: + // https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-windows-themes#know-when-dark-mode-is-enabled + bool _isSystemInDarkTheme() + { + static auto isColorLight = [](const Windows::UI::Color& clr) -> bool { + return (((5 * clr.G) + (2 * clr.R) + clr.B) > (8 * 128)); + }; + return isColorLight(Windows::UI::ViewManagement::UISettings().GetColorValue(Windows::UI::ViewManagement::UIColorType::Foreground)); + } + + void TerminalSettings::_ApplyAppearanceSettings(const IAppearanceConfig& appearance, + const Windows::Foundation::Collections::IMapView& schemes, + const winrt::Microsoft::Terminal::Settings::Model::Theme currentTheme) { _CursorShape = appearance.CursorShape(); _CursorHeight = appearance.CursorHeight(); - if (!appearance.ColorSchemeName().empty()) + + auto requestedTheme = currentTheme.RequestedTheme(); + if (requestedTheme == winrt::Windows::UI::Xaml::ElementTheme::Default) + { + requestedTheme = _isSystemInDarkTheme() ? + winrt::Windows::UI::Xaml::ElementTheme::Dark : + winrt::Windows::UI::Xaml::ElementTheme::Light; + } + + switch (requestedTheme) { - if (const auto scheme = schemes.TryLookup(appearance.ColorSchemeName())) + case winrt::Windows::UI::Xaml::ElementTheme::Light: + if (const auto scheme = schemes.TryLookup(appearance.LightColorSchemeName())) { ApplyColorScheme(scheme); } + break; + case winrt::Windows::UI::Xaml::ElementTheme::Dark: + if (const auto scheme = schemes.TryLookup(appearance.DarkColorSchemeName())) + { + ApplyColorScheme(scheme); + } + break; + case winrt::Windows::UI::Xaml::ElementTheme::Default: + // This shouldn't happen! + break; } + if (appearance.Foreground()) { _DefaultForeground = til::color{ appearance.Foreground().Value() }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 0dd65cdfbb7..e92ce3aeed9 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -170,7 +170,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept; void _ApplyAppearanceSettings(const Microsoft::Terminal::Settings::Model::IAppearanceConfig& appearance, - const Windows::Foundation::Collections::IMapView& schemes); + const Windows::Foundation::Collections::IMapView& schemes, + const winrt::Microsoft::Terminal::Settings::Model::Theme currentTheme); friend class SettingsModelLocalTests::TerminalSettingsTests; }; diff --git a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp index 840835cf1f3..373282866a6 100644 --- a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp @@ -53,7 +53,8 @@ static winrt::com_ptr makeProfile(const std::wstring& d std::wstring command{}; THROW_IF_FAILED(wil::GetSystemDirectoryW(command)); WSLDistro->Commandline(winrt::hstring{ command + L"\\wsl.exe -d " + distName }); - WSLDistro->DefaultAppearance().ColorSchemeName(L"Campbell"); + WSLDistro->DefaultAppearance().DarkColorSchemeName(L"Campbell"); + WSLDistro->DefaultAppearance().LightColorSchemeName(L"Campbell"); if (isWslDashDashCdAvailableForLinuxPaths()) { WSLDistro->StartingDirectory(winrt::hstring{ WslHomeDirectory }); diff --git a/src/cascadia/TerminalSettingsModel/pch.h b/src/cascadia/TerminalSettingsModel/pch.h index f8266b66fd1..75b789ed581 100644 --- a/src/cascadia/TerminalSettingsModel/pch.h +++ b/src/cascadia/TerminalSettingsModel/pch.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 90524d9a148..4ab2d0031bd 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -390,6 +390,7 @@ void AppHost::Initialize() _window->DragRegionClicked([this]() { _logic.TitlebarClicked(); }); _window->WindowVisibilityChanged([this](bool showOrHide) { _logic.WindowVisibilityChanged(showOrHide); }); + _window->UpdateSettingsRequested([this]() { _logic.ReloadSettings(); }); _revokers.RequestedThemeChanged = _logic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); _revokers.FullscreenChanged = _logic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index f42212912d2..446d1b46506 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -658,6 +658,18 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize } break; } + case WM_SETTINGCHANGE: + { + const std::wstring param{ (wchar_t*)lparam }; + // ImmersiveColorSet seems to be the notification that the OS theme + // changed. If that happens, let the app know, so it can hot-reload + // themes, color schemes that might depend on the OS theme + if (param == L"ImmersiveColorSet") + { + _UpdateSettingsRequestedHandlers(); + } + break; + } case WM_ENDSESSION: { // For WM_QUERYENDSESSION and WM_ENDSESSION, refer to: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 368e5321e8b..b0d3efff5c5 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -81,6 +81,7 @@ class IslandWindow : WINRT_CALLBACK(WindowMoved, winrt::delegate); WINRT_CALLBACK(WindowVisibilityChanged, winrt::delegate); + WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); protected: void ForceResize() diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 5911fa26183..8132d579e59 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -16,7 +16,7 @@ X(winrt::Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors, winrt::Microsoft::Terminal::Core::AdjustTextMode::Never) // --------------------------- Control Appearance --------------------------- -// All of these settings are defined in IControlSettings. +// All of these settings are defined in IControlAppearance. #define CONTROL_APPEARANCE_SETTINGS(X) \ X(til::color, SelectionBackground, DEFAULT_FOREGROUND) \ X(double, Opacity, 1.0) \