diff --git a/res/controllers/Dummy Device Screen.hid.xml b/res/controllers/Dummy Device Screen.hid.xml index 90526cc1a33..1d6526459a8 100644 --- a/res/controllers/Dummy Device Screen.hid.xml +++ b/res/controllers/Dummy Device Screen.hid.xml @@ -8,6 +8,62 @@ + + + + + + + + + + diff --git a/src/controllers/legacycontrollersettings.cpp b/src/controllers/legacycontrollersettings.cpp index f97d2b9d628..5240b71ed94 100644 --- a/src/controllers/legacycontrollersettings.cpp +++ b/src/controllers/legacycontrollersettings.cpp @@ -2,12 +2,19 @@ #include +#include #include +#include #include #include +#include #include #include +#include +#include +#include #include +#include #include "moc_legacycontrollersettings.cpp" @@ -54,6 +61,8 @@ LegacyControllerSettingBuilder::LegacyControllerSettingBuilder() { registerType(); registerType(); registerType(); + registerType(); + registerType(); } AbstractLegacyControllerSetting::AbstractLegacyControllerSetting(const QDomElement& element) { @@ -221,7 +230,8 @@ LegacyControllerEnumSetting::LegacyControllerEnumSetting( !value.isNull(); value = value.nextSiblingElement("value")) { QString val = value.text(); - m_options.append(std::tuple(val, value.attribute("label", val))); + QColor color = QColor(value.attribute("color")); + m_options.emplace_back(Item{val, value.attribute("label", val), color}); if (value.hasAttribute("default")) { m_defaultValue = pos; } @@ -239,8 +249,8 @@ void LegacyControllerEnumSetting::parse(const QString& in, bool* ok) { save(); size_t pos = 0; - for (const auto& value : std::as_const(m_options)) { - if (std::get<0>(value) == in) { + for (const auto& item : std::as_const(m_options)) { + if (item.value == in) { if (ok != nullptr) { *ok = true; } @@ -255,8 +265,15 @@ void LegacyControllerEnumSetting::parse(const QString& in, bool* ok) { QWidget* LegacyControllerEnumSetting::buildInputWidget(QWidget* pParent) { auto* pComboBox = new QComboBox(pParent); - for (const auto& value : std::as_const(m_options)) { - pComboBox->addItem(std::get<1>(value)); + for (const auto& item : std::as_const(m_options)) { + if (item.color.isValid()) { + QPixmap icon(24, 24); + QPainter painter(&icon); + painter.fillRect(0, 0, 24, 24, item.color); + pComboBox->addItem(QIcon(icon), item.label); + } else { + pComboBox->addItem(item.label); + } } pComboBox->setCurrentIndex(static_cast(m_editedValue)); @@ -274,3 +291,140 @@ QWidget* LegacyControllerEnumSetting::buildInputWidget(QWidget* pParent) { return pComboBox; } + +LegacyControllerColorSetting::LegacyControllerColorSetting( + const QDomElement& element) + : AbstractLegacyControllerSetting(element), + m_defaultValue(QColor(element.attribute("default"))), + m_savedValue(m_defaultValue), + m_editedValue(m_defaultValue) { + reset(); + save(); +} + +LegacyControllerColorSetting::~LegacyControllerColorSetting() = default; + +void LegacyControllerColorSetting::parse(const QString& in, bool* ok) { + if (ok != nullptr) { + *ok = false; + } + reset(); + save(); + + m_savedValue = QColor(in); + if (ok != nullptr) { + *ok = m_editedValue.isValid(); + } + if (!m_editedValue.isValid()) { + return; + } + m_editedValue = m_savedValue; +} + +QWidget* LegacyControllerColorSetting::buildInputWidget(QWidget* pParent) { + auto* pPushButton = new QPushButton(tr("Change color"), pParent); + + auto setColorIcon = [pPushButton](const QColor& color) { + QPixmap icon(24, 24); + QPainter painter(&icon); + painter.fillRect(0, 0, 24, 24, color); + pPushButton->setIcon(QIcon(icon)); + }; + + connect(this, + &AbstractLegacyControllerSetting::valueReset, + pPushButton, + [this, pPushButton, setColorIcon]() { + if (m_editedValue.isValid()) { + setColorIcon(m_editedValue); + } else { + pPushButton->setIcon(QIcon()); + } + }); + + connect(pPushButton, &QPushButton::clicked, this, [this, pPushButton, setColorIcon](bool) { + auto color = QColorDialog::getColor(m_editedValue, pPushButton, tr("Choose a new color")); + if (color.isValid()) { + m_editedValue = color; + setColorIcon(color); + emit changed(); + } + }); + + if (m_savedValue.isValid()) { + setColorIcon(m_savedValue); + } + + return pPushButton; +} + +LegacyControllerFileSetting::LegacyControllerFileSetting( + const QDomElement& element) + : AbstractLegacyControllerSetting(element), + m_fileFilter(element.attribute("pattern")), + m_defaultValue(QFileInfo(element.attribute("default"))), + m_savedValue(m_defaultValue), + m_editedValue(m_defaultValue) { + reset(); + save(); +} + +void LegacyControllerFileSetting::parse(const QString& in, bool* ok) { + if (ok != nullptr) { + *ok = false; + } + reset(); + save(); + + m_editedValue = QFileInfo(in); + if (ok != nullptr) { + *ok = m_editedValue.exists(); + } + if (!m_editedValue.exists()) { + return; + } + m_savedValue = m_editedValue; +} + +QWidget* LegacyControllerFileSetting::buildInputWidget(QWidget* pParent) { + auto* pWidget = new QWidget(pParent); + pWidget->setLayout(new QHBoxLayout); + auto* pPushButton = new QPushButton(tr("Browse..."), pWidget); + auto* pLabel = new QLabel(pWidget); + auto setLabelText = [pLabel](QString&& text) { + pLabel->setText(QStringLiteral("%1").arg(text)); + }; + pWidget->layout()->addWidget(pLabel); + pWidget->layout()->addWidget(pPushButton); + + connect(this, &AbstractLegacyControllerSetting::valueReset, pLabel, [this, setLabelText]() { + if (m_editedValue.exists()) { + setLabelText(m_editedValue.absoluteFilePath()); + } else { + setLabelText(tr("No file selected")); + } + }); + + connect(pPushButton, + &QPushButton::clicked, + this, + [this, pLabel, pPushButton](bool) { + auto file = QFileInfo(QFileDialog::getOpenFileName(pPushButton, + tr("Select a file"), + QString(), + m_fileFilter)); + if (file.exists()) { + m_editedValue = file; + pLabel->setText(QStringLiteral("%1").arg(file.absoluteFilePath())); + emit changed(); + } + }); + + if (m_savedValue.exists()) { + setLabelText(m_savedValue.absoluteFilePath()); + } else { + setLabelText(tr("No file selected")); + } + + return pWidget; +} diff --git a/src/controllers/legacycontrollersettings.h b/src/controllers/legacycontrollersettings.h index b7d2f378d9c..13bf730606b 100644 --- a/src/controllers/legacycontrollersettings.h +++ b/src/controllers/legacycontrollersettings.h @@ -1,5 +1,9 @@ #pragma once +#include + +#include +#include #include #include "controllers/legacycontrollersettingsfactory.h" @@ -180,7 +184,7 @@ class LegacyControllerBooleanSetting bool m_defaultValue; bool m_editedValue; - friend class LegacyControllerMappingSettingsTest_booleanSettingEditing_Test; + FRIEND_TEST(LegacyControllerMappingSettingsTest, booleanSettingEditing); }; template @@ -297,8 +301,8 @@ class LegacyControllerNumberSetting SettingType m_editedValue; - friend class LegacyControllerMappingSettingsTest_integerSettingEditing_Test; - friend class LegacyControllerMappingSettingsTest_doubleSettingEditing_Test; + FRIEND_TEST(LegacyControllerMappingSettingsTest, integerSettingEditing); + FRIEND_TEST(LegacyControllerMappingSettingsTest, doubleSettingEditing); }; template @@ -351,6 +355,12 @@ class LegacyControllerEnumSetting : public LegacyControllerSettingFactory, public AbstractLegacyControllerSetting { public: + struct Item { + QString value; + QString label; + QColor color; + }; + LegacyControllerEnumSetting(const QDomElement& element); virtual ~LegacyControllerEnumSetting() = default; @@ -359,12 +369,12 @@ class LegacyControllerEnumSetting return QJSValue(stringify()); } - const QList>& options() const { + const QList& options() const { return m_options; } QString stringify() const override { - return std::get<0>(m_options.value(static_cast(m_savedValue))); + return m_options.value(static_cast(m_savedValue)).value; } void parse(const QString& in, bool* ok) override; bool isDefault() const override { @@ -400,7 +410,7 @@ class LegacyControllerEnumSetting protected: LegacyControllerEnumSetting(const QDomElement& element, - const QList>& options, + const QList& options, size_t currentValue, size_t defaultValue) : AbstractLegacyControllerSetting(element), @@ -413,14 +423,158 @@ class LegacyControllerEnumSetting private: // We use a QList instead of QHash here because we want to keep the natural order - QList> m_options; + QList m_options; size_t m_savedValue; size_t m_defaultValue; size_t m_editedValue; - friend class LegacyControllerMappingSettingsTest_enumSettingEditing_Test; - friend class ControllerS4MK3SettingTest_ensureLibrarySettingValueAndEnumEquals; + FRIEND_TEST(LegacyControllerMappingSettingsTest, enumSettingEditing); + FRIEND_TEST(ControllerS4MK3SettingTest, ensureLibrarySettingValueAndEnumEquals); +}; + +class LegacyControllerColorSetting + : public LegacyControllerSettingFactory, + public AbstractLegacyControllerSetting { + public: + LegacyControllerColorSetting(const QDomElement& element); + + ~LegacyControllerColorSetting() override; + + QJSValue value() const override { + return QJSValue(stringify()); + } + + QString stringify() const override { + return m_savedValue.name(QColor::HexRgb); + } + void parse(const QString& in, bool* ok) override; + bool isDefault() const override { + return m_savedValue == m_defaultValue; + } + bool isDirty() const override { + return m_savedValue != m_editedValue; + } + + virtual void save() override { + m_savedValue = m_editedValue; + } + + virtual void reset() override { + m_editedValue = m_defaultValue; + emit valueReset(); + } + + /// @brief Whether or not this setting definition and its current state are + /// valid. Validity scope includes a known default/current/dirty option. + /// @return true if valid + bool valid() const override { + return AbstractLegacyControllerSetting::valid() && + m_defaultValue.isValid() && + m_savedValue.isValid(); + } + + static AbstractLegacyControllerSetting* createFrom(const QDomElement& element) { + return new LegacyControllerColorSetting(element); + } + static inline bool match(const QDomElement& element) { + return element.hasAttribute("type") && + QString::compare(element.attribute("type"), + "color", + Qt::CaseInsensitive) == 0; + } + + protected: + LegacyControllerColorSetting(const QDomElement& element, + QColor currentValue, + QColor defaultValue) + : AbstractLegacyControllerSetting(element), + m_defaultValue(defaultValue), + m_savedValue(currentValue) { + } + + virtual QWidget* buildInputWidget(QWidget* parent) override; + + private: + QColor m_defaultValue; + QColor m_savedValue; + + QColor m_editedValue; + + FRIEND_TEST(ControllerS4MK3SettingTest, ensureLibrarySettingValueAndEnumEquals); + FRIEND_TEST(LegacyControllerMappingSettingsTest, enumSettingEditing); +}; + +class LegacyControllerFileSetting + : public LegacyControllerSettingFactory, + public AbstractLegacyControllerSetting { + public: + LegacyControllerFileSetting(const QDomElement& element); + + virtual ~LegacyControllerFileSetting() = default; + + QJSValue value() const override { + return QJSValue(stringify()); + } + + QString stringify() const override { + return m_savedValue.absoluteFilePath(); + } + void parse(const QString& in, bool* ok) override; + bool isDefault() const override { + return m_savedValue == m_defaultValue; + } + bool isDirty() const override { + return m_savedValue != m_editedValue; + } + + virtual void save() override { + m_savedValue = m_editedValue; + } + + virtual void reset() override { + m_editedValue = m_defaultValue; + emit valueReset(); + } + + /// @brief Whether or not this setting definition and its current state are + /// valid. Validity scope includes a known default/current/dirty option. + /// @return true if valid + bool valid() const override { + return AbstractLegacyControllerSetting::valid() && + (m_defaultValue == m_savedValue || m_savedValue.exists()); + } + + static AbstractLegacyControllerSetting* createFrom(const QDomElement& element) { + return new LegacyControllerFileSetting(element); + } + static inline bool match(const QDomElement& element) { + return element.hasAttribute("type") && + QString::compare(element.attribute("type"), + "file", + Qt::CaseInsensitive) == 0; + } + + protected: + LegacyControllerFileSetting(const QDomElement& element, + const QFileInfo& currentValue, + const QFileInfo& defaultValue) + : AbstractLegacyControllerSetting(element), + m_defaultValue(defaultValue), + m_savedValue(currentValue) { + } + + virtual QWidget* buildInputWidget(QWidget* parent) override; + + private: + QString m_fileFilter; + QFileInfo m_defaultValue; + QFileInfo m_savedValue; + + QFileInfo m_editedValue; + + FRIEND_TEST(LegacyControllerMappingSettingsTest, enumSettingEditing); + FRIEND_TEST(ControllerS4MK3SettingTest, ensureLibrarySettingValueAndEnumEquals); }; template<> diff --git a/src/test/controllers/controller_columnid_regression_test.cpp b/src/test/controllers/controller_columnid_regression_test.cpp index 7eac4448184..8b42a2c018a 100644 --- a/src/test/controllers/controller_columnid_regression_test.cpp +++ b/src/test/controllers/controller_columnid_regression_test.cpp @@ -76,7 +76,7 @@ TEST_F(ControllerLibraryColumnIDRegressionTest, ensureS4MK3) { auto pEnum = std::dynamic_pointer_cast(setting); EXPECT_TRUE(pEnum); for (const auto& opt : pEnum->options()) { - EXPECT_EQ(static_cast(COLUMN_MAPPING[std::get<0>(opt)]), std::get<1>(opt).toInt()); + EXPECT_EQ(static_cast(COLUMN_MAPPING[opt.value]), opt.label.toInt()); } count++; }