2020-10-27 18:35:09 +01:00
|
|
|
/*++
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-08 23:01:40 +01:00
|
|
|
void ClearParents()
|
|
|
|
{
|
|
|
|
_parents.clear();
|
|
|
|
}
|
|
|
|
|
2020-10-27 18:35:09 +01:00
|
|
|
void InsertParent(com_ptr<T> parent)
|
|
|
|
{
|
Reduce usage of Json::Value throughout Terminal.Settings.Model (#11184)
This commit reduces the code surface that interacts with raw JSON data,
reducing code complexity and improving maintainability.
Files that needed to be changed drastically were additionally
cleaned up to remove any code cruft that has accrued over time.
In order to facility this the following changes were made:
* Move JSON handling from `CascadiaSettings` into `SettingsLoader`
This allows us to use STL containers for data model instances.
For instance profiles are now added to a hashmap for O(1) lookup.
* JSON parsing within `SettingsLoader` doesn't differentiate between user,
inbox and fragment JSON data, reducing code complexity and size.
It also centralizes common concerns, like profile deduplication and
ensuring that all profiles are assigned a GUID.
* Direct JSON modification, like the insertion of dynamic profiles into
settings.json were removed. This vastly reduces code complexity,
but unfortunately removes support for comments in JSON on first start.
* `ColorScheme`s cannot be layered. As such its `LayerJson` API was replaced
with `FromJson`, allowing us to remove JSON-based color scheme validation.
* `Profile`s used to test their wish to layer using `ShouldBeLayered`, which
was replaced with a GUID-based hashmap lookup on previously parsed profiles.
Further changes were made as improvements upon the previous changes:
* Compact the JSON files embedded binary, saving 28kB
* Prevent double-initialization of the color table in `ColorScheme`
* Making `til::color` getters `constexpr`, allow better optimizations
The result is a reduction of:
* 48kB binary size for the Settings.Model.dll
* 5-10% startup duration
* 26% code for the `CascadiaSettings` class
* 1% overall code in this project
Furthermore this results in the following breaking changes:
* The long deprecated "globals" settings object will not be detected and no
warning will be created during load.
* The initial creation of a new settings.json will not produce helpful comments.
Both cases are caused by the removal of manual JSON handling and the
move to representing the settings file with model objects instead
## PR Checklist
* [x] Closes #5276
* [x] Closes #7421
* [x] I work here
* [x] Tests added/passed
## Validation Steps Performed
* Out-of-box-experience is identical to before ✔️
(Except for the settings.json file lacking comments.)
* Existing user settings load correctly ✔️
* New WSL instances are added to user settings ✔️
* New fragments are added to user settings ✔️
* All profiles are assigned GUIDs ✔️
2021-09-22 18:27:31 +02:00
|
|
|
_parents.emplace_back(std::move(parent));
|
2020-10-27 18:35:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void InsertParent(size_t index, com_ptr<T> parent)
|
|
|
|
{
|
|
|
|
auto pos{ _parents.begin() + index };
|
Reduce usage of Json::Value throughout Terminal.Settings.Model (#11184)
This commit reduces the code surface that interacts with raw JSON data,
reducing code complexity and improving maintainability.
Files that needed to be changed drastically were additionally
cleaned up to remove any code cruft that has accrued over time.
In order to facility this the following changes were made:
* Move JSON handling from `CascadiaSettings` into `SettingsLoader`
This allows us to use STL containers for data model instances.
For instance profiles are now added to a hashmap for O(1) lookup.
* JSON parsing within `SettingsLoader` doesn't differentiate between user,
inbox and fragment JSON data, reducing code complexity and size.
It also centralizes common concerns, like profile deduplication and
ensuring that all profiles are assigned a GUID.
* Direct JSON modification, like the insertion of dynamic profiles into
settings.json were removed. This vastly reduces code complexity,
but unfortunately removes support for comments in JSON on first start.
* `ColorScheme`s cannot be layered. As such its `LayerJson` API was replaced
with `FromJson`, allowing us to remove JSON-based color scheme validation.
* `Profile`s used to test their wish to layer using `ShouldBeLayered`, which
was replaced with a GUID-based hashmap lookup on previously parsed profiles.
Further changes were made as improvements upon the previous changes:
* Compact the JSON files embedded binary, saving 28kB
* Prevent double-initialization of the color table in `ColorScheme`
* Making `til::color` getters `constexpr`, allow better optimizations
The result is a reduction of:
* 48kB binary size for the Settings.Model.dll
* 5-10% startup duration
* 26% code for the `CascadiaSettings` class
* 1% overall code in this project
Furthermore this results in the following breaking changes:
* The long deprecated "globals" settings object will not be detected and no
warning will be created during load.
* The initial creation of a new settings.json will not produce helpful comments.
Both cases are caused by the removal of manual JSON handling and the
move to representing the settings file with model objects instead
## PR Checklist
* [x] Closes #5276
* [x] Closes #7421
* [x] I work here
* [x] Tests added/passed
## Validation Steps Performed
* Out-of-box-experience is identical to before ✔️
(Except for the settings.json file lacking comments.)
* Existing user settings load correctly ✔️
* New WSL instances are added to user settings ✔️
* New fragments are added to user settings ✔️
* All profiles are assigned GUIDs ✔️
2021-09-22 18:27:31 +02:00
|
|
|
_parents.emplace(pos, std::move(parent));
|
2020-10-27 18:35:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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>
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
using NullableSetting = std::optional<std::optional<T>>;
|
2020-10-27 18:35:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use this macro to quickly implement both getters and the setter for an
|
2021-03-04 20:27:03 +01:00
|
|
|
// inheritable setting property. This is similar to the WINRT_PROPERTY macro, except...
|
2020-10-27 18:35:09 +01:00
|
|
|
// - Has(): checks if the user explicitly set a value for this setting
|
2021-02-20 00:50:52 +01:00
|
|
|
// - SourceGetter(): return the object that provides the resolved value
|
2020-10-27 18:35:09 +01:00
|
|
|
// - 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
|
2021-03-04 20:27:03 +01:00
|
|
|
#define INHERITABLE_SETTING(projectedType, type, name, ...) \
|
2020-10-27 18:35:09 +01:00
|
|
|
public: \
|
|
|
|
/* Returns true if the user explicitly set the value, false otherwise*/ \
|
|
|
|
bool Has##name() const \
|
|
|
|
{ \
|
|
|
|
return _##name.has_value(); \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
2021-02-20 00:50:52 +01:00
|
|
|
projectedType name##OverrideSource() \
|
|
|
|
{ \
|
|
|
|
/*user set value was not set*/ \
|
|
|
|
/*iterate through parents to find one with a value*/ \
|
|
|
|
for (auto& parent : _parents) \
|
|
|
|
{ \
|
|
|
|
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
|
|
|
{ \
|
|
|
|
return source; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*no value was found*/ \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
\
|
2020-10-27 18:35:09 +01:00
|
|
|
/* 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__ }; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
/* Overwrite the user set value */ \
|
|
|
|
void name(const type& value) \
|
|
|
|
{ \
|
|
|
|
_##name = value; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
/* Clear the user set value */ \
|
|
|
|
void Clear##name() \
|
|
|
|
{ \
|
|
|
|
_##name = std::nullopt; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
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*/ \
|
2021-03-16 00:15:25 +01:00
|
|
|
for (const auto& parent : _parents) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
|
|
|
if (auto val{ parent->_get##name##Impl() }) \
|
|
|
|
{ \
|
|
|
|
return val; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*no value was found*/ \
|
|
|
|
return std::nullopt; \
|
2021-02-20 00:50:52 +01:00
|
|
|
} \
|
|
|
|
projectedType _get##name##OverrideSourceImpl() const \
|
|
|
|
{ \
|
|
|
|
/*we have a value*/ \
|
|
|
|
if (_##name) \
|
|
|
|
{ \
|
|
|
|
return *this; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*user set value was not set*/ \
|
|
|
|
/*iterate through parents to find one with a value*/ \
|
2021-03-16 00:15:25 +01:00
|
|
|
for (const auto& parent : _parents) \
|
2021-02-20 00:50:52 +01:00
|
|
|
{ \
|
|
|
|
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
|
|
|
{ \
|
|
|
|
return source; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*no value was found*/ \
|
|
|
|
return nullptr; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
}
|
2020-10-27 18:35:09 +01:00
|
|
|
|
|
|
|
// 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
|
2021-03-04 20:27:03 +01:00
|
|
|
#define INHERITABLE_NULLABLE_SETTING(projectedType, type, name, ...) \
|
2020-10-27 18:35:09 +01:00
|
|
|
public: \
|
|
|
|
/* Returns true if the user explicitly set the value, false otherwise*/ \
|
|
|
|
bool Has##name() const \
|
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
return _##name.has_value(); \
|
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
2021-02-20 00:50:52 +01:00
|
|
|
projectedType name##OverrideSource() \
|
|
|
|
{ \
|
|
|
|
/*user set value was not set*/ \
|
|
|
|
/*iterate through parents to find one with a value*/ \
|
2021-03-16 00:15:25 +01:00
|
|
|
for (const auto& parent : _parents) \
|
2021-02-20 00:50:52 +01:00
|
|
|
{ \
|
|
|
|
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
|
|
|
{ \
|
|
|
|
return source; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*no source was found*/ \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
\
|
2020-10-27 18:35:09 +01:00
|
|
|
/* 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() }; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (val) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (*val) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
return **val; \
|
2020-10-27 18:35:09 +01:00
|
|
|
} \
|
|
|
|
return nullptr; \
|
|
|
|
} \
|
|
|
|
return winrt::Windows::Foundation::IReference<type>{ __VA_ARGS__ }; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
/* Overwrite the user set value */ \
|
|
|
|
void name(const winrt::Windows::Foundation::IReference<type>& value) \
|
|
|
|
{ \
|
|
|
|
if (value) /*set value is different*/ \
|
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
_##name = std::optional<type>{ value.Value() }; \
|
2020-10-27 18:35:09 +01:00
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
/* note we're setting the _inner_ value */ \
|
|
|
|
_##name = std::optional<type>{ std::nullopt }; \
|
2020-10-27 18:35:09 +01:00
|
|
|
} \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
/* Clear the user set value */ \
|
|
|
|
void Clear##name() \
|
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
_##name = std::nullopt; \
|
|
|
|
} \
|
2020-10-27 18:35:09 +01:00
|
|
|
\
|
|
|
|
private: \
|
|
|
|
NullableSetting<type> _##name{}; \
|
|
|
|
NullableSetting<type> _get##name##Impl() const \
|
|
|
|
{ \
|
|
|
|
/*return user set value*/ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (_##name) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
|
|
|
return _##name; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*user set value was not set*/ \
|
|
|
|
/*iterate through parents to find a value*/ \
|
2021-03-16 00:15:25 +01:00
|
|
|
for (const auto& parent : _parents) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
if (auto val{ parent->_get##name##Impl() }) \
|
2020-10-27 18:35:09 +01:00
|
|
|
{ \
|
|
|
|
return val; \
|
|
|
|
} \
|
|
|
|
} \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
\
|
2020-10-27 18:35:09 +01:00
|
|
|
/*no value was found*/ \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
return std::nullopt; \
|
2021-02-20 00:50:52 +01:00
|
|
|
} \
|
|
|
|
projectedType _get##name##OverrideSourceImpl() const \
|
|
|
|
{ \
|
|
|
|
/*we have a value*/ \
|
|
|
|
if (_##name) \
|
|
|
|
{ \
|
|
|
|
return *this; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*user set value was not set*/ \
|
|
|
|
/*iterate through parents to find one with a value*/ \
|
2021-03-16 00:15:25 +01:00
|
|
|
for (const auto& parent : _parents) \
|
2021-02-20 00:50:52 +01:00
|
|
|
{ \
|
|
|
|
if (auto source{ parent->_get##name##OverrideSourceImpl() }) \
|
|
|
|
{ \
|
|
|
|
return source; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
/*no value was found*/ \
|
|
|
|
return nullptr; \
|
Rework JsonUtils' optional handling to let Converters see null (#8175)
The JsonUtils changes in #8018 revealed that we need more robust,
configurable optional handling. We learned that there's a class of
values that was previously underrepresented in our API: _strings that
have an explicit empty value_.
The Settings model supports starting directory, icon, background image
et al values that are empty. That emptiness _overrides_ a value set in a
lower layer, so it is not sufficient to represent the empty value for
any one of those fields as an unset optional.
There are a couple other settings for which we've implemented a
hand-rolled option type (for roughly the same reason): foreground,
background, any color fields that override values from the color scheme
_or_ the lower layer profile.
These requirements are best fulfilled by better optional support in
JsonUtils. Where the library would originally detect known types of
optional and pre-filter them out during `GetValue` and `SetValue`, it
will now defer to another conversion trait.
This commit introduces a helper conversion trait and an "option oracle".
The conversion trait will use the option oracle to detect emptiness,
generate empty option values, and read values out of option types. In so
doing, the trait is insulated from the implementation details of any
specific option type.
Any special logic for handling JSON null and option types has been
stripped from GetValue. Due to this, there is an express change in
behavior for some converters:
* `GetValue<T>(jsonNull)` where `T` is **not** an option type[1] has
been upgraded from a silent no-op to an exception.
Further, I took the opportunity to replace NullableSetting with
std::optional<std::optional<T>>, which accurately represents "setting
that the user might explicitly clear". I've added a test to
JsonUtilsTests to make sure it can serialize/deserialize double
optionals the way we expect it to.
Tests (Local, Unit for TerminalApp/SettingsModel):
Summary: Total=140, Passed=140, Failed=0, Blocked=0, Not Run=0, Skipped=0
[1]: Explicitly, if `T` is not an option type _and the converter does
not support null_.
2020-11-10 00:13:02 +01:00
|
|
|
}
|