From 928b795f4ed95764ba37c0b7e83a26fa217c7e83 Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Sun, 7 Dec 2025 10:56:30 +0100 Subject: [PATCH 1/4] Added an AbstractDialog class and using it as base for a NewItemDialog class (no new item functionality yet). --- CMakeLists.txt | 2 ++ Dialogs/abstractdialog.cpp | 51 ++++++++++++++++++++++++++++++++ Dialogs/abstractdialog.h | 33 +++++++++++++++++++++ Dialogs/newitemdialog.cpp | 60 ++++++++++++++++++++++++++++++++++++++ Dialogs/newitemdialog.h | 32 ++++++++++++++++++++ mainwindow.cpp | 38 ++++++++++++++++-------- mainwindow.h | 12 ++++++-- 7 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 Dialogs/abstractdialog.cpp create mode 100644 Dialogs/abstractdialog.h create mode 100644 Dialogs/newitemdialog.cpp create mode 100644 Dialogs/newitemdialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cd661e0..fd42f13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,8 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) ${PROJECT_SOURCES} utils/messagehandler.h assets/icons.qrc + Dialogs/abstractdialog.h Dialogs/abstractdialog.cpp + Dialogs/newitemdialog.h Dialogs/newitemdialog.cpp ) # Define target properties for Android with Qt 6 as: # set_property(TARGET ${TARGET_APP} APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR diff --git a/Dialogs/abstractdialog.cpp b/Dialogs/abstractdialog.cpp new file mode 100644 index 0000000..31582bc --- /dev/null +++ b/Dialogs/abstractdialog.cpp @@ -0,0 +1,51 @@ +#include "abstractdialog.h" + +#include +#include +#include +#include +#include + +AbstractDialog::AbstractDialog(QWidget* parent) + : QDialog(parent) { + setWindowTitle(tr("Dialog does what?...")); + setModal(true); + + m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AbstractDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, this, &AbstractDialog::reject); + + m_contentContainer = new QLabel("content", this); + + m_outerLayout = new QVBoxLayout; + m_outerLayout->addWidget(m_contentContainer); + m_outerLayout->addWidget(m_buttonBox); + + setLayout(m_outerLayout); +} + +void AbstractDialog::show() { + centerInParent(); + QWidget::show(); +} + +void AbstractDialog::accept() { QDialog::accept(); } + +void AbstractDialog::reject() { QDialog::reject(); } + +void AbstractDialog::centerInParent() { + // BUG the centering in the parent doesn't work the first time (and the later ones seem off too) + QWidget* parent = parentWidget(); + + if (parent) { + auto parentRect = parent->geometry(); + move(parentRect.center() - rect().center()); + } else { + QRect screenGeometry = QGuiApplication::screens().at(0)->geometry(); + int x = (screenGeometry.width() - width()) / 2; + int y = (screenGeometry.height() - height()) / 2; + move(x, y); + } +} + +void AbstractDialog::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } diff --git a/Dialogs/abstractdialog.h b/Dialogs/abstractdialog.h new file mode 100644 index 0000000..66f2240 --- /dev/null +++ b/Dialogs/abstractdialog.h @@ -0,0 +1,33 @@ +#ifndef ABSTRACTDIALOG_H +#define ABSTRACTDIALOG_H + +#include + +class QGridLayout; +class QDialogButtonBox; +class QVBoxLayout; + +class AbstractDialog : public QDialog { + Q_OBJECT + public: + AbstractDialog(QWidget* parent = nullptr); + virtual void createContent() = 0; + + /// QDialog interface + public slots: + void show(); + void accept() override; + void reject() override; + + protected: + void centerInParent(); + + protected: + QWidget* m_contentContainer; + QVBoxLayout* m_outerLayout; + QDialogButtonBox* m_buttonBox = nullptr; + + void closeEvent(QCloseEvent* event) override; +}; + +#endif // ABSTRACTDIALOG_H diff --git a/Dialogs/newitemdialog.cpp b/Dialogs/newitemdialog.cpp new file mode 100644 index 0000000..f0159dd --- /dev/null +++ b/Dialogs/newitemdialog.cpp @@ -0,0 +1,60 @@ +#include "newitemdialog.h" + +#include +#include +#include +#include + +#include + +NewItemDialog::NewItemDialog(QWidget* parent) + : AbstractDialog(parent) {} + +void NewItemDialog::createContent() { + if (m_contentContainer) { + delete m_contentContainer; + } + + setWindowTitle(tr("New item...")); + + m_contentContainer = new QWidget(this); + + // REFACTOR deduce label names and types from meta data & use a factory + m_nameLabel = new QLabel("&Name"); + m_nameEdit = new QLineEdit(); + m_nameLabel->setBuddy(m_nameEdit); + + m_descriptionLabel = new QLabel("&Description"); + m_descriptionEdit = new QLineEdit(); + m_descriptionLabel->setBuddy(m_descriptionEdit); + + m_infoLabel = new QLabel("&Info"); + m_infoEdit = new QLineEdit(); + m_infoLabel->setBuddy(m_infoEdit); + + m_amountLabel = new QLabel("&Amount"); + m_amountBox = new QSpinBox(); + m_amountBox->setMaximum(1000); + m_amountLabel->setBuddy(m_amountBox); + + m_factorLabel = new QLabel("&Factor"); + m_factorBox = new QSpinBox(); + m_factorBox->setMaximum(1000); + m_factorLabel->setBuddy(m_factorBox); + + QGridLayout* layout = new QGridLayout(); + layout->addWidget(m_nameLabel, 0, 0, 1, 1); + layout->addWidget(m_nameEdit, 0, 1, 1, 1); + layout->addWidget(m_descriptionLabel, 1, 0, 1, 1); + layout->addWidget(m_descriptionEdit, 1, 1, 1, 1); + layout->addWidget(m_infoLabel, 2, 0, 1, 1); + layout->addWidget(m_infoEdit, 2, 1, 1, 1); + layout->addWidget(m_amountLabel, 3, 0, 1, 1); + layout->addWidget(m_amountBox, 3, 1, 1, 1); + layout->addWidget(m_factorLabel, 4, 0, 1, 1); + layout->addWidget(m_factorBox, 4, 1, 1, 1); + + m_contentContainer->setLayout(layout); + + m_outerLayout->insertWidget(0, m_contentContainer); +} diff --git a/Dialogs/newitemdialog.h b/Dialogs/newitemdialog.h new file mode 100644 index 0000000..c1f3655 --- /dev/null +++ b/Dialogs/newitemdialog.h @@ -0,0 +1,32 @@ +#ifndef NEWITEMDIALOG_H +#define NEWITEMDIALOG_H + +#include "abstractdialog.h" + +class QLineEdit; +class QSpinBox; +class QLabel; +class NewItemDialog : public AbstractDialog { + public: + NewItemDialog(QWidget* parent = nullptr); + + void createContent() override; + + private: + QLabel* m_nameLabel = nullptr; + QLineEdit* m_nameEdit = nullptr; + + QLabel* m_descriptionLabel = nullptr; + QLineEdit* m_descriptionEdit = nullptr; + + QLabel* m_infoLabel = nullptr; + QLineEdit* m_infoEdit = nullptr; + + QLabel* m_amountLabel = nullptr; + QSpinBox* m_amountBox = nullptr; + + QLabel* m_factorLabel = nullptr; + QSpinBox* m_factorBox = nullptr; +}; + +#endif // NEWITEMDIALOG_H diff --git a/mainwindow.cpp b/mainwindow.cpp index 9dc7987..63215c4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5,6 +5,7 @@ #include #include "../../ApplicationConfig.h" +#include "Dialogs/newitemdialog.h" #include "data/settingshandler.h" #include "genericcore.h" @@ -30,13 +31,14 @@ MainWindow::MainWindow(QWidget* parent) setWindowIcon(QIcon(iconString)); #endif - createActions(); - createHelpMenu(); - const QVariantMap settings = SettingsHandler::getSettings("GUI"); restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("windowState").toByteArray()); + createActions(); + createHelpMenu(); + createGuiDialogs(); + connect(m_core.get(), &GenericCore::displayStatusMessage, this, &MainWindow::displayStatusMessage); connect(this, &MainWindow::displayStatusMessage, this, &MainWindow::showStatusMessage); @@ -125,14 +127,11 @@ void MainWindow::on_pushButton_clicked() { ui->label->setText(prefix + m_core->toString()); } -void MainWindow::createHelpMenu() { - QMenu* helpMenu = ui->menu_Help; - helpMenu->addSeparator(); - QAction* aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::onAboutClicked); - aboutAct->setStatusTip(tr("Show the application's About box")); +void MainWindow::openNewItemDialog() { + showStatusMessage(tr("Invoked 'Edit|New Item'")); + qInfo() << "'open new item dialog' action triggered..."; - QAction* aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); - aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); + m_newItemDialog->show(); } void MainWindow::createActions() { @@ -228,8 +227,8 @@ void MainWindow::createEditActions() { m_openNewItemDialogAct = make_unique(tr("&New item"), this); m_openNewItemDialogAct->setShortcut(QKeySequence::New); m_openNewItemDialogAct->setStatusTip(tr("Opens a dialog to add a new item")); - // connect(m_openNewItemDialogAct, &QAction::triggered, this, &MainWindow::openNewItemDialog); - m_openNewItemDialogAct->setEnabled(false); + connect(m_openNewItemDialogAct.get(), &QAction::triggered, this, &MainWindow::openNewItemDialog); + // m_openNewItemDialogAct->setEnabled(false); ui->menu_Edit->addAction(m_openNewItemDialogAct.get()); m_openEditItemDialogAct = make_unique(tr("&Edit item"), this); @@ -255,3 +254,18 @@ void MainWindow::createEditActions() { m_findItemAct->setEnabled(false); ui->menu_Edit->addAction(m_findItemAct.get()); } + +void MainWindow::createHelpMenu() { + QMenu* helpMenu = ui->menu_Help; + helpMenu->addSeparator(); + QAction* aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::onAboutClicked); + aboutAct->setStatusTip(tr("Show the application's About box")); + + QAction* aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); + aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); +} + +void MainWindow::createGuiDialogs() { + m_newItemDialog = make_unique(this); + m_newItemDialog->createContent(); +} diff --git a/mainwindow.h b/mainwindow.h index 40ea7ec..c57565c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -3,10 +3,11 @@ #include -QT_BEGIN_NAMESPACE - +class NewItemDialog; class QAbstractItemModel; +QT_BEGIN_NAMESPACE + namespace Ui { class MainWindow; } @@ -37,6 +38,9 @@ class MainWindow : public QMainWindow { void on_pushButton_clicked(); + /// slots for menu actions + void openNewItemDialog(); + private: Ui::MainWindow* ui; @@ -63,10 +67,14 @@ class MainWindow : public QMainWindow { unique_ptr m_deleteItemAct; unique_ptr m_findItemAct; + /// Dialogs + unique_ptr m_newItemDialog; + /// Setup functions void createActions(); void createFileActions(); void createEditActions(); void createHelpMenu(); + void createGuiDialogs(); }; #endif // MAINWINDOW_H From ea75e57dbfc8ef1b78c73f51e18bae3d4208b1cf Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Mon, 8 Dec 2025 13:26:42 +0100 Subject: [PATCH 2/4] An item can be added to the model with accepting the NewItemDialog. --- Dialogs/newitemdialog.cpp | 20 ++++++++++++++++++++ Dialogs/newitemdialog.h | 10 ++++++++++ mainwindow.cpp | 11 ++++++----- mainwindow.h | 4 ++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Dialogs/newitemdialog.cpp b/Dialogs/newitemdialog.cpp index f0159dd..e93989c 100644 --- a/Dialogs/newitemdialog.cpp +++ b/Dialogs/newitemdialog.cpp @@ -1,6 +1,8 @@ #include "newitemdialog.h" #include +#include +#include #include #include #include @@ -58,3 +60,21 @@ void NewItemDialog::createContent() { m_outerLayout->insertWidget(0, m_contentContainer); } + +void NewItemDialog::accept() { + QJsonObject itemObject; + itemObject.insert("Name", m_nameEdit->text()); + itemObject.insert("Description", m_descriptionEdit->text()); + itemObject.insert("Info", m_infoEdit->text()); + itemObject.insert("Amount", m_amountBox->value()); + itemObject.insert("Factor", m_factorBox->value()); + + QJsonDocument jsonDoc; + QJsonArray itemArray; + itemArray.append(itemObject); + jsonDoc.setArray(itemArray); + emit addItems(jsonDoc.toJson(QJsonDocument::Compact)); + + // resetContent(); + AbstractDialog::accept(); +} diff --git a/Dialogs/newitemdialog.h b/Dialogs/newitemdialog.h index c1f3655..678f8dd 100644 --- a/Dialogs/newitemdialog.h +++ b/Dialogs/newitemdialog.h @@ -3,15 +3,25 @@ #include "abstractdialog.h" +class QDoubleSpinBox; class QLineEdit; class QSpinBox; class QLabel; class NewItemDialog : public AbstractDialog { + Q_OBJECT + public: NewItemDialog(QWidget* parent = nullptr); void createContent() override; + signals: + void addItems(const QByteArray& jsonDoc); + + public slots: + void accept() override; + // void reject() override; + private: QLabel* m_nameLabel = nullptr; QLineEdit* m_nameEdit = nullptr; diff --git a/mainwindow.cpp b/mainwindow.cpp index 63215c4..7b63b11 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -8,6 +8,7 @@ #include "Dialogs/newitemdialog.h" #include "data/settingshandler.h" #include "genericcore.h" +#include "model/tablemodel.h" static QString updateTextClean = "Do you want to update the application now?"; static QString updateTextDirty = "Do you want to save the tasks & update the application now?"; @@ -35,6 +36,9 @@ MainWindow::MainWindow(QWidget* parent) restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("windowState").toByteArray()); + m_tableModel = m_core->getModel(); + ui->tableView->setModel(m_tableModel.get()); + createActions(); createHelpMenu(); createGuiDialogs(); @@ -44,9 +48,6 @@ MainWindow::MainWindow(QWidget* parent) connect(this, &MainWindow::displayStatusMessage, this, &MainWindow::showStatusMessage); connect(this, &MainWindow::checkForUpdates, this, &MainWindow::on_actionCheck_for_update_triggered, Qt::QueuedConnection); - - m_tableModel = m_core->getModel(); - ui->tableView->setModel(m_tableModel.get()); } MainWindow::~MainWindow() { delete ui; } @@ -129,8 +130,6 @@ void MainWindow::on_pushButton_clicked() { void MainWindow::openNewItemDialog() { showStatusMessage(tr("Invoked 'Edit|New Item'")); - qInfo() << "'open new item dialog' action triggered..."; - m_newItemDialog->show(); } @@ -268,4 +267,6 @@ void MainWindow::createHelpMenu() { void MainWindow::createGuiDialogs() { m_newItemDialog = make_unique(this); m_newItemDialog->createContent(); + connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_tableModel.get(), + &TableModel::appendItems); } diff --git a/mainwindow.h b/mainwindow.h index c57565c..ae1cebb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -4,7 +4,7 @@ #include class NewItemDialog; -class QAbstractItemModel; +class TableModel; QT_BEGIN_NAMESPACE @@ -46,7 +46,7 @@ class MainWindow : public QMainWindow { // GenericCore* m_core; unique_ptr m_core; - shared_ptr m_tableModel; + shared_ptr m_tableModel; /// File actions unique_ptr m_newFileAct; From c51c06ba50af1c4e5d07903337cd1d4256d3de0b Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Mon, 8 Dec 2025 13:28:01 +0100 Subject: [PATCH 3/4] The "factor" value can be inserted as a floating point. --- Dialogs/newitemdialog.cpp | 2 +- Dialogs/newitemdialog.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dialogs/newitemdialog.cpp b/Dialogs/newitemdialog.cpp index e93989c..9433f51 100644 --- a/Dialogs/newitemdialog.cpp +++ b/Dialogs/newitemdialog.cpp @@ -40,7 +40,7 @@ void NewItemDialog::createContent() { m_amountLabel->setBuddy(m_amountBox); m_factorLabel = new QLabel("&Factor"); - m_factorBox = new QSpinBox(); + m_factorBox = new QDoubleSpinBox(); m_factorBox->setMaximum(1000); m_factorLabel->setBuddy(m_factorBox); diff --git a/Dialogs/newitemdialog.h b/Dialogs/newitemdialog.h index 678f8dd..b3db670 100644 --- a/Dialogs/newitemdialog.h +++ b/Dialogs/newitemdialog.h @@ -35,8 +35,8 @@ class NewItemDialog : public AbstractDialog { QLabel* m_amountLabel = nullptr; QSpinBox* m_amountBox = nullptr; - QLabel* m_factorLabel = nullptr; - QSpinBox* m_factorBox = nullptr; + QLabel* m_factorLabel = nullptr; + QDoubleSpinBox* m_factorBox = nullptr; }; #endif // NEWITEMDIALOG_H From c80a692a281d3e1d6bc51df274f0871788e57a1e Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Mon, 8 Dec 2025 15:05:04 +0100 Subject: [PATCH 4/4] Selected items can be deleted from model via menu action "Delete item(s)" --- mainwindow.cpp | 57 +++++++++++++++++++++++++++++++++++++++++--------- mainwindow.h | 6 +++++- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 7b63b11..f058c32 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -48,6 +48,11 @@ MainWindow::MainWindow(QWidget* parent) connect(this, &MainWindow::displayStatusMessage, this, &MainWindow::showStatusMessage); connect(this, &MainWindow::checkForUpdates, this, &MainWindow::on_actionCheck_for_update_triggered, Qt::QueuedConnection); + + connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &MainWindow::onSelectionChanged); + + onSelectionChanged(QItemSelection(), QItemSelection()); } MainWindow::~MainWindow() { delete ui; } @@ -90,6 +95,26 @@ void MainWindow::closeEvent(QCloseEvent* event) { } } +void MainWindow::showStatusMessage(const QString text) { + qInfo() << text; + ui->statusbar->showMessage(text); +} + +void MainWindow::onSelectionChanged(const QItemSelection& selected, + const QItemSelection& deselected) { + Q_UNUSED(selected); + Q_UNUSED(deselected); + + QItemSelection localSelection = ui->tableView->selectionModel()->selection(); + if (localSelection.empty()) { + // qDebug() << "Nothing selected. Disabling delete action"; + m_deleteItemAct->setEnabled(false); + } else { + // qDebug() << "Something selected. Enabling delete action"; + m_deleteItemAct->setEnabled(true); + } +} + void MainWindow::onAboutClicked() { const QString applicationName = APPLICATION_NAME; const QString titlePrefix = tr("About "); @@ -104,11 +129,6 @@ void MainWindow::onAboutClicked() { QMessageBox::about(this, titlePrefix + applicationName, aboutText); } -void MainWindow::showStatusMessage(const QString text) { - qInfo() << text; - ui->statusbar->showMessage(text); -} - void MainWindow::on_actionCheck_for_update_triggered() { showStatusMessage("Checking for update..."); const bool updateAvailable = m_core->isApplicationUpdateAvailable(); @@ -133,6 +153,25 @@ void MainWindow::openNewItemDialog() { m_newItemDialog->show(); } +void MainWindow::deleteSelectedtItems() { + showStatusMessage(tr("Invoked 'Edit|Delete Item'")); + QItemSelection localSelection = ui->tableView->selectionModel()->selection(); + if (localSelection.empty()) { + qDebug() << "No items selected. Nothing to remove."; + } else { + for (QList::reverse_iterator iter = localSelection.rbegin(), + rend = localSelection.rend(); + iter != rend; ++iter) { + // qInfo() << "iter:" << *iter; + // const QModelIndex parentIndex = iter->parent(); + const int topRow = iter->top(); + const int bottomRow = iter->bottom(); + const int nRows = bottomRow - topRow + 1; + m_tableModel->removeRows(topRow, nRows); + } + } +} + void MainWindow::createActions() { // TODO add generic menu actions (file/new, edit/cut, ...) createFileActions(); @@ -227,7 +266,6 @@ void MainWindow::createEditActions() { m_openNewItemDialogAct->setShortcut(QKeySequence::New); m_openNewItemDialogAct->setStatusTip(tr("Opens a dialog to add a new item")); connect(m_openNewItemDialogAct.get(), &QAction::triggered, this, &MainWindow::openNewItemDialog); - // m_openNewItemDialogAct->setEnabled(false); ui->menu_Edit->addAction(m_openNewItemDialogAct.get()); m_openEditItemDialogAct = make_unique(tr("&Edit item"), this); @@ -237,11 +275,10 @@ void MainWindow::createEditActions() { m_openEditItemDialogAct->setEnabled(false); ui->menu_Edit->addAction(m_openEditItemDialogAct.get()); - m_deleteItemAct = make_unique(tr("&Delete item"), this); + m_deleteItemAct = make_unique(tr("&Delete item(s)"), this); m_deleteItemAct->setShortcuts(QKeySequence::Delete); - m_deleteItemAct->setStatusTip(tr("Delete currently selected items")); - // connect(m_deleteAct, &QAction::triggered, this, &MainWindow::deleteItem); - m_deleteItemAct->setEnabled(false); + m_deleteItemAct->setStatusTip(tr("Delete currently selected item(s)")); + connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteSelectedtItems); ui->menu_Edit->addAction(m_deleteItemAct.get()); ui->menu_Edit->addSeparator(); diff --git a/mainwindow.h b/mainwindow.h index ae1cebb..1c6c249 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -1,6 +1,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include #include class NewItemDialog; @@ -32,14 +33,17 @@ class MainWindow : public QMainWindow { void closeEvent(QCloseEvent* event) override; private slots: - void onAboutClicked(); void showStatusMessage(const QString text); + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + + void onAboutClicked(); void on_actionCheck_for_update_triggered(); void on_pushButton_clicked(); /// slots for menu actions void openNewItemDialog(); + void deleteSelectedtItems(); private: Ui::MainWindow* ui;