#define SETTINGS_VARIABLE_IMPLEMENTATION #include #include #include #include namespace scwx { namespace qt { 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() {} ~Impl() {} T value_ {}; T default_ {}; std::optional staged_ {}; std::optional minimum_ {}; std::optional maximum_ {}; std::function validator_ {nullptr}; std::vector valueChangedCallbackFunctions_ {}; std::vector valueStagedCallbackFunctions_ {}; }; template SettingsVariable::SettingsVariable(const std::string& name) : SettingsVariableBase(name), p(std::make_unique()) { } template SettingsVariable::~SettingsVariable() = default; template SettingsVariable::SettingsVariable(SettingsVariable&&) noexcept = default; template SettingsVariable& SettingsVariable::operator=(SettingsVariable&&) noexcept = default; template inline auto FormatParameter(const T& value) { if constexpr (std::is_integral_v || std::is_same_v) { return value; } else { return fmt::join(value, ", "); } } template T SettingsVariable::GetValue() const { return p->value_; } template bool SettingsVariable::SetValue(const T& value) { bool validated = false; if (Validate(value)) { p->value_ = value; validated = true; for (auto& callback : p->valueChangedCallbackFunctions_) { callback(p->value_); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } } return validated; } template bool SettingsVariable::SetValueOrDefault(const T& value) { bool validated = false; if (Validate(value)) { p->value_ = value; validated = true; } 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_; } for (auto& callback : p->valueChangedCallbackFunctions_) { callback(p->value_); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } return validated; } template void SettingsVariable::SetValueToDefault() { p->value_ = p->default_; for (auto& callback : p->valueChangedCallbackFunctions_) { callback(p->value_); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } } template void SettingsVariable::StageDefault() { if (p->value_ != p->default_) { p->staged_ = p->default_; } else { p->staged_.reset(); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->default_); } } template bool SettingsVariable::StageValue(const T& value) { bool validated = false; if (Validate(value)) { if (p->value_ != value) { p->staged_ = value; } else { p->staged_.reset(); } validated = true; for (auto& callback : p->valueStagedCallbackFunctions_) { callback(value); } } return validated; } template bool SettingsVariable::Commit() { bool committed = false; if (p->staged_.has_value()) { p->value_ = std::move(*p->staged_); p->staged_.reset(); committed = true; for (auto& callback : p->valueChangedCallbackFunctions_) { callback(p->value_); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } } return committed; } template void SettingsVariable::Reset() { p->staged_.reset(); for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } } template std::optional SettingsVariable::GetStaged() const { return p->staged_; } template T SettingsVariable::GetDefault() const { return p->default_; } template void SettingsVariable::SetDefault(const T& value) { p->default_ = value; } template void SettingsVariable::SetMinimum(const T& value) { p->minimum_ = value; } template std::optional SettingsVariable::GetMinimum() const { return p->minimum_; } template void SettingsVariable::SetMaximum(const T& value) { p->maximum_ = value; } template std::optional SettingsVariable::GetMaximum() const { return p->maximum_; } template void SettingsVariable::SetValidator(std::function validator) { p->validator_ = validator; } template bool SettingsVariable::Validate(const T& value) const { return ( (!p->minimum_.has_value() || value >= p->minimum_) && // Validate minimum (!p->maximum_.has_value() || value <= p->maximum_) && // Validate maximum (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_; } for (auto& callback : p->valueChangedCallbackFunctions_) { callback(p->value_); } for (auto& callback : p->valueStagedCallbackFunctions_) { callback(p->value_); } return validated; } template void SettingsVariable::WriteValue(boost::json::object& json) const { json[name()] = boost::json::value_from(p->value_); } template void SettingsVariable::RegisterValueChangedCallback( ValueCallbackFunction callback) { p->valueChangedCallbackFunctions_.push_back(std::move(callback)); } template void SettingsVariable::RegisterValueStagedCallback( ValueCallbackFunction callback) { p->valueStagedCallbackFunctions_.push_back(std::move(callback)); } 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