File indexing completed on 2024-05-19 05:42:18

0001 // ct_lvtqtc_inputdialog.cpp                                    -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtqtc_inputdialog.h>
0021 
0022 #include <QComboBox>
0023 #include <QDialogButtonBox>
0024 #include <QFormLayout>
0025 #include <QLineEdit>
0026 #include <QShowEvent>
0027 #include <QSpinBox>
0028 
0029 using namespace Codethink::lvtqtc;
0030 
0031 struct InputDialog::Private {
0032     std::map<QByteArray, QWidget *> entities;
0033     QFormLayout *mainLayout = nullptr;
0034     QDialogButtonBox *buttonBox = nullptr;
0035     QWidget *firstWidget = nullptr;
0036     bool finishedBuild = false;
0037     bool justShown = false;
0038     std::function<bool(void)> doExec;
0039 };
0040 
0041 InputDialog::InputDialog(const QString& title, QWidget *parent):
0042     QDialog(parent), d(std::make_unique<InputDialog::Private>())
0043 {
0044     setWindowFlags(Qt::Tool | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
0045 
0046     d->mainLayout = new QFormLayout();
0047     setWindowTitle(title);
0048 
0049     d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0050     connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0051     connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0052 }
0053 
0054 InputDialog::~InputDialog() = default;
0055 
0056 void InputDialog::prepareFields(const std::vector<std::pair<QByteArray, QString>>& fields)
0057 {
0058     for (const auto& [fieldId, labelText] : fields) {
0059         addTextField(fieldId, labelText);
0060     }
0061     finish();
0062 }
0063 
0064 void InputDialog::setTextFieldValue(const QByteArray& fieldId, const QString& value)
0065 {
0066     auto *lineEdit = qobject_cast<QLineEdit *>(d->entities.at(fieldId));
0067     lineEdit->setText(value);
0068 }
0069 
0070 void InputDialog::addTextField(const QByteArray& fieldId, const QString& labelText)
0071 {
0072     auto *textField = new QLineEdit();
0073 
0074     // HACK: Qt is always selecting the text, it doesn't matter how I
0075     // ask it to deselect. this is needed so the user doesn't remove
0076     // the parent name on a mistype.
0077     connect(textField, &QLineEdit::selectionChanged, this, [this, textField] {
0078         if (d->justShown) {
0079             textField->deselect();
0080             d->justShown = false;
0081         }
0082     });
0083 
0084     textField->setObjectName(fieldId);
0085     registerField(fieldId, labelText, textField);
0086 }
0087 
0088 void InputDialog::addComboBoxField(const QByteArray& fieldId,
0089                                    const QString& labelText,
0090                                    const std::vector<QString>& options)
0091 {
0092     auto *combobox = new QComboBox();
0093     combobox->setObjectName(fieldId);
0094     for (const auto& v : options) {
0095         combobox->addItem(v);
0096     }
0097     registerField(fieldId, labelText, combobox);
0098 }
0099 
0100 void InputDialog::addSpinBoxField(const QByteArray& fieldId,
0101                                   const QString& labelText,
0102                                   std::pair<int, int> minMax,
0103                                   int value)
0104 {
0105     auto *spinbox = new QSpinBox();
0106     spinbox->setObjectName(fieldId);
0107     spinbox->setMinimum(minMax.first);
0108     spinbox->setMaximum(minMax.second);
0109     spinbox->setValue(value);
0110     registerField(fieldId, labelText, spinbox);
0111 }
0112 
0113 void InputDialog::finish()
0114 {
0115     assert(!d->finishedBuild && "finish() called twice");
0116     d->mainLayout->addWidget(d->buttonBox);
0117     d->finishedBuild = true;
0118 
0119     setLayout(d->mainLayout);
0120     adjustSize();
0121     setFixedSize(size());
0122 }
0123 
0124 void InputDialog::registerField(const QByteArray& fieldId, const QString& labelText, QWidget *widget)
0125 {
0126     assert(!d->finishedBuild && "Cannot add more fields after finish() was called");
0127 
0128     auto *labelWidget = new QLabel(labelText);
0129     d->mainLayout->addWidget(labelWidget);
0130     d->mainLayout->addWidget(widget);
0131     d->entities[fieldId] = widget;
0132     if (!d->firstWidget) {
0133         d->firstWidget = widget;
0134     }
0135 }
0136 
0137 std::any InputDialog::fieldValue(const QByteArray& fieldId) const
0138 {
0139     assert(d->entities.count(fieldId) != 0);
0140 
0141     auto className = std::string(d->entities[fieldId]->metaObject()->className());
0142     if (className == "QLineEdit") {
0143         return std::make_any<QString>(qobject_cast<QLineEdit *>(d->entities[fieldId])->text());
0144     }
0145     if (className == "QComboBox") {
0146         return std::make_any<QString>(qobject_cast<QComboBox *>(d->entities[fieldId])->currentText());
0147     }
0148     if (className == "QSpinBox") {
0149         return std::make_any<int>(qobject_cast<QSpinBox *>(d->entities[fieldId])->value());
0150     }
0151 
0152     assert(false && "Unkwnown data type");
0153     return "";
0154 }
0155 
0156 void InputDialog::showEvent(QShowEvent *ev)
0157 {
0158     QDialog::showEvent(ev);
0159     d->justShown = true;
0160     if (d->firstWidget) {
0161         d->firstWidget->setFocus(Qt::MouseFocusReason);
0162         if (auto *lineEdit = qobject_cast<QLineEdit *>(d->firstWidget)) {
0163             lineEdit->deselect();
0164         }
0165     }
0166 }
0167 
0168 void InputDialog::overrideExec(std::function<bool(void)> exec)
0169 {
0170     d->doExec = std::move(exec);
0171 }
0172 
0173 int InputDialog::exec()
0174 {
0175     if (d->doExec) {
0176         return d->doExec();
0177     }
0178     return QDialog::exec();
0179 }