diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index b315eb25..3a42fdd4 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -111,12 +111,14 @@ set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp source/scwx/qt/settings/map_settings.hpp source/scwx/qt/settings/palette_settings.hpp source/scwx/qt/settings/settings_container.hpp - source/scwx/qt/settings/settings_variable.hpp) + source/scwx/qt/settings/settings_variable.hpp + source/scwx/qt/settings/settings_variable_base.hpp) set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp source/scwx/qt/settings/map_settings.cpp source/scwx/qt/settings/palette_settings.cpp source/scwx/qt/settings/settings_container.cpp - source/scwx/qt/settings/settings_variable.cpp) + source/scwx/qt/settings/settings_variable.cpp + source/scwx/qt/settings/settings_variable_base.cpp) set(HDR_TYPES source/scwx/qt/types/qt_types.hpp source/scwx/qt/types/radar_product_record.hpp source/scwx/qt/types/text_event_key.hpp) diff --git a/scwx-qt/source/scwx/qt/settings/settings_container.cpp b/scwx-qt/source/scwx/qt/settings/settings_container.cpp index 9e067622..1eb5b06a 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_container.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_container.cpp @@ -11,6 +11,7 @@ namespace settings { static const std::string logPrefix_ = "scwx::qt::settings::settings_container"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); template class SettingsContainer::Impl @@ -61,16 +62,29 @@ bool SettingsContainer::SetValueOrDefault(const Container& c) } else if (p->elementMinimum_.has_value() && value < p->elementMinimum_) { + logger_->warn("{0} less than minimum ({1} < {2}), setting to: {2}", + this->name(), + value, + *p->elementMinimum_); validated = false; return *p->elementMinimum_; } else if (p->elementMaximum_.has_value() && value > p->elementMaximum_) { + logger_->warn( + "{0} greater than maximum ({1} > {2}), setting to: {2}", + this->name(), + value, + *p->elementMaximum_); validated = false; return *p->elementMaximum_; } else { + logger_->warn("{} validation failed ({}), setting to default: {}", + this->name(), + value, + p->elementDefault_); validated = false; return p->elementDefault_; } @@ -143,6 +157,21 @@ bool SettingsContainer::ValidateElement(const T& value) const (p->elementValidator_ == nullptr || p->elementValidator_(value))); } +template +bool SettingsContainer::Equals(const SettingsVariableBase& o) const +{ + // This is only ever called with SettingsContainer, so static_cast + // is safe + const SettingsContainer& v = + static_cast&>(o); + + // Don't compare validator + return SettingsVariable::Equals(o) && + p->elementDefault_ == v.p->elementDefault_ && + p->elementMinimum_ == v.p->elementMinimum_ && + p->elementMaximum_ == v.p->elementMaximum_; +} + } // namespace settings } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/settings_container.hpp b/scwx-qt/source/scwx/qt/settings/settings_container.hpp index 44850bfa..b35201c6 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_container.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_container.hpp @@ -24,25 +24,83 @@ public: SettingsContainer(SettingsContainer&&) noexcept; SettingsContainer& operator=(SettingsContainer&&) noexcept; + /** + * Sets the current value of the settings variable. If the value is out of + * range, the value is set to the minimum or maximum. If the value fails + * validation, the value is set to default. + * + * @param c Container value to set + * + * @return true if the value is valid, false if the value was modified. + */ bool SetValueOrDefault(const Container& c) override; + /** + * Validate the container value against the defined parameters of the + * settings variable. + * + * @param c Container value to validate + * + * @return true if the value is valid, false if the value failed validation. + */ bool Validate(const Container& c) const override; + + /** + * Validate the element value against the defined parameters of the settings + * variable. + * + * @param value Element value to validate + * + * @return true if the value is valid, false if the value failed validation. + */ bool ValidateElement(const T& value) const; + /** + * Gets the default element value of the settings variable. + * + * @return Default element value + */ T GetElementDefault() const; + /** + * Sets the default element value of the settings variable. + * + * @param value Default element value + */ void SetElementDefault(const T& value); + + /** + * Sets the minimum element value of the settings variable. + * + * @param value Minimum element value + */ void SetElementMinimum(const T& value); + + /** + * Sets the maximum element value of the settings variable. + * + * @param value Maximum element value + */ void SetElementMaximum(const T& value); + + /** + * Sets a custom validator function for each element of the settings + * variable. + * + * @param validator Element validator function + */ void SetElementValidator(std::function validator); +protected: + virtual bool Equals(const SettingsVariableBase& o) const override; + private: class Impl; std::unique_ptr p; }; #ifdef SETTINGS_CONTAINER_IMPLEMENTATION -template class SettingsContainer>; +template class SettingsContainer>; #endif } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp index 7d2992c1..b1688684 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp @@ -3,6 +3,11 @@ #include #include +#include + +#include +#include + namespace scwx { namespace qt @@ -11,16 +16,16 @@ namespace settings { static const std::string logPrefix_ = "scwx::qt::settings::settings_variable"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); template class SettingsVariable::Impl { public: - explicit Impl(const std::string& name) : name_ {name} {} + explicit Impl() {} ~Impl() {} - const std::string name_; T value_ {}; T default_ {}; std::optional staged_ {}; @@ -31,7 +36,7 @@ public: template SettingsVariable::SettingsVariable(const std::string& name) : - p(std::make_unique(name)) + SettingsVariableBase(name), p(std::make_unique()) { } template @@ -44,9 +49,16 @@ SettingsVariable& SettingsVariable::operator=(SettingsVariable&&) noexcept = default; template -std::string SettingsVariable::name() const +inline auto FormatParameter(const T& value) { - return p->name_; + if constexpr (std::is_integral_v || std::is_same_v) + { + return value; + } + else + { + return fmt::join(value, ", "); + } } template @@ -81,14 +93,26 @@ bool SettingsVariable::SetValueOrDefault(const T& value) } else if (p->minimum_.has_value() && value < p->minimum_) { + logger_->warn("{0} less than minimum ({1} < {2}), setting to: {2}", + name(), + FormatParameter(value), + FormatParameter(*p->minimum_)); p->value_ = *p->minimum_; } else if (p->maximum_.has_value() && value > p->maximum_) { + logger_->warn("{0} greater than maximum ({1} > {2}), setting to: {2}", + name(), + FormatParameter(value), + FormatParameter(*p->maximum_)); p->value_ = *p->maximum_; } else { + logger_->warn("{} validation failed ({}), setting to default: {}", + name(), + FormatParameter(value), + FormatParameter(p->default_)); p->value_ = p->default_; } @@ -164,6 +188,59 @@ bool SettingsVariable::Validate(const T& value) const (p->validator_ == nullptr || p->validator_(value))); // User-validation } +template +bool SettingsVariable::ReadValue(const boost::json::object& json) +{ + const boost::json::value* jv = json.if_contains(name()); + bool validated = false; + + if (jv != nullptr) + { + try + { + validated = SetValueOrDefault(boost::json::value_to(*jv)); + } + catch (const std::exception& ex) + { + logger_->warn("{} is invalid ({}), setting to default: {}", + name(), + ex.what(), + FormatParameter(p->default_)); + p->value_ = p->default_; + } + } + else + { + logger_->debug("{} is not present, setting to default: {}", + name(), + FormatParameter(p->default_)); + p->value_ = p->default_; + } + + return validated; +} + +template +void SettingsVariable::WriteValue(boost::json::object& json) const +{ + json[name()] = boost::json::value_from(p->value_); +} + +template +bool SettingsVariable::Equals(const SettingsVariableBase& o) const +{ + // This is only ever called with SettingsVariable, so static_cast is safe + const SettingsVariable& v = static_cast&>(o); + + // Don't compare validator + return SettingsVariableBase::Equals(o) && // + p->value_ == v.p->value_ && // + p->default_ == v.p->default_ && // + p->staged_ == v.p->staged_ && // + p->minimum_ == v.p->minimum_ && // + p->maximum_ == v.p->maximum_; +} + } // namespace settings } // namespace qt } // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp index 6032f29f..5bb258ed 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp @@ -1,8 +1,8 @@ #pragma once +#include + #include -#include -#include namespace scwx { @@ -12,7 +12,7 @@ namespace settings { template -class SettingsVariable +class SettingsVariable : public SettingsVariableBase { public: explicit SettingsVariable(const std::string& name); @@ -24,25 +24,120 @@ public: SettingsVariable(SettingsVariable&&) noexcept; SettingsVariable& operator=(SettingsVariable&&) noexcept; - std::string name() const; + /** + * Gets the current value of the settings variable. + * + * @return Current value + */ + T GetValue() const; - T GetValue() const; - bool SetValue(const T& value); + /** + * Sets the current value of the settings variable. + * + * @param value Value to set + * + * @return true if the current value was set, false if the value failed + * validation. + */ + bool SetValue(const T& value); + + /** + * Sets the current value of the settings variable. If the value is out of + * range, the value is set to the minimum or maximum. If the value fails + * validation, the value is set to default. + * + * @param value Value to set + * + * @return true if the value is valid, false if the value was modified. + */ virtual bool SetValueOrDefault(const T& value); - void SetValueToDefault(); + /** + * Sets the current value of the settings variable to default. + */ + void SetValueToDefault() override; + + /** + * Sets the staged value of the settings variable. + * + * @param value Value to stage + * + * @return true if the staged value was set, false if the value failed + * validation. + */ bool StageValue(const T& value); + + /** + * Sets the current value of the settings variable to the staged value. + */ void Commit(); + /** + * Validate the value against the defined parameters of the settings + * variable. + * + * @param value Value to validate + * + * @return true if the value is valid, false if the value failed validation. + */ virtual bool Validate(const T& value) const; + /** + * Gets the default value of the settings variable. + * + * @return Default value + */ T GetDefault() const; + /** + * Sets the default value of the settings variable. + * + * @param value Default value + */ void SetDefault(const T& value); + + /** + * Sets the minimum value of the settings variable. + * + * @param value Minimum value + */ void SetMinimum(const T& value); + + /** + * Sets the maximum value of the settings variable. + * + * @param value Maximum value + */ void SetMaximum(const T& value); + + /** + * Sets a custom validator function for the settings variable. + * + * @param validator Validator function + */ void SetValidator(std::function validator); + /** + * Reads the value from the JSON object. If the read value is out of range, + * the value is set to the minimum or maximum. If the read value fails + * validation, the value is set to default. + * + * @param json JSON object to read + * + * @return true if the read value is valid, false if the value was modified. + */ + virtual bool ReadValue(const boost::json::object& json) override; + + /** + * Writes the current value to the JSON object. + * + * @param json JSON object to write + */ + virtual void WriteValue(boost::json::object& json) const override; + +protected: + virtual bool Equals(const SettingsVariableBase& o) const override; + private: class Impl; std::unique_ptr p; @@ -50,11 +145,11 @@ private: #ifdef SETTINGS_VARIABLE_IMPLEMENTATION template class SettingsVariable; -template class SettingsVariable; +template class SettingsVariable; template class SettingsVariable; // Containers are not to be used directly -template class SettingsVariable>; +template class SettingsVariable>; #endif } // namespace settings diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable_base.cpp b/scwx-qt/source/scwx/qt/settings/settings_variable_base.cpp new file mode 100644 index 00000000..55ce72ea --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/settings_variable_base.cpp @@ -0,0 +1,54 @@ +#include + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +static const std::string logPrefix_ = + "scwx::qt::settings::settings_variable_base"; + +class SettingsVariableBase::Impl +{ +public: + explicit Impl(const std::string& name) : name_ {name} {} + + ~Impl() {} + + const std::string name_; +}; + +SettingsVariableBase::SettingsVariableBase(const std::string& name) : + p(std::make_unique(name)) +{ +} + +SettingsVariableBase::~SettingsVariableBase() = default; + +SettingsVariableBase::SettingsVariableBase(SettingsVariableBase&&) noexcept = + default; + +SettingsVariableBase& +SettingsVariableBase::operator=(SettingsVariableBase&&) noexcept = default; + +std::string SettingsVariableBase::name() const +{ + return p->name_; +} + +bool SettingsVariableBase::Equals(const SettingsVariableBase& o) const +{ + return p->name_ == o.p->name_; +} + +bool operator==(const SettingsVariableBase& lhs, + const SettingsVariableBase& rhs) +{ + return typeid(lhs) == typeid(rhs) && lhs.Equals(rhs); +} + +} // namespace settings +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable_base.hpp b/scwx-qt/source/scwx/qt/settings/settings_variable_base.hpp new file mode 100644 index 00000000..87607a8c --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/settings_variable_base.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +/** + * @brief Settings Variable base class + */ +class SettingsVariableBase +{ +protected: + explicit SettingsVariableBase(const std::string& name); + ~SettingsVariableBase(); + +public: + SettingsVariableBase(const SettingsVariableBase&) = delete; + SettingsVariableBase& operator=(const SettingsVariableBase&) = delete; + + SettingsVariableBase(SettingsVariableBase&&) noexcept; + SettingsVariableBase& operator=(SettingsVariableBase&&) noexcept; + + std::string name() const; + + /** + * Sets the current value of the settings variable to default. + */ + virtual void SetValueToDefault() = 0; + + /** + * Sets the current value of the settings variable to the staged value. + */ + virtual void Commit() = 0; + + /** + * Reads the value from the JSON object. If the read value is out of range, + * the value is set to the minimum or maximum. If the read value fails + * validation, the value is set to default. + * + * @param json JSON object to read + * + * @return true if the read value is valid, false if the value was modified. + */ + virtual bool ReadValue(const boost::json::object& json) = 0; + + /** + * Writes the current value to the JSON object. + * + * @param json JSON object to write + */ + virtual void WriteValue(boost::json::object& json) const = 0; + +protected: + friend bool operator==(const SettingsVariableBase& lhs, + const SettingsVariableBase& rhs); + virtual bool Equals(const SettingsVariableBase& o) const; + +private: + class Impl; + std::unique_ptr p; +}; + +bool operator==(const SettingsVariableBase& lhs, + const SettingsVariableBase& rhs); + +} // namespace settings +} // namespace qt +} // namespace scwx