configuration_shared.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // SPDX-FileCopyrightText: 2016 Citra Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <memory>
  4. #include <QCheckBox>
  5. #include <QHBoxLayout>
  6. #include <QLabel>
  7. #include <QLineEdit>
  8. #include <QObject>
  9. #include <QString>
  10. #include <QWidget>
  11. #include <qnamespace.h>
  12. #include "common/settings.h"
  13. #include "yuzu/configuration/configuration_shared.h"
  14. #include "yuzu/configuration/configure_per_game.h"
  15. #include "yuzu/configuration/shared_translation.h"
  16. namespace ConfigurationShared {
  17. static std::pair<QWidget*, std::function<void()>> CreateCheckBox(Settings::BasicSetting* setting,
  18. const QString& label,
  19. QWidget* parent,
  20. std::list<CheckState>& trackers) {
  21. QCheckBox* checkbox = new QCheckBox(label, parent);
  22. checkbox->setObjectName(QString::fromStdString(setting->GetLabel()));
  23. checkbox->setCheckState(setting->ToString() == "true" ? Qt::CheckState::Checked
  24. : Qt::CheckState::Unchecked);
  25. CheckState* tracker{};
  26. // Per-game config highlight
  27. if (setting->Switchable() && !Settings::IsConfiguringGlobal()) {
  28. bool global_state = setting->ToStringGlobal() == "true";
  29. bool state = setting->ToString() == "true";
  30. bool global = setting->UsingGlobal();
  31. tracker = &trackers.emplace_front(CheckState{});
  32. SetColoredTristate(checkbox, global, state, global_state, *tracker);
  33. }
  34. auto load_func = [checkbox, setting, tracker]() {
  35. if (Settings::IsConfiguringGlobal()) {
  36. setting->LoadString(checkbox->checkState() == Qt::Checked ? "true" : "false");
  37. }
  38. if (Settings::IsConfiguringGlobal() || !setting->Switchable()) {
  39. return;
  40. }
  41. if (*tracker != CheckState::Global) {
  42. setting->SetGlobal(false);
  43. setting->LoadString(checkbox->checkState() == Qt::Checked ? "true" : "false");
  44. } else {
  45. setting->SetGlobal(true);
  46. }
  47. };
  48. return {checkbox, load_func};
  49. }
  50. static std::tuple<QWidget*, void*, std::function<void()>> CreateCombobox(
  51. Settings::BasicSetting* setting, const QString& label, QWidget* parent) {
  52. const auto type = setting->TypeId();
  53. QWidget* group = new QWidget(parent);
  54. group->setObjectName(QString::fromStdString(setting->GetLabel()));
  55. QLayout* combobox_layout = new QHBoxLayout(group);
  56. QLabel* qt_label = new QLabel(label, parent);
  57. QComboBox* combobox = new QComboBox(parent);
  58. std::forward_list<QString> combobox_enumerations = ComboboxEnumeration(type, parent);
  59. for (const auto& item : combobox_enumerations) {
  60. combobox->addItem(item);
  61. }
  62. combobox_layout->addWidget(qt_label);
  63. combobox_layout->addWidget(combobox);
  64. combobox_layout->setSpacing(6);
  65. combobox_layout->setContentsMargins(0, 0, 0, 0);
  66. if (setting->Switchable() && !Settings::IsConfiguringGlobal()) {
  67. int current = std::stoi(setting->ToString());
  68. int global_value = std::stoi(setting->ToStringGlobal());
  69. SetColoredComboBox(combobox, group, global_value);
  70. if (setting->UsingGlobal()) {
  71. combobox->setCurrentIndex(USE_GLOBAL_INDEX);
  72. } else {
  73. SetHighlight(group, true);
  74. combobox->setCurrentIndex(current + USE_GLOBAL_OFFSET);
  75. }
  76. } else {
  77. combobox->setCurrentIndex(std::stoi(setting->ToString()));
  78. }
  79. const auto load_func = [combobox, setting]() {
  80. if (Settings::IsConfiguringGlobal()) {
  81. setting->LoadString(std::to_string(combobox->currentIndex()));
  82. }
  83. if (Settings::IsConfiguringGlobal() || !setting->Switchable()) {
  84. return;
  85. }
  86. bool using_global = combobox->currentIndex() == USE_GLOBAL_INDEX;
  87. int index = combobox->currentIndex() - USE_GLOBAL_OFFSET;
  88. setting->SetGlobal(using_global);
  89. if (!using_global) {
  90. setting->LoadString(std::to_string(index));
  91. }
  92. };
  93. return {group, combobox, load_func};
  94. }
  95. static std::tuple<QWidget*, void*, std::function<void()>> CreateLineEdit(
  96. Settings::BasicSetting* setting, const QString& label, QWidget* parent) {
  97. QWidget* widget = new QWidget(parent);
  98. QHBoxLayout* layout = new QHBoxLayout(widget);
  99. QLabel* q_label = new QLabel(label, widget);
  100. QLineEdit* line_edit = new QLineEdit(widget);
  101. layout->addWidget(q_label);
  102. layout->addStretch();
  103. layout->addWidget(line_edit);
  104. layout->setContentsMargins(0, 0, 0, 0);
  105. return {widget, line_edit, []() {}};
  106. }
  107. std::pair<QWidget*, void*> CreateWidget(Settings::BasicSetting* setting,
  108. const TranslationMap& translations, QWidget* parent,
  109. bool runtime_lock,
  110. std::forward_list<std::function<void(bool)>>& apply_funcs,
  111. std::list<CheckState>& trackers, RequestType request) {
  112. const auto type = setting->TypeId();
  113. const int id = setting->Id();
  114. QWidget* widget{nullptr};
  115. void* extra{nullptr};
  116. std::function<void()> load_func;
  117. const auto [label, tooltip] = [&]() {
  118. const auto& setting_label = setting->GetLabel();
  119. if (translations.contains(id)) {
  120. return std::pair{translations.at(id).first, translations.at(id).second};
  121. }
  122. LOG_ERROR(Frontend, "Translation table lacks entry for \"{}\"", setting_label);
  123. return std::pair{QString::fromStdString(setting_label), QStringLiteral("")};
  124. }();
  125. if (label == QStringLiteral("")) {
  126. LOG_DEBUG(Frontend, "Translation table has emtpy entry for \"{}\", skipping...",
  127. setting->GetLabel());
  128. return {nullptr, nullptr};
  129. }
  130. if (type == typeid(bool)) {
  131. auto pair = CreateCheckBox(setting, label, parent, trackers);
  132. widget = pair.first;
  133. load_func = pair.second;
  134. } else if (setting->IsEnum()) {
  135. auto tuple = CreateCombobox(setting, label, parent);
  136. widget = std::get<0>(tuple);
  137. extra = std::get<1>(tuple);
  138. load_func = std::get<2>(tuple);
  139. } else if (type == typeid(u32) || type == typeid(int)) {
  140. switch (request) {
  141. case RequestType::Default: {
  142. auto tuple = CreateLineEdit(setting, label, parent);
  143. widget = std::get<0>(tuple);
  144. extra = std::get<1>(tuple);
  145. load_func = std::get<2>(tuple);
  146. break;
  147. }
  148. case RequestType::ComboBox: {
  149. auto tuple = CreateCombobox(setting, label, parent);
  150. widget = std::get<0>(tuple);
  151. extra = std::get<1>(tuple);
  152. load_func = std::get<2>(tuple);
  153. break;
  154. }
  155. case RequestType::SpinBox:
  156. case RequestType::Slider:
  157. case RequestType::MaxEnum:
  158. break;
  159. }
  160. }
  161. if (widget == nullptr) {
  162. LOG_ERROR(Frontend, "No widget was created for \"{}\"", setting->GetLabel());
  163. return {nullptr, nullptr};
  164. }
  165. apply_funcs.push_front([load_func, setting](bool powered_on) {
  166. if (setting->RuntimeModfiable() || !powered_on) {
  167. load_func();
  168. }
  169. });
  170. bool enable = runtime_lock || setting->RuntimeModfiable();
  171. enable &=
  172. setting->Switchable() && !(Settings::IsConfiguringGlobal() && !setting->UsingGlobal());
  173. enable |= !setting->Switchable() && Settings::IsConfiguringGlobal() && runtime_lock;
  174. widget->setEnabled(enable);
  175. widget->setVisible(Settings::IsConfiguringGlobal() || setting->Switchable());
  176. widget->setToolTip(tooltip);
  177. return {widget, extra};
  178. }
  179. Tab::Tab(std::shared_ptr<std::forward_list<Tab*>> group_, QWidget* parent)
  180. : QWidget(parent), group{group_} {
  181. if (group != nullptr) {
  182. group->push_front(this);
  183. }
  184. }
  185. Tab::~Tab() = default;
  186. } // namespace ConfigurationShared
  187. void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting,
  188. const QCheckBox* checkbox,
  189. const CheckState& tracker) {
  190. if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
  191. setting->SetValue(checkbox->checkState());
  192. } else if (!Settings::IsConfiguringGlobal()) {
  193. if (tracker == CheckState::Global) {
  194. setting->SetGlobal(true);
  195. } else {
  196. setting->SetGlobal(false);
  197. setting->SetValue(checkbox->checkState());
  198. }
  199. }
  200. }
  201. void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
  202. const Settings::SwitchableSetting<bool>* setting) {
  203. if (setting->UsingGlobal()) {
  204. checkbox->setCheckState(Qt::PartiallyChecked);
  205. } else {
  206. checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
  207. }
  208. }
  209. void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
  210. if (highlighted) {
  211. widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
  212. .arg(widget->objectName()));
  213. } else {
  214. widget->setStyleSheet(QStringLiteral(""));
  215. }
  216. widget->show();
  217. }
  218. void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
  219. bool global_state, CheckState& tracker) {
  220. if (global) {
  221. tracker = CheckState::Global;
  222. } else {
  223. tracker = (state == global_state) ? CheckState::On : CheckState::Off;
  224. }
  225. SetHighlight(checkbox, tracker != CheckState::Global);
  226. QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
  227. tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
  228. static_cast<int>(CheckState::Count));
  229. if (tracker == CheckState::Global) {
  230. checkbox->setChecked(global_state);
  231. }
  232. SetHighlight(checkbox, tracker != CheckState::Global);
  233. });
  234. }
  235. void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
  236. InsertGlobalItem(combobox, global);
  237. QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
  238. [target](int index) { SetHighlight(target, index != 0); });
  239. }
  240. void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
  241. const QString use_global_text =
  242. ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
  243. combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
  244. combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
  245. }
  246. int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
  247. if (Settings::IsConfiguringGlobal()) {
  248. return combobox->currentIndex();
  249. }
  250. if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
  251. return global_setting_index;
  252. }
  253. return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
  254. }