Merge branch 'develop' into feature/UndoRedoWithModelData

This commit is contained in:
2025-12-09 09:59:16 +01:00
7 changed files with 299 additions and 27 deletions

View File

@ -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

View File

@ -0,0 +1,51 @@
#include "abstractdialog.h"
#include <QDialogButtonBox>
#include <QGuiApplication>
#include <QLabel>
#include <QScreen>
#include <QVBoxLayout>
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); }

33
Dialogs/abstractdialog.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef ABSTRACTDIALOG_H
#define ABSTRACTDIALOG_H
#include <QDialog>
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

80
Dialogs/newitemdialog.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "newitemdialog.h"
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <model/tablemodel.h>
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 QDoubleSpinBox();
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);
}
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();
}

42
Dialogs/newitemdialog.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef NEWITEMDIALOG_H
#define NEWITEMDIALOG_H
#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;
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;
QDoubleSpinBox* m_factorBox = nullptr;
};
#endif // NEWITEMDIALOG_H

View File

@ -6,8 +6,10 @@
#include <QUndoStack>
#include "../../ApplicationConfig.h"
#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?";
@ -31,21 +33,27 @@ 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());
m_tableModel = m_core->getModel();
ui->tableView->setModel(m_tableModel.get());
createActions();
createHelpMenu();
createGuiDialogs();
connect(m_core.get(), &GenericCore::displayStatusMessage, this,
&MainWindow::displayStatusMessage);
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());
connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&MainWindow::onSelectionChanged);
onSelectionChanged(QItemSelection(), QItemSelection());
}
MainWindow::~MainWindow() { delete ui; }
@ -88,6 +96,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 ");
@ -102,11 +130,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();
@ -126,14 +149,28 @@ 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'"));
m_newItemDialog->show();
}
QAction* aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
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<QItemSelectionRange>::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() {
@ -243,8 +280,7 @@ void MainWindow::createEditActions() {
m_openNewItemDialogAct = make_unique<QAction>(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);
ui->menu_Edit->addAction(m_openNewItemDialogAct.get());
m_openEditItemDialogAct = make_unique<QAction>(tr("&Edit item"), this);
@ -254,11 +290,10 @@ void MainWindow::createEditActions() {
m_openEditItemDialogAct->setEnabled(false);
ui->menu_Edit->addAction(m_openEditItemDialogAct.get());
m_deleteItemAct = make_unique<QAction>(tr("&Delete item"), this);
m_deleteItemAct = make_unique<QAction>(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();
@ -270,3 +305,20 @@ 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<NewItemDialog>(this);
m_newItemDialog->createContent();
connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_tableModel.get(),
&TableModel::appendItems);
}

View File

@ -1,11 +1,13 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QItemSelection>
#include <QMainWindow>
QT_BEGIN_NAMESPACE
class NewItemDialog;
class TableModel;
class QAbstractItemModel;
QT_BEGIN_NAMESPACE
class QUndoStack;
namespace Ui {
@ -32,18 +34,24 @@ 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;
// GenericCore* m_core;
unique_ptr<GenericCore> m_core;
shared_ptr<QAbstractItemModel> m_tableModel;
shared_ptr<TableModel> m_tableModel;
shared_ptr<QUndoStack> m_modelUndoStack;
/// File actions
@ -65,10 +73,14 @@ class MainWindow : public QMainWindow {
unique_ptr<QAction> m_deleteItemAct;
unique_ptr<QAction> m_findItemAct;
/// Dialogs
unique_ptr<NewItemDialog> m_newItemDialog;
/// Setup functions
void createActions();
void createFileActions();
void createEditActions();
void createHelpMenu();
void createGuiDialogs();
};
#endif // MAINWINDOW_H