diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 5d3410951fb..2727ec0aba1 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -52,6 +52,117 @@ } ] }, + "AppearanceConfig": { + "properties": { + "colorScheme": { + "description": "The name of a color scheme to use when unfocused.", + "type": "string" + }, + "foreground": { + "$ref": "#/definitions/Color", + "default": "#cccccc", + "description": "Sets the text color when unfocused. Overrides \"foreground\" from the color scheme. Uses hex color format: \"#rrggbb\".", + "type": ["string", "null"] + }, + "background": { + "$ref": "#/definitions/Color", + "default": "#0c0c0c", + "description": "Sets the background color of the text when unfocused. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".", + "type": ["string", "null"] + }, + "selectionBackground": { + "oneOf": [ + {"$ref": "#/definitions/Color"}, + { "type": "null" } + ], + "description": "Sets the background color of selected text when unfocused. Overrides selectionBackground set in the color scheme. Uses hex color format: \"#rrggbb\"." + }, + "cursorColor": { + "oneOf": [ + { "$ref": "#/definitions/Color" }, + {"type": "null"} + ], + "description": "Sets the color of the cursor when unfocused. Overrides the cursor color from the color scheme. Uses hex color format: \"#rrggbb\"." + }, + "cursorShape": { + "default": "bar", + "description": "Sets the shape of the cursor when unfocused. Possible values:\n -\"bar\" ( ┃, default )\n -\"doubleUnderscore\" ( ‗ )\n -\"emptyBox\" ( ▯ )\n -\"filledBox\" ( █ )\n -\"underscore\" ( ▁ )\n -\"vintage\" ( ▃ )", + "enum": [ + "bar", + "doubleUnderscore", + "emptyBox", + "filledBox", + "underscore", + "vintage" + ], + "type": "string" + }, + "cursorHeight": { + "description": "Sets the percentage height of the cursor (when unfocused) starting from the bottom. Only works when cursorShape is set to \"vintage\". Accepts values from 1-100.", + "maximum": 100, + "minimum": 1, + "type": ["integer","null"], + "default": 25 + }, + "backgroundImage": { + "description": "Sets the file location of the image to draw over the window background when unfocused.", + "oneOf": [ + { + "type": ["string", null] + }, + { + "enum": [ + "desktopWallpaper" + ] + } + ], + "type": [ "string", "null" ] + }, + "backgroundImageOpacity": { + "default": 1.0, + "description": "Sets the transparency of the background image when unfocused. Accepts floating point values from 0-1.", + "maximum": 1.0, + "minimum": 0.0, + "type": "number" + }, + "backgroundImageStretchMode": { + "default": "uniformToFill", + "description": "Sets how the background image is resized to fill the window when unfocused.", + "enum": [ + "fill", + "none", + "uniform", + "uniformToFill" + ], + "type": "string" + }, + "backgroundImageAlignment": { + "default": "center", + "enum": [ + "bottom", + "bottomLeft", + "bottomRight", + "center", + "left", + "right", + "top", + "topLeft", + "topRight" + ], + "description": "Sets how the background image aligns to the boundaries of the window when unfocused. Possible values: \"center\", \"left\", \"top\", \"right\", \"bottom\", \"topLeft\", \"topRight\", \"bottomLeft\", \"bottomRight\"", + "type": "string" + }, + "experimental.retroTerminalEffect": { + "description": "When set to true, enable retro terminal effects when unfocused. This is an experimental feature, and its continued existence is not guaranteed.", + "type": "boolean" + }, + "experimental.pixelShaderPath": { + "description": "Use to set a path to a pixel shader to use with the Terminal when unfocused. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.", + "type": "string" + } + }, + "type": "object" + }, "ProfileGuid": { "default": "{}", "pattern": "^\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}$", @@ -962,6 +1073,11 @@ "description": "Sets the background color of the text. Overrides \"background\" from the color scheme. Uses hex color format: \"#rrggbb\".", "type": ["string", "null"] }, + "unfocusedAppearance": { + "$ref": "#/definitions/AppearanceConfig", + "description": "Sets the appearance of the terminal when it is unfocused.", + "type": ["object", "null"] + }, "backgroundImage": { "description": "Sets the file location of the image to draw over the window background.", "oneOf": [ diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp index d2a87b995b1..b4545ed8536 100644 --- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp @@ -338,34 +338,34 @@ namespace SettingsModelLocalTests // verify profile defaults Log::Comment(L"Profile Defaults"); - VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().ColorSchemeName()); - VERIFY_IS_TRUE(settings->ProfileDefaults().HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName()); + VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName()); // verify all other profiles const auto& profiles{ settings->AllProfiles() }; { const auto& prof{ profiles.GetAt(0) }; Log::Comment(prof.Name().c_str()); - VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); - VERIFY_IS_TRUE(prof.HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(1) }; Log::Comment(prof.Name().c_str()); - VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); - VERIFY_IS_TRUE(prof.HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(2) }; Log::Comment(prof.Name().c_str()); - VERIFY_ARE_EQUAL(newName, prof.ColorSchemeName()); - VERIFY_IS_FALSE(prof.HasColorSchemeName()); + VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName()); + VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName()); } { const auto& prof{ profiles.GetAt(3) }; Log::Comment(prof.Name().c_str()); - VERIFY_ARE_EQUAL(L"Scheme 2", prof.ColorSchemeName()); - VERIFY_IS_TRUE(prof.HasColorSchemeName()); + VERIFY_ARE_EQUAL(L"Scheme 2", prof.DefaultAppearance().ColorSchemeName()); + VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName()); } } } diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp index c2c81832b70..d0a075a7de6 100644 --- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp @@ -1311,9 +1311,9 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size()); VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size()); - VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName()); - VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).ColorSchemeName()); - VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName()); + VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName()); + VERIFY_ARE_EQUAL(L"InvalidSchemeName", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName()); + VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName()); settings->_ValidateAllSchemesExist(); @@ -1323,9 +1323,9 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size()); VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size()); - VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).ColorSchemeName()); - VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).ColorSchemeName()); - VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).ColorSchemeName()); + VERIFY_ARE_EQUAL(L"schemeOne", settings->_allProfiles.GetAt(0).DefaultAppearance().ColorSchemeName()); + VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(1).DefaultAppearance().ColorSchemeName()); + VERIFY_ARE_EQUAL(L"Campbell", settings->_allProfiles.GetAt(2).DefaultAppearance().ColorSchemeName()); } void DeserializationTests::ValidateColorSchemeInCommands() @@ -1543,7 +1543,7 @@ namespace SettingsModelLocalTests settings->_ParseJsonString(settingsJson, false); settings->LayerJson(settings->_userSettings); VERIFY_ARE_NOT_EQUAL(0u, settings->_allProfiles.Size()); - VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath()); + VERIFY_ARE_EQUAL(expectedPath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath()); } void DeserializationTests::TestProfileBackgroundImageWithDesktopWallpaper() { @@ -1564,8 +1564,8 @@ namespace SettingsModelLocalTests auto settings = winrt::make_self(); settings->_ParseJsonString(settingsJson, false); settings->LayerJson(settings->_userSettings); - VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).BackgroundImagePath()); - VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).ExpandedBackgroundImagePath()); + VERIFY_ARE_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().BackgroundImagePath()); + VERIFY_ARE_NOT_EQUAL(expectedBackgroundImagePath, settings->_allProfiles.GetAt(0).DefaultAppearance().ExpandedBackgroundImagePath()); } void DeserializationTests::TestCloseOnExitParsing() { diff --git a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp b/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp index d157ad9cd8b..27e8f190129 100644 --- a/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp @@ -117,14 +117,14 @@ namespace SettingsModelLocalTests const auto profile2Json = VerifyParseSucceeded(profile2String); auto profile0 = implementation::Profile::FromJson(profile0Json); - VERIFY_IS_NOT_NULL(profile0->Foreground()); - VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->Foreground().Value() }); + VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Foreground()); + VERIFY_ARE_EQUAL(til::color(0, 0, 0), til::color{ profile0->DefaultAppearance().Foreground().Value() }); - VERIFY_IS_NOT_NULL(profile0->Background()); - VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() }); + VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().Background()); + VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().Background().Value() }); - VERIFY_IS_NOT_NULL(profile0->SelectionBackground()); - VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->SelectionBackground().Value() }); + VERIFY_IS_NOT_NULL(profile0->DefaultAppearance().SelectionBackground()); + VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->DefaultAppearance().SelectionBackground().Value() }); VERIFY_ARE_EQUAL(L"profile0", profile0->Name()); @@ -135,14 +135,14 @@ namespace SettingsModelLocalTests auto profile1{ profile0->CreateChild() }; profile1->LayerJson(profile1Json); - VERIFY_IS_NOT_NULL(profile1->Foreground()); - VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->Foreground().Value() }); + VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Foreground()); + VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->DefaultAppearance().Foreground().Value() }); - VERIFY_IS_NOT_NULL(profile1->Background()); - VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() }); + VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background()); + VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() }); - VERIFY_IS_NOT_NULL(profile1->Background()); - VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() }); + VERIFY_IS_NOT_NULL(profile1->DefaultAppearance().Background()); + VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->DefaultAppearance().Background().Value() }); VERIFY_ARE_EQUAL(L"profile1", profile1->Name()); @@ -154,14 +154,14 @@ namespace SettingsModelLocalTests auto profile2{ profile1->CreateChild() }; profile2->LayerJson(profile2Json); - VERIFY_IS_NOT_NULL(profile2->Foreground()); - VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->Foreground().Value() }); + VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Foreground()); + VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->DefaultAppearance().Foreground().Value() }); - VERIFY_IS_NOT_NULL(profile2->Background()); - VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->Background().Value() }); + VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().Background()); + VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->DefaultAppearance().Background().Value() }); - VERIFY_IS_NOT_NULL(profile2->SelectionBackground()); - VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->SelectionBackground().Value() }); + VERIFY_IS_NOT_NULL(profile2->DefaultAppearance().SelectionBackground()); + VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->DefaultAppearance().SelectionBackground().Value() }); VERIFY_ARE_EQUAL(L"profile2", profile2->Name()); diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index fd6d9f61091..e46d4eb78d3 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -126,7 +126,8 @@ namespace SettingsModelLocalTests VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, guid); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); @@ -147,7 +148,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, guid); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); @@ -168,7 +170,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, guid); VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); @@ -189,7 +192,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, guid); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(3, termSettings.HistorySize()); @@ -210,7 +214,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, guid); VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); @@ -232,7 +237,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, guid); VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(2, termSettings.HistorySize()); @@ -251,7 +257,8 @@ namespace SettingsModelLocalTests VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, guid); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); @@ -271,7 +278,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, guid); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory()); @@ -293,7 +301,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, guid); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(L"c:\\foo", termSettings.StartingDirectory()); @@ -314,7 +323,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid0, guid); VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); @@ -336,7 +346,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(profile2Guid, guid); VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); @@ -360,7 +371,8 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile()); const auto guid{ settings.GetProfileForArgs(realArgs.TerminalArgs()) }; - const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) }; + const auto termSettings = settingsStruct.DefaultSettings(); VERIFY_ARE_EQUAL(guid1, guid); VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline()); VERIFY_ARE_EQUAL(L"bar", termSettings.StartingTitle()); @@ -398,7 +410,7 @@ namespace SettingsModelLocalTests { auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid1, nullptr) }; VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings); - VERIFY_ARE_EQUAL(1, terminalSettings.HistorySize()); + VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize()); } catch (...) { @@ -409,7 +421,7 @@ namespace SettingsModelLocalTests { auto terminalSettings{ TerminalSettings::CreateWithProfileByID(settings, guid2, nullptr) }; VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings); - VERIFY_ARE_EQUAL(2, terminalSettings.HistorySize()); + VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize()); } catch (...) { @@ -422,7 +434,7 @@ namespace SettingsModelLocalTests { const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) }; VERIFY_ARE_NOT_EQUAL(nullptr, termSettings); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize()); } catch (...) { @@ -461,7 +473,7 @@ namespace SettingsModelLocalTests { const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) }; VERIFY_ARE_NOT_EQUAL(nullptr, termSettings); - VERIFY_ARE_EQUAL(1, termSettings.HistorySize()); + VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize()); } catch (...) { @@ -522,7 +534,8 @@ namespace SettingsModelLocalTests auto createTerminalSettings = [&](const auto& profile, const auto& schemes) { auto terminalSettings{ winrt::make_self() }; - terminalSettings->_ApplyProfileSettings(profile, schemes); + terminalSettings->_ApplyProfileSettings(profile); + terminalSettings->_ApplyAppearanceSettings(profile.DefaultAppearance(), schemes); return terminalSettings; }; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index c8acf24bac0..e690576bfa6 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -579,7 +579,7 @@ namespace winrt::TerminalApp::implementation // Use the default profile to determine how big of a window we need. const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) }; - auto proposedSize = TermControl::GetProposedDimensions(settings, dpi); + auto proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi); const float scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 802fc83999f..c2043638668 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -628,7 +628,7 @@ void Pane::_FocusFirstChild() // - profile: The GUID of the profile these settings should apply to. // Return Value: // - -void Pane::UpdateSettings(const TerminalSettings& settings, const GUID& profile) +void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile) { if (!_IsLeaf()) { @@ -639,9 +639,20 @@ void Pane::UpdateSettings(const TerminalSettings& settings, const GUID& profile) { if (profile == _profile) { + auto controlSettings = _control.Settings().as(); // Update the parent of the control's settings object (and not the object itself) so // that any overrides made by the control don't get affected by the reload - _control.Settings().as().SetParent(settings); + controlSettings.SetParent(settings.DefaultSettings()); + auto unfocusedSettings{ settings.UnfocusedSettings() }; + if (unfocusedSettings) + { + // Note: the unfocused settings needs to be entirely unchanged _except_ we need to + // set its parent to the settings object that lives in the control. This is because + // the overrides made by the control live in that settings object, so we want to make + // sure the unfocused settings inherit from that. + unfocusedSettings.SetParent(controlSettings); + } + _control.UnfocusedAppearance(unfocusedSettings); _control.UpdateSettings(); } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index e92c0880557..2838bab1a86 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -51,7 +51,7 @@ class Pane : public std::enable_shared_from_this void ClearActive(); void SetActive(); - void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettings& settings, + void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const GUID& profile); void ResizeContent(const winrt::Windows::Foundation::Size& newSize); void Relayout(); diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 5bd01ab7a36..66bca4b22bc 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -83,9 +83,9 @@ namespace winrt::TerminalApp::implementation TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"), TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"), TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"), - TraceLoggingBool(settings.UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"), - TraceLoggingFloat64(settings.TintOpacity(), "TintOpacity", "Opacity preference from the settings"), - TraceLoggingWideString(settings.FontFace().c_str(), "FontFace", "Font face chosen in the settings"), + TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"), + TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"), + TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"), TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); @@ -99,11 +99,11 @@ namespace winrt::TerminalApp::implementation // - profileGuid: ID to use to lookup profile settings for this connection // - settings: the TerminalSettings object to use to create the TerminalControl with. // - existingConnection: optionally receives a connection from the outside world instead of attempting to create one - void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings, TerminalConnection::ITerminalConnection existingConnection) + void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection) { // Initialize the new tab // Create a connection based on the values in our settings object if we weren't given one. - auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings); + auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings.DefaultSettings()); TerminalConnection::ITerminalConnection debugConnection{ nullptr }; if (_settings.GlobalSettings().DebugFeaturesEnabled()) @@ -303,15 +303,15 @@ namespace winrt::TerminalApp::implementation const auto& profileGuid = tab.GetFocusedProfile(); if (profileGuid.has_value()) { - const auto settings{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid.value(), *_bindings) }; + const auto settingsCreateResult{ TerminalSettings::CreateWithProfileByID(_settings, profileGuid.value(), *_bindings) }; const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory(); const auto validWorkingDirectory = !workingDirectory.empty(); if (validWorkingDirectory) { - settings.StartingDirectory(workingDirectory); + settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory); } - _CreateNewTabFromSettings(profileGuid.value(), settings); + _CreateNewTabFromSettings(profileGuid.value(), settingsCreateResult); } } CATCH_LOG(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 05b7d6c0745..e12e1b94581 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1195,7 +1195,7 @@ namespace winrt::TerminalApp::implementation try { - TerminalSettings controlSettings{ nullptr }; + TerminalSettingsCreateResult controlSettings{ nullptr }; GUID realGuid; bool profileFound = false; @@ -1210,7 +1210,7 @@ namespace winrt::TerminalApp::implementation const auto validWorkingDirectory = !workingDirectory.empty(); if (validWorkingDirectory) { - controlSettings.StartingDirectory(workingDirectory); + controlSettings.DefaultSettings().StartingDirectory(workingDirectory); } realGuid = current_guid.value(); } @@ -1233,7 +1233,7 @@ namespace winrt::TerminalApp::implementation controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings); } - const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings); + const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings.DefaultSettings()); const float contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); const float contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); @@ -1773,9 +1773,16 @@ namespace winrt::TerminalApp::implementation } } - TermControl TerminalPage::_InitControl(const TerminalSettings& settings, const ITerminalConnection& connection) + TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) { - return TermControl{ TerminalSettings::CreateWithParent(settings), connection }; + // Give term control a child of the settings so that any overrides go in the child + // This way, when we do a settings reload we just update the parent and the overrides remain + const auto child = TerminalSettings::CreateWithParent(settings); + TermControl term{ child.DefaultSettings(), connection }; + + term.UnfocusedAppearance(child.UnfocusedSettings()); // It is okay for the unfocused settings to be null + + return term; } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 1a6f4278a97..4ba97413e41 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -172,7 +172,7 @@ namespace winrt::TerminalApp::implementation void _CreateNewTabFlyout(); void _OpenNewTabDropdown(); void _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); - void _CreateNewTabFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); + void _CreateNewTabFromSettings(GUID profileGuid, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr); winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, Microsoft::Terminal::Settings::Model::TerminalSettings settings); winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); @@ -277,7 +277,7 @@ namespace winrt::TerminalApp::implementation void _Find(); - winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettings& settings, + winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); winrt::fire_and_forget _RefreshUIForSettingsReload(); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 83dd0c846db..bec6eae9d73 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -207,11 +207,11 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Attempts to update the settings of this tab's tree of panes. // Arguments: - // - settings: The new TerminalSettings to apply to any matching controls + // - settings: The new TerminalSettingsCreateResult to apply to any matching controls // - profile: The GUID of the profile these settings should apply to. // Return Value: // - - void TerminalTab::UpdateSettings(const TerminalSettings& settings, const GUID& profile) + void TerminalTab::UpdateSettings(const TerminalSettingsCreateResult& settings, const GUID& profile) { _rootPane->UpdateSettings(settings, profile); diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index e349bf36ad8..9887e785358 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -54,7 +54,7 @@ namespace winrt::TerminalApp::implementation void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction); - void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettings& settings, const GUID& profile); + void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const GUID& profile); winrt::fire_and_forget UpdateTitle(); void Shutdown() override; diff --git a/src/cascadia/TerminalControl/IControlAppearance.idl b/src/cascadia/TerminalControl/IControlAppearance.idl new file mode 100644 index 00000000000..88de9e4b609 --- /dev/null +++ b/src/cascadia/TerminalControl/IControlAppearance.idl @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace Microsoft.Terminal.Control +{ + interface IControlAppearance requires Microsoft.Terminal.Core.ICoreAppearance + { + Microsoft.Terminal.Core.Color SelectionBackground; + String BackgroundImage; + Double BackgroundImageOpacity; + Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode; + Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment; + Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment; + + // Experimental settings + Boolean RetroTerminalEffect; + String PixelShaderPath; + }; +} diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index baf63841127..1bbc178090c 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -2,6 +2,7 @@ // Licensed under the MIT license. import "IKeyBindings.idl"; +import "IControlAppearance.idl"; namespace Microsoft.Terminal.Control { @@ -23,7 +24,7 @@ namespace Microsoft.Terminal.Control // TermControl's behavior. In these settings there is both the entirety // of the Core ITerminalSettings interface, and any additional settings // for specifically the control. - interface IControlSettings requires Microsoft.Terminal.Core.ICoreSettings + interface IControlSettings requires Microsoft.Terminal.Core.ICoreSettings, Microsoft.Terminal.Control.IControlAppearance { String ProfileName; @@ -45,21 +46,10 @@ namespace Microsoft.Terminal.Control String StartingDirectory; String EnvironmentVariables; - String BackgroundImage; - Double BackgroundImageOpacity; - Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode; - Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment; - Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment; - - Microsoft.Terminal.Core.Color SelectionBackground; - TextAntialiasingMode AntialiasingMode; // Experimental Settings - Boolean RetroTerminalEffect; Boolean ForceFullRepaintRendering; Boolean SoftwareRendering; - String PixelShaderPath; - }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 6a743ae2035..403df7dae13 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -295,16 +295,80 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Method Description: - // - Given new settings for this profile, applies the settings to the current terminal. + // - Given Settings having been updated, applies the settings to the current terminal. + // Return Value: + // - + winrt::fire_and_forget TermControl::UpdateSettings() + { + auto weakThis{ get_weak() }; + + // Dispatch a call to the UI thread to apply the new settings to the + // terminal. + co_await winrt::resume_foreground(Dispatcher()); + + _UpdateSettingsFromUIThread(_settings); + auto appearance = _settings.try_as(); + if (!_focused && _UnfocusedAppearance) + { + appearance = _UnfocusedAppearance; + } + _UpdateAppearanceFromUIThread(appearance); + } + + // Method Description: + // - Dispatches a call to the UI thread and updates the appearance + // Arguments: + // - newAppearance: the new appearance to set + winrt::fire_and_forget TermControl::UpdateAppearance(const IControlAppearance newAppearance) + { + // Dispatch a call to the UI thread + co_await winrt::resume_foreground(Dispatcher()); + _UpdateAppearanceFromUIThread(newAppearance); + } + + // Method Description: + // - Writes the given sequence as input to the active terminal connection, + // Arguments: + // - wstr: the string of characters to write to the terminal connection. + // Return Value: + // - + void TermControl::SendInput(const winrt::hstring& wstr) + { + _SendInputToConnection(wstr); + } + + void TermControl::ToggleShaderEffects() + { + auto lock = _terminal->LockForWriting(); + // Originally, this action could be used to enable the retro effects + // even when they're set to `false` in the settings. If the user didn't + // specify a custom pixel shader, manually enable the legacy retro + // effect first. This will ensure that a toggle off->on will still work, + // even if they currently have retro effect off. + if (_settings.PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect()) + { + // SetRetroTerminalEffect to true will enable the effect. In this + // case, the shader effect will already be disabled (because neither + // a pixel shader nor the retro effects were originally requested). + // So we _don't_ want to toggle it again below, because that would + // toggle it back off. + _renderEngine->SetRetroTerminalEffect(true); + } + else + { + _renderEngine->ToggleShaderEffects(); + } + } + + // Method Description: + // - Updates the settings of the current terminal. // - This method is separate from UpdateSettings because there is an apparent optimizer // issue that causes one of our hstring -> wstring_view conversions to result in garbage, // but only from a coroutine context. See GH#8723. // - INVARIANT: This method must be called from the UI thread. // Arguments: - // - - // Return Value: - // - - void TermControl::_UpdateSettingsOnUIThread() + // - newSettings: the new settings to set + void TermControl::_UpdateSettingsFromUIThread(IControlSettings newSettings) { if (_closing) { @@ -327,10 +391,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Update DxEngine settings under the lock - _renderEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() }); - - _renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect()); - _renderEngine->SetPixelShaderPath(_settings.PixelShaderPath()); _renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); _renderEngine->SetSoftwareRendering(_settings.SoftwareRendering()); @@ -359,56 +419,67 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Method Description: - // - Given Settings having been updated, applies the settings to the current terminal. - // Return Value: - // - - winrt::fire_and_forget TermControl::UpdateSettings() + // - Updates the appearance + // - This should only be called from the UI thread + // Arguments: + // - newAppearance: the new appearance to set + void TermControl::_UpdateAppearanceFromUIThread(IControlAppearance newAppearance) { - auto weakThis{ get_weak() }; - - // Dispatch a call to the UI thread to apply the new settings to the - // terminal. - co_await winrt::resume_foreground(Dispatcher()); - - // If 'weakThis' is locked, then we can safely work with 'this' - if (auto control{ weakThis.get() }) + if (_closing) { - _UpdateSettingsOnUIThread(); + return; } - } - - // Method Description: - // - Writes the given sequence as input to the active terminal connection, - // Arguments: - // - wstr: the string of characters to write to the terminal connection. - // Return Value: - // - - void TermControl::SendInput(const winrt::hstring& wstr) - { - _SendInputToConnection(wstr); - } - void TermControl::ToggleShaderEffects() - { - auto lock = _terminal->LockForWriting(); - // Originally, this action could be used to enable the retro effects - // even when they're set to `false` in the settings. If the user didn't - // specify a custom pixel shader, manually enable the legacy retro - // effect first. This will ensure that a toggle off->on will still work, - // even if they currently have retro effect off. - if (_settings.PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect()) + if (!newAppearance.BackgroundImage().empty()) { - // SetRetroTerminalEffect to true will enable the effect. In this - // case, the shader effect will already be disabled (because neither - // a pixel shader nor the retro effects were originally requested). - // So we _don't_ want to toggle it again below, because that would - // toggle it back off. - _renderEngine->SetRetroTerminalEffect(true); + Windows::Foundation::Uri imageUri{ newAppearance.BackgroundImage() }; + + // Check if the image brush is already pointing to the image + // in the modified settings; if it isn't (or isn't there), + // set a new image source for the brush + auto imageSource = BackgroundImage().Source().try_as(); + + if (imageSource == nullptr || + imageSource.UriSource() == nullptr || + imageSource.UriSource().RawUri() != imageUri.RawUri()) + { + // Note that BitmapImage handles the image load asynchronously, + // which is especially important since the image + // may well be both large and somewhere out on the + // internet. + Media::Imaging::BitmapImage image(imageUri); + BackgroundImage().Source(image); + } + + // Apply stretch, opacity and alignment settings + BackgroundImage().Stretch(newAppearance.BackgroundImageStretchMode()); + BackgroundImage().Opacity(newAppearance.BackgroundImageOpacity()); + BackgroundImage().HorizontalAlignment(newAppearance.BackgroundImageHorizontalAlignment()); + BackgroundImage().VerticalAlignment(newAppearance.BackgroundImageVerticalAlignment()); } else { - _renderEngine->ToggleShaderEffects(); + BackgroundImage().Source(nullptr); } + + // Update our control settings + const auto bg = newAppearance.DefaultBackground(); + _BackgroundColorChanged(bg); + + // Set TSF Foreground + Media::SolidColorBrush foregroundBrush{}; + foregroundBrush.Color(static_cast(newAppearance.DefaultForeground())); + TSFInputControl().Foreground(foregroundBrush); + + // Update the terminal core with its new Core settings + auto lock = _terminal->LockForWriting(); + _terminal->UpdateAppearance(newAppearance); + + // Update DxEngine settings under the lock + _renderEngine->SetSelectionBackground(til::color{ newAppearance.SelectionBackground() }); + _renderEngine->SetRetroTerminalEffect(newAppearance.RetroTerminalEffect()); + _renderEngine->SetPixelShaderPath(newAppearance.PixelShaderPath()); + _renderer->TriggerRedrawAll(); } // Method Description: @@ -427,9 +498,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _InitializeBackgroundBrush(); - const auto bg = newSettings.DefaultBackground(); - _BackgroundColorChanged(bg); - // Apply padding as swapChainPanel's margin auto newMargin = _ParseThicknessFromPadding(newSettings.Padding()); SwapChainPanel().Margin(newMargin); @@ -447,10 +515,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontHeight }, CP_UTF8, false }; _desiredFont = { _actualFont }; - // set TSF Foreground - Media::SolidColorBrush foregroundBrush{}; - foregroundBrush.Color(static_cast(newSettings.DefaultForeground())); - TSFInputControl().Foreground(foregroundBrush); TSFInputControl().Margin(newMargin); // Apply settings for scrollbar @@ -534,38 +598,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _renderEngine->SetDefaultTextBackgroundOpacity(1.0f); } } - - if (!_settings.BackgroundImage().empty()) - { - Windows::Foundation::Uri imageUri{ _settings.BackgroundImage() }; - - // Check if the image brush is already pointing to the image - // in the modified settings; if it isn't (or isn't there), - // set a new image source for the brush - auto imageSource = BackgroundImage().Source().try_as(); - - if (imageSource == nullptr || - imageSource.UriSource() == nullptr || - imageSource.UriSource().RawUri() != imageUri.RawUri()) - { - // Note that BitmapImage handles the image load asynchronously, - // which is especially important since the image - // may well be both large and somewhere out on the - // internet. - Media::Imaging::BitmapImage image(imageUri); - BackgroundImage().Source(image); - } - - // Apply stretch, opacity and alignment settings - BackgroundImage().Stretch(_settings.BackgroundImageStretchMode()); - BackgroundImage().Opacity(_settings.BackgroundImageOpacity()); - BackgroundImage().HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment()); - BackgroundImage().VerticalAlignment(_settings.BackgroundImageVerticalAlignment()); - } - else - { - BackgroundImage().Source(nullptr); - } } // Method Description: @@ -2001,6 +2033,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation } _UpdateSystemParameterSettings(); + + // Only update the appearance here if an unfocused config exists - + // if an unfocused config does not exist then we never would have switched + // appearances anyway so there's no need to switch back upon gaining focus + if (_UnfocusedAppearance) + { + UpdateAppearance(_settings); + } } // Method Description @@ -2052,6 +2092,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _blinkTimer.value().Stop(); } + + // Check if there is an unfocused config we should set the appearance to + // upon losing focus + if (_UnfocusedAppearance) + { + UpdateAppearance(_UnfocusedAppearance); + } } // Method Description: diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 72abee5b5fe..b4fe20a9ff0 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -26,6 +26,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection); winrt::fire_and_forget UpdateSettings(); + winrt::fire_and_forget UpdateAppearance(const IControlAppearance newAppearance); hstring Title(); hstring GetProfileName() const; @@ -114,6 +115,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable); // clang-format on + WINRT_PROPERTY(IControlAppearance, UnfocusedAppearance); + private: friend struct TermControlT; // friend our parent so it can bind private event handlers TerminalConnection::ITerminalConnection _connection; @@ -194,10 +197,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker; + void _UpdateSettingsFromUIThread(IControlSettings newSettings); + void _UpdateAppearanceFromUIThread(IControlAppearance newAppearance); bool _isReadOnly{ false }; void _ApplyUISettings(const IControlSettings&); - void _UpdateSettingsOnUIThread(); void _UpdateSystemParameterSettings() noexcept; void _InitializeBackgroundBrush(); winrt::fire_and_forget _BackgroundColorChanged(const til::color color); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 99597123cd8..8e028618354 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -18,6 +18,7 @@ namespace Microsoft.Terminal.Control void UpdateSettings(); Microsoft.Terminal.Control.IControlSettings Settings { get; }; + Microsoft.Terminal.Control.IControlAppearance UnfocusedAppearance; event Windows.Foundation.TypedEventHandler TitleChanged; event FontSizeChangedEventArgs FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj index b16ab47321a..2781810277d 100644 --- a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj @@ -84,6 +84,7 @@ + SearchBoxControl.xaml diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl new file mode 100644 index 00000000000..30c91833ceb --- /dev/null +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace Microsoft.Terminal.Core +{ + enum CursorStyle + { + Vintage, + Bar, + Underscore, + DoubleUnderscore, + FilledBox, + EmptyBox + }; + + // TerminalCore declares its own Color struct to avoid depending + // on Windows.UI.Color and to avoid passing around unclothed uint32s. + // It is supported by til::color for conversions in and out of WinRT land. + struct Color + { + UInt8 R; + UInt8 G; + UInt8 B; + UInt8 A; + }; + + declare + { + // Forward declare this parameterized specialization so that it lives + // in TerminalCore instead of being flung to the winds of all IDL dependents. + interface Windows.Foundation.IReference; + } + + interface ICoreAppearance + { + Microsoft.Terminal.Core.Color DefaultForeground; + Microsoft.Terminal.Core.Color DefaultBackground; + Microsoft.Terminal.Core.Color GetColorTableEntry(Int32 index); + Microsoft.Terminal.Core.Color CursorColor; + CursorStyle CursorShape; + UInt32 CursorHeight; + }; +} diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index a3955fb6e24..60638c7d3d7 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -1,41 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "..\ICoreAppearance.idl"; + namespace Microsoft.Terminal.Core { - // TerminalCore declares its own Color struct to avoid depending - // on Windows.UI.Color and to avoid passing around unclothed uint32s. - // It is supported by til::color for conversions in and out of WinRT land. - struct Color - { - UInt8 R; - UInt8 G; - UInt8 B; - UInt8 A; - }; - - declare - { - // Forward declare this parameterized specialization so that it lives - // in TerminalCore instead of being flung to the winds of all IDL dependents. - interface Windows.Foundation.IReference; - } - - enum CursorStyle - { - Vintage, - Bar, - Underscore, - DoubleUnderscore, - FilledBox, - EmptyBox - }; - - interface ICoreSettings + interface ICoreSettings requires ICoreAppearance { - Microsoft.Terminal.Core.Color DefaultForeground; - Microsoft.Terminal.Core.Color DefaultBackground; - Microsoft.Terminal.Core.Color GetColorTableEntry(Int32 index); // TODO:MSFT:20642297 - define a sentinel for Infinite Scrollback Int32 HistorySize; Int32 InitialRows; @@ -44,9 +15,6 @@ namespace Microsoft.Terminal.Core Boolean SnapOnInput; Boolean AltGrAliasing; - Microsoft.Terminal.Core.Color CursorColor; - CursorStyle CursorShape; - UInt32 CursorHeight; String StartingTitle; Boolean SuppressApplicationTitle; String WordDelimiters; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index ae64d38a00c..d11efd7acaa 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -112,49 +112,7 @@ void Terminal::CreateFromSettings(ICoreSettings settings, // - settings: an ICoreSettings with new settings values for us to use. void Terminal::UpdateSettings(ICoreSettings settings) { - // Set the default background as transparent to prevent the - // DX layer from overwriting the background image or acrylic effect - til::color newBackgroundColor{ settings.DefaultBackground() }; - _defaultBg = newBackgroundColor.with_alpha(0); - _defaultFg = settings.DefaultForeground(); - - CursorType cursorShape = CursorType::VerticalBar; - switch (settings.CursorShape()) - { - case CursorStyle::Underscore: - cursorShape = CursorType::Underscore; - break; - case CursorStyle::FilledBox: - cursorShape = CursorType::FullBox; - break; - case CursorStyle::EmptyBox: - cursorShape = CursorType::EmptyBox; - break; - case CursorStyle::Vintage: - cursorShape = CursorType::Legacy; - break; - case CursorStyle::DoubleUnderscore: - cursorShape = CursorType::DoubleUnderscore; - break; - default: - case CursorStyle::Bar: - cursorShape = CursorType::VerticalBar; - break; - } - - if (_buffer) - { - _buffer->GetCursor().SetStyle(settings.CursorHeight(), - til::color{ settings.CursorColor() }, - cursorShape); - } - - _defaultCursorShape = cursorShape; - - for (int i = 0; i < 16; i++) - { - _colorTable.at(i) = til::color{ settings.GetColorTableEntry(i) }; - } + UpdateAppearance(settings); _snapOnInput = settings.SnapOnInput(); _altGrAliasing = settings.AltGrAliasing(); @@ -190,6 +148,58 @@ void Terminal::UpdateSettings(ICoreSettings settings) // remains at the bottom of the buffer. } +// Method Description: +// - Update our internal properties to match the new values in the provided +// CoreAppearance object. +// Arguments: +// - appearance: an ICoreAppearance with new settings values for us to use. +void Terminal::UpdateAppearance(const ICoreAppearance& appearance) +{ + // Set the default background as transparent to prevent the + // DX layer from overwriting the background image or acrylic effect + til::color newBackgroundColor{ appearance.DefaultBackground() }; + _defaultBg = newBackgroundColor.with_alpha(0); + _defaultFg = appearance.DefaultForeground(); + + for (int i = 0; i < 16; i++) + { + _colorTable.at(i) = til::color{ appearance.GetColorTableEntry(i) }; + } + + CursorType cursorShape = CursorType::VerticalBar; + switch (appearance.CursorShape()) + { + case CursorStyle::Underscore: + cursorShape = CursorType::Underscore; + break; + case CursorStyle::FilledBox: + cursorShape = CursorType::FullBox; + break; + case CursorStyle::EmptyBox: + cursorShape = CursorType::EmptyBox; + break; + case CursorStyle::Vintage: + cursorShape = CursorType::Legacy; + break; + case CursorStyle::DoubleUnderscore: + cursorShape = CursorType::DoubleUnderscore; + break; + default: + case CursorStyle::Bar: + cursorShape = CursorType::VerticalBar; + break; + } + + if (_buffer) + { + _buffer->GetCursor().SetStyle(appearance.CursorHeight(), + til::color{ appearance.CursorColor() }, + cursorShape); + } + + _defaultCursorShape = cursorShape; +} + // Method Description: // - Resize the terminal as the result of some user interaction. // Arguments: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 231c35a826c..03f5be58360 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -23,6 +23,7 @@ namespace winrt::Microsoft::Terminal::Core { struct ICoreSettings; + struct ICoreAppearance; } namespace Microsoft::Terminal::Core @@ -63,6 +64,7 @@ class Microsoft::Terminal::Core::Terminal final : Microsoft::Console::Render::IRenderTarget& renderTarget); void UpdateSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings); + void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance); // Write goes through the parser void Write(std::wstring_view stringView); diff --git a/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj b/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj index 33520d4c745..dba70a7d99c 100644 --- a/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj +++ b/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj @@ -45,6 +45,7 @@ + diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.h b/src/cascadia/TerminalSettingsEditor/Profiles.h index 37d1c5d8bc5..b41e846bc49 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles.h @@ -80,24 +80,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, Padding); OBSERVABLE_PROJECTED_SETTING(_profile, Commandline); OBSERVABLE_PROJECTED_SETTING(_profile, StartingDirectory); - OBSERVABLE_PROJECTED_SETTING(_profile, BackgroundImagePath); - OBSERVABLE_PROJECTED_SETTING(_profile, BackgroundImageOpacity); - OBSERVABLE_PROJECTED_SETTING(_profile, BackgroundImageStretchMode); - OBSERVABLE_PROJECTED_SETTING(_profile, BackgroundImageAlignment); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), BackgroundImagePath); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), BackgroundImageOpacity); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), BackgroundImageStretchMode); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), BackgroundImageAlignment); OBSERVABLE_PROJECTED_SETTING(_profile, AntialiasingMode); - OBSERVABLE_PROJECTED_SETTING(_profile, RetroTerminalEffect); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), RetroTerminalEffect); OBSERVABLE_PROJECTED_SETTING(_profile, ForceFullRepaintRendering); OBSERVABLE_PROJECTED_SETTING(_profile, SoftwareRendering); - OBSERVABLE_PROJECTED_SETTING(_profile, ColorSchemeName); - OBSERVABLE_PROJECTED_SETTING(_profile, Foreground); - OBSERVABLE_PROJECTED_SETTING(_profile, Background); - OBSERVABLE_PROJECTED_SETTING(_profile, SelectionBackground); - OBSERVABLE_PROJECTED_SETTING(_profile, CursorColor); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), ColorSchemeName); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Foreground); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), Background); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), SelectionBackground); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), CursorColor); OBSERVABLE_PROJECTED_SETTING(_profile, HistorySize); OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput); OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing); - OBSERVABLE_PROJECTED_SETTING(_profile, CursorShape); - OBSERVABLE_PROJECTED_SETTING(_profile, CursorHeight); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), CursorShape); + OBSERVABLE_PROJECTED_SETTING(_profile.DefaultAppearance(), CursorHeight); OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle); private: diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.idl b/src/cascadia/TerminalSettingsEditor/Profiles.idl index eca17a75fe4..f8354692dfb 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.idl +++ b/src/cascadia/TerminalSettingsEditor/Profiles.idl @@ -8,7 +8,7 @@ import "MainPage.idl"; #define OBSERVABLE_PROJECTED_PROFILE_SETTING(Type, Name) \ OBSERVABLE_PROJECTED_SETTING(Type, Name); \ - Microsoft.Terminal.Settings.Model.Profile Name##OverrideSource { get; } + Object Name##OverrideSource { get; } namespace Microsoft.Terminal.Settings.Editor { diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp index 31b643192ae..f2566da7f84 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp @@ -159,6 +159,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { tooltip = _GenerateOverrideMessage(profile); } + else if (const auto& appearanceConfig{ settingSrc.try_as() }) + { + tooltip = _GenerateOverrideMessage(appearanceConfig.SourceProfile()); + } Controls::ToolTipService::SetToolTip(button, box_value(tooltip)); button.Visibility(tooltip.empty() ? Visibility::Collapsed : Visibility::Visible); diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp new file mode 100644 index 00000000000..a7343a51ba1 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "AppearanceConfig.h" +#include "AppearanceConfig.g.cpp" +#include "TerminalSettingsSerializationHelpers.h" +#include "JsonUtils.h" + +using namespace winrt::Microsoft::Terminal::Control; +using namespace Microsoft::Terminal::Settings::Model; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; + +static constexpr std::string_view ForegroundKey{ "foreground" }; +static constexpr std::string_view BackgroundKey{ "background" }; +static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" }; +static constexpr std::string_view CursorColorKey{ "cursorColor" }; +static constexpr std::string_view CursorShapeKey{ "cursorShape" }; +static constexpr std::string_view CursorHeightKey{ "cursorHeight" }; +static constexpr std::string_view BackgroundImageKey{ "backgroundImage" }; +static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; +static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" }; +static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImageStretchMode" }; +static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" }; +static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" }; +static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" }; + +winrt::Microsoft::Terminal::Settings::Model::implementation::AppearanceConfig::AppearanceConfig(const winrt::weak_ref sourceProfile) : + _sourceProfile(sourceProfile) +{ +} + +winrt::com_ptr AppearanceConfig::CopyAppearance(const winrt::com_ptr source, const winrt::weak_ref sourceProfile) +{ + auto appearance{ winrt::make_self(sourceProfile) }; + auto const sourceAppearance = source.try_as(); + appearance->_BackgroundImagePath = sourceAppearance->_BackgroundImagePath; + appearance->_BackgroundImageOpacity = sourceAppearance->_BackgroundImageOpacity; + appearance->_BackgroundImageStretchMode = sourceAppearance->_BackgroundImageStretchMode; + appearance->_ColorSchemeName = sourceAppearance->_ColorSchemeName; + appearance->_Foreground = sourceAppearance->_Foreground; + appearance->_Background = sourceAppearance->_Background; + appearance->_SelectionBackground = sourceAppearance->_SelectionBackground; + appearance->_CursorColor = sourceAppearance->_CursorColor; + appearance->_CursorShape = sourceAppearance->_CursorShape; + appearance->_CursorHeight = sourceAppearance->_CursorHeight; + appearance->_BackgroundImageAlignment = sourceAppearance->_BackgroundImageAlignment; + appearance->_RetroTerminalEffect = sourceAppearance->_RetroTerminalEffect; + appearance->_PixelShaderPath = sourceAppearance->_PixelShaderPath; + return appearance; +} + +Json::Value AppearanceConfig::ToJson() const +{ + Json::Value json{ Json::ValueType::objectValue }; + + JsonUtils::SetValueForKey(json, ForegroundKey, _Foreground); + JsonUtils::SetValueForKey(json, BackgroundKey, _Background); + JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); + JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor); + JsonUtils::SetValueForKey(json, ColorSchemeKey, _ColorSchemeName); + JsonUtils::SetValueForKey(json, CursorHeightKey, _CursorHeight); + JsonUtils::SetValueForKey(json, CursorShapeKey, _CursorShape); + JsonUtils::SetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); + JsonUtils::SetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); + JsonUtils::SetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); + JsonUtils::SetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); + JsonUtils::SetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); + JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); + + return json; +} + +// Method Description: +// - Layer values from the given json object on top of the existing properties +// of this object. For any keys we're expecting to be able to parse in the +// given object, we'll parse them and replace our settings with values from +// the new json object. Properties that _aren't_ in the json object will _not_ +// be replaced. +// - Optional values that are set to `null` in the json object +// will be set to nullopt. +// - This is similar to Profile::LayerJson but for AppearanceConfig +// Arguments: +// - json: an object which should be a partial serialization of an AppearanceConfig object. +void AppearanceConfig::LayerJson(const Json::Value& json) +{ + JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground); + JsonUtils::GetValueForKey(json, BackgroundKey, _Background); + JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); + JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor); + JsonUtils::GetValueForKey(json, CursorHeightKey, _CursorHeight); + JsonUtils::GetValueForKey(json, ColorSchemeKey, _ColorSchemeName); + JsonUtils::GetValueForKey(json, CursorShapeKey, _CursorShape); + JsonUtils::GetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); + JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); + JsonUtils::GetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); + JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); + JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); + JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); +} + +winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourceProfile() +{ + return _sourceProfile.get(); +} + +// Method Description: +// - Returns this AppearanceConfig's background image path, if one is set, expanding +// any environment variables in the path, if there are any. +// - Or if "DesktopWallpaper" is set, then gets the path to the desktops wallpaper. +// - This is the same as Profile::ExpandedBackgroundImagePath, but for AppearanceConfig +// - NOTE: This is just placeholder for now, eventually the path will no longer be expanded in the settings model +// Return Value: +// - This profile's expanded background image path / desktops's wallpaper path /the empty string. +winrt::hstring AppearanceConfig::ExpandedBackgroundImagePath() +{ + const auto path{ BackgroundImagePath() }; + if (path.empty()) + { + return path; + } + // checks if the user would like to copy their desktop wallpaper + // if so, replaces the path with the desktop wallpaper's path + else if (path == L"desktopWallpaper") + { + WCHAR desktopWallpaper[MAX_PATH]; + + // "The returned string will not exceed MAX_PATH characters" as of 2020 + if (SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, desktopWallpaper, SPIF_UPDATEINIFILE)) + { + return winrt::hstring{ (desktopWallpaper) }; + } + else + { + return winrt::hstring{ L"" }; + } + } + else + { + return winrt::hstring{ wil::ExpandEnvironmentStringsW(path.c_str()) }; + } +} diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h new file mode 100644 index 00000000000..d0b08e9e296 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -0,0 +1,60 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- AppearanceConfig + +Abstract: +- The implementation of the AppearanceConfig winrt class. Provides settings related + to the appearance of the terminal, in both terminal control and terminal core. + +Author(s): +- Pankaj Bhojwani - Nov 2020 + +--*/ + +#pragma once + +#include "pch.h" +#include "AppearanceConfig.g.h" +#include "JsonUtils.h" +#include "../inc/cppwinrt_utils.h" +#include "IInheritable.h" +#include + +namespace winrt::Microsoft::Terminal::Settings::Model::implementation +{ + struct AppearanceConfig : AppearanceConfigT, IInheritable + { + public: + AppearanceConfig(const winrt::weak_ref sourceProfile); + static winrt::com_ptr CopyAppearance(const winrt::com_ptr source, const winrt::weak_ref sourceProfile); + Json::Value ToJson() const; + void LayerJson(const Json::Value& json); + + Model::Profile SourceProfile(); + + winrt::hstring ExpandedBackgroundImagePath(); + + INHERITABLE_SETTING(Model::IAppearanceConfig, ConvergedAlignment, BackgroundImageAlignment, ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center); + + INHERITABLE_SETTING(Model::IAppearanceConfig, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); + INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, ColorSchemeName, L"Campbell"); + INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Foreground, nullptr); + INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, Background, nullptr); + INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, SelectionBackground, nullptr); + INHERITABLE_NULLABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::Color, CursorColor, nullptr); + INHERITABLE_SETTING(Model::IAppearanceConfig, Microsoft::Terminal::Core::CursorStyle, CursorShape, Microsoft::Terminal::Core::CursorStyle::Bar); + INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, BackgroundImagePath); + + INHERITABLE_SETTING(Model::IAppearanceConfig, double, BackgroundImageOpacity, 1.0); + INHERITABLE_SETTING(Model::IAppearanceConfig, Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch::UniformToFill); + + INHERITABLE_SETTING(Model::IAppearanceConfig, bool, RetroTerminalEffect, false); + INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, PixelShaderPath, L""); + + private: + winrt::weak_ref _sourceProfile; + }; +} diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl new file mode 100644 index 00000000000..8434c13f8aa --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.idl @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "IAppearanceConfig.idl"; + +namespace Microsoft.Terminal.Settings.Model +{ + [default_interface] runtimeclass AppearanceConfig : IAppearanceConfig { + } +} diff --git a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp index 3435d795591..41b26163d76 100644 --- a/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/AzureCloudShellGenerator.cpp @@ -36,7 +36,7 @@ std::vector AzureCloudShellGenerator::GenerateProfiles() auto azureCloudShellProfile{ CreateDefaultProfile(L"Azure Cloud Shell") }; azureCloudShellProfile.Commandline(L"Azure"); azureCloudShellProfile.StartingDirectory(DEFAULT_STARTING_DIRECTORY); - azureCloudShellProfile.ColorSchemeName(L"Vintage"); + azureCloudShellProfile.DefaultAppearance().ColorSchemeName(L"Vintage"); azureCloudShellProfile.ConnectionType(AzureConnection::ConnectionType()); profiles.emplace_back(azureCloudShellProfile); } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 0e300c07437..45427d37df6 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -104,7 +104,7 @@ void CascadiaSettings::_CopyProfileInheritanceTree(winrt::com_ptr profileImpl; profileImpl.copy_from(winrt::get_self(profile)); - dummyRootSource->InsertParent(profileImpl); + Profile::InsertParentHelper(dummyRootSource, profileImpl); } auto dummyRootClone{ winrt::make_self() }; @@ -519,13 +519,22 @@ void CascadiaSettings::_ValidateAllSchemesExist() bool foundInvalidScheme = false; for (auto profile : _allProfiles) { - const auto schemeName = profile.ColorSchemeName(); + const auto schemeName = profile.DefaultAppearance().ColorSchemeName(); if (!_globals->ColorSchemes().HasKey(schemeName)) { // Clear the user set color scheme. We'll just fallback instead. - profile.ClearColorSchemeName(); + profile.DefaultAppearance().ClearColorSchemeName(); foundInvalidScheme = true; } + if (profile.UnfocusedAppearance()) + { + const auto unfocusedSchemeName = profile.UnfocusedAppearance().ColorSchemeName(); + if (!_globals->ColorSchemes().HasKey(unfocusedSchemeName)) + { + profile.UnfocusedAppearance().ClearColorSchemeName(); + foundInvalidScheme = true; + } + } } if (foundInvalidScheme) @@ -552,22 +561,41 @@ void CascadiaSettings::_ValidateMediaResources() for (auto profile : _allProfiles) { - if (!profile.BackgroundImagePath().empty()) + if (!profile.DefaultAppearance().BackgroundImagePath().empty()) { // Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable. // This covers file paths on the machine, app data, URLs, and other resource paths. try { - winrt::Windows::Foundation::Uri imagePath{ profile.ExpandedBackgroundImagePath() }; + winrt::Windows::Foundation::Uri imagePath{ profile.DefaultAppearance().ExpandedBackgroundImagePath() }; } catch (...) { // reset background image path - profile.BackgroundImagePath(L""); + profile.DefaultAppearance().BackgroundImagePath(L""); invalidBackground = true; } } + if (profile.UnfocusedAppearance()) + { + if (!profile.UnfocusedAppearance().BackgroundImagePath().empty()) + { + // Attempt to convert the path to a URI, the ctor will throw if it's invalid/unparseable. + // This covers file paths on the machine, app data, URLs, and other resource paths. + try + { + winrt::Windows::Foundation::Uri imagePath{ profile.UnfocusedAppearance().ExpandedBackgroundImagePath() }; + } + catch (...) + { + // reset background image path + profile.UnfocusedAppearance().BackgroundImagePath(L""); + invalidBackground = true; + } + } + } + if (!profile.Icon().empty()) { const auto iconPath{ wil::ExpandEnvironmentStringsW(profile.Icon().c_str()) }; @@ -861,7 +889,7 @@ winrt::Microsoft::Terminal::Settings::Model::ColorScheme CascadiaSettings::GetCo { return nullptr; } - const auto schemeName = profile.ColorSchemeName(); + const auto schemeName = profile.DefaultAppearance().ColorSchemeName(); return _globals->ColorSchemes().TryLookup(schemeName); } @@ -876,18 +904,26 @@ void CascadiaSettings::UpdateColorSchemeReferences(const hstring oldName, const { // update profiles.defaults, if necessary if (_userDefaultProfileSettings && - _userDefaultProfileSettings->HasColorSchemeName() && - _userDefaultProfileSettings->ColorSchemeName() == oldName) + _userDefaultProfileSettings->DefaultAppearance().HasColorSchemeName() && + _userDefaultProfileSettings->DefaultAppearance().ColorSchemeName() == oldName) { - _userDefaultProfileSettings->ColorSchemeName(newName); + _userDefaultProfileSettings->DefaultAppearance().ColorSchemeName(newName); } // update all profiles referencing this color scheme for (const auto& profile : _allProfiles) { - if (profile.HasColorSchemeName() && profile.ColorSchemeName() == oldName) + if (profile.DefaultAppearance().HasColorSchemeName() && profile.DefaultAppearance().ColorSchemeName() == oldName) { - profile.ColorSchemeName(newName); + profile.DefaultAppearance().ColorSchemeName(newName); + } + + if (profile.UnfocusedAppearance()) + { + if (profile.UnfocusedAppearance().HasColorSchemeName() && profile.UnfocusedAppearance().ColorSchemeName() == oldName) + { + profile.UnfocusedAppearance().ColorSchemeName(newName); + } } } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index ee35ee3e6b0..e63775a19f2 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -946,7 +946,7 @@ void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson) // We _won't_ have these settings yet for defaults, dynamic profiles. if (_userDefaultProfileSettings) { - profile->InsertParent(0, _userDefaultProfileSettings); + Profile::InsertParentHelper(profile, _userDefaultProfileSettings, 0); } profile->LayerJson(profileJson); @@ -1043,7 +1043,7 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings() auto childImpl{ parentImpl->CreateChild() }; // Add profile.defaults as the _first_ parent to the child - childImpl->InsertParent(0, _userDefaultProfileSettings); + Profile::InsertParentHelper(childImpl, _userDefaultProfileSettings, 0); // replace parent in _profiles with child _allProfiles.SetAt(profileIndex, *childImpl); diff --git a/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl new file mode 100644 index 00000000000..2b5cbbd5244 --- /dev/null +++ b/src/cascadia/TerminalSettingsModel/IAppearanceConfig.idl @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profile.idl"; +#include "IInheritable.idl.h" + +#define INHERITABLE_APPEARANCE_SETTING(Type, Name) \ + _BASE_INHERITABLE_SETTING(Type, Name); \ + Microsoft.Terminal.Settings.Model.IAppearanceConfig Name##OverrideSource { get; } + +namespace Microsoft.Terminal.Settings.Model +{ + [flags] enum ConvergedAlignment { + // low 4 bits are the horizontal + Horizontal_Center = 0x00, + Horizontal_Left = 0x01, + Horizontal_Right = 0x02, + + // high 4 bits are the vertical + Vertical_Center = 0x00, + Vertical_Top = 0x10, + Vertical_Bottom = 0x20 + }; + + interface IAppearanceConfig + { + Microsoft.Terminal.Settings.Model.Profile SourceProfile { get; }; + INHERITABLE_APPEARANCE_SETTING(String, ColorSchemeName); + INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, Foreground); + INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, Background); + INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, SelectionBackground); + INHERITABLE_APPEARANCE_SETTING(Windows.Foundation.IReference, CursorColor); + INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.CursorStyle, CursorShape); + INHERITABLE_APPEARANCE_SETTING(UInt32, CursorHeight); + + INHERITABLE_APPEARANCE_SETTING(String, BackgroundImagePath); + String ExpandedBackgroundImagePath { get; }; + + INHERITABLE_APPEARANCE_SETTING(Double, BackgroundImageOpacity); + INHERITABLE_APPEARANCE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); + INHERITABLE_APPEARANCE_SETTING(ConvergedAlignment, BackgroundImageAlignment); + + INHERITABLE_APPEARANCE_SETTING(Boolean, RetroTerminalEffect); + INHERITABLE_APPEARANCE_SETTING(String, PixelShaderPath); + }; +} diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index 2722af89299..ef8229af2db 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -53,6 +53,9 @@ Profile.idl + + AppearanceConfig.idl + EnumMappings.idl @@ -111,6 +114,9 @@ Profile.idl + + AppearanceConfig.idl + TerminalSettings.idl @@ -139,6 +145,8 @@ + + diff --git a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp index f882a929056..7baae6b0a6e 100644 --- a/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/PowershellCoreProfileGenerator.cpp @@ -311,7 +311,7 @@ std::vector PowershellCoreProfileGenerator::GenerateProfiles() auto profile{ CreateDefaultProfile(name) }; profile.Commandline(psI.executablePath.wstring()); profile.StartingDirectory(DEFAULT_STARTING_DIRECTORY); - profile.ColorSchemeName(L"Campbell"); + profile.DefaultAppearance().ColorSchemeName(L"Campbell"); profile.Icon(WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON); profiles.emplace_back(std::move(profile)); diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp index 624d134c39e..a3fc5c6ae0b 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.cpp +++ b/src/cascadia/TerminalSettingsModel/Profile.cpp @@ -9,6 +9,7 @@ #include "LegacyProfileGeneratorNamespaces.h" #include "TerminalSettingsSerializationHelpers.h" +#include "AppearanceConfig.h" #include "Profile.g.cpp" @@ -23,20 +24,13 @@ using namespace ::Microsoft::Console; static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view GuidKey{ "guid" }; static constexpr std::string_view SourceKey{ "source" }; -static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; static constexpr std::string_view HiddenKey{ "hidden" }; -static constexpr std::string_view ForegroundKey{ "foreground" }; -static constexpr std::string_view BackgroundKey{ "background" }; -static constexpr std::string_view SelectionBackgroundKey{ "selectionBackground" }; static constexpr std::string_view TabTitleKey{ "tabTitle" }; static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" }; static constexpr std::string_view HistorySizeKey{ "historySize" }; static constexpr std::string_view SnapOnInputKey{ "snapOnInput" }; static constexpr std::string_view AltGrAliasingKey{ "altGrAliasing" }; -static constexpr std::string_view CursorColorKey{ "cursorColor" }; -static constexpr std::string_view CursorShapeKey{ "cursorShape" }; -static constexpr std::string_view CursorHeightKey{ "cursorHeight" }; static constexpr std::string_view ConnectionTypeKey{ "connectionType" }; static constexpr std::string_view CommandlineKey{ "commandline" }; @@ -50,15 +44,10 @@ static constexpr std::string_view CloseOnExitKey{ "closeOnExit" }; static constexpr std::string_view PaddingKey{ "padding" }; static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; static constexpr std::string_view IconKey{ "icon" }; -static constexpr std::string_view BackgroundImageKey{ "backgroundImage" }; -static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" }; -static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImageStretchMode" }; -static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" }; -static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" }; static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" }; static constexpr std::string_view TabColorKey{ "tabColor" }; static constexpr std::string_view BellStyleKey{ "bellStyle" }; -static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" }; +static constexpr std::string_view UnfocusedAppearanceKey{ "unfocusedAppearance" }; static constexpr std::wstring_view DesktopWallpaperEnum{ L"desktopWallpaper" }; @@ -74,6 +63,7 @@ Profile::Profile(guid guid) : winrt::com_ptr Profile::CopySettings(winrt::com_ptr source) { auto profile{ winrt::make_self() }; + profile->_Guid = source->_Guid; profile->_Name = source->_Name; profile->_Source = source->_Source; @@ -92,29 +82,40 @@ winrt::com_ptr Profile::CopySettings(winrt::com_ptr source) profile->_Padding = source->_Padding; profile->_Commandline = source->_Commandline; profile->_StartingDirectory = source->_StartingDirectory; - profile->_BackgroundImagePath = source->_BackgroundImagePath; - profile->_BackgroundImageOpacity = source->_BackgroundImageOpacity; - profile->_BackgroundImageStretchMode = source->_BackgroundImageStretchMode; profile->_AntialiasingMode = source->_AntialiasingMode; - profile->_RetroTerminalEffect = source->_RetroTerminalEffect; profile->_ForceFullRepaintRendering = source->_ForceFullRepaintRendering; profile->_SoftwareRendering = source->_SoftwareRendering; - profile->_ColorSchemeName = source->_ColorSchemeName; - profile->_Foreground = source->_Foreground; - profile->_Background = source->_Background; - profile->_SelectionBackground = source->_SelectionBackground; - profile->_CursorColor = source->_CursorColor; profile->_HistorySize = source->_HistorySize; profile->_SnapOnInput = source->_SnapOnInput; profile->_AltGrAliasing = source->_AltGrAliasing; - profile->_CursorShape = source->_CursorShape; - profile->_CursorHeight = source->_CursorHeight; profile->_BellStyle = source->_BellStyle; - profile->_PixelShaderPath = source->_PixelShaderPath; - profile->_BackgroundImageAlignment = source->_BackgroundImageAlignment; profile->_ConnectionType = source->_ConnectionType; profile->_Origin = source->_Origin; + // Copy over the appearance + const auto weakRefToProfile = weak_ref(*profile); + winrt::com_ptr sourceDefaultAppearanceImpl; + sourceDefaultAppearanceImpl.copy_from(winrt::get_self(source->_DefaultAppearance)); + auto copiedDefaultAppearance = AppearanceConfig::CopyAppearance(sourceDefaultAppearanceImpl, weakRefToProfile); + profile->_DefaultAppearance = *copiedDefaultAppearance; + + if (source->_UnfocusedAppearance.has_value()) + { + Model::AppearanceConfig unfocused{ nullptr }; + if (source->_UnfocusedAppearance.value() != nullptr) + { + // Copy over the unfocused appearance + winrt::com_ptr sourceUnfocusedAppearanceImpl; + sourceUnfocusedAppearanceImpl.copy_from(winrt::get_self(source->_UnfocusedAppearance.value())); + auto copiedUnfocusedAppearance = AppearanceConfig::CopyAppearance(sourceUnfocusedAppearanceImpl, weakRefToProfile); + + // Make sure to add the default appearance as a parent + copiedUnfocusedAppearance->InsertParent(copiedDefaultAppearance); + unfocused = *copiedUnfocusedAppearance; + } + profile->_UnfocusedAppearance = unfocused; + } + return profile; } @@ -142,7 +143,7 @@ winrt::com_ptr Profile::CloneInheritanceGraph(winrt::com_ptr s if (kv != visited.end()) { // add this Profile's clone as a parent - cloneGraph->InsertParent(kv->second); + InsertParentHelper(cloneGraph, kv->second); } else { @@ -151,7 +152,7 @@ winrt::com_ptr Profile::CloneInheritanceGraph(winrt::com_ptr s winrt::com_ptr clone{ CopySettings(sourceParent) }; // add the new copy to the cloneGraph - cloneGraph->InsertParent(clone); + InsertParentHelper(cloneGraph, clone); // copy the sub-graph at "clone" CloneInheritanceGraph(sourceParent, clone, visited); @@ -167,6 +168,26 @@ winrt::com_ptr Profile::CloneInheritanceGraph(winrt::com_ptr s return cloneGraph; } +// Method Description: +// - Inserts a parent profile into a child profile, at the specified index if one was provided +// - Makes sure to call _FinalizeInheritance after inserting the parent +// Arguments: +// - child: the child profile to insert the parent into +// - parent: the parent profile to insert into the child +// - index: an optional index value to insert the parent into +void Profile::InsertParentHelper(winrt::com_ptr child, winrt::com_ptr parent, std::optional index) +{ + if (index) + { + child->InsertParent(index.value(), parent); + } + else + { + child->InsertParent(parent); + } + child->_FinalizeInheritance(); +} + // Method Description: // - Generates a Json::Value which is a "stub" of this profile. This stub will // have enough information that it could be layered with this profile. @@ -290,25 +311,20 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const // void Profile::LayerJson(const Json::Value& json) { + // Appearance Settings + auto defaultAppearanceImpl = winrt::get_self(_DefaultAppearance); + defaultAppearanceImpl->LayerJson(json); + // Profile-specific Settings JsonUtils::GetValueForKey(json, NameKey, _Name); JsonUtils::GetValueForKey(json, GuidKey, _Guid); JsonUtils::GetValueForKey(json, HiddenKey, _Hidden); JsonUtils::GetValueForKey(json, SourceKey, _Source); - // Core Settings - JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground); - JsonUtils::GetValueForKey(json, BackgroundKey, _Background); - JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); - JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor); - JsonUtils::GetValueForKey(json, ColorSchemeKey, _ColorSchemeName); - // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" JsonUtils::GetValueForKey(json, HistorySizeKey, _HistorySize); JsonUtils::GetValueForKey(json, SnapOnInputKey, _SnapOnInput); JsonUtils::GetValueForKey(json, AltGrAliasingKey, _AltGrAliasing); - JsonUtils::GetValueForKey(json, CursorHeightKey, _CursorHeight); - JsonUtils::GetValueForKey(json, CursorShapeKey, _CursorShape); JsonUtils::GetValueForKey(json, TabTitleKey, _TabTitle); // Control Settings @@ -331,50 +347,22 @@ void Profile::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, StartingDirectoryKey, _StartingDirectory); JsonUtils::GetValueForKey(json, IconKey, _Icon); - JsonUtils::GetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); - JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); - JsonUtils::GetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); - JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); - JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); JsonUtils::GetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::GetValueForKey(json, TabColorKey, _TabColor); JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle); - JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); -} -// Method Description: -// - Either Returns this profile's background image path, if one is set, expanding -// - Returns this profile's background image path, if one is set, expanding -// any environment variables in the path, if there are any. -// - Or if "DesktopWallpaper" is set, then gets the path to the desktops wallpaper. -// Return Value: -// - This profile's expanded background image path / desktops's wallpaper path /the empty string. -winrt::hstring Profile::ExpandedBackgroundImagePath() const -{ - const auto path{ BackgroundImagePath() }; - if (path.empty()) - { - return path; - } - // checks if the user would like to copy their desktop wallpaper - // if so, replaces the path with the desktop wallpaper's path - else if (path == DesktopWallpaperEnum) + if (json.isMember(JsonKey(UnfocusedAppearanceKey))) { - WCHAR desktopWallpaper[MAX_PATH]; + auto unfocusedAppearance{ winrt::make_self(weak_ref(*this)) }; - // "The returned string will not exceed MAX_PATH characters" as of 2020 - if (SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, desktopWallpaper, SPIF_UPDATEINIFILE)) - { - return winrt::hstring{ (desktopWallpaper) }; - } - else - { - return winrt::hstring{ L"" }; - } - } - else - { - return winrt::hstring{ wil::ExpandEnvironmentStringsW(path.c_str()) }; + // If an unfocused appearance is defined in this profile, any undefined parameters are + // taken from this profile's default appearance, so add it as a parent + com_ptr parentCom; + parentCom.copy_from(defaultAppearanceImpl); + unfocusedAppearance->InsertParent(parentCom); + + unfocusedAppearance->LayerJson(json[JsonKey(UnfocusedAppearanceKey)]); + _UnfocusedAppearance = *unfocusedAppearance; } } @@ -389,6 +377,25 @@ winrt::hstring Profile::EvaluatedStartingDirectory() const return path; } +void Profile::_FinalizeInheritance() +{ + if (auto defaultAppearanceImpl = get_self(_DefaultAppearance)) + { + for (auto& parent : _parents) + { + if (auto parentDefaultAppearanceImpl = parent->_DefaultAppearance.try_as()) + { + defaultAppearanceImpl->InsertParent(parentDefaultAppearanceImpl); + } + } + } +} + +winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig Profile::DefaultAppearance() +{ + return _DefaultAppearance; +} + // Method Description: // - Helper function for expanding any environment variables in a user-supplied starting directory and validating the resulting path // Arguments: @@ -483,7 +490,8 @@ winrt::guid Profile::GetGuidOrGenerateForJson(const Json::Value& json) noexcept // - the JsonObject representing this instance Json::Value Profile::ToJson() const { - Json::Value json{ Json::ValueType::objectValue }; + // Initialize the json with the appearance settings + Json::Value json{ winrt::get_self(_DefaultAppearance)->ToJson() }; // Profile-specific Settings JsonUtils::SetValueForKey(json, NameKey, _Name); @@ -491,19 +499,10 @@ Json::Value Profile::ToJson() const JsonUtils::SetValueForKey(json, HiddenKey, _Hidden); JsonUtils::SetValueForKey(json, SourceKey, _Source); - // Core Settings - JsonUtils::SetValueForKey(json, ForegroundKey, _Foreground); - JsonUtils::SetValueForKey(json, BackgroundKey, _Background); - JsonUtils::SetValueForKey(json, SelectionBackgroundKey, _SelectionBackground); - JsonUtils::SetValueForKey(json, CursorColorKey, _CursorColor); - JsonUtils::SetValueForKey(json, ColorSchemeKey, _ColorSchemeName); - // TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback" JsonUtils::SetValueForKey(json, HistorySizeKey, _HistorySize); JsonUtils::SetValueForKey(json, SnapOnInputKey, _SnapOnInput); JsonUtils::SetValueForKey(json, AltGrAliasingKey, _AltGrAliasing); - JsonUtils::SetValueForKey(json, CursorHeightKey, _CursorHeight); - JsonUtils::SetValueForKey(json, CursorShapeKey, _CursorShape); JsonUtils::SetValueForKey(json, TabTitleKey, _TabTitle); // Control Settings @@ -523,15 +522,14 @@ Json::Value Profile::ToJson() const JsonUtils::SetValueForKey(json, ScrollbarStateKey, _ScrollState); JsonUtils::SetValueForKey(json, StartingDirectoryKey, _StartingDirectory); JsonUtils::SetValueForKey(json, IconKey, _Icon); - JsonUtils::SetValueForKey(json, BackgroundImageKey, _BackgroundImagePath); - JsonUtils::SetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity); - JsonUtils::SetValueForKey(json, BackgroundImageStretchModeKey, _BackgroundImageStretchMode); - JsonUtils::SetValueForKey(json, BackgroundImageAlignmentKey, _BackgroundImageAlignment); - JsonUtils::SetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect); JsonUtils::SetValueForKey(json, AntialiasingModeKey, _AntialiasingMode); JsonUtils::SetValueForKey(json, TabColorKey, _TabColor); JsonUtils::SetValueForKey(json, BellStyleKey, _BellStyle); - JsonUtils::SetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); + + if (_UnfocusedAppearance) + { + json[JsonKey(UnfocusedAppearanceKey)] = winrt::get_self(_UnfocusedAppearance.value())->ToJson(); + } return json; } diff --git a/src/cascadia/TerminalSettingsModel/Profile.h b/src/cascadia/TerminalSettingsModel/Profile.h index 7112ab3cfe4..82e837db254 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.h +++ b/src/cascadia/TerminalSettingsModel/Profile.h @@ -7,7 +7,36 @@ Module Name: Abstract: - A profile acts as a single set of terminal settings. Many tabs or panes could - exist side-by-side with different profiles simultaneously. + exist side-by-side with different profiles simultaneously. +- Profiles could also specify their appearance when unfocused, this is what + the inheritance tree looks like for unfocused settings: + + +-------------------+ + | | + |Profile.defaults | + | | + |DefaultAppearance | + | | + +-------------------+ + ^ ^ + | | ++------------------++ ++------------------+ +| | | | +|MyProfile | |Profile.defaults | +| | | | +|DefaultAppearance | |UnfocusedAppearance| +| | | | ++-------------------+ +-------------------+ + ^ + | ++------------------++ +| | +|MyProfile | +| | +|UnfocusedAppearance| +| | ++-------------------+ + Author(s): - Mike Griese - March 2019 @@ -21,6 +50,7 @@ Author(s): #include "../inc/cppwinrt_utils.h" #include "JsonUtils.h" #include +#include "AppearanceConfig.h" // fwdecl unittest classes namespace SettingsModelLocalTests @@ -55,6 +85,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static com_ptr CloneInheritanceGraph(com_ptr oldProfile, com_ptr newProfile, std::unordered_map>& visited); static com_ptr CopySettings(com_ptr source); + static void InsertParentHelper(com_ptr child, com_ptr parent, std::optional index = std::nullopt); Json::Value GenerateStub() const; static com_ptr FromJson(const Json::Value& json); @@ -64,9 +95,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ToJson() const; hstring EvaluatedStartingDirectory() const; - hstring ExpandedBackgroundImagePath() const; static guid GetGuidOrGenerateForJson(const Json::Value& json) noexcept; + Model::IAppearanceConfig DefaultAppearance(); + + void _FinalizeInheritance() override; + WINRT_PROPERTY(OriginTag, Origin, OriginTag::Custom); INHERITABLE_SETTING(Model::Profile, guid, Guid, _GenerateGuidForProfile(Name(), Source())); @@ -95,34 +129,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::Profile, hstring, Commandline, L"cmd.exe"); INHERITABLE_SETTING(Model::Profile, hstring, StartingDirectory); - INHERITABLE_SETTING(Model::Profile, hstring, BackgroundImagePath); - INHERITABLE_SETTING(Model::Profile, double, BackgroundImageOpacity, 1.0); - INHERITABLE_SETTING(Model::Profile, Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch::UniformToFill); - INHERITABLE_SETTING(Model::Profile, ConvergedAlignment, BackgroundImageAlignment, ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center); - INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); - INHERITABLE_SETTING(Model::Profile, bool, RetroTerminalEffect, false); - INHERITABLE_SETTING(Model::Profile, hstring, PixelShaderPath, L""); INHERITABLE_SETTING(Model::Profile, bool, ForceFullRepaintRendering, false); INHERITABLE_SETTING(Model::Profile, bool, SoftwareRendering, false); - INHERITABLE_SETTING(Model::Profile, hstring, ColorSchemeName, L"Campbell"); - - INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, Foreground, nullptr); - INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, Background, nullptr); - INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, SelectionBackground, nullptr); - INHERITABLE_NULLABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::Color, CursorColor, nullptr); - INHERITABLE_SETTING(Model::Profile, int32_t, HistorySize, DEFAULT_HISTORY_SIZE); INHERITABLE_SETTING(Model::Profile, bool, SnapOnInput, true); INHERITABLE_SETTING(Model::Profile, bool, AltGrAliasing, true); - INHERITABLE_SETTING(Model::Profile, Microsoft::Terminal::Core::CursorStyle, CursorShape, Microsoft::Terminal::Core::CursorStyle::Bar); - INHERITABLE_SETTING(Model::Profile, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); - INHERITABLE_SETTING(Model::Profile, Model::BellStyle, BellStyle, BellStyle::Audible); + INHERITABLE_SETTING(Model::Profile, Model::IAppearanceConfig, UnfocusedAppearance, nullptr); + private: + Model::IAppearanceConfig _DefaultAppearance{ winrt::make(weak_ref(*this)) }; static std::wstring EvaluateStartingDirectory(const std::wstring& directory); static guid _GenerateGuidForProfile(const hstring& name, const hstring& source) noexcept; diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 9e29da0cb60..9ea28ba1841 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "IAppearanceConfig.idl"; #include "IInheritable.idl.h" #define INHERITABLE_PROFILE_SETTING(Type, Name) \ @@ -33,19 +34,6 @@ namespace Microsoft.Terminal.Settings.Model All = 0xffffffff }; - [flags] - enum ConvergedAlignment { - // low 4 bits are the horizontal - Horizontal_Center = 0x00, - Horizontal_Left = 0x01, - Horizontal_Right = 0x02, - - // high 4 bits are the vertical - Vertical_Center = 0x00, - Vertical_Top = 0x10, - Vertical_Bottom = 0x20 - }; - [default_interface] runtimeclass Profile : Windows.Foundation.IStringable { Profile(); Profile(Guid guid); @@ -80,27 +68,16 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(String, StartingDirectory); String EvaluatedStartingDirectory { get; }; - INHERITABLE_PROFILE_SETTING(String, BackgroundImagePath); - String ExpandedBackgroundImagePath { get; }; + IAppearanceConfig DefaultAppearance { get; }; + INHERITABLE_PROFILE_SETTING(IAppearanceConfig, UnfocusedAppearance); - INHERITABLE_PROFILE_SETTING(Double, BackgroundImageOpacity); - INHERITABLE_PROFILE_SETTING(Windows.UI.Xaml.Media.Stretch, BackgroundImageStretchMode); - INHERITABLE_PROFILE_SETTING(ConvergedAlignment, BackgroundImageAlignment); INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.TextAntialiasingMode, AntialiasingMode); - INHERITABLE_PROFILE_SETTING(Boolean, RetroTerminalEffect); - INHERITABLE_PROFILE_SETTING(String, PixelShaderPath); INHERITABLE_PROFILE_SETTING(Boolean, ForceFullRepaintRendering); INHERITABLE_PROFILE_SETTING(Boolean, SoftwareRendering); - INHERITABLE_PROFILE_SETTING(String, ColorSchemeName); - INHERITABLE_PROFILE_SETTING(Windows.Foundation.IReference, Foreground); - INHERITABLE_PROFILE_SETTING(Windows.Foundation.IReference, Background); - INHERITABLE_PROFILE_SETTING(Windows.Foundation.IReference, SelectionBackground); - INHERITABLE_PROFILE_SETTING(Windows.Foundation.IReference, CursorColor); + INHERITABLE_PROFILE_SETTING(Int32, HistorySize); INHERITABLE_PROFILE_SETTING(Boolean, SnapOnInput); INHERITABLE_PROFILE_SETTING(Boolean, AltGrAliasing); - INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Core.CursorStyle, CursorShape); - INHERITABLE_PROFILE_SETTING(UInt32, CursorHeight); INHERITABLE_PROFILE_SETTING(BellStyle, BellStyle); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 73b26cbf349..b4518546bbd 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -50,7 +50,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } // Method Description: - // - Create a TerminalSettings object for the provided profile guid. We'll + // - Create a TerminalSettingsCreateResult for the provided profile guid. We'll // use the guid to look up the profile that should be used to // create these TerminalSettings. Then, we'll apply settings contained in the // global and profile settings to the instance. @@ -58,7 +58,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - appSettings: the set of settings being used to construct the new terminal // - profileGuid: the unique identifier (guid) of the profile // - keybindings: the keybinding handler - Model::TerminalSettings TerminalSettings::CreateWithProfileByID(const Model::CascadiaSettings& appSettings, winrt::guid profileGuid, const IKeyBindings& keybindings) + // Return Value: + // - A TerminalSettingsCreateResult, which contains a pair of TerminalSettings objects, + // one for when the terminal is focused and the other for when the terminal is unfocused + Model::TerminalSettingsCreateResult TerminalSettings::CreateWithProfileByID(const Model::CascadiaSettings& appSettings, winrt::guid profileGuid, const IKeyBindings& keybindings) { auto settings{ winrt::make_self() }; settings->_KeyBindings = keybindings; @@ -67,10 +70,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation THROW_HR_IF_NULL(E_INVALIDARG, profile); const auto globals = appSettings.GlobalSettings(); - settings->_ApplyProfileSettings(profile, globals.ColorSchemes()); + settings->_ApplyProfileSettings(profile); settings->_ApplyGlobalSettings(globals); + settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes()); + + Model::TerminalSettings child{ nullptr }; + if (const auto& unfocusedAppearance{ profile.UnfocusedAppearance() }) + { + auto childImpl = settings->CreateChild(); + childImpl->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes()); + child = *childImpl; + } - return *settings; + return winrt::make(*settings, child); } // Method Description: @@ -88,61 +100,123 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // StartingDirectory) in this object to override the settings directly from // the profile. // - keybindings: the keybinding handler - Model::TerminalSettings TerminalSettings::CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings, - const Model::NewTerminalArgs& newTerminalArgs, - const IKeyBindings& keybindings) + // Return Value: + // - A TerminalSettingsCreateResult object, which contains a pair of TerminalSettings + // objects. One for when the terminal is focused and one for when the terminal is unfocused. + Model::TerminalSettingsCreateResult TerminalSettings::CreateWithNewTerminalArgs(const CascadiaSettings& appSettings, + const NewTerminalArgs& newTerminalArgs, + const IKeyBindings& keybindings) { const guid profileGuid = appSettings.GetProfileForArgs(newTerminalArgs); - auto settings{ CreateWithProfileByID(appSettings, profileGuid, keybindings) }; + auto settingsPair{ CreateWithProfileByID(appSettings, profileGuid, keybindings) }; + auto defaultSettings = settingsPair.DefaultSettings(); if (newTerminalArgs) { // Override commandline, starting directory if they exist in newTerminalArgs if (!newTerminalArgs.Commandline().empty()) { - settings.Commandline(newTerminalArgs.Commandline()); + defaultSettings.Commandline(newTerminalArgs.Commandline()); } if (!newTerminalArgs.StartingDirectory().empty()) { - settings.StartingDirectory(newTerminalArgs.StartingDirectory()); + defaultSettings.StartingDirectory(newTerminalArgs.StartingDirectory()); } if (!newTerminalArgs.TabTitle().empty()) { - settings.StartingTitle(newTerminalArgs.TabTitle()); + defaultSettings.StartingTitle(newTerminalArgs.TabTitle()); } if (newTerminalArgs.TabColor()) { - settings.StartingTabColor(winrt::Windows::Foundation::IReference{ til::color{ newTerminalArgs.TabColor().Value() } }); + defaultSettings.StartingTabColor(winrt::Windows::Foundation::IReference{ til::color{ newTerminalArgs.TabColor().Value() } }); } if (newTerminalArgs.SuppressApplicationTitle()) { - settings.SuppressApplicationTitle(newTerminalArgs.SuppressApplicationTitle().Value()); + defaultSettings.SuppressApplicationTitle(newTerminalArgs.SuppressApplicationTitle().Value()); } if (!newTerminalArgs.ColorScheme().empty()) { const auto schemes = appSettings.GlobalSettings().ColorSchemes(); if (const auto& scheme = schemes.TryLookup(newTerminalArgs.ColorScheme())) { - settings.ApplyColorScheme(scheme); + defaultSettings.ApplyColorScheme(scheme); } } } - return settings; + return settingsPair; + } + + void TerminalSettings::_ApplyAppearanceSettings(const IAppearanceConfig& appearance, const Windows::Foundation::Collections::IMapView& schemes) + { + _CursorShape = appearance.CursorShape(); + _CursorHeight = appearance.CursorHeight(); + if (!appearance.ColorSchemeName().empty()) + { + if (const auto scheme = schemes.TryLookup(appearance.ColorSchemeName())) + { + ApplyColorScheme(scheme); + } + } + if (appearance.Foreground()) + { + _DefaultForeground = til::color{ appearance.Foreground().Value() }; + } + if (appearance.Background()) + { + _DefaultBackground = til::color{ appearance.Background().Value() }; + } + if (appearance.SelectionBackground()) + { + _SelectionBackground = til::color{ appearance.SelectionBackground().Value() }; + } + if (appearance.CursorColor()) + { + _CursorColor = til::color{ appearance.CursorColor().Value() }; + } + if (!appearance.BackgroundImagePath().empty()) + { + _BackgroundImage = appearance.ExpandedBackgroundImagePath(); + } + + _BackgroundImageOpacity = appearance.BackgroundImageOpacity(); + _BackgroundImageStretchMode = appearance.BackgroundImageStretchMode(); + std::tie(_BackgroundImageHorizontalAlignment, _BackgroundImageVerticalAlignment) = ConvertConvergedAlignment(appearance.BackgroundImageAlignment()); + + _RetroTerminalEffect = appearance.RetroTerminalEffect(); + _PixelShaderPath = winrt::hstring{ wil::ExpandEnvironmentStringsW(appearance.PixelShaderPath().c_str()) }; } // Method Description: - // - Creates a TerminalSettings object that inherits from a parent TerminalSettings - // Arguments:: - // - parent: the TerminalSettings object that the newly created TerminalSettings will inherit from + // - Creates a TerminalSettingsCreateResult from a parent TerminalSettingsCreateResult + // - The returned defaultSettings inherits from the parent's defaultSettings, and the + // returned unfocusedSettings inherits from the returned defaultSettings + // - Note that the unfocused settings needs to be entirely unchanged _except_ we need to + // set its parent to the other settings object that we return. This is because the overrides + // made by the control will live in that other settings object, so we want to make + // sure the unfocused settings inherit from that. + // - Another way to think about this is that initially we have UnfocusedSettings inherit + // from DefaultSettings. This function simply adds another TerminalSettings object + // in the middle of these two, so UnfocusedSettings now inherits from the new object + // and the new object inherits from the DefaultSettings. And this new object is what + // the control can put overrides in. + // Arguments: + // - parent: the TerminalSettingsCreateResult that we create a new one from // Return Value: - // - a newly created child of the given parent object - Model::TerminalSettings TerminalSettings::CreateWithParent(const Model::TerminalSettings& parent) + // - A TerminalSettingsCreateResult object that contains a defaultSettings that inherits + // from parent's defaultSettings, and contains an unfocusedSettings that inherits from + // its defaultSettings + Model::TerminalSettingsCreateResult TerminalSettings::CreateWithParent(const Model::TerminalSettingsCreateResult& parent) { THROW_HR_IF_NULL(E_INVALIDARG, parent); - auto parentImpl{ get_self(parent) }; - return *parentImpl->CreateChild(); + auto defaultImpl{ get_self(parent.DefaultSettings()) }; + auto defaultChild = defaultImpl->CreateChild(); + if (parent.UnfocusedSettings()) + { + parent.UnfocusedSettings().SetParent(*defaultChild); + } + return winrt::make(*defaultChild, parent.UnfocusedSettings()); } // Method Description: @@ -164,14 +238,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - schemes: a map of schemes to look for our color scheme in, if we have one. // Return Value: // - - void TerminalSettings::_ApplyProfileSettings(const Model::Profile& profile, const Windows::Foundation::Collections::IMapView& schemes) + void TerminalSettings::_ApplyProfileSettings(const Profile& profile) { // Fill in the Terminal Setting's CoreSettings from the profile _HistorySize = profile.HistorySize(); _SnapOnInput = profile.SnapOnInput(); _AltGrAliasing = profile.AltGrAliasing(); - _CursorHeight = profile.CursorHeight(); - _CursorShape = profile.CursorShape(); // Fill in the remaining properties from the profile _ProfileName = profile.Name(); @@ -196,44 +268,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _SuppressApplicationTitle = profile.SuppressApplicationTitle(); } - if (!profile.ColorSchemeName().empty()) - { - if (const auto scheme = schemes.TryLookup(profile.ColorSchemeName())) - { - ApplyColorScheme(scheme); - } - } - if (profile.Foreground()) - { - _DefaultForeground = til::color{ profile.Foreground().Value() }; - } - if (profile.Background()) - { - _DefaultBackground = til::color{ profile.Background().Value() }; - } - if (profile.SelectionBackground()) - { - _SelectionBackground = til::color{ profile.SelectionBackground().Value() }; - } - if (profile.CursorColor()) - { - _CursorColor = til::color{ profile.CursorColor().Value() }; - } - _ScrollState = profile.ScrollState(); - if (!profile.BackgroundImagePath().empty()) - { - _BackgroundImage = profile.ExpandedBackgroundImagePath(); - } - - _BackgroundImageOpacity = profile.BackgroundImageOpacity(); - _BackgroundImageStretchMode = profile.BackgroundImageStretchMode(); - std::tie(_BackgroundImageHorizontalAlignment, _BackgroundImageVerticalAlignment) = ConvertConvergedAlignment(profile.BackgroundImageAlignment()); - - _RetroTerminalEffect = profile.RetroTerminalEffect(); - _PixelShaderPath = winrt::hstring{ wil::ExpandEnvironmentStringsW(profile.PixelShaderPath().c_str()) }; - _AntialiasingMode = profile.AntialiasingMode(); if (profile.TabColor()) diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 8346b2afad1..796bcccd637 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -15,6 +15,7 @@ Author(s): #pragma once #include "TerminalSettings.g.h" +#include "TerminalSettingsCreateResult.g.h" #include "IInheritable.h" #include "../inc/cppwinrt_utils.h" #include @@ -28,19 +29,35 @@ namespace SettingsModelLocalTests namespace winrt::Microsoft::Terminal::Settings::Model::implementation { + struct TerminalSettingsCreateResult : + public TerminalSettingsCreateResultT + { + public: + TerminalSettingsCreateResult(Model::TerminalSettings defaultSettings, Model::TerminalSettings unfocusedSettings) : + _defaultSettings(defaultSettings), + _unfocusedSettings(unfocusedSettings) {} + + Model::TerminalSettings DefaultSettings() { return _defaultSettings; }; + Model::TerminalSettings UnfocusedSettings() { return _unfocusedSettings; }; + + private: + Model::TerminalSettings _defaultSettings; + Model::TerminalSettings _unfocusedSettings; + }; + struct TerminalSettings : TerminalSettingsT, IInheritable { TerminalSettings() = default; - static Model::TerminalSettings CreateWithProfileByID(const Model::CascadiaSettings& appSettings, - guid profileGuid, - const Control::IKeyBindings& keybindings); + static Model::TerminalSettingsCreateResult CreateWithProfileByID(const Model::CascadiaSettings& appSettings, + guid profileGuid, + const Control::IKeyBindings& keybindings); - static Model::TerminalSettings CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings, - const Model::NewTerminalArgs& newTerminalArgs, - const Control::IKeyBindings& keybindings); + static Model::TerminalSettingsCreateResult CreateWithNewTerminalArgs(const Model::CascadiaSettings& appSettings, + const Model::NewTerminalArgs& newTerminalArgs, + const Control::IKeyBindings& keybindings); - static Model::TerminalSettings CreateWithParent(const Model::TerminalSettings& parent); + static Model::TerminalSettingsCreateResult CreateWithParent(const Model::TerminalSettingsCreateResult& parent); void SetParent(const Model::TerminalSettings& parent); void ApplyColorScheme(const Model::ColorScheme& scheme); @@ -123,8 +140,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation private: std::optional> _ColorTable; gsl::span _getColorTableImpl(); - void _ApplyProfileSettings(const Model::Profile& profile, const Windows::Foundation::Collections::IMapView& schemes); + void _ApplyProfileSettings(const Model::Profile& profile); + void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept; + void _ApplyAppearanceSettings(const Microsoft::Terminal::Settings::Model::IAppearanceConfig& appearance, + const Windows::Foundation::Collections::IMapView& schemes); friend class SettingsModelLocalTests::TerminalSettingsTests; }; diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl index 2a8583b58e3..0585fa828d8 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl @@ -5,6 +5,12 @@ import "CascadiaSettings.idl"; namespace Microsoft.Terminal.Settings.Model { + runtimeclass TerminalSettingsCreateResult + { + TerminalSettings DefaultSettings { get; }; + TerminalSettings UnfocusedSettings { get; }; + } + // Class Description: // TerminalSettings encapsulates all settings that control the // TermControl's behavior. In these settings there is both the entirety @@ -19,12 +25,11 @@ namespace Microsoft.Terminal.Settings.Model { TerminalSettings(); - static TerminalSettings CreateWithProfileByID(CascadiaSettings appSettings, Guid profileGuid, Microsoft.Terminal.Control.IKeyBindings keybindings); - static TerminalSettings CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings); - static TerminalSettings CreateWithParent(TerminalSettings parent); + static TerminalSettingsCreateResult CreateWithProfileByID(CascadiaSettings appSettings, Guid profileGuid, Microsoft.Terminal.Control.IKeyBindings keybindings); + static TerminalSettingsCreateResult CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings); + static TerminalSettingsCreateResult CreateWithParent(TerminalSettingsCreateResult parent); void SetParent(TerminalSettings parent); void ApplyColorScheme(ColorScheme scheme); }; - } diff --git a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp index 3cd4b7b0d95..7172bdcc7f8 100644 --- a/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp +++ b/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp @@ -127,7 +127,7 @@ std::vector WslDistroGenerator::GenerateProfiles() auto WSLDistro{ CreateDefaultProfile(distName) }; WSLDistro.Commandline(L"wsl.exe -d " + distName); - WSLDistro.ColorSchemeName(L"Campbell"); + WSLDistro.DefaultAppearance().ColorSchemeName(L"Campbell"); WSLDistro.StartingDirectory(DEFAULT_STARTING_DIRECTORY); WSLDistro.Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png"); profiles.emplace_back(WSLDistro); diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h index face736ea45..97953866a3c 100644 --- a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -12,7 +12,7 @@ using namespace winrt::Microsoft::Terminal::Core; namespace TerminalCoreUnitTests { - class MockTermSettings : public winrt::implements + class MockTermSettings : public winrt::implements { public: MockTermSettings(int32_t historySize, int32_t initialRows, int32_t initialCols) : @@ -40,6 +40,7 @@ namespace TerminalCoreUnitTests bool SuppressApplicationTitle() { return _suppressApplicationTitle; } til::color SelectionBackground() { return COLOR_WHITE; } bool ForceVTInput() { return false; } + ICoreAppearance UnfocusedAppearance() { return {}; }; winrt::Windows::Foundation::IReference TabColor() { return nullptr; } winrt::Windows::Foundation::IReference StartingTabColor() { return nullptr; } @@ -64,6 +65,7 @@ namespace TerminalCoreUnitTests void SuppressApplicationTitle(bool suppressApplicationTitle) { _suppressApplicationTitle = suppressApplicationTitle; } void SelectionBackground(til::color) {} void ForceVTInput(bool) {} + void UnfocusedAppearance(ICoreAppearance) {} void TabColor(const IInspectable&) {} void StartingTabColor(const IInspectable&) {}