/*++ 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 struct IInheritable { public: // Method Description: // - Create a new instance of T, but set its parent to this instance // Arguments: // - // Return Value: // - a new instance of T with this instance set as its parent com_ptr CreateChild() const { auto child{ winrt::make_self() }; // 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 parent; winrt::copy_from_abi(parent, const_cast(static_cast(this))); child->InsertParent(parent); child->_FinalizeInheritance(); return child; } void ClearParents() { _parents.clear(); } void InsertParent(com_ptr parent) { _parents.emplace_back(std::move(parent)); } void InsertParent(size_t index, com_ptr parent) { auto pos{ _parents.begin() + index }; _parents.emplace(pos, std::move(parent)); } const std::vector>& Parents() { return _parents; } protected: std::vector> _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: // - // Return Value: // - virtual void _FinalizeInheritance() {} }; // This is like std::optional, but we can use it in inheritance to determine whether the user explicitly cleared it template using NullableSetting = std::optional>; } // Use this macro to quickly implement both getters and the setter for an // inheritable setting property. This is similar to the WINRT_PROPERTY macro, except... // - Has(): checks if the user explicitly set a value for this setting // - SourceGetter(): return the object that provides the resolved value // - Getter(): return the resolved value // - Setter(): set the value directly // - Clear(): clear the user set value // - the setting is saved as an optional, where nullopt means // that we must inherit the value from our parent #define INHERITABLE_SETTING(projectedType, type, name, ...) \ public: \ /* Returns true if the user explicitly set the value, false otherwise*/ \ bool Has##name() const \ { \ return _##name.has_value(); \ } \ \ 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; \ } \ \ /* Returns the resolved value for this setting */ \ /* fallback: user set value --> inherited value --> system set value */ \ type name() const \ { \ const auto val{ _get##name##Impl() }; \ return val ? *val : type{ __VA_ARGS__ }; \ } \ \ /* Overwrite the user set value */ \ void name(const type& value) \ { \ _##name = value; \ } \ \ /* Clear the user set value */ \ void Clear##name() \ { \ _##name = std::nullopt; \ } \ \ private: \ std::optional _##name{ std::nullopt }; \ std::optional _get##name##Impl() const \ { \ /*return user set value*/ \ if (_##name) \ { \ return _##name; \ } \ \ /*user set value was not set*/ \ /*iterate through parents to find a value*/ \ for (const auto& parent : _parents) \ { \ if (auto val{ parent->_get##name##Impl() }) \ { \ return val; \ } \ } \ \ /*no value was found*/ \ return std::nullopt; \ } \ 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*/ \ for (const auto& parent : _parents) \ { \ if (auto source{ parent->_get##name##OverrideSourceImpl() }) \ { \ return source; \ } \ } \ \ /*no value was found*/ \ return nullptr; \ } // This macro is similar to the one above, but is reserved for optional settings // like Profile.Foreground (where null is interpreted // as an acceptable value, rather than "inherit") // "type" is exposed as an IReference #define INHERITABLE_NULLABLE_SETTING(projectedType, type, name, ...) \ public: \ /* Returns true if the user explicitly set the value, false otherwise*/ \ bool Has##name() const \ { \ return _##name.has_value(); \ } \ \ projectedType name##OverrideSource() \ { \ /*user set value was not set*/ \ /*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; \ } \ \ /* Returns the resolved value for this setting */ \ /* fallback: user set value --> inherited value --> system set value */ \ winrt::Windows::Foundation::IReference name() const \ { \ const auto val{ _get##name##Impl() }; \ if (val) \ { \ if (*val) \ { \ return **val; \ } \ return nullptr; \ } \ return winrt::Windows::Foundation::IReference{ __VA_ARGS__ }; \ } \ \ /* Overwrite the user set value */ \ void name(const winrt::Windows::Foundation::IReference& value) \ { \ if (value) /*set value is different*/ \ { \ _##name = std::optional{ value.Value() }; \ } \ else \ { \ /* note we're setting the _inner_ value */ \ _##name = std::optional{ std::nullopt }; \ } \ } \ \ /* Clear the user set value */ \ void Clear##name() \ { \ _##name = std::nullopt; \ } \ \ private: \ NullableSetting _##name{}; \ NullableSetting _get##name##Impl() const \ { \ /*return user set value*/ \ if (_##name) \ { \ return _##name; \ } \ \ /*user set value was not set*/ \ /*iterate through parents to find a value*/ \ for (const auto& parent : _parents) \ { \ if (auto val{ parent->_get##name##Impl() }) \ { \ return val; \ } \ } \ \ /*no value was found*/ \ return std::nullopt; \ } \ 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*/ \ for (const auto& parent : _parents) \ { \ if (auto source{ parent->_get##name##OverrideSourceImpl() }) \ { \ return source; \ } \ } \ \ /*no value was found*/ \ return nullptr; \ }