diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 829b1cd4..acf30d3f 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -112,6 +112,7 @@ set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp source/scwx/qt/settings/palette_settings.hpp source/scwx/qt/settings/settings_category.hpp source/scwx/qt/settings/settings_container.hpp + source/scwx/qt/settings/settings_interface.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 @@ -119,6 +120,7 @@ set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp source/scwx/qt/settings/palette_settings.cpp source/scwx/qt/settings/settings_category.cpp source/scwx/qt/settings/settings_container.cpp + source/scwx/qt/settings/settings_interface.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 diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.cpp b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp new file mode 100644 index 00000000..1db40dcf --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.cpp @@ -0,0 +1,228 @@ +#define SETTINGS_INTERFACE_IMPLEMENTATION + +#include +#include + +#include +#include +#include +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +static const std::string logPrefix_ = "scwx::qt::settings::settings_interface"; + +template +class SettingsInterface::Impl +{ +public: + explicit Impl() + { + context_->moveToThread(QCoreApplication::instance()->thread()); + } + + ~Impl() {} + + void UpdateEditWidget(); + void UpdateResetButton(); + + SettingsVariable* variable_ {nullptr}; + bool stagedValid_ {true}; + + std::unique_ptr context_ {std::make_unique()}; + QWidget* editWidget_ {nullptr}; + QAbstractButton* resetButton_ {nullptr}; +}; + +template +SettingsInterface::SettingsInterface() : p(std::make_unique()) +{ +} +template +SettingsInterface::~SettingsInterface() = default; + +template +SettingsInterface::SettingsInterface(SettingsInterface&&) noexcept = default; +template +SettingsInterface& +SettingsInterface::operator=(SettingsInterface&&) noexcept = default; + +template +void SettingsInterface::SetSettingsVariable(SettingsVariable& variable) +{ + p->variable_ = &variable; +} + +template +void SettingsInterface::SetEditWidget(QWidget* widget) +{ + if (p->editWidget_ != nullptr) + { + QObject::disconnect(p->editWidget_, nullptr, p->context_.get(), nullptr); + } + + p->editWidget_ = widget; + + if (QLineEdit* lineEdit = dynamic_cast(widget)) + { + if constexpr (std::is_same_v) + { + // If the line is edited (not programatically changed), stage the new + // value + QObject::connect(lineEdit, + &QLineEdit::textEdited, + p->context_.get(), + [this](const QString& text) + { + // Attempt to stage the value + p->stagedValid_ = + p->variable_->StageValue(text.toStdString()); + p->UpdateResetButton(); + }); + } + } + else if (QSpinBox* spinBox = dynamic_cast(widget)) + { + if constexpr (std::is_integral_v) + { + const std::optional minimum = p->variable_->GetMinimum(); + const std::optional maximum = p->variable_->GetMaximum(); + + if (minimum.has_value()) + { + spinBox->setMinimum(static_cast(*minimum)); + } + if (maximum.has_value()) + { + spinBox->setMaximum(static_cast(*maximum)); + } + + // If the spin box is edited, stage a changed value + QObject::connect( + spinBox, + &QSpinBox::valueChanged, + p->context_.get(), + [this](int i) + { + const T value = p->variable_->GetValue(); + const std::optional staged = p->variable_->GetStaged(); + + // If there is a value staged, and the new value is the same as + // the current value, reset the staged value + if (staged.has_value() && static_cast(i) == value) + { + p->variable_->Reset(); + p->stagedValid_ = true; + p->UpdateResetButton(); + } + // If there is no staged value, or if the new value is different + // than what is staged, attempt to stage the value + else if (!staged.has_value() || static_cast(i) != *staged) + { + p->stagedValid_ = p->variable_->StageValue(static_cast(i)); + p->UpdateResetButton(); + } + // Otherwise, don't process an unchanged value + }); + } + } + + p->UpdateEditWidget(); +} + +template +void SettingsInterface::SetResetButton(QAbstractButton* button) +{ + if (p->resetButton_ != nullptr) + { + QObject::disconnect(p->resetButton_, nullptr, p->context_.get(), nullptr); + } + + p->resetButton_ = button; + + QObject::connect(p->resetButton_, + &QAbstractButton::clicked, + p->context_.get(), + [this]() + { + T defaultValue = p->variable_->GetDefault(); + + if (p->variable_->GetValue() == defaultValue) + { + // If the current value is default, reset the staged + // value + p->variable_->Reset(); + p->stagedValid_ = true; + p->UpdateEditWidget(); + p->UpdateResetButton(); + } + else + { + // Stage the default value + p->stagedValid_ = + p->variable_->StageValue(defaultValue); + p->UpdateEditWidget(); + p->UpdateResetButton(); + } + }); + + p->UpdateResetButton(); +} + +template +void SettingsInterface::Impl::UpdateEditWidget() +{ + // Use the staged value if present, otherwise the current value + const std::optional staged = variable_->GetStaged(); + const T value = variable_->GetValue(); + const T& displayValue = staged.has_value() ? *staged : value; + + if (QLineEdit* lineEdit = dynamic_cast(editWidget_)) + { + if constexpr (std::is_integral_v) + { + lineEdit->setText(QString::number(displayValue)); + } + else if constexpr (std::is_same_v) + { + lineEdit->setText(QString::fromStdString(displayValue)); + } + } + else if (QSpinBox* spinBox = dynamic_cast(editWidget_)) + { + if constexpr (std::is_integral_v) + { + spinBox->setValue(static_cast(displayValue)); + } + } +} + +template +void SettingsInterface::Impl::UpdateResetButton() +{ + const std::optional staged = variable_->GetStaged(); + const T defaultValue = variable_->GetDefault(); + const T value = variable_->GetValue(); + + if (resetButton_ != nullptr) + { + if (staged.has_value()) + { + resetButton_->setVisible(!stagedValid_ || *staged != defaultValue); + } + else + { + resetButton_->setVisible(value != defaultValue); + } + } +} + +} // namespace settings +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/settings_interface.hpp b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp new file mode 100644 index 00000000..82e60d66 --- /dev/null +++ b/scwx-qt/source/scwx/qt/settings/settings_interface.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +class QAbstractButton; +class QWidget; + +namespace scwx +{ +namespace qt +{ +namespace settings +{ + +template +class SettingsVariable; + +template +class SettingsInterface +{ +public: + explicit SettingsInterface(); + ~SettingsInterface(); + + SettingsInterface(const SettingsInterface&) = delete; + SettingsInterface& operator=(const SettingsInterface&) = delete; + + SettingsInterface(SettingsInterface&&) noexcept; + SettingsInterface& operator=(SettingsInterface&&) noexcept; + + /** + * Sets the settings variable associated with the interface. This must be + * set prior to setting any widgets. + * + * @param variable Settings variable + */ + void SetSettingsVariable(SettingsVariable& variable); + + /** + * Sets the edit widget from the settings dialog. + * + * @param widget Edit widget + */ + void SetEditWidget(QWidget* widget); + + /** + * Sets the reset button from the settings dialog. + * + * @param button Reset button + */ + void SetResetButton(QAbstractButton* button); + +private: + class Impl; + std::unique_ptr p; +}; + +#ifdef SETTINGS_INTERFACE_IMPLEMENTATION +template class SettingsInterface; +template class SettingsInterface; +template class SettingsInterface; + +// Containers are not to be used directly +template class SettingsInterface>; +#endif + +} // namespace settings +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp index c679138e..027f5098 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.cpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.cpp @@ -5,11 +5,6 @@ #include #include -#include -#include -#include -#include -#include namespace scwx { @@ -25,28 +20,15 @@ template class SettingsVariable::Impl { public: - explicit Impl() - { - context_->moveToThread(QCoreApplication::instance()->thread()); - } - + explicit Impl() {} ~Impl() {} - void UpdateEditWidget(); - void UpdateResetButton(); - T value_ {}; T default_ {}; std::optional staged_ {}; std::optional minimum_ {}; std::optional maximum_ {}; std::function validator_ {nullptr}; - - bool stagedValid_ {true}; - - std::unique_ptr context_ {std::make_unique()}; - QWidget* editWidget_ {nullptr}; - QAbstractButton* resetButton_ {nullptr}; }; template @@ -143,19 +125,15 @@ void SettingsVariable::SetValueToDefault() template bool SettingsVariable::StageValue(const T& value) { + bool validated = false; + if (Validate(value)) { - p->staged_ = value; - p->stagedValid_ = true; - } - else - { - p->stagedValid_ = false; + p->staged_ = value; + validated = true; } - p->UpdateResetButton(); - - return p->stagedValid_; + return validated; } template @@ -165,7 +143,6 @@ void SettingsVariable::Commit() { p->value_ = std::move(*p->staged_); p->staged_.reset(); - p->stagedValid_ = true; } } @@ -173,10 +150,12 @@ template void SettingsVariable::Reset() { p->staged_.reset(); - p->stagedValid_ = true; +} - p->UpdateEditWidget(); - p->UpdateResetButton(); +template +std::optional SettingsVariable::GetStaged() const +{ + return p->staged_; } template @@ -268,139 +247,6 @@ void SettingsVariable::WriteValue(boost::json::object& json) const json[name()] = boost::json::value_from(p->value_); } -template -void SettingsVariable::SetEditWidget(QWidget* widget) -{ - p->editWidget_ = widget; - - if (QLineEdit* lineEdit = dynamic_cast(widget)) - { - if constexpr (std::is_same_v) - { - // If the line is edited (not programatically changed), stage the new - // value - QObject::connect(lineEdit, - &QLineEdit::textEdited, - p->context_.get(), - [this](const QString& text) - { - // Attempt to stage the value - StageValue(text.toStdString()); - }); - } - } - else if (QSpinBox* spinBox = dynamic_cast(widget)) - { - if constexpr (std::is_integral_v) - { - if (p->minimum_.has_value()) - { - spinBox->setMinimum(static_cast(*p->minimum_)); - } - if (p->maximum_.has_value()) - { - spinBox->setMaximum(static_cast(*p->maximum_)); - } - - // If the spin box is edited, stage a changed value - QObject::connect(spinBox, - &QSpinBox::valueChanged, - p->context_.get(), - [this](int i) - { - // If there is a value staged, and the new value is - // the same as the current value, reset the staged - // value - if (p->staged_.has_value() && - static_cast(i) == p->value_) - { - Reset(); - } - // If there is no staged value, or if the new value - // is different than what is staged, attempt to - // stage the value - else if (!p->staged_.has_value() || - static_cast(i) != *p->staged_) - { - StageValue(static_cast(i)); - } - // Otherwise, don't process an unchanged value - }); - } - } - - p->UpdateEditWidget(); -} - -template -void SettingsVariable::SetResetButton(QAbstractButton* button) -{ - p->resetButton_ = button; - - QObject::connect(p->resetButton_, - &QAbstractButton::clicked, - p->context_.get(), - [this]() - { - if (p->value_ == p->default_) - { - // If the current value is default, reset the staged - // value - Reset(); - } - else - { - // Stage the default value - StageValue(p->default_); - p->UpdateEditWidget(); - } - }); - - p->UpdateResetButton(); -} - -template -void SettingsVariable::Impl::UpdateEditWidget() -{ - // Use the staged value if present, otherwise the current value - T& value = staged_.has_value() ? *staged_ : value_; - - if (QLineEdit* lineEdit = dynamic_cast(editWidget_)) - { - if constexpr (std::is_integral_v) - { - lineEdit->setText(QString::number(value)); - } - else if constexpr (std::is_same_v) - { - lineEdit->setText(QString::fromStdString(value)); - } - } - else if (QSpinBox* spinBox = dynamic_cast(editWidget_)) - { - if constexpr (std::is_integral_v) - { - spinBox->setValue(static_cast(value)); - } - } -} - -template -void SettingsVariable::Impl::UpdateResetButton() -{ - if (resetButton_ != nullptr) - { - if (staged_.has_value()) - { - resetButton_->setVisible(!stagedValid_ || *staged_ != default_); - } - else - { - resetButton_->setVisible(value_ != default_); - } - } -} - template bool SettingsVariable::Equals(const SettingsVariableBase& o) const { diff --git a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp index e5835351..40c974ae 100644 --- a/scwx-qt/source/scwx/qt/settings/settings_variable.hpp +++ b/scwx-qt/source/scwx/qt/settings/settings_variable.hpp @@ -81,6 +81,13 @@ public: */ void Reset(); + /** + * Gets the staged value of the settings variable, if defined. + * + * @return Optional staged value + */ + std::optional GetStaged() const; + /** * Validate the value against the defined parameters of the settings * variable. @@ -158,20 +165,6 @@ public: */ virtual void WriteValue(boost::json::object& json) const override; - /** - * Sets the edit widget from the settings dialog. - * - * @param widget Edit widget - */ - void SetEditWidget(QWidget* widget); - - /** - * Sets the reset button from the settings dialog. - * - * @param button Reset button - */ - void SetResetButton(QAbstractButton* button); - protected: virtual bool Equals(const SettingsVariableBase& o) const override; diff --git a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp index 3e1b565a..23ee6cbf 100644 --- a/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/settings_dialog.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -51,6 +52,12 @@ public: void SetupPalettesAlertsTab(); SettingsDialog* self_; + + settings::SettingsInterface defaultRadarSite_ {}; + settings::SettingsInterface gridWidth_ {}; + settings::SettingsInterface gridHeight_ {}; + settings::SettingsInterface mapboxApiKey_ {}; + settings::SettingsInterface debugEnabled_ {}; }; SettingsDialog::SettingsDialog(QWidget* parent) : @@ -104,25 +111,24 @@ void SettingsDialogImpl::SetupGeneralTab() settings::GeneralSettings& generalSettings = manager::SettingsManager::general_settings(); - generalSettings.default_radar_site().SetEditWidget( - self_->ui->radarSiteComboBox); - generalSettings.default_radar_site().SetResetButton( - self_->ui->resetRadarSiteButton); + defaultRadarSite_.SetSettingsVariable(generalSettings.default_radar_site()); + defaultRadarSite_.SetEditWidget(self_->ui->radarSiteComboBox); + defaultRadarSite_.SetResetButton(self_->ui->resetRadarSiteButton); - generalSettings.grid_width().SetEditWidget(self_->ui->gridWidthSpinBox); - generalSettings.grid_width().SetResetButton(self_->ui->resetGridWidthButton); + gridWidth_.SetSettingsVariable(generalSettings.grid_width()); + gridWidth_.SetEditWidget(self_->ui->gridWidthSpinBox); + gridWidth_.SetResetButton(self_->ui->resetGridWidthButton); - generalSettings.grid_height().SetEditWidget(self_->ui->gridHeightSpinBox); - generalSettings.grid_height().SetResetButton( - self_->ui->resetGridHeightButton); + gridHeight_.SetSettingsVariable(generalSettings.grid_height()); + gridHeight_.SetEditWidget(self_->ui->gridHeightSpinBox); + gridHeight_.SetResetButton(self_->ui->resetGridHeightButton); - generalSettings.mapbox_api_key().SetEditWidget( - self_->ui->mapboxApiKeyLineEdit); - generalSettings.mapbox_api_key().SetResetButton( - self_->ui->resetMapboxApiKeyButton); + mapboxApiKey_.SetSettingsVariable(generalSettings.mapbox_api_key()); + mapboxApiKey_.SetEditWidget(self_->ui->mapboxApiKeyLineEdit); + mapboxApiKey_.SetResetButton(self_->ui->resetMapboxApiKeyButton); - generalSettings.debug_enabled().SetEditWidget( - self_->ui->debugEnabledCheckBox); + debugEnabled_.SetSettingsVariable(generalSettings.debug_enabled()); + debugEnabled_.SetEditWidget(self_->ui->debugEnabledCheckBox); } void SettingsDialogImpl::SetupPalettesColorTablesTab()