From 926f3e3d3e6ff57633d2d44085f02754ffe1c988 Mon Sep 17 00:00:00 2001
From: lat9nq <22451773+lat9nq@users.noreply.github.com>
Date: Wed, 21 Jun 2023 05:04:21 -0400
Subject: [PATCH] settings,configuration: Add a default suffix

---
 src/common/settings.h                         | 26 +++++++---
 src/common/settings_common.cpp                |  6 +--
 src/common/settings_common.h                  | 33 +++++++------
 src/common/settings_setting.h                 | 15 +++---
 src/yuzu/configuration/configure_audio.cpp    | 13 +----
 src/yuzu/configuration/configure_graphics.cpp |  4 +-
 src/yuzu/configuration/configure_system.cpp   | 11 +----
 src/yuzu/configuration/shared_widget.cpp      | 47 ++++++++++++++-----
 src/yuzu/configuration/shared_widget.h        | 12 ++---
 9 files changed, 93 insertions(+), 74 deletions(-)

diff --git a/src/common/settings.h b/src/common/settings.h
index b87301d4e..c4339cb1f 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -137,8 +137,15 @@ struct Values {
     SwitchableSetting<AudioMode, true> sound_index{linkage,         AudioMode::Stereo,
                                                    AudioMode::Mono, AudioMode::Surround,
                                                    "sound_index",   Category::SystemAudio};
-    SwitchableSetting<u8, true> volume{
-        linkage, 100, 0, 200, "volume", Category::Audio, Specialization::Scalar, true, true};
+    SwitchableSetting<u8, true> volume{linkage,
+                                       100,
+                                       0,
+                                       200,
+                                       "volume",
+                                       Category::Audio,
+                                       Specialization::Scalar | Specialization::Percentage,
+                                       true,
+                                       true};
     Setting<bool, false> audio_muted{
         linkage, false, "audio_muted", Category::Audio, Specialization::Default, false};
     Setting<bool, false> dump_audio_commands{
@@ -156,7 +163,7 @@ struct Values {
                                              9999,
                                              "speed_limit",
                                              Category::Core,
-                                             Specialization::Countable,
+                                             Specialization::Countable | Specialization::Percentage,
                                              true,
                                              true,
                                              &use_speed_limit};
@@ -268,9 +275,16 @@ struct Values {
                                                   Specialization::Default,
                                                   true,
                                                   true};
-    SwitchableSetting<int, true> fsr_sharpening_slider{
-        linkage, 25,  0, 200, "fsr_sharpening_slider", Category::Renderer, Specialization::Scalar,
-        true,    true};
+    SwitchableSetting<int, true> fsr_sharpening_slider{linkage,
+                                                       25,
+                                                       0,
+                                                       200,
+                                                       "fsr_sharpening_slider",
+                                                       Category::Renderer,
+                                                       Specialization::Scalar |
+                                                           Specialization::Percentage,
+                                                       true,
+                                                       true};
 
     SwitchableSetting<u8, false> bg_red{
         linkage, 0, "bg_red", Category::Renderer, Specialization::Default, true, true};
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 53d4548f5..799942980 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -7,8 +7,8 @@
 namespace Settings {
 
 BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
-                           bool save_, bool runtime_modifiable_,
-                           enum Specialization specialization_, BasicSetting* other_setting_)
+                           bool save_, bool runtime_modifiable_, u32 specialization_,
+                           BasicSetting* other_setting_)
     : label{name}, category{category_}, id{linkage.count}, save{save_},
       runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
       other_setting{other_setting_} {
@@ -40,7 +40,7 @@ Category BasicSetting::Category() const {
     return category;
 }
 
-Specialization BasicSetting::Specialization() const {
+u32 BasicSetting::Specialization() const {
     return specialization;
 }
 
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index ad005ca4e..6b717deb1 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -43,15 +43,21 @@ enum class Category : u32 {
     MaxEnum,
 };
 
-enum class Specialization : u32 {
-    Default,
-    Time,
-    Hex,
-    List,
-    RuntimeList,
-    Scalar,
-    Countable,
-    Paired,
+constexpr u8 SpecializationTypeMask = 0xf;
+constexpr u8 SpecializationAttributeMask = 0xf0;
+constexpr u8 SpecializationAttributeOffset = 4;
+
+enum Specialization : u8 {
+    Default = 0,
+    Time = 1,
+    Hex = 2,
+    List = 3,
+    RuntimeList = 4,
+    Scalar = 5,
+    Countable = 6,
+    Paired = 7,
+
+    Percentage = (1 << SpecializationAttributeOffset),
 };
 
 bool IsConfiguringGlobal();
@@ -75,7 +81,7 @@ public:
 class BasicSetting {
 protected:
     explicit BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
-                          bool save_, bool runtime_modifiable_, Specialization spec,
+                          bool save_, bool runtime_modifiable_, u32 specialization,
                           BasicSetting* other_setting);
 
 public:
@@ -195,7 +201,7 @@ public:
     /**
      * @returns Extra metadata for data representation in frontend implementations.
      */
-    [[nodiscard]] enum Specialization Specialization() const;
+    [[nodiscard]] u32 Specialization() const;
 
     /**
      * @returns Another BasicSetting if one is paired, or nullptr otherwise.
@@ -240,9 +246,8 @@ private:
     const u32 id;                 ///< Unique integer for the setting
     const bool save; ///< Suggests if the setting should be saved and read to a frontend config
     const bool
-        runtime_modifiable; ///< Suggests if the setting can be modified while a guest is running
-    const enum Specialization
-        specialization;                ///< Extra data to identify representation of a setting
+        runtime_modifiable;   ///< Suggests if the setting can be modified while a guest is running
+    const u32 specialization; ///< Extra data to identify representation of a setting
     BasicSetting* const other_setting; ///< A paired setting
 };
 
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
index dd91250a1..d1915cb43 100644
--- a/src/common/settings_setting.h
+++ b/src/common/settings_setting.h
@@ -35,8 +35,7 @@ public:
      * @param category_ Category of the setting AKA INI group
      */
     explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
-                     enum Category category_,
-                     enum Specialization specialization = Specialization::Default,
+                     enum Category category_, u32 specialization = Specialization::Default,
                      bool save_ = true, bool runtime_modifiable_ = false,
                      BasicSetting* other_setting = nullptr)
         requires(!ranged)
@@ -57,9 +56,8 @@ public:
      */
     explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
                      const Type& max_val, const std::string& name, enum Category category_,
-                     enum Specialization specialization = Specialization::Default,
-                     bool save_ = true, bool runtime_modifiable_ = false,
-                     BasicSetting* other_setting = nullptr)
+                     u32 specialization = Specialization::Default, bool save_ = true,
+                     bool runtime_modifiable_ = false, BasicSetting* other_setting = nullptr)
         requires(ranged)
         : BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization,
                        other_setting),
@@ -237,8 +235,7 @@ public:
      * @param category_ Category of the setting AKA INI group
      */
     explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
-                               Category category_,
-                               enum Specialization specialization = Specialization::Default,
+                               Category category_, u32 specialization = Specialization::Default,
                                bool save_ = true, bool runtime_modifiable_ = false,
                                BasicSetting* other_setting = nullptr)
         requires(!ranged)
@@ -261,8 +258,8 @@ public:
      */
     explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
                                const Type& max_val, const std::string& name, Category category_,
-                               enum Specialization specialization = Specialization::Default,
-                               bool save_ = true, bool runtime_modifiable_ = false,
+                               u32 specialization = Specialization::Default, bool save_ = true,
+                               bool runtime_modifiable_ = false,
                                BasicSetting* other_setting = nullptr)
         requires(ranged)
         : Setting<Type, true>{
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index cdb89ccda..11714b86d 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -43,17 +43,7 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
     push(Settings::Category::SystemAudio);
 
     for (auto* setting : settings) {
-        auto* widget = [&]() {
-            // TODO (lat9nq): Let the system manage sink_id
-            if (setting->Id() == Settings::values.volume.Id()) {
-                // volume needs to be a slider (default is line edit)
-                return builder.BuildWidget(setting, apply_funcs, nullptr,
-                                           ConfigurationShared::RequestType::Slider,
-                                           tr("%1%", "Volume percentage (e.g. 50%)"));
-            } else {
-                return builder.BuildWidget(setting, apply_funcs);
-            }
-        }();
+        auto* widget = builder.BuildWidget(setting, apply_funcs);
 
         if (widget == nullptr) {
             continue;
@@ -66,6 +56,7 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
         layout.addWidget(widget);
 
         if (setting->Id() == Settings::values.sink_id.Id()) {
+            // TODO (lat9nq): Let the system manage sink_id
             sink_combo_box = widget->combobox;
             InitializeAudioSinkComboBox();
 
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 74449e6d4..7263af13f 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -232,10 +232,10 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
     for (const auto setting : Settings::values.linkage.by_category[Settings::Category::Renderer]) {
         ConfigurationShared::Widget* widget = [&]() {
             if (setting->Id() == Settings::values.fsr_sharpening_slider.Id()) {
-                // FSR needs a reversed slider
+                // FSR needs a reversed slider and a 0.5 multiplier
                 return builder.BuildWidget(
                     setting, apply_funcs, ConfigurationShared::RequestType::ReverseSlider, true,
-                    0.5f, nullptr, tr("%1%", "FSR sharpening percentage (e.g. 50%)"));
+                    0.5f, nullptr, tr("%", "FSR sharpening percentage (e.g. 50%)"));
             } else {
                 return builder.BuildWidget(setting, apply_funcs);
             }
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index cb708051e..05706c4de 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -106,16 +106,7 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
     push(Settings::values.linkage.by_category[Settings::Category::System]);
 
     for (auto setting : settings) {
-        ConfigurationShared::Widget* widget = [this, setting, &builder]() {
-            if (setting->Id() == Settings::values.speed_limit.Id()) {
-                // speed_limit must be specified to translate the percentage
-                return builder.BuildWidget(setting, apply_funcs, &Settings::values.use_speed_limit,
-                                           ConfigurationShared::RequestType::SpinBox,
-                                           tr("%", "Limit speed percentage (e.g. 50%)"));
-            } else {
-                return builder.BuildWidget(setting, apply_funcs);
-            }
-        }();
+        ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
 
         if (widget == nullptr) {
             continue;
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index bbcee7488..978bfa590 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -50,6 +50,20 @@ static std::string RelevantDefault(const Settings::BasicSetting& setting) {
     return Settings::IsConfiguringGlobal() ? setting.DefaultToString() : setting.ToStringGlobal();
 }
 
+static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) {
+    const auto tr = [parent](const char* text, const char* context) {
+        return parent->tr(text, context);
+    };
+
+    if ((setting.Specialization() & Settings::SpecializationAttributeMask) ==
+        Settings::Specialization::Percentage) {
+        std::string context{fmt::format("{} percentage (e.g. 50%)", setting.GetLabel())};
+        return tr("%", context.c_str());
+    }
+
+    return QStringLiteral("");
+}
+
 QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
     restore_button_count++;
 
@@ -180,7 +194,7 @@ QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
     return line_edit;
 }
 
-QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& format,
+QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix,
                               std::function<std::string()>& serializer,
                               std::function<void()>& restore_func,
                               const std::function<void()>& touch) {
@@ -205,7 +219,10 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& fo
 
     int max_val = std::stoi(setting.MaxVal());
 
-    const QString use_format = format == QStringLiteral("") ? QStringLiteral("%1") : format;
+    QString suffix =
+        given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
+
+    const QString use_format = QStringLiteral("%1").append(suffix);
 
     QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
         int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
@@ -228,7 +245,8 @@ QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& fo
     return container;
 }
 
-QWidget* Widget::CreateSpinBox(const QString& suffix, std::function<std::string()>& serializer,
+QWidget* Widget::CreateSpinBox(const QString& given_suffix,
+                               std::function<std::string()>& serializer,
                                std::function<void()>& restore_func,
                                const std::function<void()>& touch) {
     const int min_val =
@@ -237,6 +255,9 @@ QWidget* Widget::CreateSpinBox(const QString& suffix, std::function<std::string(
         setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits<int>::max();
     const int default_val = std::stoi(setting.ToString());
 
+    QString suffix =
+        given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
+
     spinbox = new QSpinBox(this);
     spinbox->setRange(min_val, max_val);
     spinbox->setValue(default_val);
@@ -338,7 +359,7 @@ QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict,
 
 void Widget::SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
                             RequestType request, float multiplier,
-                            Settings::BasicSetting* other_setting, const QString& string) {
+                            Settings::BasicSetting* other_setting, const QString& suffix) {
     created = true;
     const auto type = setting.TypeId();
 
@@ -391,7 +412,7 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
         if (request != RequestType::Default) {
             return request;
         }
-        switch (setting.Specialization()) {
+        switch (setting.Specialization() & Settings::SpecializationTypeMask) {
         case Settings::Specialization::Default:
             return RequestType::Default;
         case Settings::Specialization::Time:
@@ -422,7 +443,7 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
         switch (request) {
         case RequestType::Slider:
         case RequestType::ReverseSlider:
-            data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, string,
+            data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix,
                                           serializer, restore_func, touch);
             break;
         case RequestType::Default:
@@ -434,7 +455,7 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
                                                 serializer, restore_func, touch);
             break;
         case RequestType::SpinBox:
-            data_component = CreateSpinBox(string, serializer, restore_func, touch);
+            data_component = CreateSpinBox(suffix, serializer, restore_func, touch);
             break;
         case RequestType::HexEdit:
             data_component = CreateHexEdit(serializer, restore_func, touch);
@@ -527,7 +548,7 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
                const ComboboxTranslationMap& combobox_translations_, QWidget* parent_,
                bool runtime_lock_, std::forward_list<std::function<void(bool)>>& apply_funcs_,
                RequestType request, bool managed, float multiplier,
-               Settings::BasicSetting* other_setting, const QString& string)
+               Settings::BasicSetting* other_setting, const QString& suffix)
     : QWidget(parent_), parent{parent_}, translations{translations_},
       combobox_enumerations{combobox_translations_}, setting{*setting_}, apply_funcs{apply_funcs_},
       runtime_lock{runtime_lock_} {
@@ -555,7 +576,7 @@ Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translati
 
     std::function<void()> load_func = []() {};
 
-    SetupComponent(label, load_func, managed, request, multiplier, other_setting, string);
+    SetupComponent(label, load_func, managed, request, multiplier, other_setting, suffix);
 
     if (!created) {
         LOG_WARNING(Frontend, "No widget was created for \"{}\"", setting.GetLabel());
@@ -587,7 +608,7 @@ Builder::~Builder() = default;
 Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
                              std::forward_list<std::function<void(bool)>>& apply_funcs,
                              RequestType request, bool managed, float multiplier,
-                             Settings::BasicSetting* other_setting, const QString& string) const {
+                             Settings::BasicSetting* other_setting, const QString& suffix) const {
     if (!Settings::IsConfiguringGlobal() && !setting->Switchable()) {
         return nullptr;
     }
@@ -598,14 +619,14 @@ Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
     }
 
     return new Widget(setting, *translations, *combobox_translations, parent, runtime_lock,
-                      apply_funcs, request, managed, multiplier, other_setting, string);
+                      apply_funcs, request, managed, multiplier, other_setting, suffix);
 }
 
 Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
                              std::forward_list<std::function<void(bool)>>& apply_funcs,
                              Settings::BasicSetting* other_setting, RequestType request,
-                             const QString& string) const {
-    return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, string);
+                             const QString& suffix) const {
+    return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, suffix);
 }
 
 const ComboboxTranslationMap& Builder::ComboboxTranslations() const {
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
index e8c281b81..b3f9efd78 100644
--- a/src/yuzu/configuration/shared_widget.h
+++ b/src/yuzu/configuration/shared_widget.h
@@ -58,14 +58,14 @@ public:
      * @param managed Set true if the caller will set up component data and handling
      * @param multiplier Value to multiply the slider feedback label
      * @param other_setting Second setting to modify, to replace the label with a checkbox
-     * @param string Set to specify formats for Slider feedback labels or SpinBox
+     * @param suffix Set to specify formats for Slider feedback labels or SpinBox
      */
     explicit Widget(Settings::BasicSetting* setting, const TranslationMap& translations,
                     const ComboboxTranslationMap& combobox_translations, QWidget* parent,
                     bool runtime_lock, std::forward_list<std::function<void(bool)>>& apply_funcs_,
                     RequestType request = RequestType::Default, bool managed = true,
                     float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
-                    const QString& string = QStringLiteral(""));
+                    const QString& suffix = QStringLiteral(""));
     virtual ~Widget();
 
     /**
@@ -95,7 +95,7 @@ public:
 private:
     void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
                         RequestType request, float multiplier,
-                        Settings::BasicSetting* other_setting, const QString& string);
+                        Settings::BasicSetting* other_setting, const QString& suffix);
 
     QLabel* CreateLabel(const QString& text);
     QWidget* CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
@@ -111,7 +111,7 @@ private:
                             bool managed = true);
     QWidget* CreateHexEdit(std::function<std::string()>& serializer,
                            std::function<void()>& restore_func, const std::function<void()>& touch);
-    QWidget* CreateSlider(bool reversed, float multiplier, const QString& format,
+    QWidget* CreateSlider(bool reversed, float multiplier, const QString& suffix,
                           std::function<std::string()>& serializer,
                           std::function<void()>& restore_func, const std::function<void()>& touch);
     QWidget* CreateDateTimeEdit(bool disabled, bool restrict,
@@ -140,13 +140,13 @@ public:
                         std::forward_list<std::function<void(bool)>>& apply_funcs,
                         RequestType request = RequestType::Default, bool managed = true,
                         float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
-                        const QString& string = QStringLiteral("")) const;
+                        const QString& suffix = QStringLiteral("")) const;
 
     Widget* BuildWidget(Settings::BasicSetting* setting,
                         std::forward_list<std::function<void(bool)>>& apply_funcs,
                         Settings::BasicSetting* other_setting,
                         RequestType request = RequestType::Default,
-                        const QString& string = QStringLiteral("")) const;
+                        const QString& suffix = QStringLiteral("")) const;
 
     const ComboboxTranslationMap& ComboboxTranslations() const;