diff --git a/src/widgets/newprojectdialog.cpp b/src/widgets/newprojectdialog.cpp index 6db85cdd..ac89d6e1 100644 --- a/src/widgets/newprojectdialog.cpp +++ b/src/widgets/newprojectdialog.cpp @@ -1,121 +1,128 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "newprojectdialog.h" #include "ui_newprojectdialog.h" #include #include #include #include "presentation/querytreemodelbase.h" using namespace Widgets; class TaskSourceProxy : public QSortFilterProxyModel { Q_OBJECT public: explicit TaskSourceProxy(QObject *parent = Q_NULLPTR) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); } protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &) const Q_DECL_OVERRIDE { auto sourceIndex = sourceModel()->index(sourceRow, 0); auto source = sourceIndex.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); return source && (source->contentTypes() & Domain::DataSource::Tasks); } }; NewProjectDialog::NewProjectDialog(QWidget *parent) : QDialog(parent), ui(new Ui::NewProjectDialog), m_flattenProxy(new KDescendantsProxyModel(this)) { ui->setupUi(this); - QObject::connect(ui->nameEdit, &QLineEdit::textChanged, this, &NewProjectDialog::onNameTextChanged); - onNameTextChanged(m_name); + connect(ui->nameEdit, &QLineEdit::textChanged, this, &NewProjectDialog::onUserInputChanged); auto taskSourceProxy = new TaskSourceProxy(this); taskSourceProxy->setSourceModel(m_flattenProxy); ui->sourceCombo->setModel(taskSourceProxy); - m_flattenProxy->setDisplayAncestorData(true); + connect(ui->sourceCombo, static_cast(&QComboBox::currentIndexChanged), + this, &NewProjectDialog::onUserInputChanged); + + onUserInputChanged(); } NewProjectDialog::~NewProjectDialog() { delete ui; } int NewProjectDialog::exec() { return QDialog::exec(); } void NewProjectDialog::accept() { m_name = ui->nameEdit->text(); m_source = ui->sourceCombo->itemData(ui->sourceCombo->currentIndex(), Presentation::QueryTreeModelBase::ObjectRole) .value(); QDialog::accept(); } void NewProjectDialog::setDataSourcesModel(QAbstractItemModel *model) { m_flattenProxy->setSourceModel(model); auto proxy = ui->sourceCombo->model(); for (int row = 0; row < proxy->rowCount(); row++) { auto index = proxy->index(row, 0); if (index.data(Presentation::QueryTreeModelBase::IsDefaultRole).toBool()) { ui->sourceCombo->setCurrentIndex(row); } } } QString NewProjectDialog::name() const { return m_name; } Domain::DataSource::Ptr NewProjectDialog::dataSource() const { return m_source; } -void NewProjectDialog::onNameTextChanged(const QString &text) +void NewProjectDialog::onUserInputChanged() { + const auto text = ui->nameEdit->text(); + const auto source = ui->sourceCombo->itemData(ui->sourceCombo->currentIndex(), + Presentation::QueryTreeModelBase::ObjectRole) + .value(); + auto buttonOk = ui->buttonBox->button(QDialogButtonBox::Ok); - buttonOk->setEnabled(!text.isEmpty()); + buttonOk->setEnabled(!text.isEmpty() && source); } #include "newprojectdialog.moc" diff --git a/src/widgets/newprojectdialog.h b/src/widgets/newprojectdialog.h index 1e64bdf9..75f2761e 100644 --- a/src/widgets/newprojectdialog.h +++ b/src/widgets/newprojectdialog.h @@ -1,71 +1,71 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WIDGETS_NEWPROJECTDIALOG_H #define WIDGETS_NEWPROJECTDIALOG_H #include #include "widgets/newprojectdialoginterface.h" class QModelIndex; class KDescendantsProxyModel; namespace Ui { class NewProjectDialog; } namespace Widgets { class NewProjectDialog : public QDialog, public NewProjectDialogInterface { Q_OBJECT public: explicit NewProjectDialog(QWidget *parent = Q_NULLPTR); ~NewProjectDialog(); int exec() Q_DECL_OVERRIDE; void accept() Q_DECL_OVERRIDE; void setDataSourcesModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; Domain::DataSource::Ptr dataSource() const Q_DECL_OVERRIDE; private slots: - void onNameTextChanged(const QString &text); + void onUserInputChanged(); private: void applyDefaultSource(const QModelIndex &root); Ui::NewProjectDialog *ui; KDescendantsProxyModel *m_flattenProxy; QString m_name; Domain::DataSource::Ptr m_source; }; } #endif // WIDGETS_NEWPROJECTDIALOG_H diff --git a/tests/units/widgets/newprojectdialogtest.cpp b/tests/units/widgets/newprojectdialogtest.cpp index fec9caed..767d81ea 100644 --- a/tests/units/widgets/newprojectdialogtest.cpp +++ b/tests/units/widgets/newprojectdialogtest.cpp @@ -1,242 +1,264 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "presentation/querytreemodelbase.h" #include "widgets/newprojectdialog.h" class UserInputSimulator : public QObject { Q_OBJECT public: explicit UserInputSimulator(QObject *parent = Q_NULLPTR) : QObject(parent), dialog(Q_NULLPTR), reject(false), sourceComboIndex(-1) {} void exec() { Q_ASSERT(dialog); QTimer::singleShot(50, Qt::PreciseTimer, this, &UserInputSimulator::onTimeout); dialog->exec(); } private slots: void onTimeout() { if (!nameInput.isEmpty()) { auto nameEdit = dialog->findChild(QStringLiteral("nameEdit")); QTest::keyClicks(nameEdit, nameInput); } - if (sourceComboIndex >= 0) { - auto sourceCombo = dialog->findChild(QStringLiteral("sourceCombo")); - sourceCombo->setCurrentIndex(sourceComboIndex); - } + auto sourceCombo = dialog->findChild(QStringLiteral("sourceCombo")); + sourceCombo->setCurrentIndex(sourceComboIndex); auto buttonBox = dialog->findChild(QStringLiteral("buttonBox")); if (reject) buttonBox->button(QDialogButtonBox::Cancel)->click(); else buttonBox->button(QDialogButtonBox::Ok)->click(); } public: Widgets::NewProjectDialog *dialog; bool reject; QString nameInput; int sourceComboIndex; }; class NewProjectDialogTest : public QObject { Q_OBJECT private: QStandardItem *createSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) { auto source = Domain::DataSource::Ptr::create(); source->setName(name); auto item = new QStandardItem(name); item->setData(QVariant::fromValue(source), Presentation::QueryTreeModelBase::ObjectRole); if (parent) parent->appendRow(item); return item; } QStandardItem *createTaskSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) { auto item = createSourceItem(name, parent); auto source = item->data(Presentation::QueryTreeModelBase::ObjectRole).value(); source->setContentTypes(Domain::DataSource::Tasks); return item; } QStandardItem *createDefaultSourceItem(const QString &name, QStandardItem *parent = Q_NULLPTR) { auto item = createTaskSourceItem(name, parent); item->setData(true, Presentation::QueryTreeModelBase::IsDefaultRole); return item; } QStandardItemModel *createSourceModel() { auto model = new QStandardItemModel(this); auto root1 = createSourceItem(QStringLiteral("Root 1")); createSourceItem(QStringLiteral("Null"), root1); createTaskSourceItem(QStringLiteral("Task 1.1"), root1); createTaskSourceItem(QStringLiteral("Task 1.2"), root1); model->appendRow(root1); auto root2 = createSourceItem(QStringLiteral("Root 2")); createDefaultSourceItem(QStringLiteral("Task 2.1"), root2); createTaskSourceItem(QStringLiteral("Task 2.2"), root2); model->appendRow(root2); return model; } private slots: void shouldHaveDefaultState() { Widgets::NewProjectDialog dialog; QVERIFY(dialog.name().isEmpty()); QVERIFY(dialog.dataSource().isNull()); auto nameEdit = dialog.findChild(QStringLiteral("nameEdit")); QVERIFY(nameEdit); QVERIFY(nameEdit->isVisibleTo(&dialog)); auto sourceCombo = dialog.findChild(QStringLiteral("sourceCombo")); QVERIFY(sourceCombo); QVERIFY(sourceCombo->isVisibleTo(&dialog)); auto buttonBox = dialog.findChild(QStringLiteral("buttonBox")); QVERIFY(buttonBox); QVERIFY(buttonBox->isVisibleTo(&dialog)); QVERIFY(buttonBox->button(QDialogButtonBox::Ok)); QVERIFY(buttonBox->button(QDialogButtonBox::Cancel)); } void shouldPositionDefaultProperties() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); auto sourceCombo = dialog.findChild(QStringLiteral("sourceCombo")); // WHEN dialog.setDataSourcesModel(sourceModel); // THEN QCOMPARE(sourceCombo->currentIndex(), 2); QCOMPARE(sourceCombo->currentText(), QStringLiteral("Root 2 / Task 2.1")); } void shouldProvideUserInputWhenAccepted() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 1; userInput.nameInput = QStringLiteral("name"); auto expectedSource = sourceModel->item(0) ->child(2) ->data(Presentation::QueryTreeModelBase::ObjectRole) .value(); // WHEN userInput.exec(); // THEN QCOMPARE(dialog.name(), userInput.nameInput); QVERIFY(dialog.dataSource()); QCOMPARE(dialog.dataSource(), expectedSource); } void shouldNotProvideUserInputWhenReject() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 1; userInput.nameInput = QStringLiteral("name"); userInput.reject = true; // WHEN userInput.exec(); // THEN QCOMPARE(dialog.name(), QString()); QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); } void shouldNotAllowEmptyName() { // GIVEN Widgets::NewProjectDialog dialog; auto sourceModel = createSourceModel(); dialog.setDataSourcesModel(sourceModel); UserInputSimulator userInput; userInput.dialog = &dialog; userInput.sourceComboIndex = 0; userInput.nameInput = QString(); userInput.reject = true; // WHEN userInput.exec(); // THEN auto buttonOk = dialog.findChild(QStringLiteral("buttonBox"))->button(QDialogButtonBox::Ok); QVERIFY(!buttonOk->isEnabled()); QCOMPARE(dialog.name(), QString()); QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); } + + void shouldNotAllowNoSelectedSource() + { + // GIVEN + Widgets::NewProjectDialog dialog; + + auto sourceModel = createSourceModel(); + dialog.setDataSourcesModel(sourceModel); + + UserInputSimulator userInput; + userInput.dialog = &dialog; + userInput.sourceComboIndex = -1; + userInput.nameInput = QStringLiteral("name"); + userInput.reject = true; + + // WHEN + userInput.exec(); + + // THEN + auto buttonOk = dialog.findChild(QStringLiteral("buttonBox"))->button(QDialogButtonBox::Ok); + QVERIFY(!buttonOk->isEnabled()); + QCOMPARE(dialog.name(), QString()); + QCOMPARE(dialog.dataSource(), Domain::DataSource::Ptr()); + } }; ZANSHIN_TEST_MAIN(NewProjectDialogTest) #include "newprojectdialogtest.moc"