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