Make Global and Profile settings inheritable (#7923)

## Summary of the Pull Request
Introduces `IInheritable` as an interface that helps move cascading settings into the Terminal Settings Model. `GlobalAppSettings` and `Profile` both are now `IInheritable`. `CascadiaSettings` was updated to `CreateChild()` for globals and each profile when we are loading the JSON data.

IInheritable does most of the heavy lifting. It introduces a two new macros and the interface. The macros help implement the fallback functionality for nullable and non-nullable settings.

## References
#7876 - Spec Addendum
#6904 - TSM Spec
#1564 - Settings UI

#7876 - `Copy()` needs to be updated to include _parent
This commit is contained in:
Carlos Zamora 2020-10-27 10:35:09 -07:00 committed by GitHub
parent 7e8600147e
commit b603929214
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1055 additions and 301 deletions

View file

@ -21,6 +21,7 @@ ICustom
IDialog
IDirect
IExplorer
IInheritable
IMap
IObject
IStorage

View file

@ -81,6 +81,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestRebindNestedCommand);
TEST_METHOD(TestCopy);
TEST_METHOD(TestCloneInheritanceTree);
TEST_CLASS_SETUP(ClassSetup)
{
@ -831,7 +832,7 @@ namespace SettingsModelLocalTests
const auto serialized0Profile = profile0->GenerateStub();
const auto profile1 = implementation::Profile::FromJson(serialized0Profile);
VERIFY_IS_FALSE(profile0->HasGuid());
VERIFY_IS_FALSE(profile1->HasGuid());
VERIFY_IS_TRUE(profile1->HasGuid());
auto settings = winrt::make_self<implementation::CascadiaSettings>();
settings->_profiles.Append(*profile1);
@ -1355,6 +1356,7 @@ namespace SettingsModelLocalTests
const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-6666-49a3-80bd-e8fdd045185c}") };
const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{2C4DE342-38B7-51CF-B940-2309A097F518}") };
const winrt::guid fakeGuid{ ::Microsoft::Console::Utils::GuidFromString(L"{FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF}") };
const winrt::guid autogeneratedGuid{ implementation::Profile::_GenerateGuidForProfile(name3, L"") };
const std::optional<winrt::guid> badGuid{};
VerifyParseSucceeded(settings0String);
@ -1366,7 +1368,7 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(guid0, settings->_GetProfileGuidByName(name0));
VERIFY_ARE_EQUAL(guid1, settings->_GetProfileGuidByName(name1));
VERIFY_ARE_EQUAL(guid2, settings->_GetProfileGuidByName(name2));
VERIFY_ARE_EQUAL(badGuid, settings->_GetProfileGuidByName(name3));
VERIFY_ARE_EQUAL(autogeneratedGuid, settings->_GetProfileGuidByName(name3));
VERIFY_ARE_EQUAL(badGuid, settings->_GetProfileGuidByName(badName));
auto prof0{ settings->FindProfile(guid0) };
@ -1521,9 +1523,9 @@ namespace SettingsModelLocalTests
{
auto settings = winrt::make_self<implementation::CascadiaSettings>(false);
settings->_ParseJsonString(settings0String, false);
VERIFY_IS_TRUE(settings->_userDefaultProfileSettings == Json::Value::null);
VERIFY_IS_NULL(settings->_userDefaultProfileSettings);
settings->_ApplyDefaultsFromUserSettings();
VERIFY_IS_FALSE(settings->_userDefaultProfileSettings == Json::Value::null);
VERIFY_IS_NOT_NULL(settings->_userDefaultProfileSettings);
settings->LayerJson(settings->_userSettings);
VERIFY_ARE_EQUAL(guid1String, settings->_globals->UnparsedDefaultProfile());
@ -1573,9 +1575,9 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(2u, settings->_profiles.Size());
settings->_ParseJsonString(settings0String, false);
VERIFY_IS_TRUE(settings->_userDefaultProfileSettings == Json::Value::null);
VERIFY_IS_NULL(settings->_userDefaultProfileSettings);
settings->_ApplyDefaultsFromUserSettings();
VERIFY_IS_FALSE(settings->_userDefaultProfileSettings == Json::Value::null);
VERIFY_IS_NOT_NULL(settings->_userDefaultProfileSettings);
Log::Comment(NoThrowString().Format(
L"Ensure that cmd and powershell don't get their GUIDs overwritten"));
@ -1692,10 +1694,6 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", settings->_profiles.GetAt(1).Source());
VERIFY_ARE_EQUAL(L"Terminal.App.UnitTest.1", settings->_profiles.GetAt(2).Source());
VERIFY_IS_TRUE(settings->_profiles.GetAt(0).HasGuid());
VERIFY_IS_TRUE(settings->_profiles.GetAt(1).HasGuid());
VERIFY_IS_TRUE(settings->_profiles.GetAt(2).HasGuid());
VERIFY_ARE_EQUAL(guid1, settings->_profiles.GetAt(0).Guid());
VERIFY_ARE_EQUAL(guid1, settings->_profiles.GetAt(1).Guid());
VERIFY_ARE_EQUAL(guid2, settings->_profiles.GetAt(2).Guid());
@ -2256,6 +2254,7 @@ namespace SettingsModelLocalTests
settings->_ParseJsonString(settings1Json, false);
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
commands = settings->_globals->Commands();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(0u, commands.Size());
@ -2350,6 +2349,7 @@ namespace SettingsModelLocalTests
settings->_ParseJsonString(settings1Json, false);
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
commands = settings->_globals->Commands();
_logCommandNames(commands);
VERIFY_ARE_EQUAL(0u, settings->_warnings.Size());
VERIFY_ARE_EQUAL(1u, commands.Size());
@ -2460,4 +2460,126 @@ namespace SettingsModelLocalTests
copyImpl->_globals->WordDelimiters(L"changed value");
VERIFY_ARE_NOT_EQUAL(settings->_globals->WordDelimiters(), copyImpl->_globals->WordDelimiters());
}
void DeserializationTests::TestCloneInheritanceTree()
{
const std::string settingsJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
{
"defaults": {
"name": "PROFILE DEFAULTS"
},
"list": [
{
"guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"name": "CMD"
},
{
"guid": "{61c54bbd-2222-5271-96e7-009a87ff44bf}",
"name": "PowerShell"
},
{
"guid": "{61c54bbd-3333-5271-96e7-009a87ff44bf}"
}
]
}
})" };
VerifyParseSucceeded(settingsJson);
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
settings->_ParseJsonString(settingsJson, false);
settings->_ApplyDefaultsFromUserSettings();
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
const auto copy{ settings->Copy() };
const auto copyImpl{ winrt::get_self<implementation::CascadiaSettings>(copy) };
// test globals
VERIFY_ARE_EQUAL(settings->_globals->DefaultProfile(), copyImpl->_globals->DefaultProfile());
// test profiles
VERIFY_ARE_EQUAL(settings->_profiles.Size(), copyImpl->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_profiles.GetAt(0).Name(), copyImpl->_profiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(settings->_profiles.GetAt(1).Name(), copyImpl->_profiles.GetAt(1).Name());
VERIFY_ARE_EQUAL(settings->_profiles.GetAt(2).Name(), copyImpl->_profiles.GetAt(2).Name());
VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->Name(), copyImpl->_userDefaultProfileSettings->Name());
// Modifying profile.defaults should...
VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->HasName(), copyImpl->_userDefaultProfileSettings->HasName());
copyImpl->_userDefaultProfileSettings->Name(L"changed value");
// ...keep the same name for the first two profiles
VERIFY_ARE_EQUAL(settings->_profiles.Size(), copyImpl->_profiles.Size());
VERIFY_ARE_EQUAL(settings->_profiles.GetAt(0).Name(), copyImpl->_profiles.GetAt(0).Name());
VERIFY_ARE_EQUAL(settings->_profiles.GetAt(1).Name(), copyImpl->_profiles.GetAt(1).Name());
// ...but change the name for the one that inherited it from profile.defaults
VERIFY_ARE_NOT_EQUAL(settings->_profiles.GetAt(2).Name(), copyImpl->_profiles.GetAt(2).Name());
// profile.defaults should be different between the two graphs
VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings->HasName(), copyImpl->_userDefaultProfileSettings->HasName());
VERIFY_ARE_NOT_EQUAL(settings->_userDefaultProfileSettings->Name(), copyImpl->_userDefaultProfileSettings->Name());
Log::Comment(L"Test empty profiles.defaults");
const std::string emptyPDJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
{
"defaults": {
},
"list": [
{
"guid": "{61c54bbd-2222-5271-96e7-009a87ff44bf}",
"name": "PowerShell"
}
]
}
})" };
const std::string missingPDJson{ R"(
{
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"profiles":
[
{
"guid": "{61c54bbd-2222-5271-96e7-009a87ff44bf}",
"name": "PowerShell"
}
]
})" };
auto verifyEmptyPD = [this](const std::string json) {
VerifyParseSucceeded(json);
auto settings{ winrt::make_self<implementation::CascadiaSettings>() };
settings->_ParseJsonString(json, false);
settings->_ApplyDefaultsFromUserSettings();
settings->LayerJson(settings->_userSettings);
settings->_ValidateSettings();
const auto copy{ settings->Copy() };
const auto copyImpl{ winrt::get_self<implementation::CascadiaSettings>(copy) };
// test optimization: if we don't have profiles.defaults, don't add it to the tree
VERIFY_IS_NULL(settings->_userDefaultProfileSettings);
VERIFY_ARE_EQUAL(settings->_userDefaultProfileSettings, copyImpl->_userDefaultProfileSettings);
VERIFY_ARE_EQUAL(settings->Profiles().Size(), 1u);
VERIFY_ARE_EQUAL(settings->Profiles().Size(), copyImpl->Profiles().Size());
// so we should only have one parent, instead of two
auto srcProfile{ winrt::get_self<implementation::Profile>(settings->Profiles().GetAt(0)) };
auto copyProfile{ winrt::get_self<implementation::Profile>(copyImpl->Profiles().GetAt(0)) };
VERIFY_ARE_EQUAL(srcProfile->Parents().size(), 0u);
VERIFY_ARE_EQUAL(srcProfile->Parents().size(), copyProfile->Parents().size());
};
verifyEmptyPD(emptyPDJson);
verifyEmptyPD(missingPDJson);
}
}

View file

@ -87,7 +87,7 @@ namespace SettingsModelLocalTests
// A profile _can_ be layered with itself, though what's the point?
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile1Json));
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile2Json));
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile3Json));
VERIFY_IS_TRUE(profile3->ShouldBeLayered(profile3Json));
}
void ProfileTests::LayerProfileProperties()
@ -132,39 +132,41 @@ namespace SettingsModelLocalTests
Log::Comment(NoThrowString().Format(
L"Layering profile1 on top of profile0"));
profile0->LayerJson(profile1Json);
auto profile1{ profile0->CreateChild() };
profile1->LayerJson(profile1Json);
VERIFY_IS_NOT_NULL(profile0->Foreground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile0->Foreground().Value() });
VERIFY_IS_NOT_NULL(profile1->Foreground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile1->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(profile1->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
VERIFY_IS_NOT_NULL(profile0->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile0->Background().Value() });
VERIFY_IS_NOT_NULL(profile1->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile1->Background().Value() });
VERIFY_ARE_EQUAL(L"profile1", profile0->Name());
VERIFY_ARE_EQUAL(L"profile1", profile1->Name());
VERIFY_IS_FALSE(profile0->StartingDirectory().empty());
VERIFY_ARE_EQUAL(L"C:/", profile0->StartingDirectory());
VERIFY_IS_FALSE(profile1->StartingDirectory().empty());
VERIFY_ARE_EQUAL(L"C:/", profile1->StartingDirectory());
Log::Comment(NoThrowString().Format(
L"Layering profile2 on top of (profile0+profile1)"));
profile0->LayerJson(profile2Json);
auto profile2{ profile1->CreateChild() };
profile2->LayerJson(profile2Json);
VERIFY_IS_NOT_NULL(profile0->Foreground());
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile0->Foreground().Value() });
VERIFY_IS_NOT_NULL(profile2->Foreground());
VERIFY_ARE_EQUAL(til::color(3, 3, 3), til::color{ profile2->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(profile2->Background());
VERIFY_ARE_EQUAL(til::color(1, 1, 1), til::color{ profile2->Background().Value() });
VERIFY_IS_NOT_NULL(profile0->SelectionBackground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile0->SelectionBackground().Value() });
VERIFY_IS_NOT_NULL(profile2->SelectionBackground());
VERIFY_ARE_EQUAL(til::color(2, 2, 2), til::color{ profile2->SelectionBackground().Value() });
VERIFY_ARE_EQUAL(L"profile2", profile0->Name());
VERIFY_ARE_EQUAL(L"profile2", profile2->Name());
VERIFY_IS_FALSE(profile0->StartingDirectory().empty());
VERIFY_ARE_EQUAL(L"C:/", profile0->StartingDirectory());
VERIFY_IS_FALSE(profile2->StartingDirectory().empty());
VERIFY_ARE_EQUAL(L"C:/", profile2->StartingDirectory());
}
void ProfileTests::LayerProfileIcon()

View file

@ -499,6 +499,7 @@ namespace TerminalAppLocalTests
const std::string settings0String{ R"(
{
"defaultProfile": "profile5",
"profiles": [
{
"name" : "profile0",

View file

@ -756,17 +756,10 @@ namespace winrt::TerminalApp::implementation
TerminalConnection::ITerminalConnection connection{ nullptr };
winrt::guid connectionType{};
winrt::guid connectionType = profile.ConnectionType();
winrt::guid sessionGuid{};
const auto hasConnectionType = profile.HasConnectionType();
if (hasConnectionType)
{
connectionType = profile.ConnectionType();
}
if (hasConnectionType &&
connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
if (connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
{
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better

View file

@ -94,10 +94,7 @@ namespace winrt::TerminalApp::implementation
_Commandline = profile.Commandline();
if (!profile.StartingDirectory().empty())
{
_StartingDirectory = profile.EvaluatedStartingDirectory();
}
_StartingDirectory = profile.EvaluatedStartingDirectory();
// GH#2373: Use the tabTitle as the starting title if it exists, otherwise
// use the profile name

View file

@ -71,11 +71,6 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
// dynamic profile generators added by default
auto settings{ winrt::make_self<CascadiaSettings>() };
settings->_globals = _globals->Copy();
for (const auto profile : _profiles)
{
auto profImpl{ winrt::get_self<Profile>(profile) };
settings->_profiles.Append(*profImpl->Copy());
}
for (auto warning : _warnings)
{
settings->_warnings.Append(warning);
@ -85,10 +80,54 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
settings->_userSettingsString = _userSettingsString;
settings->_userSettings = _userSettings;
settings->_defaultSettings = _defaultSettings;
settings->_userDefaultProfileSettings = _userDefaultProfileSettings;
_CopyProfileInheritanceTree(settings);
return *settings;
}
// Method Description:
// - Copies the inheritance tree for profiles and hooks them up to a clone CascadiaSettings
// Arguments:
// - cloneSettings: the CascadiaSettings we're copying the inheritance tree to
// Return Value:
// - <none>
void CascadiaSettings::_CopyProfileInheritanceTree(winrt::com_ptr<CascadiaSettings>& cloneSettings) const
{
// Our profiles inheritance graph doesn't have a formal root.
// However, if we create a dummy Profile, and set _profiles as the parent,
// we now have a root. So we'll do just that, then copy the inheritance graph
// from the dummyRoot.
auto dummyRootSource{ winrt::make_self<Profile>() };
for (const auto& profile : _profiles)
{
winrt::com_ptr<Profile> profileImpl;
profileImpl.copy_from(winrt::get_self<Profile>(profile));
dummyRootSource->InsertParent(profileImpl);
}
auto dummyRootClone{ winrt::make_self<Profile>() };
std::unordered_map<void*, winrt::com_ptr<Profile>> visited{};
if (_userDefaultProfileSettings)
{
// profile.defaults must be saved to CascadiaSettings
// So let's do that manually first, and add that to visited
cloneSettings->_userDefaultProfileSettings = Profile::CopySettings(_userDefaultProfileSettings);
visited[_userDefaultProfileSettings.get()] = cloneSettings->_userDefaultProfileSettings;
}
Profile::CloneInheritanceGraph(dummyRootSource, dummyRootClone, visited);
// All of the parents of the dummy root clone are _profiles.
// Get the parents and add them to the settings clone.
const auto cloneParents{ dummyRootClone->Parents() };
for (const auto& profile : cloneParents)
{
cloneSettings->_profiles.Append(*profile);
}
}
// Method Description:
// - Finds a profile that matches the given GUID. If there is no profile in this
// settings object that matches, returns nullptr.

View file

@ -102,10 +102,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::string _userSettingsString;
Json::Value _userSettings;
Json::Value _defaultSettings;
Json::Value _userDefaultProfileSettings{ Json::Value::null };
winrt::com_ptr<Profile> _userDefaultProfileSettings{ nullptr };
void _LayerOrCreateProfile(const Json::Value& profileJson);
winrt::com_ptr<implementation::Profile> _FindMatchingProfile(const Json::Value& profileJson);
std::optional<uint32_t> _FindMatchingProfileIndex(const Json::Value& profileJson);
void _LayerOrCreateColorScheme(const Json::Value& schemeJson);
winrt::com_ptr<implementation::ColorScheme> _FindMatchingColorScheme(const Json::Value& schemeJson);
void _ParseJsonString(std::string_view fileData, const bool isDefaultSettings);
@ -114,6 +115,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
bool _PrependSchemaDirective();
bool _AppendDynamicProfilesToUserSettings();
std::string _ApplyFirstRunChangesToSettingsTemplate(std::string_view settingsTemplate) const;
void _CopyProfileInheritanceTree(com_ptr<CascadiaSettings>& cloneSettings) const;
void _ApplyDefaultsFromUserSettings();

View file

@ -519,20 +519,6 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
for (const auto& profile : _profiles)
{
if (!profile.HasGuid())
{
// If the profile doesn't have a guid, it's a name-only profile.
// During validation, we'll generate a GUID for the profile, but
// validation occurs after this. We should ignore these types of
// profiles.
// If a dynamic profile was generated _without_ a GUID, we also
// don't want it serialized here. The first check in
// Profile::ShouldBeLayered checks that the profile has a guid. For a
// dynamic profile without a GUID, that'll _never_ be true, so it
// would be impossible to be layered.
continue;
}
// Skip profiles that are in the user settings or the default settings.
if (isInJsonObj(profile, _userSettings) || isInJsonObj(profile, _defaultSettings))
{
@ -598,6 +584,8 @@ winrt::com_ptr<CascadiaSettings> CascadiaSettings::FromJson(const Json::Value& j
// <none>
void CascadiaSettings::LayerJson(const Json::Value& json)
{
// add a new inheritance layer, and apply json values to child
_globals = _globals->CreateChild();
_globals->LayerJson(json);
if (auto schemes{ json[SchemesKey.data()] })
@ -635,10 +623,28 @@ void CascadiaSettings::LayerJson(const Json::Value& json)
void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson)
{
// Layer the json on top of an existing profile, if we have one:
auto pProfile = _FindMatchingProfile(profileJson);
if (pProfile)
auto profileIndex{ _FindMatchingProfileIndex(profileJson) };
if (profileIndex)
{
pProfile->LayerJson(profileJson);
auto parentProj{ _profiles.GetAt(*profileIndex) };
auto parent{ winrt::get_self<Profile>(parentProj) };
if (_userDefaultProfileSettings)
{
// We don't actually need to CreateChild() here.
// When we loaded Profile.Defaults, we created an empty child already.
// So this just populates the empty child
parent->LayerJson(profileJson);
}
else
{
// otherwise, add a new inheritance layer
auto childImpl{ parent->CreateChild() };
childImpl->LayerJson(profileJson);
// replace parent in _profiles with child
_profiles.SetAt(*profileIndex, *childImpl);
}
}
else
{
@ -647,13 +653,13 @@ void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson)
// `source`. Dynamic profiles _must_ be layered on an existing profile.
if (!Profile::IsDynamicProfileObject(profileJson))
{
auto profile = winrt::make_self<Profile>();
auto profile{ winrt::make_self<Profile>() };
// GH#2325: If we have a set of default profile settings, apply them here.
// GH#2325: If we have a set of default profile settings, set that as my parent.
// We _won't_ have these settings yet for defaults, dynamic profiles.
if (_userDefaultProfileSettings)
{
profile->LayerJson(_userDefaultProfileSettings);
profile->InsertParent(0, _userDefaultProfileSettings);
}
profile->LayerJson(profileJson);
@ -675,17 +681,40 @@ void CascadiaSettings::_LayerOrCreateProfile(const Json::Value& profileJson)
// profile exists.
winrt::com_ptr<Profile> CascadiaSettings::_FindMatchingProfile(const Json::Value& profileJson)
{
for (auto profile : _profiles)
auto index{ _FindMatchingProfileIndex(profileJson) };
if (index)
{
auto profileImpl = winrt::get_self<Profile>(profile);
if (profileImpl->ShouldBeLayered(profileJson))
{
return profileImpl->get_strong();
}
auto profile{ _profiles.GetAt(*index) };
auto profileImpl{ winrt::get_self<Profile>(profile) };
return profileImpl->get_strong();
}
return nullptr;
}
// Method Description:
// - Finds a profile from our list of profiles that matches the given json
// object. Uses Profile::ShouldBeLayered to determine if the Json::Value is a
// match or not. This method should be used to find a profile to layer the
// given settings upon.
// - Returns nullopt if no such match exists.
// Arguments:
// - json: an object which may be a partial serialization of a Profile object.
// Return Value:
// - The index for the matching Profile, iff it exists. Otherwise, nullopt.
std::optional<uint32_t> CascadiaSettings::_FindMatchingProfileIndex(const Json::Value& profileJson)
{
for (uint32_t i = 0; i < _profiles.Size(); ++i)
{
const auto profile{ _profiles.GetAt(i) };
const auto profileImpl = winrt::get_self<Profile>(profile);
if (profileImpl->ShouldBeLayered(profileJson))
{
return i;
}
}
return std::nullopt;
}
// Method Description:
// - Finds the "default profile settings" if they exist in the users settings,
// and applies them to the existing profiles. The "default profile settings"
@ -704,7 +733,7 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
auto defaultSettings{ Json::Value::null };
if (const auto profiles{ _userSettings[JsonKey(ProfilesKey)] })
{
if (profiles.isObject())
if (profiles.isObject() && !profiles[JsonKey(DefaultSettingsKey)].empty())
{
defaultSettings = profiles[JsonKey(DefaultSettingsKey)];
}
@ -714,16 +743,26 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
// from user settings file
if (defaultSettings)
{
_userDefaultProfileSettings = defaultSettings;
// Remove the `guid` member from the default settings. That'll
// hyper-explode, so just don't let them do that.
_userDefaultProfileSettings.removeMember({ "guid" });
defaultSettings.removeMember({ "guid" });
for (auto profile : _profiles)
_userDefaultProfileSettings = winrt::make_self<Profile>();
_userDefaultProfileSettings->LayerJson(defaultSettings);
const auto numOfProfiles{ _profiles.Size() };
for (uint32_t profileIndex = 0; profileIndex < numOfProfiles; ++profileIndex)
{
auto profileImpl = winrt::get_self<Profile>(profile);
profileImpl->LayerJson(_userDefaultProfileSettings);
// create a child, so we inherit from the defaults.json layer
auto parentProj{ _profiles.GetAt(profileIndex) };
auto parentImpl{ winrt::get_self<Profile>(parentProj) };
auto childImpl{ parentImpl->CreateChild() };
// Add profile.defaults as the _first_ parent to the child
childImpl->InsertParent(0, _userDefaultProfileSettings);
// replace parent in _profiles with child
_profiles.SetAt(profileIndex, *childImpl);
}
}
}

View file

@ -19,6 +19,7 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CreateDefaultProfile(const
{
const winrt::guid profileGuid{ Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID,
gsl::as_bytes(gsl::make_span(name))) };
auto newProfile = winrt::make<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile>(profileGuid);
newProfile.Name(name);

View file

@ -16,6 +16,9 @@ Author(s):
#include "Profile.h"
// !!! LOAD-BEARING
// If you change or delete this GUID, all dynamic profiles
// will become disconnected from user settings.
// {2bde4a90-d05f-401c-9492-e40884ead1d8}
// uuidv5 properties: name format is UTF-16LE bytes
static constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID = { 0x2bde4a90, 0xd05f, 0x401c, { 0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8 } };

View file

@ -55,7 +55,7 @@ static constexpr bool debugFeaturesDefault{ false };
GlobalAppSettings::GlobalAppSettings() :
_keymap{ winrt::make_self<KeyMapping>() },
_keybindingsWarnings{},
_unparsedDefaultProfile{},
_validDefaultProfile{ false },
_defaultProfile{},
_DebugFeaturesEnabled{ debugFeaturesDefault }
{
@ -63,6 +63,25 @@ GlobalAppSettings::GlobalAppSettings() :
_colorSchemes = winrt::single_threaded_map<winrt::hstring, Model::ColorScheme>();
}
// Method Description:
// - Copies any extraneous data from the parent before completing a CreateChild call
// Arguments:
// - <none>
// Return Value:
// - <none>
void GlobalAppSettings::_FinalizeInheritance()
{
// Globals only ever has 1 parent
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
{
_keymap = std::move(parent->_keymap);
_keybindingsWarnings = std::move(parent->_keybindingsWarnings);
_colorSchemes = std::move(parent->_colorSchemes);
_commands = std::move(parent->_commands);
}
}
winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
{
auto globals{ winrt::make_self<GlobalAppSettings>() };
@ -91,21 +110,38 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_UseTabSwitcher = _UseTabSwitcher;
globals->_DisableAnimations = _DisableAnimations;
globals->_unparsedDefaultProfile = _unparsedDefaultProfile;
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
globals->_validDefaultProfile = _validDefaultProfile;
globals->_defaultProfile = _defaultProfile;
globals->_keymap = _keymap->Copy();
if (_keymap)
{
globals->_keymap = _keymap->Copy();
}
std::copy(_keybindingsWarnings.begin(), _keybindingsWarnings.end(), std::back_inserter(globals->_keybindingsWarnings));
for (auto kv : _colorSchemes)
if (_colorSchemes)
{
const auto schemeImpl{ winrt::get_self<ColorScheme>(kv.Value()) };
globals->_colorSchemes.Insert(kv.Key(), *schemeImpl->Copy());
for (auto kv : _colorSchemes)
{
const auto schemeImpl{ winrt::get_self<ColorScheme>(kv.Value()) };
globals->_colorSchemes.Insert(kv.Key(), *schemeImpl->Copy());
}
}
for (auto kv : _commands)
if (_commands)
{
const auto commandImpl{ winrt::get_self<Command>(kv.Value()) };
globals->_commands.Insert(kv.Key(), *commandImpl->Copy());
for (auto kv : _commands)
{
const auto commandImpl{ winrt::get_self<Command>(kv.Value()) };
globals->_commands.Insert(kv.Key(), *commandImpl->Copy());
}
}
// Globals only ever has 1 parent
FAIL_FAST_IF(_parents.size() > 1);
for (auto parent : _parents)
{
globals->InsertParent(parent->Copy());
}
return globals;
}
@ -115,24 +151,71 @@ winrt::Windows::Foundation::Collections::IMapView<winrt::hstring, winrt::Microso
return _colorSchemes.GetView();
}
#pragma region DefaultProfile
void GlobalAppSettings::DefaultProfile(const winrt::guid& defaultProfile) noexcept
{
_unparsedDefaultProfile.clear();
_validDefaultProfile = true;
_defaultProfile = defaultProfile;
}
winrt::guid GlobalAppSettings::DefaultProfile() const
{
// If we have an unresolved default profile, we should likely explode.
THROW_HR_IF(E_INVALIDARG, !_unparsedDefaultProfile.empty());
THROW_HR_IF(E_INVALIDARG, !_validDefaultProfile);
return _defaultProfile;
}
bool GlobalAppSettings::HasUnparsedDefaultProfile() const
{
return _UnparsedDefaultProfile.has_value();
}
winrt::hstring GlobalAppSettings::UnparsedDefaultProfile() const
{
return _unparsedDefaultProfile;
const auto val{ _getUnparsedDefaultProfileImpl() };
return val ? *val : hstring{ L"" };
}
void GlobalAppSettings::UnparsedDefaultProfile(const hstring& value)
{
if (_UnparsedDefaultProfile != value)
{
_UnparsedDefaultProfile = value;
_validDefaultProfile = false;
}
}
void GlobalAppSettings::ClearUnparsedDefaultProfile()
{
if (HasUnparsedDefaultProfile())
{
_UnparsedDefaultProfile = std::nullopt;
}
}
std::optional<winrt::hstring> GlobalAppSettings::_getUnparsedDefaultProfileImpl() const
{
/*return user set value*/
if (_UnparsedDefaultProfile)
{
return _UnparsedDefaultProfile;
}
/*user set value was not set*/
/*iterate through parents to find a value*/
for (auto parent : _parents)
{
if (auto val{ parent->_getUnparsedDefaultProfileImpl() })
{
return val;
}
}
/*no value was found*/
return std::nullopt;
}
#pragma endregion
winrt::Microsoft::Terminal::Settings::Model::KeyMapping GlobalAppSettings::KeyMap() const noexcept
{
return *_keymap;
@ -153,7 +236,11 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::FromJson(const Json::Value&
void GlobalAppSettings::LayerJson(const Json::Value& json)
{
JsonUtils::GetValueForKey(json, DefaultProfileKey, _unparsedDefaultProfile);
// _validDefaultProfile keeps track of whether we've verified that DefaultProfile points to something
// CascadiaSettings::_ResolveDefaultProfile performs a validation and updates DefaultProfile() with the
// resolved value, then making it valid.
_validDefaultProfile = false;
JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
JsonUtils::GetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs);

View file

@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- CascadiaSettings.hpp
- GlobalAppSettings.h
Abstract:
- This class encapsulates all of the settings that are global to the app, and
@ -16,6 +16,7 @@ Author(s):
#pragma once
#include "GlobalAppSettings.g.h"
#include "IInheritable.h"
#include "KeyMapping.h"
#include "Command.h"
@ -30,10 +31,11 @@ namespace SettingsModelLocalTests
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct GlobalAppSettings : GlobalAppSettingsT<GlobalAppSettings>
struct GlobalAppSettings : GlobalAppSettingsT<GlobalAppSettings>, IInheritable<GlobalAppSettings>
{
public:
GlobalAppSettings();
void _FinalizeInheritance() override;
com_ptr<GlobalAppSettings> Copy() const;
Windows::Foundation::Collections::IMapView<hstring, Model::ColorScheme> ColorSchemes() noexcept;
@ -52,36 +54,40 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// by higher layers in the app.
void DefaultProfile(const guid& defaultProfile) noexcept;
guid DefaultProfile() const;
hstring UnparsedDefaultProfile() const;
bool HasUnparsedDefaultProfile() const;
winrt::hstring UnparsedDefaultProfile() const;
void UnparsedDefaultProfile(const hstring& value);
void ClearUnparsedDefaultProfile();
GETSET_PROPERTY(int32_t, InitialRows, DEFAULT_ROWS);
GETSET_PROPERTY(int32_t, InitialCols, DEFAULT_COLS);
GETSET_PROPERTY(bool, AlwaysShowTabs, true);
GETSET_PROPERTY(bool, ShowTitleInTitlebar, true);
GETSET_PROPERTY(bool, ConfirmCloseAllTabs, true);
GETSET_PROPERTY(winrt::Windows::UI::Xaml::ElementTheme, Theme, winrt::Windows::UI::Xaml::ElementTheme::Default);
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal);
GETSET_PROPERTY(bool, ShowTabsInTitlebar, true);
GETSET_PROPERTY(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
GETSET_PROPERTY(bool, CopyOnSelect, false);
GETSET_PROPERTY(winrt::Microsoft::Terminal::TerminalControl::CopyFormat, CopyFormatting, 0);
GETSET_PROPERTY(bool, WarnAboutLargePaste, true);
GETSET_PROPERTY(bool, WarnAboutMultiLinePaste, true);
GETSET_PROPERTY(Model::LaunchPosition, InitialPosition, nullptr, nullptr);
GETSET_PROPERTY(Model::LaunchMode, LaunchMode, LaunchMode::DefaultMode);
GETSET_PROPERTY(bool, SnapToGridOnResize, true);
GETSET_PROPERTY(bool, ForceFullRepaintRendering, false);
GETSET_PROPERTY(bool, SoftwareRendering, false);
GETSET_PROPERTY(bool, ForceVTInput, false);
GETSET_PROPERTY(bool, DebugFeaturesEnabled); // default value set in constructor
GETSET_PROPERTY(bool, StartOnUserLogin, false);
GETSET_PROPERTY(bool, AlwaysOnTop, false);
GETSET_PROPERTY(bool, UseTabSwitcher, true);
GETSET_PROPERTY(bool, DisableAnimations, false);
GETSET_SETTING(int32_t, InitialRows, DEFAULT_ROWS);
GETSET_SETTING(int32_t, InitialCols, DEFAULT_COLS);
GETSET_SETTING(bool, AlwaysShowTabs, true);
GETSET_SETTING(bool, ShowTitleInTitlebar, true);
GETSET_SETTING(bool, ConfirmCloseAllTabs, true);
GETSET_SETTING(winrt::Windows::UI::Xaml::ElementTheme, Theme, winrt::Windows::UI::Xaml::ElementTheme::Default);
GETSET_SETTING(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal);
GETSET_SETTING(bool, ShowTabsInTitlebar, true);
GETSET_SETTING(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
GETSET_SETTING(bool, CopyOnSelect, false);
GETSET_SETTING(winrt::Microsoft::Terminal::TerminalControl::CopyFormat, CopyFormatting, 0);
GETSET_SETTING(bool, WarnAboutLargePaste, true);
GETSET_SETTING(bool, WarnAboutMultiLinePaste, true);
GETSET_SETTING(Model::LaunchPosition, InitialPosition, nullptr, nullptr);
GETSET_SETTING(Model::LaunchMode, LaunchMode, LaunchMode::DefaultMode);
GETSET_SETTING(bool, SnapToGridOnResize, true);
GETSET_SETTING(bool, ForceFullRepaintRendering, false);
GETSET_SETTING(bool, SoftwareRendering, false);
GETSET_SETTING(bool, ForceVTInput, false);
GETSET_SETTING(bool, DebugFeaturesEnabled); // default value set in constructor
GETSET_SETTING(bool, StartOnUserLogin, false);
GETSET_SETTING(bool, AlwaysOnTop, false);
GETSET_SETTING(bool, UseTabSwitcher, true);
GETSET_SETTING(bool, DisableAnimations, false);
private:
hstring _unparsedDefaultProfile;
guid _defaultProfile;
std::optional<hstring> _UnparsedDefaultProfile{ std::nullopt };
bool _validDefaultProfile;
com_ptr<KeyMapping> _keymap;
std::vector<SettingsLoadWarnings> _keybindingsWarnings;
@ -89,6 +95,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Windows::Foundation::Collections::IMap<hstring, Model::ColorScheme> _colorSchemes;
Windows::Foundation::Collections::IMap<hstring, Model::Command> _commands;
std::optional<hstring> _getUnparsedDefaultProfileImpl() const;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::ColorSchemeTests;
};

View file

@ -28,31 +28,104 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
String UnparsedDefaultProfile();
Boolean HasUnparsedDefaultProfile();
void ClearUnparsedDefaultProfile();
String UnparsedDefaultProfile;
Boolean HasInitialRows();
void ClearInitialRows();
Int32 InitialRows;
Boolean HasInitialCols();
void ClearInitialCols();
Int32 InitialCols;
Boolean HasAlwaysShowTabs();
void ClearAlwaysShowTabs();
Boolean AlwaysShowTabs;
Boolean HasShowTitleInTitlebar();
void ClearShowTitleInTitlebar();
Boolean ShowTitleInTitlebar;
Boolean HasConfirmCloseAllTabs();
void ClearConfirmCloseAllTabs();
Boolean ConfirmCloseAllTabs;
Boolean HasTheme();
void ClearTheme();
Windows.UI.Xaml.ElementTheme Theme;
Boolean HasTabWidthMode();
void ClearTabWidthMode();
Microsoft.UI.Xaml.Controls.TabViewWidthMode TabWidthMode;
Boolean HasShowTabsInTitlebar();
void ClearShowTabsInTitlebar();
Boolean ShowTabsInTitlebar;
Boolean HasWordDelimiters();
void ClearWordDelimiters();
String WordDelimiters;
Boolean HasCopyOnSelect();
void ClearCopyOnSelect();
Boolean CopyOnSelect;
Boolean HasCopyFormatting();
void ClearCopyFormatting();
Microsoft.Terminal.TerminalControl.CopyFormat CopyFormatting;
Boolean HasWarnAboutLargePaste();
void ClearWarnAboutLargePaste();
Boolean WarnAboutLargePaste;
Boolean HasWarnAboutMultiLinePaste();
void ClearWarnAboutMultiLinePaste();
Boolean WarnAboutMultiLinePaste;
Boolean HasInitialPosition();
void ClearInitialPosition();
LaunchPosition InitialPosition;
Boolean HasLaunchMode();
void ClearLaunchMode();
LaunchMode LaunchMode;
Boolean HasSnapToGridOnResize();
void ClearSnapToGridOnResize();
Boolean SnapToGridOnResize;
Boolean HasForceFullRepaintRendering();
void ClearForceFullRepaintRendering();
Boolean ForceFullRepaintRendering;
Boolean HasSoftwareRendering();
void ClearSoftwareRendering();
Boolean SoftwareRendering;
Boolean HasForceVTInput();
void ClearForceVTInput();
Boolean ForceVTInput;
Boolean HasDebugFeaturesEnabled();
void ClearDebugFeaturesEnabled();
Boolean DebugFeaturesEnabled;
Boolean HasStartOnUserLogin();
void ClearStartOnUserLogin();
Boolean StartOnUserLogin;
Boolean HasAlwaysOnTop();
void ClearAlwaysOnTop();
Boolean AlwaysOnTop;
Boolean HasUseTabSwitcher();
void ClearUseTabSwitcher();
Boolean UseTabSwitcher;
Boolean HasDisableAnimations();
void ClearDisableAnimations();
Boolean DisableAnimations;
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();

View file

@ -0,0 +1,212 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- IInheritable.h
Abstract:
- An interface allowing settings objects to inherit settings from a parent
Author(s):
- Carlos Zamora - October 2020
--*/
#pragma once
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
template<typename T>
struct IInheritable
{
public:
// Method Description:
// - Create a new instance of T, but set its parent to this instance
// Arguments:
// - <none>
// Return Value:
// - a new instance of T with this instance set as its parent
com_ptr<T> CreateChild() const
{
auto child{ winrt::make_self<T>() };
// set "this" as the parent.
// However, "this" is an IInheritable, so we need to cast it as T (the impl winrt type)
// to pass ownership over to the com_ptr.
com_ptr<T> parent;
winrt::copy_from_abi(parent, const_cast<T*>(static_cast<const T*>(this)));
child->InsertParent(parent);
child->_FinalizeInheritance();
return child;
}
void InsertParent(com_ptr<T> parent)
{
_parents.push_back(parent);
}
void InsertParent(size_t index, com_ptr<T> parent)
{
auto pos{ _parents.begin() + index };
_parents.insert(pos, parent);
}
const std::vector<com_ptr<T>>& Parents()
{
return _parents;
}
protected:
std::vector<com_ptr<T>> _parents{};
// Method Description:
// - Actions to be performed after a child was created. Generally used to set
// any extraneous data from the parent into the child.
// Arguments:
// - <none>
// Return Value:
// - <none>
virtual void _FinalizeInheritance() {}
};
// This is like std::optional, but we can use it in inheritance to determine whether the user explicitly cleared it
template<typename T>
struct NullableSetting
{
std::optional<T> setting{ std::nullopt };
bool set{ false };
};
}
// Use this macro to quickly implement both getters and the setter for an
// inheritable setting property. This is similar to the GETSET_PROPERTY macro, except...
// - Has(): checks if the user explicitly set a value for this setting
// - Getter(): return the resolved value
// - Setter(): set the value directly
// - Clear(): clear the user set value
// - the setting is saved as an optional, where nullopt means
// that we must inherit the value from our parent
#define GETSET_SETTING(type, name, ...) \
public: \
/* Returns true if the user explicitly set the value, false otherwise*/ \
bool Has##name() const \
{ \
return _##name.has_value(); \
}; \
\
/* Returns the resolved value for this setting */ \
/* fallback: user set value --> inherited value --> system set value */ \
type name() const \
{ \
const auto val{ _get##name##Impl() }; \
return val ? *val : type{ __VA_ARGS__ }; \
}; \
\
/* Overwrite the user set value */ \
void name(const type& value) \
{ \
_##name = value; \
}; \
\
/* Clear the user set value */ \
void Clear##name() \
{ \
_##name = std::nullopt; \
}; \
\
private: \
std::optional<type> _##name{ std::nullopt }; \
std::optional<type> _get##name##Impl() const \
{ \
/*return user set value*/ \
if (_##name) \
{ \
return _##name; \
} \
\
/*user set value was not set*/ \
/*iterate through parents to find a value*/ \
for (auto parent : _parents) \
{ \
if (auto val{ parent->_get##name##Impl() }) \
{ \
return val; \
} \
} \
\
/*no value was found*/ \
return std::nullopt; \
};
// This macro is similar to the one above, but is reserved for optional settings
// like Profile.Foreground (where null is interpreted
// as an acceptable value, rather than "inherit")
// "type" is exposed as an IReference
#define GETSET_NULLABLE_SETTING(type, name, ...) \
public: \
/* Returns true if the user explicitly set the value, false otherwise*/ \
bool Has##name() const \
{ \
return _##name.set; \
}; \
\
/* Returns the resolved value for this setting */ \
/* fallback: user set value --> inherited value --> system set value */ \
winrt::Windows::Foundation::IReference<type> name() const \
{ \
const auto val{ _get##name##Impl() }; \
if (val.set) \
{ \
if (val.setting) \
{ \
return *val.setting; \
} \
return nullptr; \
} \
return winrt::Windows::Foundation::IReference<type>{ __VA_ARGS__ }; \
}; \
\
/* Overwrite the user set value */ \
void name(const winrt::Windows::Foundation::IReference<type>& value) \
{ \
if (value) /*set value is different*/ \
{ \
_##name.setting = value.Value(); \
} \
else \
{ \
_##name.setting = std::nullopt; \
} \
_##name.set = true; \
}; \
\
/* Clear the user set value */ \
void Clear##name() \
{ \
_##name.set = false; \
}; \
\
private: \
NullableSetting<type> _##name{}; \
NullableSetting<type> _get##name##Impl() const \
{ \
/*return user set value*/ \
if (Has##name()) \
{ \
return _##name; \
} \
\
/*user set value was not set*/ \
/*iterate through parents to find a value*/ \
for (auto parent : _parents) \
{ \
auto val{ parent->_get##name##Impl() }; \
if (val.set) \
{ \
return val; \
} \
} \
/*no value was found*/ \
return { std::nullopt, false }; \
};

View file

@ -43,6 +43,7 @@
<ClInclude Include="GlobalAppSettings.h">
<DependentUpon>GlobalAppSettings.idl</DependentUpon>
</ClInclude>
<ClInclude Include="IInheritable.h" />
<ClInclude Include="IDynamicProfileGenerator.h" />
<ClInclude Include="JsonUtils.h" />
<ClInclude Include="KeyChordSerialization.h">

View file

@ -70,49 +70,100 @@ Profile::Profile(guid guid) :
{
}
winrt::com_ptr<Profile> Profile::Copy() const
winrt::com_ptr<Profile> Profile::CopySettings(winrt::com_ptr<Profile> source)
{
auto profile{ winrt::make_self<Profile>() };
profile->_Name = _Name;
profile->_Source = _Source;
profile->_Hidden = _Hidden;
profile->_Icon = _Icon;
profile->_CloseOnExit = _CloseOnExit;
profile->_TabTitle = _TabTitle;
profile->_TabColor = _TabColor;
profile->_SuppressApplicationTitle = _SuppressApplicationTitle;
profile->_UseAcrylic = _UseAcrylic;
profile->_AcrylicOpacity = _AcrylicOpacity;
profile->_ScrollState = _ScrollState;
profile->_FontFace = _FontFace;
profile->_FontSize = _FontSize;
profile->_FontWeight = _FontWeight;
profile->_Padding = _Padding;
profile->_Commandline = _Commandline;
profile->_StartingDirectory = _StartingDirectory;
profile->_BackgroundImagePath = _BackgroundImagePath;
profile->_BackgroundImageOpacity = _BackgroundImageOpacity;
profile->_BackgroundImageStretchMode = _BackgroundImageStretchMode;
profile->_AntialiasingMode = _AntialiasingMode;
profile->_RetroTerminalEffect = _RetroTerminalEffect;
profile->_ForceFullRepaintRendering = _ForceFullRepaintRendering;
profile->_SoftwareRendering = _SoftwareRendering;
profile->_ColorSchemeName = _ColorSchemeName;
profile->_Foreground = _Foreground;
profile->_Background = _Background;
profile->_SelectionBackground = _SelectionBackground;
profile->_CursorColor = _CursorColor;
profile->_HistorySize = _HistorySize;
profile->_SnapOnInput = _SnapOnInput;
profile->_AltGrAliasing = _AltGrAliasing;
profile->_CursorShape = _CursorShape;
profile->_CursorHeight = _CursorHeight;
profile->_Guid = _Guid;
profile->_ConnectionType = _ConnectionType;
profile->_BackgroundImageAlignment = _BackgroundImageAlignment;
profile->_Guid = source->_Guid;
profile->_Name = source->_Name;
profile->_Source = source->_Source;
profile->_Hidden = source->_Hidden;
profile->_Icon = source->_Icon;
profile->_CloseOnExit = source->_CloseOnExit;
profile->_TabTitle = source->_TabTitle;
profile->_TabColor = source->_TabColor;
profile->_SuppressApplicationTitle = source->_SuppressApplicationTitle;
profile->_UseAcrylic = source->_UseAcrylic;
profile->_AcrylicOpacity = source->_AcrylicOpacity;
profile->_ScrollState = source->_ScrollState;
profile->_FontFace = source->_FontFace;
profile->_FontSize = source->_FontSize;
profile->_FontWeight = source->_FontWeight;
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->_BackgroundImageAlignment = source->_BackgroundImageAlignment;
profile->_ConnectionType = source->_ConnectionType;
return profile;
}
// Method Description:
// - Creates a copy of the inheritance graph by performing a depth-first traversal recursively.
// Profiles are recorded as visited via the "visited" parameter.
// Unvisited Profiles are copied into the "cloneGraph" parameter, then marked as visited.
// Arguments:
// - sourceGraph - the graph of Profile's we're cloning
// - cloneGraph - the clone of sourceGraph that is being constructed
// - visited - a map of which Profiles have been visited, and, if so, a reference to the Profile's clone
// Return Value:
// - a clone in both inheritance structure and Profile values of sourceGraph
winrt::com_ptr<Profile> Profile::CloneInheritanceGraph(winrt::com_ptr<Profile> sourceGraph, winrt::com_ptr<Profile> cloneGraph, std::unordered_map<void*, winrt::com_ptr<Profile>>& visited)
{
// If this is an unexplored Profile
// and we have parents...
if (visited.find(sourceGraph.get()) == visited.end() && !sourceGraph->_parents.empty())
{
// iterate through all of our parents to copy them
for (const auto& sourceParent : sourceGraph->_parents)
{
// If we visited this Profile already...
auto kv{ visited.find(sourceParent.get()) };
if (kv != visited.end())
{
// add this Profile's clone as a parent
cloneGraph->InsertParent(kv->second);
}
else
{
// We have not visited this Profile yet,
// copy contents of sourceParent to clone
winrt::com_ptr<Profile> clone{ CopySettings(sourceParent) };
// add the new copy to the cloneGraph
cloneGraph->InsertParent(clone);
// copy the sub-graph at "clone"
CloneInheritanceGraph(sourceParent, clone, visited);
// mark clone as "visited"
// save it to the map in case somebody else references it
visited[sourceParent.get()] = clone;
}
}
}
// we have no more to explore down this path.
return cloneGraph;
}
// 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.
@ -129,19 +180,17 @@ Json::Value Profile::GenerateStub() const
Json::Value stub;
///// Profile-specific settings /////
if (_Guid.has_value())
stub[JsonKey(GuidKey)] = winrt::to_string(Utils::GuidToString(Guid()));
stub[JsonKey(NameKey)] = winrt::to_string(Name());
const auto source{ Source() };
if (!source.empty())
{
stub[JsonKey(GuidKey)] = winrt::to_string(Utils::GuidToString(*_Guid));
stub[JsonKey(SourceKey)] = winrt::to_string(source);
}
stub[JsonKey(NameKey)] = winrt::to_string(_Name);
if (!_Source.empty())
{
stub[JsonKey(SourceKey)] = winrt::to_string(_Source);
}
stub[JsonKey(HiddenKey)] = _Hidden;
stub[JsonKey(HiddenKey)] = Hidden();
return stub;
}
@ -169,40 +218,37 @@ winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Prof
// - true iff the json object has the same `GUID` as we do.
bool Profile::ShouldBeLayered(const Json::Value& json) const
{
if (!_Guid.has_value())
{
return false;
}
// First, check that GUIDs match. This is easy. If they don't match, they
// should _definitely_ not layer.
if (const auto otherGuid{ JsonUtils::GetValueForKey<std::optional<winrt::guid>>(json, GuidKey) })
const auto otherGuid{ JsonUtils::GetValueForKey<std::optional<winrt::guid>>(json, GuidKey) };
const auto otherSource{ JsonUtils::GetValueForKey<std::optional<winrt::hstring>>(json, SourceKey) };
if (otherGuid)
{
if (otherGuid.value() != *_Guid)
if (otherGuid.value() != Guid())
{
return false;
}
}
else
{
// If the other json object didn't have a GUID, we definitely don't want
// to layer. We technically might have the same name, and would
// auto-generate the same guid, but they should be treated as different
// profiles.
return false;
// If the other json object didn't have a GUID,
// check if we auto-generate the same guid using the name and source.
const auto otherName{ JsonUtils::GetValueForKey<std::optional<winrt::hstring>>(json, NameKey) };
if (Guid() != _GenerateGuidForProfile(otherName ? *otherName : L"Default", otherSource ? *otherSource : L""))
{
return false;
}
}
std::optional<std::wstring> otherSource;
bool otherHadSource = JsonUtils::GetValueForKey(json, SourceKey, otherSource);
// For profiles with a `source`, also check the `source` property.
bool sourceMatches = false;
if (!_Source.empty())
const auto mySource{ Source() };
if (!mySource.empty())
{
if (otherHadSource)
if (otherSource.has_value())
{
// If we have a source and the other has a source, compare them!
sourceMatches = *otherSource == _Source;
sourceMatches = *otherSource == mySource;
}
else
{
@ -210,9 +256,9 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
// `this` is a dynamic profile with a source, and our _source is one
// of the legacy DPG namespaces. We're looking to see if the other
// json object has the same guid, but _no_ "source"
if (_Source == WslGeneratorNamespace ||
_Source == AzureGeneratorNamespace ||
_Source == PowershellCoreGeneratorNamespace)
if (mySource == WslGeneratorNamespace ||
mySource == AzureGeneratorNamespace ||
mySource == PowershellCoreGeneratorNamespace)
{
sourceMatches = true;
}
@ -247,10 +293,10 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, HiddenKey, _Hidden);
// Core Settings
JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground);
JsonUtils::GetValueForKey(json, BackgroundKey, _Background);
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground);
JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor);
_Foreground.set = JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground.setting);
_Background.set = JsonUtils::GetValueForKey(json, BackgroundKey, _Background.setting);
_SelectionBackground.set = JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _SelectionBackground.setting);
_CursorColor.set = JsonUtils::GetValueForKey(json, CursorColorKey, _CursorColor.setting);
JsonUtils::GetValueForKey(json, ColorSchemeKey, _ColorSchemeName);
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
@ -277,7 +323,16 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, PaddingKey, _Padding, JsonUtils::PermissiveStringConverter<std::wstring>{});
JsonUtils::GetValueForKey(json, ScrollbarStateKey, _ScrollState);
JsonUtils::GetValueForKey(json, StartingDirectoryKey, _StartingDirectory);
// StartingDirectory is "nullable". But we represent a null starting directory as the empty string
// When null is set in the JSON, we empty initialize startDir (empty string), and set StartingDirectory to that
// Without this, we're accidentally setting StartingDirectory to nullopt instead.
hstring startDir;
if (JsonUtils::GetValueForKey(json, StartingDirectoryKey, startDir))
{
_StartingDirectory = startDir;
}
JsonUtils::GetValueForKey(json, IconKey, _Icon);
JsonUtils::GetValueForKey(json, BackgroundImageKey, _BackgroundImagePath);
JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _BackgroundImageOpacity);
@ -286,7 +341,7 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _RetroTerminalEffect);
JsonUtils::GetValueForKey(json, AntialiasingModeKey, _AntialiasingMode);
JsonUtils::GetValueForKey(json, TabColorKey, _TabColor);
_TabColor.set = JsonUtils::GetValueForKey(json, TabColorKey, _TabColor.setting);
JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle);
}
@ -300,13 +355,14 @@ void Profile::LayerJson(const Json::Value& json)
// - This profile's expanded background image path / desktops's wallpaper path /the empty string.
winrt::hstring Profile::ExpandedBackgroundImagePath() const
{
if (_BackgroundImagePath.empty())
const auto path{ BackgroundImagePath() };
if (path.empty())
{
return _BackgroundImagePath;
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 (_BackgroundImagePath == to_hstring(DesktopWallpaperEnum))
else if (path == to_hstring(DesktopWallpaperEnum))
{
WCHAR desktopWallpaper[MAX_PATH];
@ -322,13 +378,19 @@ winrt::hstring Profile::ExpandedBackgroundImagePath() const
}
else
{
return winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(_BackgroundImagePath.c_str()) };
return winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(path.c_str()) };
}
}
winrt::hstring Profile::EvaluatedStartingDirectory() const
{
return winrt::hstring{ Profile::EvaluateStartingDirectory(_StartingDirectory.c_str()) };
auto path{ StartingDirectory() };
if (!path.empty())
{
return winrt::hstring{ Profile::EvaluateStartingDirectory(path.c_str()) };
}
// treated as "inherit directory from parent process"
return path;
}
// Method Description:
@ -368,11 +430,11 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
// will _not_ change the profile's GUID.
void Profile::GenerateGuidIfNecessary() noexcept
{
if (!_Guid.has_value())
if (!_getGuidImpl().has_value())
{
// Always use the name to generate the temporary GUID. That way, across
// reloads, we'll generate the same static GUID.
_Guid = Profile::_GenerateGuidForProfile(_Name, _Source);
_Guid = Profile::_GenerateGuidForProfile(Name(), Source());
TraceLoggingWrite(
g_hSettingsModelProvider,
@ -438,54 +500,50 @@ winrt::guid Profile::GetGuidOrGenerateForJson(const Json::Value& json) noexcept
return Profile::_GenerateGuidForProfile(name, source);
}
#pragma region BackgroundImageAlignment
bool Profile::HasBackgroundImageAlignment() const noexcept
{
return _BackgroundImageAlignment.has_value();
}
void Profile::ClearBackgroundImageAlignment() noexcept
{
_BackgroundImageAlignment = std::nullopt;
}
const HorizontalAlignment Profile::BackgroundImageHorizontalAlignment() const noexcept
{
return std::get<HorizontalAlignment>(_BackgroundImageAlignment);
const auto val{ _getBackgroundImageAlignmentImpl() };
return val ? std::get<HorizontalAlignment>(*val) : HorizontalAlignment::Center;
}
void Profile::BackgroundImageHorizontalAlignment(const HorizontalAlignment& value) noexcept
{
std::get<HorizontalAlignment>(_BackgroundImageAlignment) = value;
if (HasBackgroundImageAlignment())
{
std::get<HorizontalAlignment>(*_BackgroundImageAlignment) = value;
}
else
{
_BackgroundImageAlignment = { value, VerticalAlignment::Center };
}
}
const VerticalAlignment Profile::BackgroundImageVerticalAlignment() const noexcept
{
return std::get<VerticalAlignment>(_BackgroundImageAlignment);
const auto val{ _getBackgroundImageAlignmentImpl() };
return val ? std::get<VerticalAlignment>(*val) : VerticalAlignment::Center;
}
void Profile::BackgroundImageVerticalAlignment(const VerticalAlignment& value) noexcept
{
std::get<VerticalAlignment>(_BackgroundImageAlignment) = value;
}
bool Profile::HasGuid() const noexcept
{
return _Guid.has_value();
}
winrt::guid Profile::Guid() const
{
// This can throw if we never had our guid set to a legitimate value.
THROW_HR_IF_MSG(E_FAIL, !_Guid.has_value(), "Profile._guid always expected to have a value");
return *_Guid;
}
void Profile::Guid(const winrt::guid& guid) noexcept
{
_Guid = guid;
}
bool Profile::HasConnectionType() const noexcept
{
return _ConnectionType.has_value();
}
winrt::guid Profile::ConnectionType() const noexcept
{
return *_ConnectionType;
}
void Profile::ConnectionType(const winrt::guid& conType) noexcept
{
_ConnectionType = conType;
if (HasBackgroundImageAlignment())
{
std::get<VerticalAlignment>(*_BackgroundImageAlignment) = value;
}
else
{
_BackgroundImageAlignment = { HorizontalAlignment::Center, value };
}
}
#pragma endregion

View file

@ -16,21 +16,24 @@ Author(s):
#pragma once
#include "Profile.g.h"
#include "IInheritable.h"
#include "../inc/cppwinrt_utils.h"
#include "JsonUtils.h"
#include <DefaultSettings.h>
// fwdecl unittest classes
namespace TerminalAppLocalTests
namespace SettingsModelLocalTests
{
class SettingsTests;
class DeserializationTests;
class ProfileTests;
class ColorSchemeTests;
class KeyBindingsTests;
};
namespace TerminalAppUnitTests
{
class JsonTests;
class DynamicProfileTests;
class JsonTests;
};
// GUID used for generating GUIDs at runtime, for profiles that did not have a
@ -39,12 +42,13 @@ constexpr GUID RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID = { 0xf65ddb7e, 0x706b,
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
struct Profile : ProfileT<Profile>
struct Profile : ProfileT<Profile>, IInheritable<Profile>
{
public:
Profile();
Profile(guid guid);
com_ptr<Profile> Copy() const;
static com_ptr<Profile> CloneInheritanceGraph(com_ptr<Profile> oldProfile, com_ptr<Profile> newProfile, std::unordered_map<void*, com_ptr<Profile>>& visited);
static com_ptr<Profile> CopySettings(com_ptr<Profile> source);
Json::Value GenerateStub() const;
static com_ptr<Profile> FromJson(const Json::Value& json);
@ -57,83 +61,97 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void GenerateGuidIfNecessary() noexcept;
static guid GetGuidOrGenerateForJson(const Json::Value& json) noexcept;
bool HasGuid() const noexcept;
winrt::guid Guid() const;
void Guid(const winrt::guid& guid) noexcept;
bool HasConnectionType() const noexcept;
winrt::guid ConnectionType() const noexcept;
void ConnectionType(const winrt::guid& conType) noexcept;
// BackgroundImageAlignment is 1 setting saved as 2 separate values
bool HasBackgroundImageAlignment() const noexcept;
void ClearBackgroundImageAlignment() noexcept;
const Windows::UI::Xaml::HorizontalAlignment BackgroundImageHorizontalAlignment() const noexcept;
void BackgroundImageHorizontalAlignment(const Windows::UI::Xaml::HorizontalAlignment& value) noexcept;
const Windows::UI::Xaml::VerticalAlignment BackgroundImageVerticalAlignment() const noexcept;
void BackgroundImageVerticalAlignment(const Windows::UI::Xaml::VerticalAlignment& value) noexcept;
GETSET_PROPERTY(hstring, Name, L"Default");
GETSET_PROPERTY(hstring, Source);
GETSET_PROPERTY(bool, Hidden, false);
GETSET_SETTING(guid, Guid, _GenerateGuidForProfile(Name(), Source()));
GETSET_SETTING(hstring, Name, L"Default");
GETSET_SETTING(hstring, Source);
GETSET_SETTING(bool, Hidden, false);
GETSET_SETTING(guid, ConnectionType);
GETSET_PROPERTY(hstring, Icon);
GETSET_SETTING(hstring, Icon);
GETSET_PROPERTY(CloseOnExitMode, CloseOnExit, CloseOnExitMode::Graceful);
GETSET_PROPERTY(hstring, TabTitle);
GETSET_PROPERTY(Windows::Foundation::IReference<Windows::UI::Color>, TabColor);
GETSET_PROPERTY(bool, SuppressApplicationTitle, false);
GETSET_SETTING(CloseOnExitMode, CloseOnExit, CloseOnExitMode::Graceful);
GETSET_SETTING(hstring, TabTitle);
GETSET_NULLABLE_SETTING(Windows::UI::Color, TabColor, nullptr);
GETSET_SETTING(bool, SuppressApplicationTitle, false);
GETSET_PROPERTY(bool, UseAcrylic, false);
GETSET_PROPERTY(double, AcrylicOpacity, 0.5);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
GETSET_SETTING(bool, UseAcrylic, false);
GETSET_SETTING(double, AcrylicOpacity, 0.5);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::ScrollbarState, ScrollState, Microsoft::Terminal::TerminalControl::ScrollbarState::Visible);
GETSET_PROPERTY(hstring, FontFace, DEFAULT_FONT_FACE);
GETSET_PROPERTY(int32_t, FontSize, DEFAULT_FONT_SIZE);
GETSET_PROPERTY(Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT);
GETSET_PROPERTY(hstring, Padding, DEFAULT_PADDING);
GETSET_SETTING(hstring, FontFace, DEFAULT_FONT_FACE);
GETSET_SETTING(int32_t, FontSize, DEFAULT_FONT_SIZE);
GETSET_SETTING(Windows::UI::Text::FontWeight, FontWeight, DEFAULT_FONT_WEIGHT);
GETSET_SETTING(hstring, Padding, DEFAULT_PADDING);
GETSET_PROPERTY(hstring, Commandline, L"cmd.exe");
GETSET_PROPERTY(hstring, StartingDirectory);
GETSET_SETTING(hstring, Commandline, L"cmd.exe");
GETSET_SETTING(hstring, StartingDirectory);
GETSET_PROPERTY(hstring, BackgroundImagePath);
GETSET_PROPERTY(double, BackgroundImageOpacity, 1.0);
GETSET_PROPERTY(Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch::Fill);
GETSET_SETTING(hstring, BackgroundImagePath);
GETSET_SETTING(double, BackgroundImageOpacity, 1.0);
GETSET_SETTING(Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch::Fill);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
GETSET_PROPERTY(bool, RetroTerminalEffect, false);
GETSET_PROPERTY(bool, ForceFullRepaintRendering, false);
GETSET_PROPERTY(bool, SoftwareRendering, false);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::TextAntialiasingMode, AntialiasingMode, Microsoft::Terminal::TerminalControl::TextAntialiasingMode::Grayscale);
GETSET_SETTING(bool, RetroTerminalEffect, false);
GETSET_SETTING(bool, ForceFullRepaintRendering, false);
GETSET_SETTING(bool, SoftwareRendering, false);
GETSET_PROPERTY(hstring, ColorSchemeName, L"Campbell");
GETSET_PROPERTY(Windows::Foundation::IReference<Windows::UI::Color>, Foreground);
GETSET_PROPERTY(Windows::Foundation::IReference<Windows::UI::Color>, Background);
GETSET_PROPERTY(Windows::Foundation::IReference<Windows::UI::Color>, SelectionBackground);
GETSET_PROPERTY(Windows::Foundation::IReference<Windows::UI::Color>, CursorColor);
GETSET_SETTING(hstring, ColorSchemeName, L"Campbell");
GETSET_PROPERTY(int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
GETSET_PROPERTY(bool, SnapOnInput, true);
GETSET_PROPERTY(bool, AltGrAliasing, true);
GETSET_NULLABLE_SETTING(Windows::UI::Color, Foreground, nullptr);
GETSET_NULLABLE_SETTING(Windows::UI::Color, Background, nullptr);
GETSET_NULLABLE_SETTING(Windows::UI::Color, SelectionBackground, nullptr);
GETSET_NULLABLE_SETTING(Windows::UI::Color, CursorColor, nullptr);
GETSET_PROPERTY(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Bar);
GETSET_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
GETSET_SETTING(int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
GETSET_SETTING(bool, SnapOnInput, true);
GETSET_SETTING(bool, AltGrAliasing, true);
GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::BellStyle, BellStyle, winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible);
GETSET_SETTING(Microsoft::Terminal::TerminalControl::CursorStyle, CursorShape, Microsoft::Terminal::TerminalControl::CursorStyle::Bar);
GETSET_SETTING(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
GETSET_SETTING(Model::BellStyle, BellStyle, BellStyle::Audible);
private:
std::optional<winrt::guid> _Guid{ std::nullopt };
std::optional<winrt::guid> _ConnectionType{ std::nullopt };
std::tuple<Windows::UI::Xaml::HorizontalAlignment, Windows::UI::Xaml::VerticalAlignment> _BackgroundImageAlignment{
Windows::UI::Xaml::HorizontalAlignment::Center,
Windows::UI::Xaml::VerticalAlignment::Center
std::optional<std::tuple<Windows::UI::Xaml::HorizontalAlignment, Windows::UI::Xaml::VerticalAlignment>> _BackgroundImageAlignment{ std::nullopt };
std::optional<std::tuple<Windows::UI::Xaml::HorizontalAlignment, Windows::UI::Xaml::VerticalAlignment>> _getBackgroundImageAlignmentImpl() const
{
/*return user set value*/
if (_BackgroundImageAlignment)
{
return _BackgroundImageAlignment;
}
/*user set value was not set*/ /*iterate through parents to find a value*/
for (auto parent : _parents)
{
if (auto val{ parent->_getBackgroundImageAlignmentImpl() })
{
return val;
}
}
/*no value was found*/
return std::nullopt;
};
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
static guid _GenerateGuidForProfile(const hstring& name, const hstring& source) noexcept;
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::ProfileTests;
friend class TerminalAppUnitTests::JsonTests;
friend class SettingsModelLocalTests::DeserializationTests;
friend class SettingsModelLocalTests::ProfileTests;
friend class SettingsModelLocalTests::ColorSchemeTests;
friend class SettingsModelLocalTests::KeyBindingsTests;
friend class TerminalAppUnitTests::DynamicProfileTests;
friend class TerminalAppUnitTests::JsonTests;
};
}

View file

@ -20,59 +20,157 @@ namespace Microsoft.Terminal.Settings.Model
Profile();
Profile(Guid guid);
Boolean HasName();
void ClearName();
String Name;
Boolean HasGuid();
Guid Guid;
Boolean HasSource();
void ClearSource();
String Source;
Boolean HasConnectionType();
Guid ConnectionType;
Boolean HasHidden();
void ClearHidden();
Boolean Hidden;
Boolean HasIcon();
void ClearIcon();
String Icon;
Boolean HasCloseOnExit();
void ClearCloseOnExit();
CloseOnExitMode CloseOnExit;
Boolean HasTabTitle();
void ClearTabTitle();
String TabTitle;
Boolean HasTabColor();
void ClearTabColor();
Windows.Foundation.IReference<Windows.UI.Color> TabColor;
Boolean HasSuppressApplicationTitle();
void ClearSuppressApplicationTitle();
Boolean SuppressApplicationTitle;
Boolean HasUseAcrylic();
void ClearUseAcrylic();
Boolean UseAcrylic;
Boolean HasAcrylicOpacity();
void ClearAcrylicOpacity();
Double AcrylicOpacity;
Boolean HasScrollState();
void ClearScrollState();
Microsoft.Terminal.TerminalControl.ScrollbarState ScrollState;
Boolean HasFontFace();
void ClearFontFace();
String FontFace;
Boolean HasFontSize();
void ClearFontSize();
Int32 FontSize;
Boolean HasFontWeight();
void ClearFontWeight();
Windows.UI.Text.FontWeight FontWeight;
Boolean HasPadding();
void ClearPadding();
String Padding;
Boolean HasCommandline();
void ClearCommandline();
String Commandline;
Boolean HasStartingDirectory();
void ClearStartingDirectory();
String StartingDirectory;
String EvaluatedStartingDirectory { get; };
Boolean HasBackgroundImagePath();
void ClearBackgroundImagePath();
String BackgroundImagePath;
String ExpandedBackgroundImagePath { get; };
Boolean HasBackgroundImageOpacity();
void ClearBackgroundImageOpacity();
Double BackgroundImageOpacity;
Boolean HasBackgroundImageStretchMode();
void ClearBackgroundImageStretchMode();
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
Boolean HasBackgroundImageAlignment();
void ClearBackgroundImageAlignment();
Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment;
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment;
Boolean HasAntialiasingMode();
void ClearAntialiasingMode();
Microsoft.Terminal.TerminalControl.TextAntialiasingMode AntialiasingMode;
Boolean HasRetroTerminalEffect();
void ClearRetroTerminalEffect();
Boolean RetroTerminalEffect;
Boolean HasForceFullRepaintRendering();
void ClearForceFullRepaintRendering();
Boolean ForceFullRepaintRendering;
Boolean HasSoftwareRendering();
void ClearSoftwareRendering();
Boolean SoftwareRendering;
Boolean HasColorSchemeName();
void ClearColorSchemeName();
String ColorSchemeName;
Boolean HasForeground();
void ClearForeground();
Windows.Foundation.IReference<Windows.UI.Color> Foreground;
Boolean HasBackground();
void ClearBackground();
Windows.Foundation.IReference<Windows.UI.Color> Background;
Boolean HasSelectionBackground();
void ClearSelectionBackground();
Windows.Foundation.IReference<Windows.UI.Color> SelectionBackground;
Boolean HasCursorColor();
void ClearCursorColor();
Windows.Foundation.IReference<Windows.UI.Color> CursorColor;
Boolean HasHistorySize();
void ClearHistorySize();
Int32 HistorySize;
Boolean HasSnapOnInput();
void ClearSnapOnInput();
Boolean SnapOnInput;
Boolean HasAltGrAliasing();
void ClearAltGrAliasing();
Boolean AltGrAliasing;
Boolean HasCursorShape();
void ClearCursorShape();
Microsoft.Terminal.TerminalControl.CursorStyle CursorShape;
Boolean HasCursorHeight();
void ClearCursorHeight();
UInt32 CursorHeight;
Boolean HasBellStyle();
void ClearBellStyle();
BellStyle BellStyle;
}
}

View file

@ -57,5 +57,4 @@ namespace til
{
return t1.has_value() ? t1 : coalesce(std::forward<Ts>(t2)...);
}
}