mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-19 20:51:17 +00:00
Compare commits
1 Commits
main
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7854388a98 |
@@ -39,11 +39,12 @@ winrt::com_ptr<AppearanceConfig> AppearanceConfig::CopyAppearance(const Appearan
|
||||
|
||||
appearance->_DarkColorSchemeName = source->_DarkColorSchemeName;
|
||||
appearance->_LightColorSchemeName = source->_LightColorSchemeName;
|
||||
appearance->_json = source->_json;
|
||||
|
||||
#define APPEARANCE_SETTINGS_COPY(type, name, jsonKey, ...) \
|
||||
appearance->_##name = source->_##name;
|
||||
MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_COPY)
|
||||
#undef APPEARANCE_SETTINGS_COPY
|
||||
// Complex/mutable settings with backing fields
|
||||
appearance->_PixelShaderPath = source->_PixelShaderPath;
|
||||
appearance->_PixelShaderImagePath = source->_PixelShaderImagePath;
|
||||
appearance->_BackgroundImagePath = source->_BackgroundImagePath;
|
||||
|
||||
return appearance;
|
||||
}
|
||||
@@ -71,14 +72,116 @@ Json::Value AppearanceConfig::ToJson() const
|
||||
}
|
||||
}
|
||||
|
||||
#define APPEARANCE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::SetValueForKey(json, jsonKey, _##name);
|
||||
// MTSM appearance settings: copy from _json (the source of truth)
|
||||
#define APPEARANCE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
if (_json.isMember(jsonKey) && !_json[jsonKey].isNull()) \
|
||||
{ \
|
||||
json[JsonKey(jsonKey)] = _json[JsonKey(jsonKey)]; \
|
||||
}
|
||||
MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_TO_JSON)
|
||||
#undef APPEARANCE_SETTINGS_TO_JSON
|
||||
|
||||
// Complex/mutable settings with backing fields
|
||||
JsonUtils::SetValueForKey(json, "experimental.pixelShaderPath", _PixelShaderPath);
|
||||
JsonUtils::SetValueForKey(json, "experimental.pixelShaderImagePath", _PixelShaderImagePath);
|
||||
JsonUtils::SetValueForKey(json, "backgroundImage", _BackgroundImagePath);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool AppearanceConfig::HasSetting(AppearanceSettingKey key) const
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _APPEARANCE_HAS_SETTING(type, name, jsonKey, ...) \
|
||||
case AppearanceSettingKey::name: \
|
||||
return Has##name();
|
||||
MTSM_APPEARANCE_SETTINGS(_APPEARANCE_HAS_SETTING)
|
||||
#undef _APPEARANCE_HAS_SETTING
|
||||
case AppearanceSettingKey::_Foreground:
|
||||
return HasForeground();
|
||||
case AppearanceSettingKey::_Background:
|
||||
return HasBackground();
|
||||
case AppearanceSettingKey::_SelectionBackground:
|
||||
return HasSelectionBackground();
|
||||
case AppearanceSettingKey::_CursorColor:
|
||||
return HasCursorColor();
|
||||
case AppearanceSettingKey::_Opacity:
|
||||
return HasOpacity();
|
||||
case AppearanceSettingKey::_DarkColorSchemeName:
|
||||
return HasDarkColorSchemeName();
|
||||
case AppearanceSettingKey::_LightColorSchemeName:
|
||||
return HasLightColorSchemeName();
|
||||
case AppearanceSettingKey::_PixelShaderPath:
|
||||
return HasPixelShaderPath();
|
||||
case AppearanceSettingKey::_PixelShaderImagePath:
|
||||
return HasPixelShaderImagePath();
|
||||
case AppearanceSettingKey::_BackgroundImagePath:
|
||||
return HasBackgroundImagePath();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceConfig::ClearSetting(AppearanceSettingKey key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _APPEARANCE_CLEAR_SETTING(type, name, jsonKey, ...) \
|
||||
case AppearanceSettingKey::name: \
|
||||
Clear##name(); \
|
||||
break;
|
||||
MTSM_APPEARANCE_SETTINGS(_APPEARANCE_CLEAR_SETTING)
|
||||
#undef _APPEARANCE_CLEAR_SETTING
|
||||
case AppearanceSettingKey::_Foreground:
|
||||
ClearForeground();
|
||||
break;
|
||||
case AppearanceSettingKey::_Background:
|
||||
ClearBackground();
|
||||
break;
|
||||
case AppearanceSettingKey::_SelectionBackground:
|
||||
ClearSelectionBackground();
|
||||
break;
|
||||
case AppearanceSettingKey::_CursorColor:
|
||||
ClearCursorColor();
|
||||
break;
|
||||
case AppearanceSettingKey::_Opacity:
|
||||
ClearOpacity();
|
||||
break;
|
||||
case AppearanceSettingKey::_DarkColorSchemeName:
|
||||
ClearDarkColorSchemeName();
|
||||
break;
|
||||
case AppearanceSettingKey::_LightColorSchemeName:
|
||||
ClearLightColorSchemeName();
|
||||
break;
|
||||
case AppearanceSettingKey::_PixelShaderPath:
|
||||
ClearPixelShaderPath();
|
||||
break;
|
||||
case AppearanceSettingKey::_PixelShaderImagePath:
|
||||
ClearPixelShaderImagePath();
|
||||
break;
|
||||
case AppearanceSettingKey::_BackgroundImagePath:
|
||||
ClearBackgroundImagePath();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AppearanceSettingKey> AppearanceConfig::CurrentSettings() const
|
||||
{
|
||||
std::vector<AppearanceSettingKey> result;
|
||||
for (auto i = 0; i < static_cast<int>(AppearanceSettingKey::SETTINGS_SIZE); i++)
|
||||
{
|
||||
const auto key = static_cast<AppearanceSettingKey>(i);
|
||||
if (HasSetting(key))
|
||||
{
|
||||
result.push_back(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -92,6 +195,14 @@ Json::Value AppearanceConfig::ToJson() const
|
||||
// - json: an object which should be a partial serialization of an AppearanceConfig object.
|
||||
void AppearanceConfig::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// Merge incoming JSON keys into stored _json (key-wise, not replacement).
|
||||
// AppearanceConfig receives the full profile JSON; we store all keys and
|
||||
// read only appearance-relevant ones from it.
|
||||
for (const auto& key : json.getMemberNames())
|
||||
{
|
||||
_json[key] = json[key];
|
||||
}
|
||||
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _Foreground);
|
||||
_logSettingIfSet(ForegroundKey, _Foreground.has_value());
|
||||
|
||||
@@ -125,12 +236,28 @@ void AppearanceConfig::LayerJson(const Json::Value& json)
|
||||
_logSettingSet("colorScheme.light");
|
||||
}
|
||||
|
||||
// Normalize legacy opacity key into canonical
|
||||
if (json.isMember(JsonKey(LegacyAcrylicTransparencyKey)))
|
||||
{
|
||||
_json[JsonKey(OpacityKey)] = json[JsonKey(LegacyAcrylicTransparencyKey)];
|
||||
}
|
||||
|
||||
// MTSM settings are now JSON-backed (no backing fields).
|
||||
// Values are already in _json from the merge step above.
|
||||
// We only need to log which settings were set in this layer.
|
||||
#define APPEARANCE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, _##name); \
|
||||
_logSettingIfSet(jsonKey, _##name.has_value());
|
||||
_logSettingIfSet(jsonKey, json.isMember(jsonKey) && !json[jsonKey].isNull());
|
||||
|
||||
MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_LAYER_JSON)
|
||||
#undef APPEARANCE_SETTINGS_LAYER_JSON
|
||||
|
||||
// Complex/mutable settings that have backing fields (not JSON-backed)
|
||||
JsonUtils::GetValueForKey(json, "experimental.pixelShaderPath", _PixelShaderPath);
|
||||
_logSettingIfSet("experimental.pixelShaderPath", _PixelShaderPath.has_value());
|
||||
JsonUtils::GetValueForKey(json, "experimental.pixelShaderImagePath", _PixelShaderImagePath);
|
||||
_logSettingIfSet("experimental.pixelShaderImagePath", _PixelShaderImagePath.has_value());
|
||||
JsonUtils::GetValueForKey(json, "backgroundImage", _BackgroundImagePath);
|
||||
_logSettingIfSet("backgroundImage", _BackgroundImagePath.has_value());
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile AppearanceConfig::SourceProfile()
|
||||
|
||||
@@ -18,6 +18,7 @@ Author(s):
|
||||
|
||||
#include "AppearanceConfig.g.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
#include "IInheritable.h"
|
||||
#include "MTSMSettings.h"
|
||||
#include "MediaResourceSupport.h"
|
||||
@@ -38,6 +39,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
void ResolveMediaResources(const Model::MediaResourceResolver& resolver);
|
||||
|
||||
// Generic setting access via SettingKey
|
||||
bool HasSetting(AppearanceSettingKey key) const;
|
||||
void ClearSetting(AppearanceSettingKey key);
|
||||
std::vector<AppearanceSettingKey> CurrentSettings() const;
|
||||
|
||||
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);
|
||||
@@ -48,12 +54,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, hstring, LightColorSchemeName, L"Campbell");
|
||||
|
||||
#define APPEARANCE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, type, name, ##__VA_ARGS__)
|
||||
INHERITABLE_JSON_SETTING(Model::IAppearanceConfig, type, name, jsonKey, ##__VA_ARGS__)
|
||||
MTSM_APPEARANCE_SETTINGS(APPEARANCE_SETTINGS_INITIALIZE)
|
||||
#undef APPEARANCE_SETTINGS_INITIALIZE
|
||||
|
||||
// Complex/mutable settings that need backing fields (not JSON-backed)
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, IMediaResource, PixelShaderPath, implementation::MediaResource::Empty());
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, IMediaResource, PixelShaderImagePath, implementation::MediaResource::Empty());
|
||||
INHERITABLE_SETTING(Model::IAppearanceConfig, IMediaResource, BackgroundImagePath, implementation::MediaResource::Empty());
|
||||
|
||||
private:
|
||||
winrt::weak_ref<Profile> _sourceProfile;
|
||||
|
||||
// Raw JSON for this layer (appearance-relevant keys only).
|
||||
Json::Value _json{ Json::ValueType::objectValue };
|
||||
|
||||
std::set<std::string> _changeLog;
|
||||
|
||||
void _logSettingSet(const std::string_view& setting);
|
||||
|
||||
@@ -25,13 +25,12 @@ winrt::com_ptr<FontConfig> FontConfig::CopyFontInfo(const FontConfig* source, wi
|
||||
{
|
||||
auto fontInfo{ winrt::make_self<FontConfig>(std::move(sourceProfile)) };
|
||||
|
||||
#define FONT_SETTINGS_COPY(type, name, jsonKey, ...) \
|
||||
fontInfo->_##name = source->_##name;
|
||||
MTSM_FONT_SETTINGS(FONT_SETTINGS_COPY)
|
||||
#undef FONT_SETTINGS_COPY
|
||||
fontInfo->_json = source->_json;
|
||||
|
||||
// We cannot simply copy the font axes and features with `fontInfo->_FontAxes = source->_FontAxes;`
|
||||
// since that'll just create a reference; we have to manually copy the values.
|
||||
// Complex/mutable settings with backing fields
|
||||
fontInfo->_FontAxes = source->_FontAxes;
|
||||
fontInfo->_FontFeatures = source->_FontFeatures;
|
||||
// Deep copy font maps (IMap creates references, not copies)
|
||||
static constexpr auto cloneFontMap = [](const IFontFeatureMap& map) {
|
||||
std::map<winrt::hstring, float> fontAxes;
|
||||
for (const auto& [k, v] : map)
|
||||
@@ -56,14 +55,75 @@ Json::Value FontConfig::ToJson() const
|
||||
{
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
|
||||
#define FONT_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::SetValueForKey(json, jsonKey, _##name);
|
||||
// MTSM font settings: copy from _json (the source of truth)
|
||||
#define FONT_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
if (_json.isMember(jsonKey) && !_json[jsonKey].isNull()) \
|
||||
{ \
|
||||
json[JsonKey(jsonKey)] = _json[JsonKey(jsonKey)]; \
|
||||
}
|
||||
MTSM_FONT_SETTINGS(FONT_SETTINGS_TO_JSON)
|
||||
#undef FONT_SETTINGS_TO_JSON
|
||||
|
||||
// Complex/mutable settings with backing fields
|
||||
JsonUtils::SetValueForKey(json, "axes", _FontAxes);
|
||||
JsonUtils::SetValueForKey(json, "features", _FontFeatures);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool FontConfig::HasSetting(FontSettingKey key) const
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _FONT_HAS_SETTING(type, name, jsonKey, ...) \
|
||||
case FontSettingKey::name: \
|
||||
return Has##name();
|
||||
MTSM_FONT_SETTINGS(_FONT_HAS_SETTING)
|
||||
#undef _FONT_HAS_SETTING
|
||||
case FontSettingKey::_FontAxes:
|
||||
return HasFontAxes();
|
||||
case FontSettingKey::_FontFeatures:
|
||||
return HasFontFeatures();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FontConfig::ClearSetting(FontSettingKey key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _FONT_CLEAR_SETTING(type, name, jsonKey, ...) \
|
||||
case FontSettingKey::name: \
|
||||
Clear##name(); \
|
||||
break;
|
||||
MTSM_FONT_SETTINGS(_FONT_CLEAR_SETTING)
|
||||
#undef _FONT_CLEAR_SETTING
|
||||
case FontSettingKey::_FontAxes:
|
||||
ClearFontAxes();
|
||||
break;
|
||||
case FontSettingKey::_FontFeatures:
|
||||
ClearFontFeatures();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FontSettingKey> FontConfig::CurrentSettings() const
|
||||
{
|
||||
std::vector<FontSettingKey> result;
|
||||
for (auto i = 0; i < static_cast<int>(FontSettingKey::SETTINGS_SIZE); i++)
|
||||
{
|
||||
const auto key = static_cast<FontSettingKey>(i);
|
||||
if (HasSetting(key))
|
||||
{
|
||||
result.push_back(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -83,25 +143,47 @@ void FontConfig::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// A font object is defined, use that
|
||||
const auto fontInfoJson = json[JsonKey(FontInfoKey)];
|
||||
#define FONT_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::GetValueForKey(fontInfoJson, jsonKey, _##name); \
|
||||
_logSettingIfSet(jsonKey, _##name.has_value());
|
||||
|
||||
// Merge the font sub-object into stored _json (font-object shape).
|
||||
for (const auto& key : fontInfoJson.getMemberNames())
|
||||
{
|
||||
_json[key] = fontInfoJson[key];
|
||||
}
|
||||
|
||||
// MTSM font settings are now JSON-backed. Values are already in _json.
|
||||
// We only need to log which settings were set.
|
||||
#define FONT_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
_logSettingIfSet(jsonKey, fontInfoJson.isMember(jsonKey) && !fontInfoJson[jsonKey].isNull());
|
||||
|
||||
MTSM_FONT_SETTINGS(FONT_SETTINGS_LAYER_JSON)
|
||||
#undef FONT_SETTINGS_LAYER_JSON
|
||||
|
||||
// Complex/mutable settings that have backing fields (not JSON-backed)
|
||||
JsonUtils::GetValueForKey(fontInfoJson, "axes", _FontAxes);
|
||||
_logSettingIfSet("axes", _FontAxes.has_value());
|
||||
JsonUtils::GetValueForKey(fontInfoJson, "features", _FontFeatures);
|
||||
_logSettingIfSet("features", _FontFeatures.has_value());
|
||||
}
|
||||
else
|
||||
{
|
||||
// No font object is defined
|
||||
// No font object is defined — normalize legacy flat keys into font-object shape.
|
||||
if (json.isMember(JsonKey(LegacyFontFaceKey)))
|
||||
{
|
||||
_json["face"] = json[JsonKey(LegacyFontFaceKey)];
|
||||
}
|
||||
if (json.isMember(JsonKey(LegacyFontSizeKey)))
|
||||
{
|
||||
_json["size"] = json[JsonKey(LegacyFontSizeKey)];
|
||||
}
|
||||
if (json.isMember(JsonKey(LegacyFontWeightKey)))
|
||||
{
|
||||
_json["weight"] = json[JsonKey(LegacyFontWeightKey)];
|
||||
}
|
||||
|
||||
// Log settings as if they were a part of the font object
|
||||
JsonUtils::GetValueForKey(json, LegacyFontFaceKey, _FontFace);
|
||||
_logSettingIfSet("face", _FontFace.has_value());
|
||||
|
||||
JsonUtils::GetValueForKey(json, LegacyFontSizeKey, _FontSize);
|
||||
_logSettingIfSet("size", _FontSize.has_value());
|
||||
|
||||
JsonUtils::GetValueForKey(json, LegacyFontWeightKey, _FontWeight);
|
||||
_logSettingIfSet("weight", _FontWeight.has_value());
|
||||
_logSettingIfSet("face", json.isMember(JsonKey(LegacyFontFaceKey)));
|
||||
_logSettingIfSet("size", json.isMember(JsonKey(LegacyFontSizeKey)));
|
||||
_logSettingIfSet("weight", json.isMember(JsonKey(LegacyFontWeightKey)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,16 +194,16 @@ winrt::Microsoft::Terminal::Settings::Model::Profile FontConfig::SourceProfile()
|
||||
|
||||
void FontConfig::_logSettingSet(const std::string_view& setting)
|
||||
{
|
||||
if (setting == "axes" && _FontAxes.has_value())
|
||||
if (setting == "axes" && HasFontAxes())
|
||||
{
|
||||
for (const auto& [mapKey, _] : _FontAxes.value())
|
||||
for (const auto& [mapKey, _] : FontAxes())
|
||||
{
|
||||
_changeLog.emplace(fmt::format(FMT_COMPILE("{}.{}"), setting, til::u16u8(mapKey)));
|
||||
}
|
||||
}
|
||||
else if (setting == "features" && _FontFeatures.has_value())
|
||||
else if (setting == "features" && HasFontFeatures())
|
||||
{
|
||||
for (const auto& [mapKey, _] : _FontFeatures.value())
|
||||
for (const auto& [mapKey, _] : FontFeatures())
|
||||
{
|
||||
_changeLog.emplace(fmt::format(FMT_COMPILE("{}.{}"), setting, til::u16u8(mapKey)));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ Author(s):
|
||||
#include "pch.h"
|
||||
#include "FontConfig.g.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
#include "MTSMSettings.h"
|
||||
#include "IInheritable.h"
|
||||
#include <DefaultSettings.h>
|
||||
@@ -39,13 +40,26 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
Model::Profile SourceProfile();
|
||||
|
||||
// Generic setting access via SettingKey
|
||||
bool HasSetting(FontSettingKey key) const;
|
||||
void ClearSetting(FontSettingKey key);
|
||||
std::vector<FontSettingKey> CurrentSettings() const;
|
||||
|
||||
#define FONT_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
|
||||
INHERITABLE_SETTING(Model::FontConfig, type, name, ##__VA_ARGS__)
|
||||
INHERITABLE_JSON_SETTING(Model::FontConfig, type, name, jsonKey, ##__VA_ARGS__)
|
||||
MTSM_FONT_SETTINGS(FONT_SETTINGS_INITIALIZE)
|
||||
#undef FONT_SETTINGS_INITIALIZE
|
||||
|
||||
// Complex/mutable settings that need backing fields (not JSON-backed)
|
||||
INHERITABLE_SETTING(Model::FontConfig, IFontAxesMap, FontAxes);
|
||||
INHERITABLE_SETTING(Model::FontConfig, IFontFeatureMap, FontFeatures);
|
||||
|
||||
private:
|
||||
winrt::weak_ref<Profile> _sourceProfile;
|
||||
|
||||
// Raw JSON for this layer (font sub-object shape).
|
||||
Json::Value _json{ Json::ValueType::objectValue };
|
||||
|
||||
std::set<std::string> _changeLog;
|
||||
|
||||
void _logSettingSet(const std::string_view& setting);
|
||||
|
||||
@@ -64,11 +64,10 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
|
||||
globals->_defaultProfile = _defaultProfile;
|
||||
globals->_actionMap = _actionMap->Copy();
|
||||
globals->_keybindingsWarnings = _keybindingsWarnings;
|
||||
globals->_json = _json;
|
||||
|
||||
#define GLOBAL_SETTINGS_COPY(type, name, jsonKey, ...) \
|
||||
globals->_##name = _##name;
|
||||
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_COPY)
|
||||
#undef GLOBAL_SETTINGS_COPY
|
||||
// MTSM settings are JSON-backed — they live in _json, which is already deep-copied above.
|
||||
// No per-setting copy needed.
|
||||
|
||||
if (_colorSchemes)
|
||||
{
|
||||
@@ -86,6 +85,8 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
|
||||
globals->_themes.Insert(kv.Key(), *themeImpl->Copy());
|
||||
}
|
||||
}
|
||||
|
||||
// Complex/mutable settings with backing fields need explicit deep copy
|
||||
if (_NewTabMenu)
|
||||
{
|
||||
globals->_NewTabMenu = winrt::single_threaded_vector<Model::NewTabMenuEntry>();
|
||||
@@ -150,26 +151,66 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::FromJson(const Json::Value&
|
||||
|
||||
void GlobalAppSettings::LayerJson(const Json::Value& json, const OriginTag origin)
|
||||
{
|
||||
// Merge incoming JSON keys into stored _json (key-wise, not replacement).
|
||||
for (const auto& key : json.getMemberNames())
|
||||
{
|
||||
_json[key] = json[key];
|
||||
}
|
||||
|
||||
JsonUtils::GetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
|
||||
|
||||
// GH#8076 - when adding enum values to this key, we also changed it from
|
||||
// "useTabSwitcher" to "tabSwitcherMode". Continue supporting
|
||||
// "useTabSwitcher", but prefer "tabSwitcherMode"
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode) || _fixupsAppliedDuringLoad;
|
||||
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, _InputServiceWarning) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, _WarnAboutLargePaste) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste) || _fixupsAppliedDuringLoad;
|
||||
_fixupsAppliedDuringLoad = JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, _ConfirmCloseAllTabs) || _fixupsAppliedDuringLoad;
|
||||
// Normalize legacy keys into canonical _json keys for JSON-backed getters.
|
||||
{
|
||||
Model::TabSwitcherMode legacyVal{};
|
||||
if (JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, legacyVal))
|
||||
{
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
_json["tabSwitcherMode"] = json[JsonKey(LegacyUseTabSwitcherModeKey)];
|
||||
}
|
||||
}
|
||||
{
|
||||
bool legacyVal{};
|
||||
if (JsonUtils::GetValueForKey(json, LegacyInputServiceWarningKey, legacyVal))
|
||||
{
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
_json["warning.inputService"] = json[JsonKey(LegacyInputServiceWarningKey)];
|
||||
}
|
||||
if (JsonUtils::GetValueForKey(json, LegacyWarnAboutLargePasteKey, legacyVal))
|
||||
{
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
_json["warning.largePaste"] = json[JsonKey(LegacyWarnAboutLargePasteKey)];
|
||||
}
|
||||
if (JsonUtils::GetValueForKey(json, LegacyWarnAboutMultiLinePasteKey, legacyVal))
|
||||
{
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
_json["warning.multiLinePaste"] = json[JsonKey(LegacyWarnAboutMultiLinePasteKey)];
|
||||
}
|
||||
if (JsonUtils::GetValueForKey(json, LegacyConfirmCloseAllTabsKey, legacyVal))
|
||||
{
|
||||
_fixupsAppliedDuringLoad = true;
|
||||
_json["warning.confirmCloseAllTabs"] = json[JsonKey(LegacyConfirmCloseAllTabsKey)];
|
||||
}
|
||||
}
|
||||
|
||||
// MTSM settings are now JSON-backed (no backing fields).
|
||||
// Values are already in _json from the merge step above.
|
||||
// We only need to log which settings were set in this layer.
|
||||
#define GLOBAL_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, _##name); \
|
||||
_logSettingIfSet(jsonKey, _##name.has_value());
|
||||
_logSettingIfSet(jsonKey, json.isMember(jsonKey) && !json[jsonKey].isNull());
|
||||
|
||||
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_LAYER_JSON)
|
||||
#undef GLOBAL_SETTINGS_LAYER_JSON
|
||||
|
||||
// GH#11975 We only want to allow sensible values and prevent crashes, so we are clamping those values
|
||||
// Complex/mutable settings that have backing fields (not JSON-backed)
|
||||
JsonUtils::GetValueForKey(json, "disabledProfileSources", _DisabledProfileSources);
|
||||
_logSettingIfSet("disabledProfileSources", _DisabledProfileSources.has_value());
|
||||
JsonUtils::GetValueForKey(json, "newTabMenu", _NewTabMenu);
|
||||
_logSettingIfSet("newTabMenu", _NewTabMenu.has_value());
|
||||
|
||||
// GH#11975We only want to allow sensible values and prevent crashes, so we are clamping those values
|
||||
// We only want to assign if the value did change through clamping,
|
||||
// otherwise we could end up setting defaults that get persisted
|
||||
if (this->HasInitialCols())
|
||||
@@ -304,47 +345,113 @@ Json::Value GlobalAppSettings::ToJson()
|
||||
// These experimental options should be removed from the settings file if they're at their default value.
|
||||
// This prevents them from sticking around forever, even if the user was just experimenting with them.
|
||||
// One could consider this a workaround for the settings UI right now not having a "reset to default" button for these.
|
||||
if (_GraphicsAPI == Control::GraphicsAPI::Automatic)
|
||||
if (HasGraphicsAPI() && GraphicsAPI() == Control::GraphicsAPI::Automatic)
|
||||
{
|
||||
_GraphicsAPI.reset();
|
||||
ClearGraphicsAPI();
|
||||
}
|
||||
if (_TextMeasurement == Control::TextMeasurement::Graphemes)
|
||||
if (HasTextMeasurement() && TextMeasurement() == Control::TextMeasurement::Graphemes)
|
||||
{
|
||||
_TextMeasurement.reset();
|
||||
ClearTextMeasurement();
|
||||
}
|
||||
if (_AmbiguousWidth == Control::AmbiguousWidth::Narrow)
|
||||
if (HasAmbiguousWidth() && AmbiguousWidth() == Control::AmbiguousWidth::Narrow)
|
||||
{
|
||||
_AmbiguousWidth.reset();
|
||||
ClearAmbiguousWidth();
|
||||
}
|
||||
if (_DefaultInputScope == Control::DefaultInputScope::Default)
|
||||
if (HasDefaultInputScope() && DefaultInputScope() == Control::DefaultInputScope::Default)
|
||||
{
|
||||
_DefaultInputScope.reset();
|
||||
ClearDefaultInputScope();
|
||||
}
|
||||
|
||||
if (_DisablePartialInvalidation == false)
|
||||
if (HasDisablePartialInvalidation() && DisablePartialInvalidation() == false)
|
||||
{
|
||||
_DisablePartialInvalidation.reset();
|
||||
ClearDisablePartialInvalidation();
|
||||
}
|
||||
if (_SoftwareRendering == false)
|
||||
if (HasSoftwareRendering() && SoftwareRendering() == false)
|
||||
{
|
||||
_SoftwareRendering.reset();
|
||||
ClearSoftwareRendering();
|
||||
}
|
||||
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
|
||||
JsonUtils::SetValueForKey(json, DefaultProfileKey, _UnparsedDefaultProfile);
|
||||
|
||||
#define GLOBAL_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::SetValueForKey(json, jsonKey, _##name);
|
||||
// MTSM global settings: copy from _json (the source of truth)
|
||||
#define GLOBAL_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
if (_json.isMember(jsonKey) && !_json[jsonKey].isNull()) \
|
||||
{ \
|
||||
json[JsonKey(jsonKey)] = _json[JsonKey(jsonKey)]; \
|
||||
}
|
||||
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_TO_JSON)
|
||||
#undef GLOBAL_SETTINGS_TO_JSON
|
||||
|
||||
// Complex/mutable settings with backing fields
|
||||
JsonUtils::SetValueForKey(json, "disabledProfileSources", _DisabledProfileSources);
|
||||
JsonUtils::SetValueForKey(json, "newTabMenu", _NewTabMenu);
|
||||
|
||||
json[JsonKey(ActionsKey)] = _actionMap->ToJson();
|
||||
json[JsonKey(KeybindingsKey)] = _actionMap->KeyBindingsToJson();
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
bool GlobalAppSettings::HasSetting(GlobalSettingKey key) const
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _GLOBAL_HAS_SETTING(type, name, jsonKey, ...) \
|
||||
case GlobalSettingKey::name: \
|
||||
return Has##name();
|
||||
MTSM_GLOBAL_SETTINGS(_GLOBAL_HAS_SETTING)
|
||||
#undef _GLOBAL_HAS_SETTING
|
||||
case GlobalSettingKey::_UnparsedDefaultProfile:
|
||||
return HasUnparsedDefaultProfile();
|
||||
case GlobalSettingKey::_DisabledProfileSources:
|
||||
return HasDisabledProfileSources();
|
||||
case GlobalSettingKey::_NewTabMenu:
|
||||
return HasNewTabMenu();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalAppSettings::ClearSetting(GlobalSettingKey key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _GLOBAL_CLEAR_SETTING(type, name, jsonKey, ...) \
|
||||
case GlobalSettingKey::name: \
|
||||
Clear##name(); \
|
||||
break;
|
||||
MTSM_GLOBAL_SETTINGS(_GLOBAL_CLEAR_SETTING)
|
||||
#undef _GLOBAL_CLEAR_SETTING
|
||||
case GlobalSettingKey::_UnparsedDefaultProfile:
|
||||
ClearUnparsedDefaultProfile();
|
||||
break;
|
||||
case GlobalSettingKey::_DisabledProfileSources:
|
||||
ClearDisabledProfileSources();
|
||||
break;
|
||||
case GlobalSettingKey::_NewTabMenu:
|
||||
ClearNewTabMenu();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<GlobalSettingKey> GlobalAppSettings::CurrentSettings() const
|
||||
{
|
||||
std::vector<GlobalSettingKey> result;
|
||||
for (auto i = 0; i < static_cast<int>(GlobalSettingKey::SETTINGS_SIZE); i++)
|
||||
{
|
||||
const auto key = static_cast<GlobalSettingKey>(i);
|
||||
if (HasSetting(key))
|
||||
{
|
||||
result.push_back(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GlobalAppSettings::FixupsAppliedDuringLoad()
|
||||
{
|
||||
return _fixupsAppliedDuringLoad || _actionMap->FixupsAppliedDuringLoad();
|
||||
@@ -394,9 +501,9 @@ bool GlobalAppSettings::ShouldUsePersistedLayout() const
|
||||
void GlobalAppSettings::ResolveMediaResources(const Model::MediaResourceResolver& resolver)
|
||||
{
|
||||
_actionMap->ResolveMediaResourcesWithBasePath(SourceBasePath, resolver);
|
||||
if (_NewTabMenu)
|
||||
if (HasNewTabMenu())
|
||||
{
|
||||
for (const auto& entry : *_NewTabMenu)
|
||||
for (const auto& entry : NewTabMenu())
|
||||
{
|
||||
if (const auto resolvable{ entry.try_as<IPathlessMediaResourceContainer>() })
|
||||
{
|
||||
@@ -414,11 +521,12 @@ void GlobalAppSettings::_logSettingSet(const std::string_view& setting)
|
||||
{
|
||||
if (setting == "theme")
|
||||
{
|
||||
if (_Theme.has_value())
|
||||
if (HasTheme())
|
||||
{
|
||||
const auto theme = Theme();
|
||||
// ThemePair always has a Dark/Light value,
|
||||
// so we need to check if they were explicitly set
|
||||
if (_Theme->DarkName() == _Theme->LightName())
|
||||
if (theme.DarkName() == theme.LightName())
|
||||
{
|
||||
_changeLog.emplace(setting);
|
||||
}
|
||||
@@ -431,9 +539,9 @@ void GlobalAppSettings::_logSettingSet(const std::string_view& setting)
|
||||
}
|
||||
else if (setting == "newTabMenu")
|
||||
{
|
||||
if (_NewTabMenu.has_value())
|
||||
if (HasNewTabMenu())
|
||||
{
|
||||
for (const auto& entry : *_NewTabMenu)
|
||||
for (const auto& entry : NewTabMenu())
|
||||
{
|
||||
std::string entryType;
|
||||
switch (entry.Type())
|
||||
@@ -476,7 +584,7 @@ void GlobalAppSettings::UpdateCommandID(const Model::Command& cmd, winrt::hstrin
|
||||
_actionMap->UpdateCommandID(cmd, newID);
|
||||
// newID might have been empty when this function was called, if so actionMap would have generated a new ID, use that
|
||||
newID = cmd.ID();
|
||||
if (_NewTabMenu)
|
||||
if (HasNewTabMenu())
|
||||
{
|
||||
// Recursive lambda function to look through all the new tab menu entries and update IDs accordingly
|
||||
std::function<void(const Model::NewTabMenuEntry&)> recursiveEntryIdUpdate;
|
||||
@@ -503,7 +611,7 @@ void GlobalAppSettings::UpdateCommandID(const Model::Command& cmd, winrt::hstrin
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& entry : *_NewTabMenu)
|
||||
for (const auto& entry : NewTabMenu())
|
||||
{
|
||||
recursiveEntryIdUpdate(entry);
|
||||
}
|
||||
@@ -515,8 +623,8 @@ void GlobalAppSettings::_logSettingIfSet(const std::string_view& setting, const
|
||||
if (isSet)
|
||||
{
|
||||
// Exclude some false positives from userDefaults.json
|
||||
const bool settingCopyFormattingToDefault = til::equals_insensitive_ascii(setting, "copyFormatting") && _CopyFormatting.has_value() && _CopyFormatting.value() == static_cast<Control::CopyFormat>(0);
|
||||
const bool settingNTMToDefault = til::equals_insensitive_ascii(setting, "newTabMenu") && _NewTabMenu.has_value() && _NewTabMenu->Size() == 1 && _NewTabMenu->GetAt(0).Type() == NewTabMenuEntryType::RemainingProfiles;
|
||||
const bool settingCopyFormattingToDefault = til::equals_insensitive_ascii(setting, "copyFormatting") && HasCopyFormatting() && CopyFormatting() == static_cast<Control::CopyFormat>(0);
|
||||
const bool settingNTMToDefault = til::equals_insensitive_ascii(setting, "newTabMenu") && HasNewTabMenu() && NewTabMenu().Size() == 1 && NewTabMenu().GetAt(0).Type() == NewTabMenuEntryType::RemainingProfiles;
|
||||
if (!settingCopyFormattingToDefault && !settingNTMToDefault)
|
||||
{
|
||||
_logSettingSet(setting);
|
||||
|
||||
@@ -18,6 +18,7 @@ Author(s):
|
||||
#include "GlobalAppSettings.g.h"
|
||||
#include "IInheritable.h"
|
||||
#include "MTSMSettings.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
#include "ActionMap.h"
|
||||
#include "Command.h"
|
||||
@@ -56,6 +57,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
Json::Value ToJson();
|
||||
bool FixupsAppliedDuringLoad();
|
||||
|
||||
// Generic setting access via SettingKey
|
||||
bool HasSetting(GlobalSettingKey key) const;
|
||||
void ClearSetting(GlobalSettingKey key);
|
||||
std::vector<GlobalSettingKey> CurrentSettings() const;
|
||||
|
||||
const std::vector<SettingsLoadWarnings>& KeybindingsWarnings() const;
|
||||
|
||||
// This DefaultProfile() setter is called by CascadiaSettings,
|
||||
@@ -83,10 +89,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L"");
|
||||
|
||||
#define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
|
||||
INHERITABLE_SETTING_WITH_LOGGING(Model::GlobalAppSettings, type, name, jsonKey, ##__VA_ARGS__)
|
||||
INHERITABLE_JSON_SETTING_WITH_LOGGING(Model::GlobalAppSettings, type, name, jsonKey, ##__VA_ARGS__)
|
||||
MTSM_GLOBAL_SETTINGS(GLOBAL_SETTINGS_INITIALIZE)
|
||||
#undef GLOBAL_SETTINGS_INITIALIZE
|
||||
|
||||
// Complex/mutable settings that need backing fields (not JSON-backed)
|
||||
INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, nullptr);
|
||||
INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry>, NewTabMenu, winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} }));
|
||||
|
||||
private:
|
||||
#ifdef NDEBUG
|
||||
static constexpr bool debugFeaturesDefault{ false };
|
||||
@@ -94,6 +104,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static constexpr bool debugFeaturesDefault{ true };
|
||||
#endif
|
||||
|
||||
// Raw JSON for this layer. Populated by LayerJson(), will become the
|
||||
// source of truth for settings once the JSON-backed refactor is complete.
|
||||
Json::Value _json{ Json::ValueType::objectValue };
|
||||
|
||||
winrt::guid _defaultProfile{};
|
||||
bool _fixupsAppliedDuringLoad{ false };
|
||||
bool _legacyReloadEnvironmentVariables{ true };
|
||||
|
||||
@@ -14,6 +14,8 @@ Author(s):
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "JsonUtils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
template<typename T>
|
||||
@@ -265,3 +267,165 @@ public: \
|
||||
_##name = std::optional<type>{ std::nullopt }; \
|
||||
} \
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// JSON-backed variants of the above macros.
|
||||
// These have NO std::optional<T> backing field — _json is the source of truth.
|
||||
// Getters read from _json with type conversion inline.
|
||||
// Setters write to _json via SetValueForKey.
|
||||
// Has/Clear check/modify _json directly.
|
||||
// =============================================================================
|
||||
|
||||
// Shared base for JSON-backed inheritable settings.
|
||||
// No backing field; all state lives in _json.
|
||||
#define _BASE_INHERITABLE_JSON_SETTING(projectedType, type, name, jsonKey, ...) \
|
||||
public: \
|
||||
/* Returns true if the user explicitly set the value, false otherwise*/ \
|
||||
bool Has##name() const \
|
||||
{ \
|
||||
return _json.isMember(jsonKey) && !_json[jsonKey].isNull(); \
|
||||
} \
|
||||
\
|
||||
projectedType name##OverrideSource() \
|
||||
{ \
|
||||
/*iterate through parents to find one with a value*/ \
|
||||
for (const auto& parent : _parents) \
|
||||
{ \
|
||||
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
||||
{ \
|
||||
return *source; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/*no source was found*/ \
|
||||
return nullptr; \
|
||||
} \
|
||||
\
|
||||
/* Clear the user set value */ \
|
||||
void Clear##name() \
|
||||
{ \
|
||||
_json.removeMember(JsonKey(jsonKey)); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
/* Read value from this layer's _json only (no parent walk) */ \
|
||||
std::optional<type> _get##name##FromThisLayer() const \
|
||||
{ \
|
||||
if (Has##name()) \
|
||||
{ \
|
||||
type val{ __VA_ARGS__ }; \
|
||||
::Microsoft::Terminal::Settings::Model::JsonUtils::GetValueForKey( \
|
||||
_json, jsonKey, val); \
|
||||
return val; \
|
||||
} \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
\
|
||||
std::optional<type> _get##name##Impl() const \
|
||||
{ \
|
||||
/*return value from this layer*/ \
|
||||
if (auto val{ _get##name##FromThisLayer() }) \
|
||||
{ \
|
||||
return val; \
|
||||
} \
|
||||
\
|
||||
/*iterate through parents to find a value*/ \
|
||||
for (const auto& parent : _parents) \
|
||||
{ \
|
||||
if (auto val{ parent->_get##name##Impl() }) \
|
||||
{ \
|
||||
return val; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/*no value was found*/ \
|
||||
return std::nullopt; \
|
||||
} \
|
||||
\
|
||||
auto _get##name##OverrideSourceImpl()->decltype(get_strong()) \
|
||||
{ \
|
||||
/*we have a value*/ \
|
||||
if (Has##name()) \
|
||||
{ \
|
||||
return get_strong(); \
|
||||
} \
|
||||
\
|
||||
/*iterate through parents to find one with a value*/ \
|
||||
for (const auto& parent : _parents) \
|
||||
{ \
|
||||
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
||||
{ \
|
||||
return source; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/*no value was found*/ \
|
||||
return nullptr; \
|
||||
} \
|
||||
\
|
||||
auto _get##name##OverrideSourceAndValueImpl() \
|
||||
->std::pair<decltype(get_strong()), std::optional<type>> \
|
||||
{ \
|
||||
/*we have a value*/ \
|
||||
if (Has##name()) \
|
||||
{ \
|
||||
return { get_strong(), _get##name##FromThisLayer() }; \
|
||||
} \
|
||||
\
|
||||
/*iterate through parents to find one with a value*/ \
|
||||
for (const auto& parent : _parents) \
|
||||
{ \
|
||||
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
||||
{ \
|
||||
return { source, source->_get##name##FromThisLayer() }; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
/*no value was found*/ \
|
||||
return { nullptr, std::nullopt }; \
|
||||
}
|
||||
|
||||
// JSON-backed inheritable setting (no logging).
|
||||
// No backing field; getter reads from _json, setter writes to _json.
|
||||
#define INHERITABLE_JSON_SETTING(projectedType, type, name, jsonKey, ...) \
|
||||
_BASE_INHERITABLE_JSON_SETTING(projectedType, type, name, jsonKey, __VA_ARGS__) \
|
||||
public: \
|
||||
/* Returns the resolved value for this setting */ \
|
||||
/* fallback: this layer --> inherited value --> default */ \
|
||||
type name() const \
|
||||
{ \
|
||||
const auto val{ _get##name##Impl() }; \
|
||||
return val ? *val : type{ __VA_ARGS__ }; \
|
||||
} \
|
||||
\
|
||||
/* Overwrite the user set value in _json */ \
|
||||
void name(const type& value) \
|
||||
{ \
|
||||
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey( \
|
||||
_json, jsonKey, value); \
|
||||
}
|
||||
|
||||
// JSON-backed inheritable setting with change logging.
|
||||
// No backing field; getter reads from _json, setter writes to _json and logs.
|
||||
#define INHERITABLE_JSON_SETTING_WITH_LOGGING(projectedType, type, name, jsonKey, ...) \
|
||||
_BASE_INHERITABLE_JSON_SETTING(projectedType, type, name, jsonKey, __VA_ARGS__) \
|
||||
public: \
|
||||
/* Returns the resolved value for this setting */ \
|
||||
/* fallback: this layer --> inherited value --> default */ \
|
||||
type name() const \
|
||||
{ \
|
||||
const auto val{ _get##name##Impl() }; \
|
||||
return val ? *val : type{ __VA_ARGS__ }; \
|
||||
} \
|
||||
\
|
||||
/* Overwrite the user set value, log the change, and write to _json */ \
|
||||
void name(const type& value) \
|
||||
{ \
|
||||
const auto existingVal{ _get##name##FromThisLayer() }; \
|
||||
if (!existingVal.has_value() || existingVal.value() != value) \
|
||||
{ \
|
||||
_logSettingSet(jsonKey); \
|
||||
} \
|
||||
::Microsoft::Terminal::Settings::Model::JsonUtils::SetValueForKey( \
|
||||
_json, jsonKey, value); \
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ Module Name:
|
||||
Abstract:
|
||||
- Contains most of the settings within Terminal Settings Model (global, profile, font, appearance)
|
||||
- To add a new setting to any one of those classes, simply add it to the respective list below, following the macro format
|
||||
- Also defines SettingKey enums for generic setting access
|
||||
|
||||
Author(s):
|
||||
- Pankaj Bhojwani - October 2021
|
||||
@@ -62,13 +63,11 @@ Author(s):
|
||||
X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \
|
||||
X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \
|
||||
X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \
|
||||
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, "disabledProfileSources", nullptr) \
|
||||
X(bool, ShowAdminShield, "showAdminShield", true) \
|
||||
X(bool, TrimPaste, "trimPaste", true) \
|
||||
X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \
|
||||
X(bool, EnableShellCompletionMenu, "experimental.enableShellCompletionMenu", false) \
|
||||
X(bool, EnableUnfocusedAcrylic, "compatibility.enableUnfocusedAcrylic", true) \
|
||||
X(winrt::Windows::Foundation::Collections::IVector<Model::NewTabMenuEntry>, NewTabMenu, "newTabMenu", winrt::single_threaded_vector<Model::NewTabMenuEntry>({ Model::RemainingProfilesEntry{} })) \
|
||||
X(bool, AllowHeadless, "compatibility.allowHeadless", false) \
|
||||
X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22") \
|
||||
X(bool, ShowTabsFullscreen, "showTabsFullscreen", false)
|
||||
@@ -88,15 +87,12 @@ Author(s):
|
||||
X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \
|
||||
X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \
|
||||
X(hstring, StartingDirectory, "startingDirectory") \
|
||||
X(IMediaResource, Icon, "icon", implementation::MediaResource::FromString(L"\uE756")) \
|
||||
X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \
|
||||
X(guid, ConnectionType, "connectionType") \
|
||||
X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) \
|
||||
X(hstring, TabTitle, "tabTitle") \
|
||||
X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
|
||||
X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \
|
||||
X(bool, RightClickContextMenu, "rightClickContextMenu", false) \
|
||||
X(Windows::Foundation::Collections::IVector<IMediaResource>, BellSound, "bellSound", nullptr) \
|
||||
X(bool, Elevate, "elevate", false) \
|
||||
X(bool, AutoMarkPrompts, "autoMarkPrompts", true) \
|
||||
X(bool, ShowMarks, "showMarksOnScrollbar", false) \
|
||||
@@ -124,8 +120,6 @@ Author(s):
|
||||
X(hstring, FontFace, "face", DEFAULT_FONT_FACE) \
|
||||
X(float, FontSize, "size", DEFAULT_FONT_SIZE) \
|
||||
X(winrt::Windows::UI::Text::FontWeight, FontWeight, "weight", DEFAULT_FONT_WEIGHT) \
|
||||
X(IFontAxesMap, FontAxes, "axes") \
|
||||
X(IFontFeatureMap, FontFeatures, "features") \
|
||||
X(bool, EnableBuiltinGlyphs, "builtinGlyphs", true) \
|
||||
X(bool, EnableColorGlyphs, "colorGlyphs", true) \
|
||||
X(winrt::hstring, CellWidth, "cellWidth") \
|
||||
@@ -137,10 +131,7 @@ Author(s):
|
||||
X(float, BackgroundImageOpacity, "backgroundImageOpacity", 1.0f) \
|
||||
X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \
|
||||
X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \
|
||||
X(IMediaResource, PixelShaderPath, "experimental.pixelShaderPath", implementation::MediaResource::Empty()) \
|
||||
X(IMediaResource, PixelShaderImagePath, "experimental.pixelShaderImagePath", implementation::MediaResource::Empty()) \
|
||||
X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \
|
||||
X(IMediaResource, BackgroundImagePath, "backgroundImage", implementation::MediaResource::Empty()) \
|
||||
X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \
|
||||
X(Core::AdjustTextMode, AdjustIndistinguishableColors, "adjustIndistinguishableColors", Core::AdjustTextMode::Automatic) \
|
||||
X(bool, UseAcrylic, "useAcrylic", false)
|
||||
@@ -174,3 +165,176 @@ Author(s):
|
||||
X(winrt::Microsoft::Terminal::Settings::Model::ThemeColor, UnfocusedBackground, "unfocusedBackground", nullptr) \
|
||||
X(winrt::Microsoft::Terminal::Settings::Model::IconStyle, IconStyle, "iconStyle", winrt::Microsoft::Terminal::Settings::Model::IconStyle::Default) \
|
||||
X(winrt::Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility, ShowCloseButton, "showCloseButton", winrt::Microsoft::Terminal::Settings::Model::TabCloseButtonVisibility::Always)
|
||||
|
||||
// SettingKey enums: provide a generic way to reference settings by key.
|
||||
// Generated from the MTSM macros above. Also includes special-cased settings
|
||||
// that are not part of the macro lists (prefixed with _ to avoid name collisions).
|
||||
// SETTINGS_SIZE is a sentinel value that must remain last in each enum.
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
#define _MTSM_ENUM_VALUE(type, name, jsonKey, ...) name,
|
||||
|
||||
enum class ProfileSettingKey : int
|
||||
{
|
||||
MTSM_PROFILE_SETTINGS(_MTSM_ENUM_VALUE)
|
||||
// Special-cased profile settings (not in MTSM_PROFILE_SETTINGS due to
|
||||
// custom JSON parsing, but included here for completeness)
|
||||
_Name,
|
||||
_Guid,
|
||||
_Source,
|
||||
_Hidden,
|
||||
_Padding,
|
||||
_TabColor,
|
||||
// Complex/mutable settings with backing fields
|
||||
_Icon,
|
||||
_EnvironmentVariables,
|
||||
_BellSound,
|
||||
SETTINGS_SIZE
|
||||
};
|
||||
|
||||
enum class GlobalSettingKey : int
|
||||
{
|
||||
MTSM_GLOBAL_SETTINGS(_MTSM_ENUM_VALUE)
|
||||
// Special-cased global settings
|
||||
_UnparsedDefaultProfile,
|
||||
// Complex/mutable settings with backing fields
|
||||
_DisabledProfileSources,
|
||||
_NewTabMenu,
|
||||
SETTINGS_SIZE
|
||||
};
|
||||
|
||||
enum class FontSettingKey : int
|
||||
{
|
||||
MTSM_FONT_SETTINGS(_MTSM_ENUM_VALUE)
|
||||
// Complex/mutable settings with backing fields
|
||||
_FontAxes,
|
||||
_FontFeatures,
|
||||
SETTINGS_SIZE
|
||||
};
|
||||
|
||||
enum class AppearanceSettingKey : int
|
||||
{
|
||||
MTSM_APPEARANCE_SETTINGS(_MTSM_ENUM_VALUE)
|
||||
// Special-cased appearance settings
|
||||
_Foreground,
|
||||
_Background,
|
||||
_SelectionBackground,
|
||||
_CursorColor,
|
||||
_Opacity,
|
||||
_DarkColorSchemeName,
|
||||
_LightColorSchemeName,
|
||||
// Complex/mutable settings with backing fields
|
||||
_PixelShaderPath,
|
||||
_PixelShaderImagePath,
|
||||
_BackgroundImagePath,
|
||||
SETTINGS_SIZE
|
||||
};
|
||||
|
||||
#undef _MTSM_ENUM_VALUE
|
||||
|
||||
// JSON key lookup: returns the JSON key string for a given SettingKey.
|
||||
// Generated from the same macros to maintain a single source of truth.
|
||||
constexpr std::string_view JsonKeyForSetting(ProfileSettingKey key)
|
||||
{
|
||||
#define _MTSM_KEY_CASE(type, name, jsonKey, ...) \
|
||||
case ProfileSettingKey::name: \
|
||||
return jsonKey;
|
||||
switch (key)
|
||||
{
|
||||
MTSM_PROFILE_SETTINGS(_MTSM_KEY_CASE)
|
||||
case ProfileSettingKey::_Name:
|
||||
return "name";
|
||||
case ProfileSettingKey::_Guid:
|
||||
return "guid";
|
||||
case ProfileSettingKey::_Source:
|
||||
return "source";
|
||||
case ProfileSettingKey::_Hidden:
|
||||
return "hidden";
|
||||
case ProfileSettingKey::_Padding:
|
||||
return "padding";
|
||||
case ProfileSettingKey::_TabColor:
|
||||
return "tabColor";
|
||||
case ProfileSettingKey::_Icon:
|
||||
return "icon";
|
||||
case ProfileSettingKey::_EnvironmentVariables:
|
||||
return "environment";
|
||||
case ProfileSettingKey::_BellSound:
|
||||
return "bellSound";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
#undef _MTSM_KEY_CASE
|
||||
}
|
||||
|
||||
constexpr std::string_view JsonKeyForSetting(GlobalSettingKey key)
|
||||
{
|
||||
#define _MTSM_KEY_CASE(type, name, jsonKey, ...) \
|
||||
case GlobalSettingKey::name: \
|
||||
return jsonKey;
|
||||
switch (key)
|
||||
{
|
||||
MTSM_GLOBAL_SETTINGS(_MTSM_KEY_CASE)
|
||||
case GlobalSettingKey::_UnparsedDefaultProfile:
|
||||
return "defaultProfile";
|
||||
case GlobalSettingKey::_DisabledProfileSources:
|
||||
return "disabledProfileSources";
|
||||
case GlobalSettingKey::_NewTabMenu:
|
||||
return "newTabMenu";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
#undef _MTSM_KEY_CASE
|
||||
}
|
||||
|
||||
constexpr std::string_view JsonKeyForSetting(FontSettingKey key)
|
||||
{
|
||||
#define _MTSM_KEY_CASE(type, name, jsonKey, ...) \
|
||||
case FontSettingKey::name: \
|
||||
return jsonKey;
|
||||
switch (key)
|
||||
{
|
||||
MTSM_FONT_SETTINGS(_MTSM_KEY_CASE)
|
||||
case FontSettingKey::_FontAxes:
|
||||
return "axes";
|
||||
case FontSettingKey::_FontFeatures:
|
||||
return "features";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
#undef _MTSM_KEY_CASE
|
||||
}
|
||||
|
||||
constexpr std::string_view JsonKeyForSetting(AppearanceSettingKey key)
|
||||
{
|
||||
#define _MTSM_KEY_CASE(type, name, jsonKey, ...) \
|
||||
case AppearanceSettingKey::name: \
|
||||
return jsonKey;
|
||||
switch (key)
|
||||
{
|
||||
MTSM_APPEARANCE_SETTINGS(_MTSM_KEY_CASE)
|
||||
case AppearanceSettingKey::_Foreground:
|
||||
return "foreground";
|
||||
case AppearanceSettingKey::_Background:
|
||||
return "background";
|
||||
case AppearanceSettingKey::_SelectionBackground:
|
||||
return "selectionBackground";
|
||||
case AppearanceSettingKey::_CursorColor:
|
||||
return "cursorColor";
|
||||
case AppearanceSettingKey::_Opacity:
|
||||
return "opacity";
|
||||
case AppearanceSettingKey::_DarkColorSchemeName:
|
||||
return "colorScheme";
|
||||
case AppearanceSettingKey::_LightColorSchemeName:
|
||||
return "colorScheme";
|
||||
case AppearanceSettingKey::_PixelShaderPath:
|
||||
return "experimental.pixelShaderPath";
|
||||
case AppearanceSettingKey::_PixelShaderImagePath:
|
||||
return "experimental.pixelShaderImagePath";
|
||||
case AppearanceSettingKey::_BackgroundImagePath:
|
||||
return "backgroundImage";
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
#undef _MTSM_KEY_CASE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,15 +115,16 @@ winrt::com_ptr<Profile> Profile::CopySettings() const
|
||||
profile->_Origin = _Origin;
|
||||
profile->_FontInfo = *fontInfo;
|
||||
profile->_DefaultAppearance = *defaultAppearance;
|
||||
profile->_json = _json;
|
||||
|
||||
#define PROFILE_SETTINGS_COPY(type, name, jsonKey, ...) \
|
||||
profile->_##name = _##name;
|
||||
MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_COPY)
|
||||
#undef PROFILE_SETTINGS_COPY
|
||||
// MTSM settings are JSON-backed — they live in _json, which is already deep-copied above.
|
||||
// No per-setting copy needed.
|
||||
|
||||
// Complex/mutable settings with backing fields need explicit copy
|
||||
profile->_Icon = _Icon;
|
||||
profile->_EnvironmentVariables = _EnvironmentVariables;
|
||||
if (_BellSound)
|
||||
{
|
||||
// BellSound is an IVector<>, so we need to make a new vector pointing at the same objects
|
||||
profile->_BellSound = winrt::single_threaded_vector(wil::to_vector(*_BellSound));
|
||||
}
|
||||
|
||||
@@ -169,6 +170,13 @@ winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Prof
|
||||
// <none>
|
||||
void Profile::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// Merge incoming JSON keys into stored _json (key-wise, not replacement).
|
||||
// This preserves keys from earlier LayerJson calls that aren't in the new JSON.
|
||||
for (const auto& key : json.getMemberNames())
|
||||
{
|
||||
_json[key] = json[key];
|
||||
}
|
||||
|
||||
// Appearance Settings
|
||||
auto defaultAppearanceImpl = winrt::get_self<implementation::AppearanceConfig>(_DefaultAppearance);
|
||||
defaultAppearanceImpl->LayerJson(json);
|
||||
@@ -196,18 +204,38 @@ void Profile::LayerJson(const Json::Value& json)
|
||||
_logSettingIfSet(TabColorKey, _TabColor.has_value());
|
||||
|
||||
// Try to load some legacy keys, to migrate them.
|
||||
// Done _before_ the MTSM_PROFILE_SETTINGS, which have the updated keys.
|
||||
JsonUtils::GetValueForKey(json, LegacyShowMarksKey, _ShowMarks);
|
||||
JsonUtils::GetValueForKey(json, LegacyAutoMarkPromptsKey, _AutoMarkPrompts);
|
||||
JsonUtils::GetValueForKey(json, LegacyRightClickContextMenuKey, _RightClickContextMenu);
|
||||
// Normalize legacy keys into canonical _json keys so JSON-backed getters find them.
|
||||
// Done _before_ the MTSM_PROFILE_SETTINGS logging, which have the updated keys.
|
||||
if (json.isMember(JsonKey(LegacyShowMarksKey)))
|
||||
{
|
||||
_json["showMarksOnScrollbar"] = json[JsonKey(LegacyShowMarksKey)];
|
||||
}
|
||||
if (json.isMember(JsonKey(LegacyAutoMarkPromptsKey)))
|
||||
{
|
||||
_json["autoMarkPrompts"] = json[JsonKey(LegacyAutoMarkPromptsKey)];
|
||||
}
|
||||
if (json.isMember(JsonKey(LegacyRightClickContextMenuKey)))
|
||||
{
|
||||
_json["rightClickContextMenu"] = json[JsonKey(LegacyRightClickContextMenuKey)];
|
||||
}
|
||||
|
||||
// MTSM settings are now JSON-backed (no backing fields).
|
||||
// Values are already in _json from the merge step above.
|
||||
// We only need to log which settings were set in this layer.
|
||||
#define PROFILE_SETTINGS_LAYER_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, _##name); \
|
||||
_logSettingIfSet(jsonKey, _##name.has_value());
|
||||
_logSettingIfSet(jsonKey, json.isMember(jsonKey) && !json[jsonKey].isNull());
|
||||
|
||||
MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_LAYER_JSON)
|
||||
#undef PROFILE_SETTINGS_LAYER_JSON
|
||||
|
||||
// Complex/mutable settings that have backing fields (not JSON-backed)
|
||||
JsonUtils::GetValueForKey(json, "icon", _Icon);
|
||||
_logSettingIfSet("icon", _Icon.has_value());
|
||||
JsonUtils::GetValueForKey(json, "environment", _EnvironmentVariables);
|
||||
_logSettingIfSet("environment", _EnvironmentVariables.has_value());
|
||||
JsonUtils::GetValueForKey(json, "bellSound", _BellSound);
|
||||
_logSettingIfSet("bellSound", _BellSound.has_value());
|
||||
|
||||
if (json.isMember(JsonKey(UnfocusedAppearanceKey)))
|
||||
{
|
||||
auto unfocusedAppearance{ winrt::make_self<implementation::AppearanceConfig>(weak_ref<Model::Profile>(*this)) };
|
||||
@@ -345,12 +373,21 @@ Json::Value Profile::ToJson() const
|
||||
|
||||
JsonUtils::SetValueForKey(json, TabColorKey, _TabColor);
|
||||
|
||||
#define PROFILE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
JsonUtils::SetValueForKey(json, jsonKey, _##name);
|
||||
// MTSM profile settings: copy from _json (the source of truth)
|
||||
#define PROFILE_SETTINGS_TO_JSON(type, name, jsonKey, ...) \
|
||||
if (_json.isMember(jsonKey) && !_json[jsonKey].isNull()) \
|
||||
{ \
|
||||
json[JsonKey(jsonKey)] = _json[JsonKey(jsonKey)]; \
|
||||
}
|
||||
|
||||
MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_TO_JSON)
|
||||
#undef PROFILE_SETTINGS_TO_JSON
|
||||
|
||||
// Complex/mutable settings with backing fields
|
||||
JsonUtils::SetValueForKey(json, "icon", _Icon);
|
||||
JsonUtils::SetValueForKey(json, "environment", _EnvironmentVariables);
|
||||
JsonUtils::SetValueForKey(json, "bellSound", _BellSound);
|
||||
|
||||
if (auto fontJSON = winrt::get_self<FontConfig>(_FontInfo)->ToJson(); !fontJSON.empty())
|
||||
{
|
||||
json[JsonKey(FontInfoKey)] = std::move(fontJSON);
|
||||
@@ -364,6 +401,94 @@ Json::Value Profile::ToJson() const
|
||||
return json;
|
||||
}
|
||||
|
||||
bool Profile::HasSetting(ProfileSettingKey key) const
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _PROFILE_HAS_SETTING(type, name, jsonKey, ...) \
|
||||
case ProfileSettingKey::name: \
|
||||
return Has##name();
|
||||
MTSM_PROFILE_SETTINGS(_PROFILE_HAS_SETTING)
|
||||
#undef _PROFILE_HAS_SETTING
|
||||
case ProfileSettingKey::_Name:
|
||||
return HasName();
|
||||
case ProfileSettingKey::_Guid:
|
||||
return HasGuid();
|
||||
case ProfileSettingKey::_Source:
|
||||
return HasSource();
|
||||
case ProfileSettingKey::_Hidden:
|
||||
return HasHidden();
|
||||
case ProfileSettingKey::_Padding:
|
||||
return HasPadding();
|
||||
case ProfileSettingKey::_TabColor:
|
||||
return HasTabColor();
|
||||
case ProfileSettingKey::_Icon:
|
||||
return HasIcon();
|
||||
case ProfileSettingKey::_EnvironmentVariables:
|
||||
return HasEnvironmentVariables();
|
||||
case ProfileSettingKey::_BellSound:
|
||||
return HasBellSound();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Profile::ClearSetting(ProfileSettingKey key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
#define _PROFILE_CLEAR_SETTING(type, name, jsonKey, ...) \
|
||||
case ProfileSettingKey::name: \
|
||||
Clear##name(); \
|
||||
break;
|
||||
MTSM_PROFILE_SETTINGS(_PROFILE_CLEAR_SETTING)
|
||||
#undef _PROFILE_CLEAR_SETTING
|
||||
case ProfileSettingKey::_Name:
|
||||
ClearName();
|
||||
break;
|
||||
case ProfileSettingKey::_Guid:
|
||||
ClearGuid();
|
||||
break;
|
||||
case ProfileSettingKey::_Source:
|
||||
ClearSource();
|
||||
break;
|
||||
case ProfileSettingKey::_Hidden:
|
||||
ClearHidden();
|
||||
break;
|
||||
case ProfileSettingKey::_Padding:
|
||||
ClearPadding();
|
||||
break;
|
||||
case ProfileSettingKey::_TabColor:
|
||||
ClearTabColor();
|
||||
break;
|
||||
case ProfileSettingKey::_Icon:
|
||||
ClearIcon();
|
||||
break;
|
||||
case ProfileSettingKey::_EnvironmentVariables:
|
||||
ClearEnvironmentVariables();
|
||||
break;
|
||||
case ProfileSettingKey::_BellSound:
|
||||
ClearBellSound();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ProfileSettingKey> Profile::CurrentSettings() const
|
||||
{
|
||||
std::vector<ProfileSettingKey> result;
|
||||
for (auto i = 0; i < static_cast<int>(ProfileSettingKey::SETTINGS_SIZE); i++)
|
||||
{
|
||||
const auto key = static_cast<ProfileSettingKey>(i);
|
||||
if (HasSetting(key))
|
||||
{
|
||||
result.push_back(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a commandLine like the following:
|
||||
// * "C:\WINDOWS\System32\cmd.exe"
|
||||
// * "pwsh -WorkingDirectory ~"
|
||||
@@ -507,8 +632,8 @@ void Profile::_logSettingIfSet(const std::string_view& setting, const bool isSet
|
||||
const bool isACS = _Name.has_value() && til::equals_insensitive_ascii(*_Name, L"Azure Cloud Shell");
|
||||
const bool isWTDynamicProfile = _Source.has_value() && til::starts_with(*_Source, L"Windows.Terminal");
|
||||
const bool settingHiddenToFalse = til::equals_insensitive_ascii(setting, HiddenKey) && _Hidden.has_value() && _Hidden == false;
|
||||
const bool settingCommandlineToWinPow = til::equals_insensitive_ascii(setting, "commandline") && _Commandline.has_value() && til::equals_insensitive_ascii(*_Commandline, L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe");
|
||||
const bool settingCommandlineToCmd = til::equals_insensitive_ascii(setting, "commandline") && _Commandline.has_value() && til::equals_insensitive_ascii(*_Commandline, L"%SystemRoot%\\System32\\cmd.exe");
|
||||
const bool settingCommandlineToWinPow = til::equals_insensitive_ascii(setting, "commandline") && HasCommandline() && til::equals_insensitive_ascii(Commandline(), L"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe");
|
||||
const bool settingCommandlineToCmd = til::equals_insensitive_ascii(setting, "commandline") && HasCommandline() && til::equals_insensitive_ascii(Commandline(), L"%SystemRoot%\\System32\\cmd.exe");
|
||||
// clang-format off
|
||||
if (!(isWinPow && (settingHiddenToFalse || settingCommandlineToWinPow))
|
||||
&& !(isCmd && (settingHiddenToFalse || settingCommandlineToCmd))
|
||||
@@ -541,7 +666,7 @@ void Profile::ResolveMediaResources(const Model::MediaResourceResolver& resolver
|
||||
auto newIcon{ MediaResource::FromString(icon->Path()) };
|
||||
const std::wstring cmdline{ NormalizeCommandLine(iconSource->Commandline().c_str()) };
|
||||
newIcon.Resolve(cmdline.c_str() /* c_str: give hstring a chance to find the null terminator */);
|
||||
iconSource->_Icon = std::move(newIcon);
|
||||
iconSource->Icon(std::move(newIcon));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Author(s):
|
||||
#include "MTSMSettings.h"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
#include <DefaultSettings.h>
|
||||
#include "MediaResourceSupport.h"
|
||||
#include "AppearanceConfig.h"
|
||||
@@ -98,6 +99,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
void LayerJson(const Json::Value& json);
|
||||
Json::Value ToJson() const;
|
||||
|
||||
// Generic setting access via SettingKey
|
||||
bool HasSetting(ProfileSettingKey key) const;
|
||||
void ClearSetting(ProfileSettingKey key);
|
||||
std::vector<ProfileSettingKey> CurrentSettings() const;
|
||||
|
||||
hstring EvaluatedStartingDirectory() const;
|
||||
|
||||
Model::IAppearanceConfig DefaultAppearance();
|
||||
@@ -137,14 +143,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
public:
|
||||
#define PROFILE_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \
|
||||
INHERITABLE_SETTING_WITH_LOGGING(Model::Profile, type, name, jsonKey, ##__VA_ARGS__)
|
||||
INHERITABLE_JSON_SETTING_WITH_LOGGING(Model::Profile, type, name, jsonKey, ##__VA_ARGS__)
|
||||
MTSM_PROFILE_SETTINGS(PROFILE_SETTINGS_INITIALIZE)
|
||||
#undef PROFILE_SETTINGS_INITIALIZE
|
||||
|
||||
// Complex/mutable settings that need backing fields (not JSON-backed)
|
||||
INHERITABLE_SETTING(Model::Profile, IMediaResource, Icon, implementation::MediaResource::FromString(L"\uE756"));
|
||||
INHERITABLE_SETTING(Model::Profile, IEnvironmentVariableMap, EnvironmentVariables, nullptr);
|
||||
INHERITABLE_SETTING(Model::Profile, Windows::Foundation::Collections::IVector<IMediaResource>, BellSound, nullptr);
|
||||
|
||||
private:
|
||||
Model::IAppearanceConfig _DefaultAppearance{ winrt::make<AppearanceConfig>(weak_ref<Model::Profile>(*this)) };
|
||||
Model::FontConfig _FontInfo{ winrt::make<FontConfig>(weak_ref<Model::Profile>(*this)) };
|
||||
|
||||
// Raw JSON for this layer. Populated by LayerJson(), will become the
|
||||
// source of truth for settings once the JSON-backed refactor is complete.
|
||||
Json::Value _json{ Json::ValueType::objectValue };
|
||||
|
||||
std::set<std::string> _changeLog;
|
||||
|
||||
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace SettingsModelUnitTests
|
||||
TEST_METHOD(TestGenGuidsForProfiles);
|
||||
TEST_METHOD(TestCorrectOldDefaultShellPaths);
|
||||
TEST_METHOD(ProfileDefaultsProhibitedSettings);
|
||||
|
||||
TEST_METHOD(JsonSyncOnSetAndClear);
|
||||
TEST_METHOD(SettingKeyEnumAndJsonKeyLookup);
|
||||
TEST_METHOD(GenericHasAndClearMatchTypedAPIs);
|
||||
TEST_METHOD(CurrentSettingsReturnsCorrectKeys);
|
||||
};
|
||||
|
||||
void ProfileTests::ProfileGeneratesGuid()
|
||||
@@ -532,4 +537,168 @@ namespace SettingsModelUnitTests
|
||||
VERIFY_ARE_NOT_EQUAL(L"Default Profile Source", allProfiles.GetAt(2).Source());
|
||||
VERIFY_ARE_NOT_EQUAL(L"foo.exe", allProfiles.GetAt(2).Commandline());
|
||||
}
|
||||
|
||||
void ProfileTests::JsonSyncOnSetAndClear()
|
||||
{
|
||||
// Verify that setting a value via the typed setter updates the internal
|
||||
// _json, and clearing it removes the key from _json.
|
||||
static constexpr std::string_view settingsJson{ R"({
|
||||
"profiles": {
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1000
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||
const auto profile = settings->AllProfiles().GetAt(0);
|
||||
const auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
|
||||
// Verify initial value
|
||||
VERIFY_ARE_EQUAL(1000, profile.HistorySize());
|
||||
VERIFY_IS_TRUE(profile.HasHistorySize());
|
||||
|
||||
// Modify setting; _json should be updated
|
||||
profile.HistorySize(5000);
|
||||
VERIFY_ARE_EQUAL(5000, profile.HistorySize());
|
||||
|
||||
// Verify ToJson reflects the change
|
||||
const auto json = profileImpl->ToJson();
|
||||
VERIFY_ARE_EQUAL(5000, json["historySize"].asInt());
|
||||
|
||||
// Clear setting; should fall back to default
|
||||
profile.ClearHistorySize();
|
||||
VERIFY_IS_FALSE(profile.HasHistorySize());
|
||||
// Should now inherit or use default (9001 is the DEFAULT_HISTORY_SIZE)
|
||||
VERIFY_ARE_EQUAL(DEFAULT_HISTORY_SIZE, profile.HistorySize());
|
||||
|
||||
// ToJson should no longer have historySize
|
||||
const auto json2 = profileImpl->ToJson();
|
||||
VERIFY_IS_FALSE(json2.isMember("historySize"));
|
||||
}
|
||||
|
||||
void ProfileTests::SettingKeyEnumAndJsonKeyLookup()
|
||||
{
|
||||
// Verify that the SettingKey enums map to correct JSON keys.
|
||||
using namespace implementation;
|
||||
|
||||
// Spot-check a few profile setting keys
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "historySize" }, JsonKeyForSetting(ProfileSettingKey::HistorySize));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "snapOnInput" }, JsonKeyForSetting(ProfileSettingKey::SnapOnInput));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "commandline" }, JsonKeyForSetting(ProfileSettingKey::Commandline));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "tabTitle" }, JsonKeyForSetting(ProfileSettingKey::TabTitle));
|
||||
|
||||
// Special-cased settings
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "name" }, JsonKeyForSetting(ProfileSettingKey::_Name));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "guid" }, JsonKeyForSetting(ProfileSettingKey::_Guid));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "hidden" }, JsonKeyForSetting(ProfileSettingKey::_Hidden));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "padding" }, JsonKeyForSetting(ProfileSettingKey::_Padding));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "tabColor" }, JsonKeyForSetting(ProfileSettingKey::_TabColor));
|
||||
|
||||
// Global setting keys
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "initialRows" }, JsonKeyForSetting(GlobalSettingKey::InitialRows));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "alwaysOnTop" }, JsonKeyForSetting(GlobalSettingKey::AlwaysOnTop));
|
||||
|
||||
// Font setting keys
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "face" }, JsonKeyForSetting(FontSettingKey::FontFace));
|
||||
VERIFY_ARE_EQUAL(std::string_view{ "size" }, JsonKeyForSetting(FontSettingKey::FontSize));
|
||||
|
||||
// SETTINGS_SIZE should be a valid (but large) enum value
|
||||
VERIFY_IS_TRUE(static_cast<int>(ProfileSettingKey::SETTINGS_SIZE) > 0);
|
||||
VERIFY_IS_TRUE(static_cast<int>(GlobalSettingKey::SETTINGS_SIZE) > 0);
|
||||
VERIFY_IS_TRUE(static_cast<int>(FontSettingKey::SETTINGS_SIZE) > 0);
|
||||
VERIFY_IS_TRUE(static_cast<int>(AppearanceSettingKey::SETTINGS_SIZE) > 0);
|
||||
}
|
||||
|
||||
void ProfileTests::GenericHasAndClearMatchTypedAPIs()
|
||||
{
|
||||
// Verify that HasSetting(key) and ClearSetting(key) match the
|
||||
// typed HasXxx() and ClearXxx() methods.
|
||||
static constexpr std::string_view settingsJson{ R"({
|
||||
"profiles": {
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1000,
|
||||
"snapOnInput": false
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||
const auto profile = settings->AllProfiles().GetAt(0);
|
||||
const auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
|
||||
// HasSetting should match HasXxx for set values
|
||||
VERIFY_ARE_EQUAL(profile.HasHistorySize(), profileImpl->HasSetting(implementation::ProfileSettingKey::HistorySize));
|
||||
VERIFY_ARE_EQUAL(profile.HasSnapOnInput(), profileImpl->HasSetting(implementation::ProfileSettingKey::SnapOnInput));
|
||||
|
||||
// HasSetting should match HasXxx for unset values
|
||||
VERIFY_ARE_EQUAL(profile.HasTabTitle(), profileImpl->HasSetting(implementation::ProfileSettingKey::TabTitle));
|
||||
VERIFY_IS_FALSE(profileImpl->HasSetting(implementation::ProfileSettingKey::TabTitle));
|
||||
|
||||
// ClearSetting should behave like ClearXxx
|
||||
profileImpl->ClearSetting(implementation::ProfileSettingKey::HistorySize);
|
||||
VERIFY_IS_FALSE(profile.HasHistorySize());
|
||||
VERIFY_IS_FALSE(profileImpl->HasSetting(implementation::ProfileSettingKey::HistorySize));
|
||||
}
|
||||
|
||||
void ProfileTests::CurrentSettingsReturnsCorrectKeys()
|
||||
{
|
||||
// Verify that CurrentSettings() returns the keys that are explicitly
|
||||
// set at the current layer.
|
||||
static constexpr std::string_view settingsJson{ R"({
|
||||
"profiles": {
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"historySize": 1000,
|
||||
"snapOnInput": false,
|
||||
"tabTitle": "MyTab"
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||
const auto profile = settings->AllProfiles().GetAt(0);
|
||||
const auto profileImpl = winrt::get_self<implementation::Profile>(profile);
|
||||
|
||||
const auto currentKeys = profileImpl->CurrentSettings();
|
||||
|
||||
// historySize, snapOnInput, and tabTitle should be in the list
|
||||
auto hasHistorySize = false;
|
||||
auto hasSnapOnInput = false;
|
||||
auto hasTabTitle = false;
|
||||
for (const auto& key : currentKeys)
|
||||
{
|
||||
if (key == implementation::ProfileSettingKey::HistorySize)
|
||||
hasHistorySize = true;
|
||||
if (key == implementation::ProfileSettingKey::SnapOnInput)
|
||||
hasSnapOnInput = true;
|
||||
if (key == implementation::ProfileSettingKey::TabTitle)
|
||||
hasTabTitle = true;
|
||||
}
|
||||
VERIFY_IS_TRUE(hasHistorySize, L"historySize should be in CurrentSettings");
|
||||
VERIFY_IS_TRUE(hasSnapOnInput, L"snapOnInput should be in CurrentSettings");
|
||||
VERIFY_IS_TRUE(hasTabTitle, L"tabTitle should be in CurrentSettings");
|
||||
|
||||
// Clear one and verify it's removed
|
||||
profileImpl->ClearSetting(implementation::ProfileSettingKey::TabTitle);
|
||||
const auto updatedKeys = profileImpl->CurrentSettings();
|
||||
auto hasTabTitleAfterClear = false;
|
||||
for (const auto& key : updatedKeys)
|
||||
{
|
||||
if (key == implementation::ProfileSettingKey::TabTitle)
|
||||
hasTabTitleAfterClear = true;
|
||||
}
|
||||
VERIFY_IS_FALSE(hasTabTitleAfterClear, L"tabTitle should NOT be in CurrentSettings after clear");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user