From 370bcd5a4184c491870b296e45dfd7c7236f3511 Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Sat, 6 Dec 2025 10:36:21 +0100 Subject: [PATCH 1/6] Retrieve QUndoStack pointer from core and create actions for undo and redo. --- mainwindow.cpp | 15 +++++++++++++++ mainwindow.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index 9dc7987..f8fdb53 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../../ApplicationConfig.h" #include "data/settingshandler.h" @@ -196,6 +197,20 @@ void MainWindow::createFileActions() { } void MainWindow::createEditActions() { + m_modelUndoStack = m_core->getModUndoStack(); + + connect(m_modelUndoStack.get(), SIGNAL(cleanChanged(bool)), this, + SLOT(onCleanStateChanged(bool))); + m_undoAct.reset(m_modelUndoStack->createUndoAction(this, tr("&Undo"))); + m_undoAct->setShortcuts(QKeySequence::Undo); + m_undoAct->setStatusTip(tr("Undo the last operation")); + ui->menu_Edit->addAction(m_undoAct.get()); + + m_redoAct.reset(m_modelUndoStack->createRedoAction(this, tr("&Redo"))); + m_redoAct->setShortcuts(QKeySequence::Redo); + m_redoAct->setStatusTip(tr("Redo the last operation")); + ui->menu_Edit->addAction(m_redoAct.get()); + m_cutAct = make_unique(tr("Cu&t"), this); m_cutAct->setShortcuts(QKeySequence::Cut); m_cutAct->setStatusTip( diff --git a/mainwindow.h b/mainwindow.h index 40ea7ec..de54266 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -7,6 +7,7 @@ QT_BEGIN_NAMESPACE class QAbstractItemModel; +class QUndoStack; namespace Ui { class MainWindow; } @@ -43,6 +44,7 @@ class MainWindow : public QMainWindow { // GenericCore* m_core; unique_ptr m_core; shared_ptr m_tableModel; + shared_ptr m_modelUndoStack; /// File actions unique_ptr m_newFileAct; From 902ed1c84b48b50847bd850a473c15488d80d72d Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Thu, 11 Dec 2025 16:01:48 +0100 Subject: [PATCH 2/6] Added a undo view and react to change of the cleanness of the undo stack. --- mainwindow.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- mainwindow.h | 11 +++++++++- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 36afc26..fc036d5 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../../ApplicationConfig.h" #include "Dialogs/newitemdialog.h" @@ -20,6 +21,7 @@ MainWindow::MainWindow(QWidget* parent) ui->setupUi(this); m_core = std::make_unique(); + setWindowTitle(windowTitle() + " [*]"); /// application icon const QString iconString = "://feature.png"; @@ -173,9 +175,33 @@ void MainWindow::deleteSelectedtItems() { } } +void MainWindow::onCleanStateChanged(bool clean) { + qInfo() << "Slot onCleanStateChanged triggered: clean:" << clean; + setWindowModified(!clean); + if (!clean) { + ui->statusbar->clearMessage(); + } +} + +void MainWindow::onShowUndoViewToggled(bool checked) { + // qInfo() << "Slot onShowUndoViewToggled toggled: checked:" << checked; + // m_undoView->setVisible(checked); + + /// workaround until m_showUndoViewAction is checkable + qInfo() << "Slot onShowUndoViewToggled triggered, toggleing undo view..."; + Q_UNUSED(checked); + if (m_modelUndoView->isVisible()) { + m_modelUndoView->setVisible(false); + + } else { + m_modelUndoView->setVisible(true); + } +} + void MainWindow::createActions() { // TODO add generic menu actions (file/new, edit/cut, ...) createFileActions(); + createUndoActions(); createEditActions(); } @@ -233,11 +259,15 @@ void MainWindow::createFileActions() { ui->menu_File->addAction(m_exitAct.get()); } -void MainWindow::createEditActions() { - m_modelUndoStack = m_core->getModUndoStack(); +void MainWindow::createUndoActions() { + if (!m_core) { + qCritical() << "No core instance set, aborting..."; + return; + } + m_modelUndoStack = m_core->getModelUndoStack(); + + connect(m_modelUndoStack, &QUndoStack::cleanChanged, this, &MainWindow::onCleanStateChanged); - connect(m_modelUndoStack.get(), SIGNAL(cleanChanged(bool)), this, - SLOT(onCleanStateChanged(bool))); m_undoAct.reset(m_modelUndoStack->createUndoAction(this, tr("&Undo"))); m_undoAct->setShortcuts(QKeySequence::Undo); m_undoAct->setStatusTip(tr("Undo the last operation")); @@ -248,6 +278,26 @@ void MainWindow::createEditActions() { m_redoAct->setStatusTip(tr("Redo the last operation")); ui->menu_Edit->addAction(m_redoAct.get()); + m_modelUndoView = make_unique(m_modelUndoStack); + m_modelUndoView->setWindowTitle(tr("Undo list")); + m_modelUndoView->setAttribute(Qt::WA_QuitOnClose, false); + m_modelUndoView->setMinimumSize(450, 600); + m_showModelUndoViewAct = make_unique(tr("Show undo/redo window"), this); + m_showModelUndoViewAct->setStatusTip(tr("Opens a window displaying the items on the undo stack")); + + // TODO showUndoViewAction should be checkable + // m_showUndoViewAction->setCheckable(true); + // connect(m_showUndoViewAction, &QAction::toggled, this, &MainWindow::onShowUndoViewToggled); + connect(m_showModelUndoViewAct.get(), &QAction::triggered, this, + &MainWindow::onShowUndoViewToggled); + + // TODO ? add a keyboard short cut? + // m_showUndoViewAction->setShortcuts(QKeySequence::Copy); + + ui->menu_View->addAction(m_showModelUndoViewAct.get()); +} + +void MainWindow::createEditActions() { m_cutAct = make_unique(tr("Cu&t"), this); m_cutAct->setShortcuts(QKeySequence::Cut); m_cutAct->setStatusTip( diff --git a/mainwindow.h b/mainwindow.h index f49bd80..4d720a1 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -10,6 +10,8 @@ class TableModel; QT_BEGIN_NAMESPACE class QUndoStack; + +class QUndoView; namespace Ui { class MainWindow; } @@ -46,13 +48,17 @@ class MainWindow : public QMainWindow { void openNewItemDialog(); void deleteSelectedtItems(); + void onCleanStateChanged(bool clean); + void onShowUndoViewToggled(bool checked); + private: Ui::MainWindow* ui; // GenericCore* m_core; unique_ptr m_core; shared_ptr m_tableModel; - shared_ptr m_modelUndoStack; + QUndoStack* m_modelUndoStack; + unique_ptr m_modelUndoView; /// File actions unique_ptr m_newFileAct; @@ -72,6 +78,8 @@ class MainWindow : public QMainWindow { unique_ptr m_openEditItemDialogAct; unique_ptr m_deleteItemAct; unique_ptr m_findItemAct; + /// View actions + unique_ptr m_showModelUndoViewAct; /// Dialogs unique_ptr m_newItemDialog; @@ -79,6 +87,7 @@ class MainWindow : public QMainWindow { /// Setup functions void createActions(); void createFileActions(); + void createUndoActions(); void createEditActions(); void createHelpMenu(); void createGuiDialogs(); From 47338e5ae2c6bf20f8d3739ad0c5e7167f403243 Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Wed, 17 Dec 2025 10:33:18 +0100 Subject: [PATCH 3/6] Added an EditItemDialog dummy, based on the NewItemDialog. --- CMakeLists.txt | 1 + Dialogs/edititemdialog.cpp | 60 ++++++++++++++++++++++++++++++++++++++ Dialogs/edititemdialog.h | 36 +++++++++++++++++++++++ Dialogs/newitemdialog.h | 1 + mainwindow.cpp | 14 +++++++-- mainwindow.h | 8 +++-- 6 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 Dialogs/edititemdialog.cpp create mode 100644 Dialogs/edititemdialog.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fd42f13..ddb1ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) assets/icons.qrc Dialogs/abstractdialog.h Dialogs/abstractdialog.cpp Dialogs/newitemdialog.h Dialogs/newitemdialog.cpp + Dialogs/edititemdialog.h Dialogs/edititemdialog.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/edititemdialog.cpp b/Dialogs/edititemdialog.cpp new file mode 100644 index 0000000..de7807a --- /dev/null +++ b/Dialogs/edititemdialog.cpp @@ -0,0 +1,60 @@ +#include "edititemdialog.h" + +#include +#include +#include +#include +#include +#include + +EditItemDialog::EditItemDialog(QWidget* parent) + : AbstractDialog(parent) {} + +void EditItemDialog::createContent() { + if (m_contentContainer) { + delete m_contentContainer; + } + + setWindowTitle(tr("Edit 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); +} diff --git a/Dialogs/edititemdialog.h b/Dialogs/edititemdialog.h new file mode 100644 index 0000000..59dc8b8 --- /dev/null +++ b/Dialogs/edititemdialog.h @@ -0,0 +1,36 @@ +#ifndef EDITITEMDIALOG_H +#define EDITITEMDIALOG_H + +#include "abstractdialog.h" + +class QDoubleSpinBox; +class QLineEdit; +class QSpinBox; +class QLabel; + +class EditItemDialog : public AbstractDialog { + Q_OBJECT + public: + EditItemDialog(QWidget* parent = nullptr); + + /// AbstractDialog interface + 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; + QDoubleSpinBox* m_factorBox = nullptr; +}; + +#endif // EDITITEMDIALOG_H diff --git a/Dialogs/newitemdialog.h b/Dialogs/newitemdialog.h index b3db670..23b1987 100644 --- a/Dialogs/newitemdialog.h +++ b/Dialogs/newitemdialog.h @@ -7,6 +7,7 @@ class QDoubleSpinBox; class QLineEdit; class QSpinBox; class QLabel; + class NewItemDialog : public AbstractDialog { Q_OBJECT diff --git a/mainwindow.cpp b/mainwindow.cpp index fc036d5..a0781f5 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -7,6 +7,7 @@ #include #include "../../ApplicationConfig.h" +#include "Dialogs/edititemdialog.h" #include "Dialogs/newitemdialog.h" #include "data/settingshandler.h" #include "genericcore.h" @@ -156,6 +157,11 @@ void MainWindow::openNewItemDialog() { m_newItemDialog->show(); } +void MainWindow::openEditItemDialog() { + showStatusMessage(tr("Invoked 'Edit|Edit Item'")); + m_editItemDialog->show(); +} + void MainWindow::deleteSelectedtItems() { showStatusMessage(tr("Invoked 'Edit|Delete Item'")); QItemSelection localSelection = ui->tableView->selectionModel()->selection(); @@ -336,8 +342,8 @@ void MainWindow::createEditActions() { m_openEditItemDialogAct = make_unique(tr("&Edit item"), this); m_openEditItemDialogAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_E)); m_openEditItemDialogAct->setStatusTip(tr("Opens a edit dialog for the current item")); - // connect(m_openEditItemDialogAct, &QAction::triggered, this, &MainWindow::openEditDialog); - m_openEditItemDialogAct->setEnabled(false); + connect(m_openEditItemDialogAct.get(), &QAction::triggered, this, + &MainWindow::openEditItemDialog); ui->menu_Edit->addAction(m_openEditItemDialogAct.get()); m_deleteItemAct = make_unique(tr("&Delete item(s)"), this); @@ -367,8 +373,12 @@ void MainWindow::createHelpMenu() { } void MainWindow::createGuiDialogs() { + /// new item dialog m_newItemDialog = make_unique(this); m_newItemDialog->createContent(); connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_tableModel.get(), &TableModel::appendItems); + /// edit item dialog + m_editItemDialog = make_unique(this); + m_editItemDialog->createContent(); } diff --git a/mainwindow.h b/mainwindow.h index 4d720a1..c8ba839 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -4,9 +4,6 @@ #include #include -class NewItemDialog; -class TableModel; - QT_BEGIN_NAMESPACE class QUndoStack; @@ -18,6 +15,9 @@ class MainWindow; QT_END_NAMESPACE class GenericCore; +class TableModel; +class NewItemDialog; +class EditItemDialog; using namespace std; @@ -46,6 +46,7 @@ class MainWindow : public QMainWindow { /// slots for menu actions void openNewItemDialog(); + void openEditItemDialog(); void deleteSelectedtItems(); void onCleanStateChanged(bool clean); @@ -83,6 +84,7 @@ class MainWindow : public QMainWindow { /// Dialogs unique_ptr m_newItemDialog; + unique_ptr m_editItemDialog; /// Setup functions void createActions(); From 467ab58fae6e198dc9d2a5834d4417f3249770cd Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Wed, 17 Dec 2025 10:39:34 +0100 Subject: [PATCH 4/6] Renamed source code folder "Dialogs" into "dialogs". --- CMakeLists.txt | 6 +++--- {Dialogs => dialogs}/abstractdialog.cpp | 0 {Dialogs => dialogs}/abstractdialog.h | 0 {Dialogs => dialogs}/edititemdialog.cpp | 0 {Dialogs => dialogs}/edititemdialog.h | 0 {Dialogs => dialogs}/newitemdialog.cpp | 0 {Dialogs => dialogs}/newitemdialog.h | 0 mainwindow.cpp | 4 ++-- 8 files changed, 5 insertions(+), 5 deletions(-) rename {Dialogs => dialogs}/abstractdialog.cpp (100%) rename {Dialogs => dialogs}/abstractdialog.h (100%) rename {Dialogs => dialogs}/edititemdialog.cpp (100%) rename {Dialogs => dialogs}/edititemdialog.h (100%) rename {Dialogs => dialogs}/newitemdialog.cpp (100%) rename {Dialogs => dialogs}/newitemdialog.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddb1ca9..42e5c48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,9 +29,9 @@ 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 - Dialogs/edititemdialog.h Dialogs/edititemdialog.cpp + dialogs/abstractdialog.h dialogs/abstractdialog.cpp + dialogs/newitemdialog.h dialogs/newitemdialog.cpp + dialogs/edititemdialog.h dialogs/edititemdialog.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 similarity index 100% rename from Dialogs/abstractdialog.cpp rename to dialogs/abstractdialog.cpp diff --git a/Dialogs/abstractdialog.h b/dialogs/abstractdialog.h similarity index 100% rename from Dialogs/abstractdialog.h rename to dialogs/abstractdialog.h diff --git a/Dialogs/edititemdialog.cpp b/dialogs/edititemdialog.cpp similarity index 100% rename from Dialogs/edititemdialog.cpp rename to dialogs/edititemdialog.cpp diff --git a/Dialogs/edititemdialog.h b/dialogs/edititemdialog.h similarity index 100% rename from Dialogs/edititemdialog.h rename to dialogs/edititemdialog.h diff --git a/Dialogs/newitemdialog.cpp b/dialogs/newitemdialog.cpp similarity index 100% rename from Dialogs/newitemdialog.cpp rename to dialogs/newitemdialog.cpp diff --git a/Dialogs/newitemdialog.h b/dialogs/newitemdialog.h similarity index 100% rename from Dialogs/newitemdialog.h rename to dialogs/newitemdialog.h diff --git a/mainwindow.cpp b/mainwindow.cpp index a0781f5..14174f1 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -7,9 +7,9 @@ #include #include "../../ApplicationConfig.h" -#include "Dialogs/edititemdialog.h" -#include "Dialogs/newitemdialog.h" #include "data/settingshandler.h" +#include "dialogs/edititemdialog.h" +#include "dialogs/newitemdialog.h" #include "genericcore.h" #include "model/tablemodel.h" From 8d523bb5bca83176ca3d69da4730e2fc1e1a962c Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Wed, 17 Dec 2025 20:52:12 +0100 Subject: [PATCH 5/6] Moved EditItemDialog content widget into separate widget class ItemDetailMapper. --- CMakeLists.txt | 1 + dialogs/edititemdialog.cpp | 48 ++++---------------------------------- views/itemdetailmapper.cpp | 48 ++++++++++++++++++++++++++++++++++++++ views/itemdetailmapper.h | 35 +++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 views/itemdetailmapper.cpp create mode 100644 views/itemdetailmapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 42e5c48..87f5dd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) dialogs/abstractdialog.h dialogs/abstractdialog.cpp dialogs/newitemdialog.h dialogs/newitemdialog.cpp dialogs/edititemdialog.h dialogs/edititemdialog.cpp + views/itemdetailmapper.h views/itemdetailmapper.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/edititemdialog.cpp b/dialogs/edititemdialog.cpp index de7807a..df596ac 100644 --- a/dialogs/edititemdialog.cpp +++ b/dialogs/edititemdialog.cpp @@ -1,11 +1,8 @@ #include "edititemdialog.h" -#include -#include -#include -#include -#include -#include +#include + +#include "../views/itemdetailmapper.h" EditItemDialog::EditItemDialog(QWidget* parent) : AbstractDialog(parent) {} @@ -17,44 +14,7 @@ void EditItemDialog::createContent() { setWindowTitle(tr("Edit 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_contentContainer = new ItemDetailMapper(this); m_outerLayout->insertWidget(0, m_contentContainer); } diff --git a/views/itemdetailmapper.cpp b/views/itemdetailmapper.cpp new file mode 100644 index 0000000..62945ce --- /dev/null +++ b/views/itemdetailmapper.cpp @@ -0,0 +1,48 @@ +#include "itemdetailmapper.h" + +#include +#include +#include +#include +#include +#include + +ItemDetailMapper::ItemDetailMapper(QWidget* parent) + : QWidget{parent} { + // 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); + + setLayout(layout); +} diff --git a/views/itemdetailmapper.h b/views/itemdetailmapper.h new file mode 100644 index 0000000..79bb239 --- /dev/null +++ b/views/itemdetailmapper.h @@ -0,0 +1,35 @@ +#ifndef ITEMDETAILMAPPER_H +#define ITEMDETAILMAPPER_H + +#include + +class QLabel; +class QLineEdit; +class QDoubleSpinBox; +class QSpinBox; + +class ItemDetailMapper : public QWidget { + Q_OBJECT + public: + explicit ItemDetailMapper(QWidget* parent = nullptr); + + signals: + + 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 // ITEMDETAILMAPPER_H From b3f83ccdb0ece612debc6dd3c2b94c594b7228b6 Mon Sep 17 00:00:00 2001 From: Bent Witthold Date: Mon, 22 Dec 2025 11:21:44 +0100 Subject: [PATCH 6/6] Using QDataWidgetMapper in ItemDetailMapper to work with the model data (current index of the TableView). --- dialogs/edititemdialog.cpp | 18 ++++++-- dialogs/edititemdialog.h | 12 ++++- mainwindow.cpp | 37 +++++++++++++-- mainwindow.h | 2 + views/itemdetailmapper.cpp | 94 ++++++++++++++++++++++++++++++++++++++ views/itemdetailmapper.h | 30 ++++++++++++ 6 files changed, 185 insertions(+), 8 deletions(-) diff --git a/dialogs/edititemdialog.cpp b/dialogs/edititemdialog.cpp index df596ac..94b56a2 100644 --- a/dialogs/edititemdialog.cpp +++ b/dialogs/edititemdialog.cpp @@ -4,8 +4,9 @@ #include "../views/itemdetailmapper.h" -EditItemDialog::EditItemDialog(QWidget* parent) - : AbstractDialog(parent) {} +EditItemDialog::EditItemDialog(QTableView* tableView, QWidget* parent) + : AbstractDialog(parent) + , m_tableView(tableView) {} void EditItemDialog::createContent() { if (m_contentContainer) { @@ -14,7 +15,18 @@ void EditItemDialog::createContent() { setWindowTitle(tr("Edit item...")); - m_contentContainer = new ItemDetailMapper(this); + m_detailMapper = new ItemDetailMapper(this); + m_detailMapper->setModelMappings(m_tableView); + m_contentContainer = m_detailMapper; m_outerLayout->insertWidget(0, m_contentContainer); + +void EditItemDialog::accept() { + m_detailMapper->submit(); + QDialog::accept(); +} + +void EditItemDialog::reject() { + m_detailMapper->revert(); + QDialog::reject(); } diff --git a/dialogs/edititemdialog.h b/dialogs/edititemdialog.h index 59dc8b8..13693f5 100644 --- a/dialogs/edititemdialog.h +++ b/dialogs/edititemdialog.h @@ -7,16 +7,26 @@ class QDoubleSpinBox; class QLineEdit; class QSpinBox; class QLabel; +class QTableView; + +class ItemDetailMapper; class EditItemDialog : public AbstractDialog { Q_OBJECT public: - EditItemDialog(QWidget* parent = nullptr); + EditItemDialog(QTableView* tableView, QWidget* parent = nullptr); /// AbstractDialog interface void createContent() override; + public slots: + void accept() override; + void reject() override; + private: + QTableView* m_tableView = nullptr; + ItemDetailMapper* m_detailMapper; + QLabel* m_nameLabel = nullptr; QLineEdit* m_nameEdit = nullptr; diff --git a/mainwindow.cpp b/mainwindow.cpp index 14174f1..5397ccc 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -53,10 +53,13 @@ MainWindow::MainWindow(QWidget* parent) connect(this, &MainWindow::checkForUpdates, this, &MainWindow::on_actionCheck_for_update_triggered, Qt::QueuedConnection); - connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - &MainWindow::onSelectionChanged); + // connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + // &MainWindow::onSelectionChanged); + connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentChanged, this, + &MainWindow::onCurrentChanged); onSelectionChanged(QItemSelection(), QItemSelection()); + onCurrentChanged(QModelIndex(), QModelIndex()); } MainWindow::~MainWindow() { delete ui; } @@ -104,6 +107,20 @@ void MainWindow::showStatusMessage(const QString text) { ui->statusbar->showMessage(text); } +void MainWindow::onCurrentChanged(const QModelIndex& current, const QModelIndex& previous) { + // Q_UNUSED(current); + Q_UNUSED(previous); + + // QItemSelection localSelection = ui->tableView->selectionModel()->selection(); + if (current == QModelIndex()) { + // qDebug() << "Nothing selected. Disabling delete action"; + m_deleteItemAct->setEnabled(false); + } else { + // qDebug() << "Something selected. Enabling delete action"; + m_deleteItemAct->setEnabled(true); + } +} + void MainWindow::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { Q_UNUSED(selected); @@ -162,6 +179,17 @@ void MainWindow::openEditItemDialog() { m_editItemDialog->show(); } +void MainWindow::deleteCurrentItem() { + showStatusMessage(tr("Invoked 'Edit|Delete Item'")); + // QItemSelection localSelection = ui->tableView->selectionModel()->selection(); + const QModelIndex currentIndex = ui->tableView->selectionModel()->currentIndex(); + if (currentIndex == QModelIndex()) { + qDebug() << "No current item. Nothing to remove."; + } else { + m_tableModel->removeRows(currentIndex.row(), 1); + } +} + void MainWindow::deleteSelectedtItems() { showStatusMessage(tr("Invoked 'Edit|Delete Item'")); QItemSelection localSelection = ui->tableView->selectionModel()->selection(); @@ -349,7 +377,8 @@ void MainWindow::createEditActions() { m_deleteItemAct = make_unique(tr("&Delete item(s)"), this); m_deleteItemAct->setShortcuts(QKeySequence::Delete); m_deleteItemAct->setStatusTip(tr("Delete currently selected item(s)")); - connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteSelectedtItems); + // connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteSelectedtItems); + connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteCurrentItem); ui->menu_Edit->addAction(m_deleteItemAct.get()); ui->menu_Edit->addSeparator(); @@ -379,6 +408,6 @@ void MainWindow::createGuiDialogs() { connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_tableModel.get(), &TableModel::appendItems); /// edit item dialog - m_editItemDialog = make_unique(this); + m_editItemDialog = make_unique(ui->tableView, this); m_editItemDialog->createContent(); } diff --git a/mainwindow.h b/mainwindow.h index c8ba839..652087d 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -37,6 +37,7 @@ class MainWindow : public QMainWindow { private slots: void showStatusMessage(const QString text); + void onCurrentChanged(const QModelIndex& current, const QModelIndex& previous); void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void onAboutClicked(); @@ -47,6 +48,7 @@ class MainWindow : public QMainWindow { /// slots for menu actions void openNewItemDialog(); void openEditItemDialog(); + void deleteCurrentItem(); void deleteSelectedtItems(); void onCleanStateChanged(bool clean); diff --git a/views/itemdetailmapper.cpp b/views/itemdetailmapper.cpp index 62945ce..4b5c7c8 100644 --- a/views/itemdetailmapper.cpp +++ b/views/itemdetailmapper.cpp @@ -1,14 +1,30 @@ #include "itemdetailmapper.h" +#include +#include #include #include #include #include #include +#include #include +#include ItemDetailMapper::ItemDetailMapper(QWidget* parent) : QWidget{parent} { + /// model mapping + m_mapper = std::make_unique(this); + m_mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + + /// model mapping buttons + m_nextButton = new QPushButton(tr("Ne&xt")); + m_previousButton = new QPushButton(tr("&Previous")); + + connect(m_previousButton, &QAbstractButton::clicked, this, &ItemDetailMapper::toPrevious); + connect(m_nextButton, &QAbstractButton::clicked, this, &ItemDetailMapper::toNext); + + /// the individual widgets // REFACTOR deduce label names and types from meta data & use a factory m_nameLabel = new QLabel("&Name"); m_nameEdit = new QLineEdit(); @@ -44,5 +60,83 @@ ItemDetailMapper::ItemDetailMapper(QWidget* parent) layout->addWidget(m_factorLabel, 4, 0, 1, 1); layout->addWidget(m_factorBox, 4, 1, 1, 1); + layout->addWidget(m_previousButton, 5, 0, 1, 1); + layout->addWidget(m_nextButton, 5, 1, 1, 1); + setLayout(layout); } + +void ItemDetailMapper::setModelMappings(QTableView* tableView) { + qDebug() << "Setting model..."; + m_tableView = tableView; + m_model = tableView->model(); + m_selectionModel = tableView->selectionModel(); + + m_mapper->setModel(m_model); + m_mapper->addMapping(m_nameEdit, 0); + m_mapper->addMapping(m_descriptionEdit, 1); + m_mapper->addMapping(m_infoEdit, 2); + m_mapper->addMapping(m_amountBox, 3); + m_mapper->addMapping(m_factorBox, 4); + + m_mapper->setCurrentIndex(m_selectionModel->currentIndex().row()); + + connect(m_model, &QAbstractItemModel::rowsInserted, this, &ItemDetailMapper::rowsInserted); + connect(m_model, &QAbstractItemModel::rowsRemoved, this, &ItemDetailMapper::rowsRemoved); + connect(m_selectionModel, &QItemSelectionModel::currentChanged, this, + &ItemDetailMapper::onCurrentIndexChanged); + + updateButtons(m_selectionModel->currentIndex().row()); +} + +bool ItemDetailMapper::submit() { return m_mapper->submit(); } + +void ItemDetailMapper::revert() { m_mapper->revert(); } + +void ItemDetailMapper::onCurrentIndexChanged(const QModelIndex& current, + const QModelIndex& previous) { + if (!isEnabled()) { + setEnabled(true); + } + m_mapper->setCurrentModelIndex(current); + updateButtons(current.row()); +} + +void ItemDetailMapper::rowsInserted(const QModelIndex& parent, int start, int end) { + updateButtons(m_mapper->currentIndex()); +} + +void ItemDetailMapper::rowsRemoved(const QModelIndex& parent, int start, int end) { + if (m_model->rowCount() == 0) { + setEnabled(false); + + m_nameEdit->clear(); + m_descriptionEdit->clear(); + m_infoEdit->clear(); + m_amountBox->clear(); + m_factorBox->clear(); + } + + updateButtons(m_mapper->currentIndex()); +} + +void ItemDetailMapper::toPrevious() { + int currentRow = m_selectionModel->currentIndex().row(); + QModelIndex previousIndex = m_selectionModel->currentIndex().siblingAtRow(currentRow - 1); + if (previousIndex.isValid()) { + m_selectionModel->setCurrentIndex(previousIndex, QItemSelectionModel::ClearAndSelect); + } +} + +void ItemDetailMapper::toNext() { + int currentRow = m_selectionModel->currentIndex().row(); + QModelIndex nextIndex = m_selectionModel->currentIndex().siblingAtRow(currentRow + 1); + if (nextIndex.isValid()) { + m_selectionModel->setCurrentIndex(nextIndex, QItemSelectionModel::ClearAndSelect); + } +} + +void ItemDetailMapper::updateButtons(int row) { + m_previousButton->setEnabled(row > 0); + m_nextButton->setEnabled(row < m_model->rowCount() - 1); +} diff --git a/views/itemdetailmapper.h b/views/itemdetailmapper.h index 79bb239..2bdd8b3 100644 --- a/views/itemdetailmapper.h +++ b/views/itemdetailmapper.h @@ -1,21 +1,48 @@ #ifndef ITEMDETAILMAPPER_H #define ITEMDETAILMAPPER_H +#include #include class QLabel; class QLineEdit; class QDoubleSpinBox; class QSpinBox; +class QPushButton; +class QAbstractItemModel; +class QItemSelectionModel; +class QTableView; class ItemDetailMapper : public QWidget { Q_OBJECT public: explicit ItemDetailMapper(QWidget* parent = nullptr); + void setModelMappings(QTableView* tableView); + + bool submit(); + void revert(); + signals: + private slots: + void onCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous); + void rowsInserted(const QModelIndex& parent, int start, int end); + void rowsRemoved(const QModelIndex& parent, int start, int end); + void toPrevious(); + void toNext(); + void updateButtons(int row); + private: + /// *** members *** + /// Model stuff + QTableView* m_tableView = nullptr; + QAbstractItemModel* m_model = nullptr; + QItemSelectionModel* m_selectionModel = nullptr; + + std::unique_ptr m_mapper; + + /// GUI elements QLabel* m_nameLabel = nullptr; QLineEdit* m_nameEdit = nullptr; @@ -30,6 +57,9 @@ class ItemDetailMapper : public QWidget { QLabel* m_factorLabel = nullptr; QDoubleSpinBox* m_factorBox = nullptr; + + QPushButton* m_nextButton; + QPushButton* m_previousButton; }; #endif // ITEMDETAILMAPPER_H