diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d07d6a9..7b91c73a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ - Dev: Merged top/bottom and left/right notebook layouts. (#6215) - Dev: Refactored `Button` and friends. (#6102, #6255, #6266, #6302, #6268) - Dev: Made Settings & Account button on Linux/macOS SVGs. (#6267) +- Dev: Some more setting widget refactors. (#6317) - Dev: Emoji style / set is now stored lowercase (and matched case-insensitively). Changing emoji style from this point on and then running an old version might mean you will use the Twitter emoji style by default. (#6300) - Dev: Refactored `OnceFlag`. (#6237, #6316) - Dev: Bumped clang-format requirement to 19. (#6236) diff --git a/src/common/StreamerModeSetting.hpp b/src/common/StreamerModeSetting.hpp new file mode 100644 index 00000000..886125d7 --- /dev/null +++ b/src/common/StreamerModeSetting.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace chatterino { + +enum StreamerModeSetting : std::uint8_t { + Disabled, + Enabled, + DetectStreamingSoftware, +}; + +constexpr std::optional qmagicenumDisplayName( + StreamerModeSetting value) noexcept +{ + switch (value) + { + case Disabled: + return "Disabled"; + + case Enabled: + return "Enabled"; + + case DetectStreamingSoftware: + return "Automatic (Detect streaming software)"; + } +} + +} // namespace chatterino diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 8aaf7a01..78eac3db 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -5,6 +5,7 @@ #include "common/LastMessageLineStyle.hpp" #include "common/Modes.hpp" #include "common/SignalVector.hpp" +#include "common/StreamerModeSetting.hpp" #include "common/ThumbnailPreviewMode.hpp" #include "common/TimeoutStackStyle.hpp" #include "controllers/filters/FilterRecord.hpp" @@ -84,12 +85,6 @@ enum class StreamLinkPreferredQuality : std::uint8_t { AudioOnly, }; -enum StreamerModeSetting { - Disabled = 0, - Enabled = 1, - DetectStreamingSoftware = 2, -}; - enum class TabStyle : std::uint8_t { Normal, Compact, @@ -402,7 +397,9 @@ public: // - "Always hide" // - "Don't hide" EnumSetting enableStreamerMode = { - "/streamerMode/enabled", StreamerModeSetting::DetectStreamingSoftware}; + "/streamerMode/enabled", + StreamerModeSetting::DetectStreamingSoftware, + }; BoolSetting streamerModeHideUsercardAvatars = { "/streamerMode/hideUsercardAvatars", true}; BoolSetting streamerModeHideLinkThumbnails = { diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index 876ee9b4..c783d047 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -131,40 +131,25 @@ void GeneralPage::initLayout(GeneralPageView &layout) available.emplace_back("System", "System"); #endif - auto addThemeDropdown = [&](auto name, auto &setting, - const auto &options, - const QString &tooltip = {}) { - return layout.addDropdown( - name, options, setting, - [](const auto *combo, const auto &themeKey) { - return combo->findData(themeKey, Qt::UserRole); - }, - [](const auto &args) { - return args.combobox->itemData(args.index, Qt::UserRole) - .toString(); - }, - tooltip, Theme::fallbackTheme.name); - }; - - addThemeDropdown("Theme", themes->themeName, available); + SettingWidget::dropdown("Theme", themes->themeName, available) + ->addTo(layout); #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - auto *darkDropdown = addThemeDropdown( - "Dark system theme", themes->darkSystemThemeName, - themes->availableThemes(), - "This theme is selected if your system is in a dark theme and you " - "enabled the adaptive 'System' theme."); - auto *lightDropdown = addThemeDropdown( - "Light system theme", themes->lightSystemThemeName, - themes->availableThemes(), - "This theme is selected if your system is in a light theme and you " - "enabled the adaptive 'System' theme."); + SettingWidget::dropdown("Dark system theme", + themes->darkSystemThemeName, + themes->availableThemes()) + ->setTooltip("This theme is selected if your system is in a dark " + "theme and you enabled the adaptive 'System' theme.") + ->conditionallyEnabledBy(themes->themeName, "System") + ->addTo(layout); - auto isSystem = [](const auto &s) { - return s == "System"; - }; - layout.enableIf(darkDropdown, themes->themeName, isSystem); - layout.enableIf(lightDropdown, themes->themeName, isSystem); + SettingWidget::dropdown("Light system theme", + themes->lightSystemThemeName, + themes->availableThemes()) + ->setTooltip("This theme is selected if your system is in a light " + "theme and you enabled the adaptive 'System' theme.") + ->conditionallyEnabledBy(themes->themeName, "System") + ->addTo(layout); #endif } @@ -705,19 +690,8 @@ void GeneralPage::initLayout(GeneralPageView &layout) "streaming software is running.\nSelect which things you want to " "change while streaming"); - ComboBox *dankDropdown = - layout.addDropdown>( - "Enable Streamer Mode", - {"Disabled", "Enabled", "Automatic (Detect streaming software)"}, - s.enableStreamerMode, - [](int value) { - return value; - }, - [](DropdownArgs args) { - return static_cast(args.index); - }, - false); - dankDropdown->setMinimumWidth(dankDropdown->minimumSizeHint().width() + 30); + SettingWidget::dropdown("Enable Streamer Mode", s.enableStreamerMode) + ->addTo(layout); SettingWidget::checkbox("Hide usercard avatars", s.streamerModeHideUsercardAvatars) diff --git a/src/widgets/settingspages/GeneralPageView.cpp b/src/widgets/settingspages/GeneralPageView.cpp index 09709d11..383bad1d 100644 --- a/src/widgets/settingspages/GeneralPageView.cpp +++ b/src/widgets/settingspages/GeneralPageView.cpp @@ -183,34 +183,6 @@ ComboBox *GeneralPageView::addDropdown(const QString &text, return combo; } -ComboBox *GeneralPageView::addDropdown( - const QString &text, const QStringList &items, - pajlada::Settings::Setting &setting, bool editable, - QString toolTipText) -{ - auto *combo = this->addDropdown(text, items, toolTipText); - - if (editable) - { - combo->setEditable(true); - } - - // update when setting changes - setting.connect( - [combo](const QString &value, auto) { - combo->setCurrentText(value); - }, - this->managedConnections_); - - QObject::connect(combo, &QComboBox::currentTextChanged, - [&setting](const QString &newValue) { - setting = newValue; - getApp()->getWindows()->forceLayoutChannelViews(); - }); - - return combo; -} - void GeneralPageView::addNavigationSpacing() { assert(this->navigationLayout_ != nullptr && diff --git a/src/widgets/settingspages/GeneralPageView.hpp b/src/widgets/settingspages/GeneralPageView.hpp index 07414b22..eebabc98 100644 --- a/src/widgets/settingspages/GeneralPageView.hpp +++ b/src/widgets/settingspages/GeneralPageView.hpp @@ -134,9 +134,6 @@ public: ComboBox *addDropdown(const QString &text, const QStringList &items, QString toolTipText = {}); - ComboBox *addDropdown(const QString &text, const QStringList &items, - pajlada::Settings::Setting &setting, - bool editable = false, QString toolTipText = {}); void addNavigationSpacing(); template @@ -240,67 +237,6 @@ public: return combo; } - template - ComboBox *addDropdown( - const QString &text, - const std::vector> &items, - pajlada::Settings::Setting &setting, - std::function(ComboBox *, T)> getValue, - std::function setValue, QString toolTipText = {}, - const QString &defaultValueText = {}) - { - auto *combo = this->addDropdown(text, {}, std::move(toolTipText)); - - for (const auto &[itemText, userData] : items) - { - combo->addItem(itemText, userData); - } - - if (!defaultValueText.isEmpty()) - { - combo->setCurrentText(defaultValueText); - } - - setting.connect( - [getValue = std::move(getValue), combo](const T &value, auto) { - auto var = getValue(combo, value); - if (var.which() == 0) - { - const auto index = boost::get(var); - if (index >= 0) - { - combo->setCurrentIndex(index); - } - } - else - { - combo->setCurrentText(boost::get(var)); - combo->setEditText(boost::get(var)); - } - }, - this->managedConnections_); - - QObject::connect( - combo, QOverload::of(&QComboBox::currentIndexChanged), - [combo, &setting, - setValue = std::move(setValue)](const int newIndex) { - setting = setValue(DropdownArgs{combo->itemText(newIndex), - combo->currentIndex(), combo}); - getApp()->getWindows()->forceLayoutChannelViews(); - }); - - return combo; - } - - void enableIf(QComboBox *widget, auto &setting, auto cb) - { - auto updateVisibility = [cb = std::move(cb), &setting, widget]() { - auto enabled = cb(setting.getValue()); - widget->setEnabled(enabled); - }; - setting.connect(updateVisibility, this->managedConnections_); - } - DescriptionLabel *addDescription(const QString &text); void addSeparator(); diff --git a/src/widgets/settingspages/SettingWidget.cpp b/src/widgets/settingspages/SettingWidget.cpp index 83409896..451fd165 100644 --- a/src/widgets/settingspages/SettingWidget.cpp +++ b/src/widgets/settingspages/SettingWidget.cpp @@ -181,7 +181,7 @@ SettingWidget *SettingWidget::dropdown(const QString &label, } // TODO: this can probably use some other size hint/size strategy - combo->setMinimumWidth(combo->minimumSizeHint().width()); + combo->setMinimumWidth(combo->minimumSizeHint().width() + 30); widget->actionWidget = combo; widget->label = lbl; @@ -254,7 +254,7 @@ SettingWidget *SettingWidget::dropdown(const QString &label, } // TODO: this can probably use some other size hint/size strategy - combo->setMinimumWidth(combo->minimumSizeHint().width()); + combo->setMinimumWidth(combo->minimumSizeHint().width() + 30); widget->actionWidget = combo; widget->label = lbl; @@ -313,6 +313,72 @@ template SettingWidget *SettingWidget::dropdown( const QString &label, EnumSetting &setting); template SettingWidget *SettingWidget::dropdown( const QString &label, EnumSetting &setting); +template SettingWidget *SettingWidget::dropdown( + const QString &label, EnumSetting &setting); + +SettingWidget *SettingWidget::dropdown( + const QString &label, QStringSetting &setting, + const std::vector> &items) +{ + auto *widget = new SettingWidget(label); + + auto *lbl = new QLabel(label % ":"); + auto *combo = new ComboBox; + combo->setFocusPolicy(Qt::StrongFocus); + + for (const auto &[itemText, itemData] : items) + { + combo->addItem(itemText, itemData); + } + + // TODO: this can probably use some other size hint/size strategy + combo->setMinimumWidth(combo->minimumSizeHint().width() + 30); + + widget->actionWidget = combo; + widget->label = lbl; + + widget->hLayout->addWidget(lbl); + widget->hLayout->addStretch(1); + widget->hLayout->addWidget(combo); + + setting.connect( + [combo, label](const auto &value) { + std::optional foundRow; + + for (auto row = 0; row < combo->model()->rowCount(); ++row) + { + auto index = combo->model()->index(row, 0); + auto rowEnumValue = index.data(Qt::UserRole); + if (rowEnumValue == value) + { + foundRow = row; + break; + } + } + + if (foundRow) + { + combo->setCurrentIndex(*foundRow); + } + else + { + qCWarning(chatterinoWidget) + << "Did not find a correct combo box row for" << label + << " with value" << value; + } + }, + widget->managedConnections); + + QObject::connect(combo, &QComboBox::currentTextChanged, + [label, combo, &setting](const auto &newText) { + bool ok = true; + auto stringValue = combo->currentData().toString(); + + setting.setValue(stringValue); + }); + + return widget; +} SettingWidget *SettingWidget::colorButton(const QString &label, QStringSetting &setting) @@ -498,26 +564,39 @@ SettingWidget *SettingWidget::conditionallyEnabledBy(BoolSetting &setting) return this; } +SettingWidget *SettingWidget::conditionallyEnabledBy( + QStringSetting &setting, const QString &expectedValue) +{ + setting.connect( + [this, expectedValue](const auto &value, const auto &) { + this->actionWidget->setEnabled(value == expectedValue); + }, + this->managedConnections); + + return this; +} + void SettingWidget::addTo(GeneralPageView &view) { view.pushWidget(this); - if (this->label != nullptr) - { - view.registerWidget(this->label, this->keywords, this); - } - view.registerWidget(this->actionWidget, this->keywords, this); + this->registerWidget(view); } void SettingWidget::addTo(GeneralPageView &view, QFormLayout *formLayout) +{ + this->registerWidget(view); + + formLayout->addRow(this->label, this->actionWidget); +} + +void SettingWidget::registerWidget(GeneralPageView &view) { if (this->label != nullptr) { view.registerWidget(this->label, this->keywords, this); } view.registerWidget(this->actionWidget, this->keywords, this); - - formLayout->addRow(this->label, this->actionWidget); } } // namespace chatterino diff --git a/src/widgets/settingspages/SettingWidget.hpp b/src/widgets/settingspages/SettingWidget.hpp index 4321e1b7..67196dbe 100644 --- a/src/widgets/settingspages/SettingWidget.hpp +++ b/src/widgets/settingspages/SettingWidget.hpp @@ -16,6 +16,8 @@ #include #include +#include +#include class QFormLayout; @@ -66,14 +68,25 @@ public: intInput(const QString &label, IntSetting &setting, IntInputParams params); + /// Create a dropdown backed by an enum + /// + /// The setting itself expects the enum name (i.e. "Foo") template [[nodiscard("Must use created setting widget")]] static SettingWidget * dropdown(const QString &label, EnumStringSetting &setting); + /// Create a dropdown backed by an enum + /// + /// The setting itself expects the enum value (i.e. 3) template [[nodiscard("Must use created setting widget")]] static SettingWidget * dropdown(const QString &label, EnumSetting &setting); + /// Create a dropdown for a String setting that is not backed by an enum + [[nodiscard("Must use created setting widget")]] static SettingWidget * + dropdown(const QString &label, QStringSetting &setting, + const std::vector> &items); + [[nodiscard("Must use created setting widget")]] static SettingWidget * colorButton(const QString &label, QStringSetting &setting); [[nodiscard("Must use created setting widget")]] static SettingWidget * @@ -96,13 +109,22 @@ public: [[nodiscard("Must use created setting widget")]] SettingWidget *addKeywords( const QStringList &newKeywords); + /// Conditionally enable the widget if the given bool setting is true [[nodiscard("Must use created setting widget")]] SettingWidget * conditionallyEnabledBy(BoolSetting &setting); + /// Conditionally enable the widget if the given string setting is equal to expectedValue + [[nodiscard("Must use created setting widget")]] SettingWidget * + conditionallyEnabledBy(QStringSetting &setting, + const QString &expectedValue); + void addTo(GeneralPageView &view); void addTo(GeneralPageView &view, QFormLayout *formLayout); private: + /// Registers this widget & its optional label to the given page view + void registerWidget(GeneralPageView &view); + QWidget *label = nullptr; QWidget *actionWidget = nullptr;