Compare commits

...

24 Commits

Author SHA1 Message Date
6408eaa10a Refactored the creation of control widgets. 2026-04-02 09:51:04 +02:00
24192df5d6 Using the new SpinBoxDelegate in int and double columns even if the cell is empty. 2026-03-14 09:35:42 +01:00
c7b0e4f3e5 Removed status message which overwrites message from the core on CSV import. 2026-03-12 10:38:31 +01:00
7fa8612313 Removed not used icon and renamed the application icon properly. 2026-03-06 11:07:05 +01:00
8dbceabb8a Removed messagehandler.h because it now lives in the GenericCore project. 2026-03-03 18:55:55 +01:00
2d8c97b5f0 NewItemDialog uses dynamically generated control widgets for model data as well. Added a WidgetHelper class to consolidate identical source code for ItemDetailMapper and NewItemDialog. 2026-03-02 19:25:27 +01:00
2fb7560e6e Minor refactoring of ItemDetailMapper. 2026-03-02 18:51:31 +01:00
560cadb2c0 All mapped control widgets (plus labels) are dynamically generated from meta data. 2026-03-02 17:11:31 +01:00
0e7f803d42 Refactored ItemDetailMapper to encapsulate widget creation and their mappings into functions to be able to iterate over meta data to auto generate them. (Only working for string roles for now) 2026-03-02 14:09:55 +01:00
d56af7231c Added the type role to the EditItemDialog. 2026-03-02 08:43:21 +01:00
385dc7ca76 Added a ComboBoxDelegate for the new "type" role. 2026-03-01 11:42:30 +01:00
5e3950f6ba Bumped version to 0.3.0. 2026-02-04 11:28:11 +01:00
34a34891b4 In SettingsDialog: Disabled the email and password input edits. 2026-02-03 12:12:51 +01:00
4c4d734b1b Added a SettingsDialog with a "Server" tab to configure the server settings. 2026-02-03 11:22:55 +01:00
0eef55fc32 Don't use the SettingsHandler directly. Go through the GenericCore instead. 2026-02-03 11:21:33 +01:00
d109eb31f8 Added "Server/Delete item" menu action. 2026-02-02 16:30:59 +01:00
a9f24ac8f2 Adjusted to GenericCore::postItemsToServer and using ModelItemsValues typedef in new item dialog. 2026-02-02 16:25:17 +01:00
67d9a3914d Retrieving JSON data from the proxy model and send it via core to the server. 2026-01-29 15:11:59 +01:00
518bebcbb7 Added "Server" menu with "fetch items" and "post items" actions to trigger communication with the RESTful server. 2026-01-29 09:00:12 +01:00
8f61c6bc2f Bugfix: Save changes when closing the EditItemDialog. 2026-01-19 18:40:39 +01:00
a966b26185 Getting rid of warning, that a widget has already a layout. 2026-01-19 14:43:07 +01:00
a8bf5b4032 Displaying a QR code in the EditItemDialog containing the full data of the current item as a string. 2026-01-15 14:06:02 +01:00
c83ba2da9d Bumped version to 0.2.0. 2026-01-14 10:27:38 +01:00
232b9ceb78 Added a find items dialog to select items based on if they contain a specific text. 2026-01-12 15:02:34 +01:00
22 changed files with 773 additions and 340 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)
set(TARGET_APP "GenericQtClient-Widgets")
project(${TARGET_APP} VERSION 0.1.0 LANGUAGES CXX)
project(${TARGET_APP} VERSION 0.3.0 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
@ -27,12 +27,16 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(${TARGET_APP}
MANUAL_FINALIZATION
${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/settingsdialog.h dialogs/settingsdialog.cpp
views/itemdetailmapper.h views/itemdetailmapper.cpp
widgets/controls/comboboxdelegate.h widgets/controls/comboboxdelegate.cpp
widgethelper.h widgethelper.cpp
widgets/controls/spinboxdelegate.h widgets/controls/spinboxdelegate.cpp
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET ${TARGET_APP} APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
@ -63,6 +67,8 @@ target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/)
target_link_libraries(${TARGET_APP} PRIVATE GenericCore)
target_include_directories(${TARGET_APP} PRIVATE ${QR_LIB_DIR}/src)
target_link_libraries(${TARGET_APP} PRIVATE qrcode)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,7 +1,6 @@
<RCC>
<qresource prefix="/">
<file>software-application.png</file>
<file>feature.png</file>
<file>no-picture-taking.png</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,13 +1,15 @@
#include "edititemdialog.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
#include "../views/itemdetailmapper.h"
EditItemDialog::EditItemDialog(QTableView* tableView, QWidget* parent)
: AbstractDialog(QDialogButtonBox::Close, parent)
, m_tableView(tableView) {}
: AbstractDialog(QDialogButtonBox::Ok, parent)
, m_tableView(tableView)
, m_qrCodeDisplay(new QLabel("QR Code")) {}
void EditItemDialog::createContent() {
if (m_contentContainer) {
@ -16,9 +18,16 @@ void EditItemDialog::createContent() {
setWindowTitle(tr("Edit item..."));
m_detailMapper = new ItemDetailMapper(this);
m_detailMapper->setModelMappings(m_tableView);
m_contentContainer = m_detailMapper;
m_contentContainer = new QWidget(this);
QHBoxLayout* innerLayout = new QHBoxLayout();
m_contentContainer->setLayout(innerLayout);
m_detailMapper = new ItemDetailMapper(m_tableView, this);
innerLayout->addWidget(m_detailMapper);
updateQRCode();
connect(m_detailMapper, &ItemDetailMapper::contentChanged, this, &EditItemDialog::updateQRCode);
innerLayout->addWidget(m_qrCodeDisplay);
m_outerLayout->insertWidget(0, m_contentContainer);
}
@ -32,3 +41,16 @@ void EditItemDialog::reject() {
m_detailMapper->revert();
QDialog::reject();
}
void EditItemDialog::updateQRCode(const QString text) {
QImage unscaledImage;
if (text.isEmpty()) {
unscaledImage = QImage("://no-picture-taking.png");
} else {
unscaledImage = m_generator.generateQr(text);
}
QImage image = unscaledImage.scaled(250, 250);
m_qrCodeDisplay->setPixmap(QPixmap::fromImage(image));
m_qrCodeDisplay->setToolTip(text);
}

View File

@ -1,6 +1,7 @@
#ifndef EDITITEMDIALOG_H
#define EDITITEMDIALOG_H
#include "QrCodeGenerator.h"
#include "abstractdialog.h"
class QDoubleSpinBox;
@ -23,6 +24,9 @@ class EditItemDialog : public AbstractDialog {
void accept() override;
void reject() override;
private slots:
void updateQRCode(const QString text = "");
private:
QTableView* m_tableView = nullptr;
ItemDetailMapper* m_detailMapper;
@ -41,6 +45,9 @@ class EditItemDialog : public AbstractDialog {
QLabel* m_factorLabel = nullptr;
QDoubleSpinBox* m_factorBox = nullptr;
QLabel* m_qrCodeDisplay = nullptr;
QrCodeGenerator m_generator;
};
#endif // EDITITEMDIALOG_H

View File

@ -1,11 +1,15 @@
#include "newitemdialog.h"
#include <QComboBox>
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QStringListModel>
#include "../widgethelper.h"
#include "formats/jsonparser.h"
#include "model/metadata.h"
@ -21,61 +25,84 @@ void NewItemDialog::createContent() {
m_contentContainer = new QWidget(this);
// REFACTOR use a data structure for input widgets which can be iterated through
// using a factory which iterates through the roles from metadata.h
// and create the input widgets based on the data type of this role
m_nameLabel = new QLabel("&Name");
m_nameEdit = new QLineEdit();
m_nameLabel->setBuddy(m_nameEdit);
m_layout = new QGridLayout();
m_descriptionLabel = new QLabel("&Description");
m_descriptionEdit = new QLineEdit();
m_descriptionLabel->setBuddy(m_descriptionEdit);
for (int i = 0; i < USER_FACING_ROLES.size(); ++i) {
setupWidgetPairForColumn(i);
}
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->setLayout(m_layout);
m_outerLayout->insertWidget(0, m_contentContainer);
}
void NewItemDialog::accept() {
QHash<int, QVariant> itemValues;
// TODO (after refactoring data structure for input widgets) use iteration through the relevant
// roles and their input widgets
itemValues.insert(NameRole, m_nameEdit->text());
itemValues.insert(DescriptionRole, m_descriptionEdit->text());
itemValues.insert(InfoRole, m_infoEdit->text());
itemValues.insert(AmountRole, m_amountBox->value());
itemValues.insert(FactorRole, m_factorBox->value());
addItemToModel();
const QByteArray jsonDoc = JsonParser::itemValuesListToJson({itemValues}, ITEM_KEY_STRING);
emit addItems(jsonDoc);
resetContent();
// resetContent();
AbstractDialog::accept();
}
void NewItemDialog::setupWidgetPairForColumn(const int column) {
// REFACTOR consolidate NewItemDialog::setupWidgetPairForColumn(...)
// with ItemDetailMapper::setupWidgetPairForColumn(...)
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
QWidget* control = WidgetHelper::createControlWidget(role, this);
const QString string = QString("&%1").arg(GET_HEADER_FOR_COLUMN(column));
QLabel* label = new QLabel(string);
label->setBuddy(control);
m_layout->addWidget(label, column, 0);
m_layout->addWidget(control, column, 1);
m_controlWidgets.append(control);
}
void NewItemDialog::addItemToModel() {
ModelItemValues itemValues;
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[i]);
itemValues.insert(role, lineEdit->text());
} else if (TYPE_ROLES.contains(role)) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[i]);
itemValues.insert(role, comboBox->currentText());
} else if (INT_ROLES.contains(role)) {
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(m_controlWidgets[i]);
itemValues.insert(role, spinBox->value());
} else if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* doubleSpinBox = dynamic_cast<QDoubleSpinBox*>(m_controlWidgets[i]);
itemValues.insert(role, doubleSpinBox->value());
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
const QByteArray jsonDoc = JsonParser::itemValuesListToJson({itemValues}, ITEMS_KEY_STRING);
emit addItems(jsonDoc);
}
void NewItemDialog::resetContent() {
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[i]);
lineEdit->clear();
} else if (TYPE_ROLES.contains(role)) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[i]);
comboBox->setCurrentText("");
} else if (INT_ROLES.contains(role)) {
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(m_controlWidgets[i]);
spinBox->setValue(0);
} else if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* doubleSpinBox = dynamic_cast<QDoubleSpinBox*>(m_controlWidgets[i]);
doubleSpinBox->setValue(0.0);
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
}

View File

@ -24,20 +24,13 @@ class NewItemDialog : public AbstractDialog {
// void reject() override;
private:
QLabel* m_nameLabel = nullptr;
QLineEdit* m_nameEdit = nullptr;
QGridLayout* m_layout;
QList<QWidget*> m_controlWidgets;
QLabel* m_descriptionLabel = nullptr;
QLineEdit* m_descriptionEdit = nullptr;
void setupWidgetPairForColumn(const int column);
QLabel* m_infoLabel = nullptr;
QLineEdit* m_infoEdit = nullptr;
QLabel* m_amountLabel = nullptr;
QSpinBox* m_amountBox = nullptr;
QLabel* m_factorLabel = nullptr;
QDoubleSpinBox* m_factorBox = nullptr;
void addItemToModel();
void resetContent();
};
#endif // NEWITEMDIALOG_H

View File

@ -0,0 +1,63 @@
#include "settingsdialog.h"
#include <QCoreApplication>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTabWidget>
SettingsDialog::SettingsDialog(QWidget* parent)
: AbstractDialog(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent) {}
void SettingsDialog::createContent() {
if (m_contentContainer) {
delete m_contentContainer;
}
const QString dialogTitle = tr("Settings - ");
const QString applicationName = QCoreApplication::applicationName();
setWindowTitle(dialogTitle + applicationName);
setModal(true);
setGeometry(0, 0, 350, 250);
QGridLayout* serverLayout = new QGridLayout();
QLabel* urlLabel = new QLabel("Server URL:");
m_urlEdit = new QLineEdit();
serverLayout->addWidget(urlLabel, 0, 0);
serverLayout->addWidget(m_urlEdit, 0, 1);
QLabel* emailLabel = new QLabel("Email:");
m_emailEdit = new QLineEdit();
m_emailEdit->setEnabled(false);
serverLayout->addWidget(emailLabel, 1, 0);
serverLayout->addWidget(m_emailEdit, 1, 1);
QLabel* passwordLabel = new QLabel("Password:");
m_passwordEdit = new QLineEdit();
m_passwordEdit->setEnabled(false);
m_passwordEdit->setEchoMode(QLineEdit::Password);
serverLayout->addWidget(passwordLabel, 2, 0);
serverLayout->addWidget(m_passwordEdit, 2, 1);
QWidget* serverTab = new QWidget();
serverTab->setLayout(serverLayout);
QTabWidget* widget = new QTabWidget();
widget->addTab(serverTab, "Server");
m_contentContainer = widget;
m_outerLayout->insertWidget(0, m_contentContainer);
}
void SettingsDialog::fillContent(const QVariantMap& settings) {
m_urlEdit->setText(settings.value("url").toString());
m_emailEdit->setText(settings.value("email").toString());
m_passwordEdit->setText(settings.value("password").toString());
}
QVariantMap SettingsDialog::getSettings() const {
QVariantMap result;
result.insert("url", m_urlEdit->text());
result.insert("email", m_emailEdit->text());
result.insert("password", m_passwordEdit->text());
return result;
}

23
dialogs/settingsdialog.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include "abstractdialog.h"
class QLineEdit;
class SettingsDialog : public AbstractDialog {
Q_OBJECT
public:
SettingsDialog(QWidget* parent = nullptr);
void createContent() override;
void fillContent(const QVariantMap& settings);
QVariantMap getSettings() const;
private:
QLineEdit* m_urlEdit = nullptr;
QLineEdit* m_emailEdit = nullptr;
QLineEdit* m_passwordEdit = nullptr;
};
#endif // SETTINGSDIALOG_H

View File

@ -3,18 +3,22 @@
#include <QCloseEvent>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QUndoStack>
#include <QUndoView>
#include "../../ApplicationConfig.h"
#include "data/settingshandler.h"
#include "dialogs/edititemdialog.h"
#include "dialogs/newitemdialog.h"
#include "dialogs/settingsdialog.h"
#include "genericcore.h"
#include "model/generalsortfiltermodel.h"
#include "model/metadata.h"
#include "model/tablemodel.h"
#include "widgets/controls/comboboxdelegate.h"
#include "widgets/controls/spinboxdelegate.h"
static QStandardPaths::StandardLocation standardLocation = QStandardPaths::HomeLocation;
static QString updateTextClean = "Do you want to update the application now?";
@ -30,26 +34,23 @@ MainWindow::MainWindow(QWidget* parent)
setWindowTitle(QCoreApplication::applicationName() + " [*]");
/// application icon
const QString iconString = "://feature.png";
const QString iconString = "://software-application.png";
#ifdef QT_DEBUG
QPixmap pixmap = QPixmap(iconString);
QTransform transform = QTransform();
transform.rotate(180);
QPixmap rotated = pixmap.transformed(transform);
QIcon appIcon = QIcon(rotated);
setWindowIcon(QIcon(rotated));
#else
setWindowIcon(QIcon(iconString));
#endif
const QVariantMap settings = SettingsHandler::getSettings("GUI");
const QVariantMap settings = m_core->getSettings("GUI");
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
m_tableModel = m_core->getModel();
// ui->tableView->setModel(m_tableModel.get());
m_sortModel = m_core->getSortFilterModel();
ui->tableView->setModel((QAbstractItemModel*)m_sortModel.get());
ui->tableView->setSortingEnabled(true);
setupModelViews();
createActions();
createHelpMenu();
@ -105,8 +106,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
if (event->isAccepted()) {
qInfo() << "Saving GUI settings...";
SettingsHandler::saveSettings({{"geometry", saveGeometry()}, {"windowState", saveState()}},
"GUI");
m_core->applySettings({{"geometry", saveGeometry()}, {"windowState", saveState()}}, "GUI");
}
}
@ -194,7 +194,7 @@ void MainWindow::deleteCurrentItem() {
if (currentIndex == QModelIndex()) {
qDebug() << "No current item. Nothing to remove.";
} else {
m_sortModel->removeRows(currentIndex.row(), 1);
m_proxyModel->removeRows(currentIndex.row(), 1);
}
}
@ -212,7 +212,7 @@ void MainWindow::deleteSelectedtItems() {
const int topRow = iter->top();
const int bottomRow = iter->bottom();
const int nRows = bottomRow - topRow + 1;
m_sortModel->removeRows(topRow, nRows);
m_proxyModel->removeRows(topRow, nRows);
}
}
}
@ -252,7 +252,6 @@ void MainWindow::importCSV() {
tr("CSV Files (*.csv)"));
if (QFileInfo::exists(csvFilePath)) {
m_core->importCSVFile(csvFilePath);
showStatusMessage(tr("Imported CSV file."));
} else {
qWarning() << "Selected CSV file path doesn't exist. Doing nothing...";
showStatusMessage(tr("Could't find CSV file!"));
@ -278,11 +277,98 @@ void MainWindow::exportCSV() {
}
}
void MainWindow::findItems() {
showStatusMessage(tr("Invoked 'Edit|Find items'"));
bool ok;
QString text = QInputDialog::getText(this, tr("Find items"), tr("Enter the text to search for:"),
QLineEdit::Normal, "", &ok);
if (ok && !text.isEmpty()) {
const QItemSelection itemsToSelect = m_proxyModel->findItems(text);
if (itemsToSelect.empty()) {
ui->tableView->setCurrentIndex(QModelIndex());
ui->tableView->selectionModel()->select(QModelIndex(), QItemSelectionModel::ClearAndSelect);
} else {
ui->tableView->setCurrentIndex(itemsToSelect.first().topLeft());
ui->tableView->selectionModel()->select(itemsToSelect, QItemSelectionModel::ClearAndSelect);
}
}
}
void MainWindow::fetchItems() {
showStatusMessage(tr("Invoked 'Server|Fetch items'"));
emit m_core->fetchItemsFromServer();
}
void MainWindow::postItems() {
showStatusMessage(tr("Invoked 'Server|Post items'"));
const QModelIndex currentIndex = ui->tableView->currentIndex();
const QByteArray jsonData = m_proxyModel->jsonDataForServer(currentIndex);
emit m_core->postItemToServer(jsonData);
}
void MainWindow::deleteItem() {
showStatusMessage(tr("Invoked 'Server|Delete items'"));
const QModelIndex currentIndex = ui->tableView->currentIndex();
// const QByteArray jsonData = m_proxyModel->jsonDataForServer(currentIndex);
const QString currentId = m_proxyModel->getUuid(currentIndex);
emit m_core->deleteItemFromServer(currentId);
}
void MainWindow::execSettingsDialog() {
showStatusMessage(tr("Invoked 'Tools|Settings'"));
QVariantMap oldSettings = m_core->getSettings("Server");
// SettingsDialog* settingsDialog = new SettingsDialog(settingMap, this);
SettingsDialog* settingsDialog = new SettingsDialog(this);
settingsDialog->createContent();
settingsDialog->fillContent(oldSettings);
int returnCode = settingsDialog->exec();
if (returnCode == QDialog::Accepted) {
qDebug() << "Settings dialog accepted, writing settings...";
const QVariantMap settings = settingsDialog->getSettings();
m_core->applySettings(settings, "Server");
// TODO use signal-slot connection Core::syncServerSetupChanged(bool enabled) ->
// MainWindow::onSyncServerSetupChanged(bool enabled)
// enableDisableServerActions();
} else {
qDebug() << "Settings dialog rejected";
}
delete settingsDialog;
}
void MainWindow::setupModelViews() {
// m_tableModel = m_core->getModel();
// ui->tableView->setModel(m_tableModel.get());
m_proxyModel = m_core->getSortFilterModel();
// TODO iterate over INT_ROLES and DOUBLE_ROLES to set spinbox delegate
/// setting number delegates to combo boxes
SpinboxDelegate* spinboxDelegate = new SpinboxDelegate(this);
const int amountColumn = GET_COLUMN_FOR_ROLE(AmountRole);
ui->tableView->setItemDelegateForColumn(amountColumn, spinboxDelegate);
const int factorColumn = GET_COLUMN_FOR_ROLE(FactorRole);
ui->tableView->setItemDelegateForColumn(factorColumn, spinboxDelegate);
// TODO iterate over TYPE_ROLES to set combobox delegate
/// setting type delegates to combo boxes
const int typeColumn = GET_COLUMN_FOR_ROLE(TypeRole);
ComboboxDelegate* shareTypeDelegate = new ComboboxDelegate(TYPES, this);
ui->tableView->setItemDelegateForColumn(typeColumn, shareTypeDelegate);
ui->tableView->setModel((QAbstractItemModel*)m_proxyModel.get());
ui->tableView->setSortingEnabled(true);
}
void MainWindow::createActions() {
// TODO add generic menu actions (file/new, edit/cut, ...)
createFileActions();
createUndoActions();
createEditActions();
createServerActions();
createToolsActions();
}
void MainWindow::createFileActions() {
@ -414,7 +500,7 @@ void MainWindow::createEditActions() {
ui->menu_Edit->addAction(m_openEditItemDialogAct.get());
m_deleteItemAct = make_unique<QAction>(tr("&Delete item(s)"), this);
m_deleteItemAct->setShortcuts(QKeySequence::Delete);
m_deleteItemAct->setShortcut(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::deleteCurrentItem);
@ -425,11 +511,38 @@ void MainWindow::createEditActions() {
m_findItemAct = make_unique<QAction>(tr("&Find item(s)"), this);
m_findItemAct->setShortcuts(QKeySequence::Find);
m_findItemAct->setStatusTip(tr("Opens a dialog to find item(s) by containing text"));
// connect(m_findItemAct, &QAction::triggered, this, &MainWindow::findItems);
m_findItemAct->setEnabled(false);
connect(m_findItemAct.get(), &QAction::triggered, this, &MainWindow::findItems);
ui->menu_Edit->addAction(m_findItemAct.get());
}
void MainWindow::createServerActions() {
m_fetchItemsAct = make_unique<QAction>(tr("&Fetch item(s)"), this);
m_fetchItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Down));
m_fetchItemsAct->setStatusTip(tr("Fetches all item on configured server"));
connect(m_fetchItemsAct.get(), &QAction::triggered, this, &MainWindow::fetchItems);
ui->menu_Server->addAction(m_fetchItemsAct.get());
m_postItemsAct = make_unique<QAction>(tr("&Post item(s)"), this);
m_postItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Up));
// m_postItemsAct->setStatusTip(tr("Posts the selected items on configured server"));
m_postItemsAct->setStatusTip(tr("Posts the current item on configured server"));
connect(m_postItemsAct.get(), &QAction::triggered, this, &MainWindow::postItems);
ui->menu_Server->addAction(m_postItemsAct.get());
m_deleteItemsAct = make_unique<QAction>(tr("&Delete item"), this);
m_deleteItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Backspace));
// m_deleteItemsAct->setStatusTip(tr("Deletes the selected items on configured server"));
m_deleteItemsAct->setStatusTip(tr("Deletes the current item on configured server"));
connect(m_deleteItemsAct.get(), &QAction::triggered, this, &MainWindow::deleteItem);
ui->menu_Server->addAction(m_deleteItemsAct.get());
}
void MainWindow::createToolsActions() {
QMenu* menu = ui->menu_Tools;
QAction* settingsAct = menu->addAction(tr("&Settings"), this, &MainWindow::execSettingsDialog);
settingsAct->setStatusTip(tr("Opens a dialog to configure applications settings."));
}
void MainWindow::createHelpMenu() {
QMenu* helpMenu = ui->menu_Help;
helpMenu->addSeparator();
@ -444,7 +557,7 @@ void MainWindow::createGuiDialogs() {
/// new item dialog
m_newItemDialog = make_unique<NewItemDialog>(this);
m_newItemDialog->createContent();
connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_sortModel.get(),
connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_proxyModel.get(),
&GeneralSortFilterModel::appendItems);
/// edit item dialog
m_editItemDialog = make_unique<EditItemDialog>(ui->tableView, this);

View File

@ -60,13 +60,22 @@ class MainWindow : public QMainWindow {
void importCSV();
void exportCSV();
/// 'Edit' slots
void findItems();
/// 'Server' slots
void fetchItems();
void postItems();
void deleteItem();
/// 'Tools' slots
void execSettingsDialog();
private:
Ui::MainWindow* ui;
// GenericCore* m_core;
unique_ptr<GenericCore> m_core;
shared_ptr<TableModel> m_tableModel;
shared_ptr<GeneralSortFilterModel> m_sortModel;
shared_ptr<GeneralSortFilterModel> m_proxyModel;
QUndoStack* m_modelUndoStack;
unique_ptr<QUndoView> m_modelUndoView;
@ -88,6 +97,11 @@ class MainWindow : public QMainWindow {
unique_ptr<QAction> m_openEditItemDialogAct;
unique_ptr<QAction> m_deleteItemAct;
unique_ptr<QAction> m_findItemAct;
/// Server actions
unique_ptr<QAction> m_fetchItemsAct;
unique_ptr<QAction> m_postItemsAct;
unique_ptr<QAction> m_deleteItemsAct;
/// View actions
unique_ptr<QAction> m_showModelUndoViewAct;
@ -96,10 +110,13 @@ class MainWindow : public QMainWindow {
unique_ptr<EditItemDialog> m_editItemDialog;
/// Setup functions
void setupModelViews();
void createActions();
void createFileActions();
void createUndoActions();
void createEditActions();
void createServerActions();
void createToolsActions();
void createHelpMenu();
void createGuiDialogs();
};

View File

@ -40,7 +40,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>25</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@ -64,9 +64,21 @@
</property>
<addaction name="actionCheck_for_update"/>
</widget>
<widget class="QMenu" name="menu_Server">
<property name="title">
<string>&amp;Server</string>
</property>
</widget>
<widget class="QMenu" name="menu_Tools">
<property name="title">
<string>&amp;Tools</string>
</property>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
<addaction name="menu_View"/>
<addaction name="menu_Server"/>
<addaction name="menu_Tools"/>
<addaction name="menu_Help"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>

View File

@ -1,164 +0,0 @@
#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H
/**
* Color and formatting codes
* @see: http://misc.flogisoft.com/bash/tip_colors_and_formatting
*/
#include <QObject>
// qSetMessagePattern("%{file}(%{line}): %{message}");
// qSetMessagePattern("%{type}(%{line}):\t%{message}");
// qSetMessagePattern("%{type}%{file}(%{line}):\t%{message}");
void consoleHandlerColoredVerbose(QtMsgType type,
const QMessageLogContext& context,
const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
// fprintf(stderr, "\033[1;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file,
// context.line, context.function, localMsg.constData()); // bold
fprintf(stderr, "\033[107;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[107;32mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[43;30mWarning: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[41;30mCritical: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[41;30mFatal: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
abort();
}
}
void consoleHandlerColoredVerboseInDarkTheme(QtMsgType type,
const QMessageLogContext& context,
const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
// fprintf(stderr, "\033[1;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file,
// context.line, context.function, localMsg.constData()); // bold
fprintf(stderr, "\033[107;37mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[107;32mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[43;30mWarning: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[41;30mCritical: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[41;30mFatal: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
abort();
}
}
void consoleHandlerColored(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "\033[1;30mDebug: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[0;30mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[1;33mWarning: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[31mCritical: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[31mFatal: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
abort();
}
}
void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file,
context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
abort();
}
}
#ifdef Q_OS_ANDROID
#include <android/log.h>
const char* const applicationName = "Pensieve";
void androidMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QString report = msg;
if (context.file && !QString(context.file).isEmpty()) {
report += " in file ";
report += QString(context.file);
report += " line ";
report += QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty()) {
report += +" function ";
report += QString(context.function);
}
const char* const local = report.toLocal8Bit().constData();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG, applicationName, local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO, applicationName, local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN, applicationName, local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR, applicationName, local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL, applicationName, local);
abort();
}
}
#endif
#endif // MESSAGEHANDLER_H

View File

@ -11,7 +11,9 @@
#include <QSpinBox>
#include <QTableView>
ItemDetailMapper::ItemDetailMapper(QWidget* parent)
#include "../widgethelper.h"
ItemDetailMapper::ItemDetailMapper(QTableView* tableView, QWidget* parent)
: QWidget{parent} {
/// model mapping
m_mapper = std::make_unique<QDataWidgetMapper>(this);
@ -24,76 +26,16 @@ ItemDetailMapper::ItemDetailMapper(QWidget* parent)
// m_mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
m_mapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
/// 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();
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);
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());
setupWidgets();
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());
setupConnections();
}
bool ItemDetailMapper::submit() { return m_mapper->submit(); }
@ -101,12 +43,13 @@ bool ItemDetailMapper::submit() { return m_mapper->submit(); }
void ItemDetailMapper::revert() { m_mapper->revert(); }
void ItemDetailMapper::onCurrentIndexChanged(const QModelIndex& current,
const QModelIndex& previous) {
const QModelIndex& /*previous*/) {
if (!isEnabled()) {
setEnabled(true);
}
m_mapper->setCurrentModelIndex(current);
updateButtons(current.row());
emitContentChanged(current);
}
void ItemDetailMapper::rowsInserted(const QModelIndex& parent, int start, int end) {
@ -115,13 +58,7 @@ void ItemDetailMapper::rowsInserted(const QModelIndex& parent, int start, int en
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();
clearControlWidgets();
}
updateButtons(m_mapper->currentIndex());
@ -147,3 +84,108 @@ void ItemDetailMapper::updateButtons(int row) {
m_previousButton->setEnabled(row > 0);
m_nextButton->setEnabled(row < m_model->rowCount() - 1);
}
void ItemDetailMapper::emitContentChanged(const QModelIndex& currentIndex) {
// BUG QR-Code isn't updated after changes through the ItemDetailMapper #18
QString toStringText = "";
if (currentIndex.isValid()) {
toStringText = currentIndex.data(ToStringRole).toString();
}
emit contentChanged(toStringText);
}
void ItemDetailMapper::setupConnections() {
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());
}
void ItemDetailMapper::setupNavigationButtons() {
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);
const int nRows = m_layout->rowCount();
m_layout->addWidget(m_previousButton, nRows, 0);
m_layout->addWidget(m_nextButton, nRows, 1);
}
void ItemDetailMapper::setupWidgets() {
m_layout = new QGridLayout();
for (int i = 0; i < USER_FACING_ROLES.size(); ++i) {
setupWidgetPairForColumn(i);
}
setupNavigationButtons();
setLayout(m_layout);
}
void ItemDetailMapper::setupWidgetPairForColumn(const int column) {
// REFACTOR consolidate ItemDetailMapper::setupWidgetPairForColumn(...)
// with NewItemDialog::setupWidgetPairForColumn(...)
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
QWidget* control = WidgetHelper::createControlWidget(role, this);
const QString string = QString("&%1").arg(GET_HEADER_FOR_COLUMN(column));
QLabel* label = new QLabel(string);
label->setBuddy(control);
m_layout->addWidget(label, column, 0);
m_layout->addWidget(control, column, 1);
m_controlWidgets.append(control);
if (TYPE_ROLES.contains(role)) {
m_mapper->addMapping(control, column, "currentText");
} else {
m_mapper->addMapping(control, column);
}
}
void ItemDetailMapper::clearControlWidgets() {
setEnabled(false);
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
clearLineEdit(i);
} else if (TYPE_ROLES.contains(role)) {
clearComboBox(i);
} else if (INT_ROLES.contains(role)) {
clearSpinBox(i);
} else if (DOUBLE_ROLES.contains(role)) {
clearSpinBox(i);
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
}
void ItemDetailMapper::clearLineEdit(const int column) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[column]);
if (lineEdit) {
lineEdit->clear();
}
}
void ItemDetailMapper::clearComboBox(const int column) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[column]);
if (comboBox) {
comboBox->setCurrentText("");
}
}
void ItemDetailMapper::clearSpinBox(const int column) {
QAbstractSpinBox* spinBox = dynamic_cast<QAbstractSpinBox*>(m_controlWidgets[column]);
if (spinBox) {
spinBox->clear();
}
}

View File

@ -1,9 +1,13 @@
#ifndef ITEMDETAILMAPPER_H
#define ITEMDETAILMAPPER_H
#include <QComboBox>
#include <QDataWidgetMapper>
#include <QStringListModel>
#include <QWidget>
#include "model/metadata.h"
class QGridLayout;
class QLabel;
class QLineEdit;
class QDoubleSpinBox;
@ -16,14 +20,13 @@ class QTableView;
class ItemDetailMapper : public QWidget {
Q_OBJECT
public:
explicit ItemDetailMapper(QWidget* parent = nullptr);
void setModelMappings(QTableView* tableView);
explicit ItemDetailMapper(QTableView* tableView, QWidget* parent = nullptr);
bool submit();
void revert();
signals:
void contentChanged(const QString text);
private slots:
void onCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous);
@ -32,6 +35,7 @@ class ItemDetailMapper : public QWidget {
void toPrevious();
void toNext();
void updateButtons(int row);
void emitContentChanged(const QModelIndex& currentIndex);
private:
/// *** members ***
@ -43,23 +47,22 @@ class ItemDetailMapper : public QWidget {
std::unique_ptr<QDataWidgetMapper> m_mapper;
/// GUI elements
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;
QGridLayout* m_layout;
QList<QWidget*> m_controlWidgets;
QPushButton* m_nextButton;
QPushButton* m_previousButton;
void setupConnections();
void setupNavigationButtons();
void setupWidgets();
void setupWidgetPairForColumn(const int column);
void clearControlWidgets();
void clearLineEdit(const int column);
void clearComboBox(const int column);
void clearSpinBox(const int column);
};
#endif // ITEMDETAILMAPPER_H

66
widgethelper.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "widgethelper.h"
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <QStringListModel>
QWidget* WidgetHelper::createControlWidget(const UserRoles role, QWidget* parent) {
QWidget* control;
if (STRING_ROLES.contains(role)) {
control = createLineEdit(role, parent);
} else if (TYPE_ROLES.contains(role)) {
control = createComboBox(role, parent);
} else if (NUMBER_ROLES.contains(role)) {
control = createSpinBox(role, parent);
} else {
qCritical() << QString("Unsupported role %1!!!").arg(role);
qDebug() << "Using line edit as well and pretend it's a string role...";
control = createLineEdit(role, parent);
}
return control;
}
WidgetHelper::WidgetHelper() {}
QWidget* WidgetHelper::createLineEdit(const UserRoles role, QWidget* /*parent*/) {
QLineEdit* lineEdit = new QLineEdit();
if (READ_ONLY_ROLES.contains(role)) {
lineEdit->setReadOnly(true);
}
return lineEdit;
}
QWidget* WidgetHelper::createSpinBox(const UserRoles role, QWidget* /*parent*/) {
QAbstractSpinBox* abstractSpinBox;
if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* spinBox = new QDoubleSpinBox();
spinBox->setMaximum(1000);
abstractSpinBox = spinBox;
} else {
QSpinBox* spinBox = new QSpinBox();
spinBox->setMaximum(1000);
abstractSpinBox = spinBox;
}
if (READ_ONLY_ROLES.contains(role)) {
abstractSpinBox->setReadOnly(true);
}
return abstractSpinBox;
}
QWidget* WidgetHelper::createComboBox(const UserRoles role, QWidget* parent) {
// TODO add support for read only type roles?
QStringListModel* typeModel;
if (role == TypeRole) {
typeModel = new QStringListModel(TYPES, parent);
} else {
qCritical() << "Unsupported type with role:" << role
<< "- Using string list model with only one empty string!";
typeModel = new QStringListModel({""}, parent);
}
QComboBox* comboBox = new QComboBox();
comboBox->setModel(typeModel);
comboBox->setCurrentText("");
return comboBox;
}

20
widgethelper.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef WIDGETHELPER_H
#define WIDGETHELPER_H
#include "model/metadata.h"
class QWidget;
class WidgetHelper {
public:
static QWidget* createControlWidget(const UserRoles role, QWidget* parent);
private:
explicit WidgetHelper();
static QWidget* createLineEdit(const UserRoles role, QWidget* parent);
static QWidget* createSpinBox(const UserRoles role, QWidget* parent);
static QWidget* createComboBox(const UserRoles role, QWidget* parent);
};
#endif // WIDGETHELPER_H

View File

@ -0,0 +1,57 @@
#include "comboboxdelegate.h"
#include <QComboBox>
#include <QStringListModel>
#include "model/metadata.h"
ComboboxDelegate::ComboboxDelegate(const QStringList items, QObject* parent)
: QStyledItemDelegate(parent)
, m_types(new QStringListModel(items)) {}
void ComboboxDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
}
QSize ComboboxDelegate::sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const {
return QStyledItemDelegate::sizeHint(option, index);
}
QWidget* ComboboxDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& /*option*/,
const QModelIndex& /*index*/) const {
QComboBox* editor = new QComboBox(parent);
editor->setModel(m_types);
return editor;
// return QStyledItemDelegate::createEditor(parent, option, index);
}
void ComboboxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
/// Get the value via index of the Model
const QAbstractItemModel* localModel = index.model();
const QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isType = TYPE_ROLES.contains(role);
/// Put the value into the SpinBox
if (isType) {
const QString valueString = index.model()->data(index, TypeRole).toString();
int value = TYPES.indexOf(valueString);
QComboBox* combobox = static_cast<QComboBox*>(editor);
combobox->setCurrentIndex(value);
// QStyledItemDelegate::setEditorData(editor, index);
} else {
qCritical() << "Could not find the correct type role for index:" << index << "!!!";
QComboBox* combobox = static_cast<QComboBox*>(editor);
combobox->setCurrentIndex(-1);
}
}
void ComboboxDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}

View File

@ -0,0 +1,31 @@
#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H
#include <QStyledItemDelegate>
class QStringListModel;
class ComboboxDelegate : public QStyledItemDelegate {
// TODO move source code files into subfolder "widgets/delegate"
Q_OBJECT
public:
explicit ComboboxDelegate(const QStringList items, QObject* parent = nullptr);
/// QAbstractItemDelegate interface
public:
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex&) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const override;
private:
QStringListModel* m_types = nullptr;
};
#endif // COMBOBOXDELEGATE_H

View File

@ -0,0 +1,69 @@
#include "spinboxdelegate.h"
#include <QSpinBox>
#include "model/metadata.h"
SpinboxDelegate::SpinboxDelegate(QObject* parent)
: QStyledItemDelegate(parent) {}
void SpinboxDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
}
QSize SpinboxDelegate::sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const {
return QStyledItemDelegate::sizeHint(option, index);
}
QWidget* SpinboxDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& /*option*/,
const QModelIndex& index) const {
const QAbstractItemModel* localModel = index.model();
QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isInt = INT_ROLES.contains(role);
if (isInt) {
QSpinBox* editor = new QSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(23000);
return editor;
} else {
QDoubleSpinBox* editor = new QDoubleSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(23000);
return editor;
}
// return QStyledItemDelegate::createEditor(parent, option, index);
}
void SpinboxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
// Get the value via index of the Model
const QAbstractItemModel* localModel = index.model();
QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isInt = INT_ROLES.contains(role);
if (isInt) {
int value = index.model()->data(index, Qt::EditRole).toInt();
// Put the value into the SpinBox
QSpinBox* spinbox = static_cast<QSpinBox*>(editor);
spinbox->setValue(value);
} else {
// Put the value into the SpinBox
qreal value = index.model()->data(index, Qt::EditRole).toReal();
QDoubleSpinBox* spinbox = static_cast<QDoubleSpinBox*>(editor);
spinbox->setValue(value);
}
// QStyledItemDelegate::setEditorData(editor, index);
}
void SpinboxDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}

View File

@ -0,0 +1,27 @@
#ifndef SPINBOXDELEGATE_H
#define SPINBOXDELEGATE_H
#include <QStyledItemDelegate>
class SpinboxDelegate : public QStyledItemDelegate {
// TODO move source code files into subfolder "widgets/delegate"
Q_OBJECT
public:
explicit SpinboxDelegate(QObject* parent = nullptr);
/// QAbstractItemDelegate interface
public:
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const override;
};
#endif // SPINBOXDELEGATE_H