File indexing completed on 2024-11-24 03:56:28
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 0010 #include <QFormLayout> 0011 #include <QLabel> 0012 #include <QCheckBox> 0013 #include <QSpinBox> 0014 #include <QLineEdit> 0015 #include <QComboBox> 0016 #include <QDialog> 0017 #include <QDialogButtonBox> 0018 0019 #ifndef WITHOUT_QT_COLOR_WIDGETS 0020 #include "QtColorWidgets/ColorSelector" 0021 #endif 0022 0023 #include "app/settings/setting.hpp" 0024 0025 #include <QEvent> 0026 #include "app/settings/settings_group.hpp" 0027 0028 namespace app::settings { 0029 0030 0031 class WidgetBuilder 0032 { 0033 public: 0034 void add_widgets(const SettingList& settings_list, QWidget* parent, 0035 QFormLayout* layout, QVariantMap& target, 0036 const QString& name_infix = {}) const 0037 { 0038 for ( const Setting& opt : settings_list ) 0039 { 0040 if ( opt.type == Setting::Internal ) 0041 continue; 0042 0043 target[opt.slug] = opt.get_variant(target); 0044 QWidget* wid = make_setting_widget(opt, target); 0045 if ( !wid ) 0046 continue; 0047 0048 QLabel* label = new QLabel(opt.label, parent); 0049 label->setToolTip(opt.description); 0050 wid->setParent(parent); 0051 wid->setToolTip(opt.description); 0052 wid->setWhatsThis(opt.description); 0053 wid->setObjectName(object_name("widget", name_infix, opt.slug)); 0054 label->setObjectName(object_name("label", name_infix, opt.slug)); 0055 layout->addRow(label, wid); 0056 } 0057 } 0058 0059 void translate_widgets(const SettingList& settings_list, QWidget* parent, const QString& name_infix = {}) 0060 { 0061 for ( const Setting& opt : settings_list ) 0062 { 0063 if ( opt.type == Setting::Internal ) 0064 continue; 0065 0066 if ( QWidget* wid = parent->findChild<QWidget*>(object_name("widget", name_infix, opt.slug)) ) 0067 { 0068 wid->setToolTip(opt.description); 0069 wid->setWhatsThis(opt.description); 0070 } 0071 0072 if ( QLabel* label = parent->findChild<QLabel*>(object_name("label", name_infix, opt.slug)) ) 0073 { 0074 label->setToolTip(opt.description); 0075 label->setText(opt.label); 0076 } 0077 } 0078 } 0079 0080 bool show_dialog(const SettingList& settings_list, QVariantMap& target, 0081 const QString& title, QWidget* parent = nullptr) 0082 { 0083 QDialog dialog(parent); 0084 0085 dialog.setWindowTitle(title); 0086 0087 QFormLayout layout; 0088 dialog.setLayout(&layout); 0089 0090 add_widgets(settings_list, &dialog, &layout, target); 0091 QDialogButtonBox box(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0092 layout.setWidget(layout.rowCount(), QFormLayout::SpanningRole, &box); 0093 QObject::connect(&box, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); 0094 QObject::connect(&box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); 0095 0096 if ( dialog.exec() == QDialog::Rejected ) 0097 return false; 0098 0099 return true; 0100 } 0101 0102 private: 0103 QString object_name(const QString& labwid, const QString& name_infix, const QString& slug) const 0104 { 0105 return QString("__settings_%1__%2%3").arg(labwid).arg(name_infix).arg(slug); 0106 } 0107 0108 QWidget* make_setting_widget(const Setting& opt, QVariantMap& target) const 0109 { 0110 if ( !opt.choices.isEmpty() ) 0111 { 0112 auto wid = new QComboBox(); 0113 int index = 0; 0114 QVariant val = opt.get_variant(target); 0115 for ( const QString& key : opt.choices.keys() ) 0116 { 0117 QVariant choice = opt.choices[key]; 0118 wid->addItem(key, choice); 0119 if ( choice == val ) 0120 wid->setCurrentIndex(index); 0121 index++; 0122 } 0123 QObject::connect(wid, &QComboBox::currentTextChanged, [wid, slug=opt.slug, &target, side_effects=opt.side_effects](){ 0124 target[slug] = wid->currentData(); 0125 if ( side_effects ) 0126 side_effects(wid->currentData()); 0127 }); 0128 return wid; 0129 } 0130 else if ( opt.type == Setting::Info ) 0131 { 0132 return new QLabel(opt.description); 0133 } 0134 else if ( opt.type == Setting::Bool ) 0135 { 0136 auto wid = new QCheckBox(); 0137 wid->setChecked(opt.get<bool>(target)); 0138 QObject::connect(wid, &QCheckBox::toggled, SettingSetter<bool>{opt.slug, &target, opt.side_effects}); 0139 return wid; 0140 } 0141 else if ( opt.type == Setting::Int ) 0142 { 0143 auto wid = new QSpinBox(); 0144 if ( opt.min == opt.max && opt.max == -1 ) 0145 { 0146 wid->setMinimum(std::numeric_limits<int>::min()); 0147 wid->setMaximum(std::numeric_limits<int>::max()); 0148 } 0149 else 0150 { 0151 wid->setMinimum(opt.min); 0152 wid->setMaximum(opt.max); 0153 } 0154 wid->setValue(opt.get<int>(target)); 0155 QObject::connect(wid, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, 0156 SettingSetter<int>{opt.slug, &target, opt.side_effects}); 0157 return wid; 0158 } 0159 else if ( opt.type == Setting::Float ) 0160 { 0161 auto wid = new QDoubleSpinBox(); 0162 if ( opt.min == opt.max && opt.max == -1 ) 0163 { 0164 wid->setMinimum(std::numeric_limits<double>::min()); 0165 wid->setMaximum(std::numeric_limits<double>::max()); 0166 } 0167 else 0168 { 0169 wid->setMinimum(opt.min); 0170 wid->setMaximum(opt.max); 0171 } 0172 wid->setValue(opt.get<float>(target)); 0173 QObject::connect(wid, (void(QDoubleSpinBox::*)(double))&QDoubleSpinBox::valueChanged, 0174 SettingSetter<float>{opt.slug, &target, opt.side_effects}); 0175 return wid; 0176 } 0177 else if ( opt.type == Setting::String ) 0178 { 0179 auto wid = new QLineEdit(); 0180 wid->setText(opt.get<QString>(target)); 0181 QObject::connect(wid, &QLineEdit::textChanged, SettingSetter<QString>{opt.slug, &target, opt.side_effects}); 0182 return wid; 0183 } 0184 #ifndef WITHOUT_QT_COLOR_WIDGETS 0185 else if ( opt.type == Setting::Color ) 0186 { 0187 auto wid = new color_widgets::ColorSelector(); 0188 wid->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); 0189 wid->setColor(opt.get<QColor>(target)); 0190 QObject::connect(wid, &color_widgets::ColorSelector::colorChanged, SettingSetter<QColor>{opt.slug, &target, opt.side_effects}); 0191 return wid; 0192 } 0193 #endif 0194 0195 return nullptr; 0196 } 0197 0198 template<class T> 0199 struct SettingSetter 0200 { 0201 QString slug; 0202 QVariantMap* options; 0203 std::function<void(const QVariant&)> side_effects; 0204 0205 void operator()(T v) 0206 { 0207 if ( side_effects ) 0208 side_effects(v); 0209 (*options)[slug] = QVariant(v); 0210 } 0211 }; 0212 }; 0213 0214 class SettingsGroupWidget : public QWidget 0215 { 0216 public: 0217 SettingsGroupWidget(SettingsGroup* group, QWidget* parent = nullptr) 0218 : QWidget(parent), 0219 group(group) 0220 { 0221 QFormLayout* lay = new QFormLayout(this); 0222 this->setLayout(lay); 0223 bob.add_widgets(group->settings(), this, lay, group->values(), group->slug() + "__"); 0224 } 0225 0226 void changeEvent(QEvent *e) override 0227 { 0228 QWidget::changeEvent(e); 0229 0230 if ( e->type() == QEvent::LanguageChange) 0231 { 0232 bob.translate_widgets(group->settings(), this, group->slug() + "__"); 0233 } 0234 } 0235 0236 private: 0237 app::settings::WidgetBuilder bob; 0238 SettingsGroup* group; 0239 }; 0240 0241 } // namespace app::settings