rearch, add note, add sqlite

This commit is contained in:
2026-03-22 17:54:37 +01:00
parent 523a4085cc
commit 8b9d388b1d
20 changed files with 975 additions and 233 deletions

191
src/core/listservice.cpp Normal file
View File

@@ -0,0 +1,191 @@
#include "src/core/listservice.h"
#include <QDir>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QStandardPaths>
#include <QVariant>
#include <stdexcept>
namespace {
const QString kConnectionName = "todo_connection";
const QString kDatabaseFileName = "todo.sqlite3";
QString toDbUuid(const QUuid &uuid)
{
return uuid.toString(QUuid::WithoutBraces);
}
std::runtime_error makeSqlException(const QString &context, const QString &error)
{
return std::runtime_error(QString("%1: %2").arg(context, error).toStdString());
}
QSqlDatabase getOpenDatabase()
{
QSqlDatabase db;
if (QSqlDatabase::contains(kConnectionName)) {
db = QSqlDatabase::database(kConnectionName);
} else {
const QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (appDataPath.isEmpty()) {
throw std::runtime_error("Unable to resolve AppDataLocation for SQLite database");
}
QDir dir(appDataPath);
if (!dir.exists() && !dir.mkpath(".")) {
throw std::runtime_error("Unable to create application data directory for SQLite database");
}
db = QSqlDatabase::addDatabase("QSQLITE", kConnectionName);
db.setDatabaseName(dir.filePath(kDatabaseFileName));
}
if (!db.isOpen() && !db.open()) {
throw makeSqlException("Failed to open SQLite database", db.lastError().text());
}
QSqlQuery pragma(db);
if (!pragma.exec("PRAGMA foreign_keys = ON;")) {
throw makeSqlException("Failed to enable SQLite foreign keys", pragma.lastError().text());
}
return db;
}
void ensureSchema(QSqlDatabase &db)
{
QSqlQuery query(db);
if (!query.exec("CREATE TABLE IF NOT EXISTS lists ("
"id TEXT PRIMARY KEY,"
"name TEXT NOT NULL"
");")) {
throw makeSqlException("Failed to create lists table", query.lastError().text());
}
if (!query.exec("CREATE TABLE IF NOT EXISTS notes ("
"id TEXT PRIMARY KEY,"
"list_id TEXT NOT NULL,"
"content TEXT NOT NULL,"
"finished BOOL NOT NULL,"
"FOREIGN KEY(list_id) REFERENCES lists(id) ON DELETE CASCADE"
");")) {
throw makeSqlException("Failed to create notes table", query.lastError().text());
}
if (!query.exec("CREATE INDEX IF NOT EXISTS idx_notes_list_id ON notes(list_id);")) {
throw makeSqlException("Failed to create notes index", query.lastError().text());
}
}
} // namespace
ListService* ListService::_instance = nullptr;
ListService::ListService()
{
QSqlDatabase db = getOpenDatabase();
ensureSchema(db);
}
ListService *ListService::getInstance()
{
if (_instance == nullptr) {
_instance = new ListService();
}
return _instance;
}
List ListService::create(QString name)
{
List list(name);
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("INSERT INTO lists(id, name) VALUES(:id, :name);");
query.bindValue(":id", toDbUuid(list.getUUID()));
query.bindValue(":name", list.getName());
if (!query.exec()) {
throw makeSqlException("Failed to insert list", query.lastError().text());
}
emit onListCreated(list);
return list;
}
List ListService::update(QUuid uuid, QString newName)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("UPDATE lists SET name = :name WHERE id = :id;");
query.bindValue(":id", toDbUuid(uuid));
query.bindValue(":name", newName);
if (!query.exec()) {
throw makeSqlException("Failed to update list", query.lastError().text());
}
if (query.numRowsAffected() <= 0) {
throw std::runtime_error("List not found");
}
List updated(uuid, newName);
emit onListUpdated(updated);
return updated;
}
void ListService::remove(QUuid uuid)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("DELETE FROM lists WHERE id = :id;");
query.bindValue(":id", toDbUuid(uuid));
if (!query.exec()) {
throw makeSqlException("Failed to delete list", query.lastError().text());
}
emit onListDeleted(uuid);
}
QList<List> ListService::getAll()
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
if (!query.exec("SELECT id, name FROM lists ORDER BY name COLLATE NOCASE ASC;")) {
throw makeSqlException("Failed to read lists", query.lastError().text());
}
QList<List> lists;
while (query.next()) {
const QUuid uuid(query.value(0).toString());
const QString name = query.value(1).toString();
lists.append(List(uuid, name));
}
return lists;
}
List ListService::getByUuid(QUuid uuid)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("SELECT name FROM lists WHERE id = :id LIMIT 1;");
query.bindValue(":id", toDbUuid(uuid));
if (!query.exec()) {
throw makeSqlException("Failed to read list by uuid", query.lastError().text());
}
if (!query.next()) {
throw std::runtime_error("List not found");
}
return List(uuid, query.value(0).toString());
}

34
src/core/listservice.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef LISTSERVICE_H
#define LISTSERVICE_H
#include <QObject>
#include <QList>
#include <QUuid>
#include "src/obj/list.h"
class ListService : public QObject
{
Q_OBJECT
private:
ListService();
static ListService* _instance;
public:
static ListService* getInstance();
List create(QString name);
List update(QUuid uuid, QString newName);
void remove(QUuid uuid);
QList<List> getAll();
List getByUuid(QUuid uuid);
signals:
void onListCreated(List value);
void onListUpdated(List value);
void onListDeleted(QUuid uuid);
};
#endif // LISTSERVICE_H

248
src/core/noteservice.cpp Normal file
View File

@@ -0,0 +1,248 @@
#include "noteservice.h"
#include <QDir>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QStandardPaths>
#include <QVariant>
#include <stdexcept>
namespace {
const QString kConnectionName = "todo_connection";
const QString kDatabaseFileName = "todo.sqlite3";
QString toDbUuid(const QUuid &uuid)
{
return uuid.toString(QUuid::WithoutBraces);
}
std::runtime_error makeSqlException(const QString &context, const QString &error)
{
return std::runtime_error(QString("%1: %2").arg(context, error).toStdString());
}
QSqlDatabase getOpenDatabase()
{
QSqlDatabase db;
if (QSqlDatabase::contains(kConnectionName)) {
db = QSqlDatabase::database(kConnectionName);
} else {
const QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (appDataPath.isEmpty()) {
throw std::runtime_error("Unable to resolve AppDataLocation for SQLite database");
}
QDir dir(appDataPath);
if (!dir.exists() && !dir.mkpath(".")) {
throw std::runtime_error("Unable to create application data directory for SQLite database");
}
db = QSqlDatabase::addDatabase("QSQLITE", kConnectionName);
db.setDatabaseName(dir.filePath(kDatabaseFileName));
}
if (!db.isOpen() && !db.open()) {
throw makeSqlException("Failed to open SQLite database", db.lastError().text());
}
QSqlQuery pragma(db);
if (!pragma.exec("PRAGMA foreign_keys = ON;")) {
throw makeSqlException("Failed to enable SQLite foreign keys", pragma.lastError().text());
}
return db;
}
void ensureSchema(QSqlDatabase &db)
{
QSqlQuery query(db);
if (!query.exec("CREATE TABLE IF NOT EXISTS lists ("
"id TEXT PRIMARY KEY,"
"name TEXT NOT NULL"
");")) {
throw makeSqlException("Failed to create lists table", query.lastError().text());
}
if (!query.exec("CREATE TABLE IF NOT EXISTS notes ("
"id TEXT PRIMARY KEY,"
"list_id TEXT NOT NULL,"
"content TEXT NOT NULL,"
"finished BOOL NOT NULL,"
"FOREIGN KEY(list_id) REFERENCES lists(id) ON DELETE CASCADE"
");")) {
throw makeSqlException("Failed to create notes table", query.lastError().text());
}
if (!query.exec("CREATE INDEX IF NOT EXISTS idx_notes_list_id ON notes(list_id);")) {
throw makeSqlException("Failed to create notes index", query.lastError().text());
}
}
void ensureListExists(QSqlDatabase &db, const QUuid &listUuid)
{
QSqlQuery query(db);
query.prepare("SELECT 1 FROM lists WHERE id = :id LIMIT 1;");
query.bindValue(":id", toDbUuid(listUuid));
if (!query.exec()) {
throw makeSqlException("Failed to validate list existence", query.lastError().text());
}
if (!query.next()) {
throw std::runtime_error("Parent list not found");
}
}
} // namespace
NoteService* NoteService::_instance = nullptr;
NoteService::NoteService()
{
QSqlDatabase db = getOpenDatabase();
ensureSchema(db);
}
NoteService *NoteService::getInstance()
{
if (_instance == nullptr) {
_instance = new NoteService();
}
return _instance;
}
QUuid NoteService::create(QUuid listUuid, QString value)
{
QSqlDatabase db = getOpenDatabase();
ensureListExists(db, listUuid);
const QUuid newNoteUuid = QUuid::createUuid();
QSqlQuery query(db);
query.prepare("INSERT INTO notes(id, list_id, content, finished) VALUES(:id, :list_id, :content, false);");
query.bindValue(":id", toDbUuid(newNoteUuid));
query.bindValue(":list_id", toDbUuid(listUuid));
query.bindValue(":content", value);
if (!query.exec()) {
throw makeSqlException("Failed to insert note", query.lastError().text());
}
Note n = Note(listUuid, newNoteUuid, value, false);
emit onNoteCreated(n);
return newNoteUuid;
}
QString NoteService::update(QUuid listUuid, QUuid noteUuid, QString newValue)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("UPDATE notes SET content = :content WHERE id = :id AND list_id = :list_id;");
query.bindValue(":id", toDbUuid(noteUuid));
query.bindValue(":list_id", toDbUuid(listUuid));
query.bindValue(":content", newValue);
if (!query.exec()) {
throw makeSqlException("Failed to update note", query.lastError().text());
}
if (query.numRowsAffected() <= 0) {
throw std::runtime_error("Note not found");
}
Note n = Note(listUuid, noteUuid, newValue, false);
emit onNoteUpdated(n);
return newValue;
}
void NoteService::setFinishedValue(QUuid listUuid, QUuid noteUuid, bool isFinished)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("UPDATE notes SET finished = :finished WHERE id = :id AND list_id = :list_id;");
query.bindValue(":id", toDbUuid(noteUuid));
query.bindValue(":list_id", toDbUuid(listUuid));
query.bindValue(":finished", isFinished);
if (!query.exec()) {
throw makeSqlException("Failed to update note", query.lastError().text());
}
if (query.numRowsAffected() <= 0) {
throw std::runtime_error("Note not found");
}
std::optional<Note> note = getByUUID(noteUuid);
if (!note.has_value()) {
throw std::runtime_error("database integrity corrupted");
}
Note n = Note(listUuid, noteUuid, note.value().getContent(), isFinished);
emit onNoteUpdated(n);
}
void NoteService::remove(QUuid listUuid, QUuid noteUuid)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("DELETE FROM notes WHERE id = :id AND list_id = :list_id;");
query.bindValue(":id", toDbUuid(noteUuid));
query.bindValue(":list_id", toDbUuid(listUuid));
if (!query.exec()) {
throw makeSqlException("Failed to delete note", query.lastError().text());
}
emit onNoteDeleted(listUuid, noteUuid);
}
QList<Note> NoteService::getByList(QUuid listUuid)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("SELECT id, content, finished FROM notes WHERE list_id = :list_id;");
query.bindValue(":list_id", toDbUuid(listUuid));
if (!query.exec()) {
throw makeSqlException("Failed to read notes by list", query.lastError().text());
}
QList<Note> notes;
while (query.next()) {
const QUuid noteId(query.value(0).toString());
const QString content = query.value(1).toString();
const bool finished = query.value(2).toBool();
notes.append(Note(listUuid, noteId, content, finished));
}
return notes;
}
std::optional<Note> NoteService::getByUUID(QUuid noteUuid)
{
QSqlDatabase db = getOpenDatabase();
QSqlQuery query(db);
query.prepare("SELECT id, content, finished, list_id FROM notes WHERE id = :id;");
query.bindValue(":id", toDbUuid(noteUuid));
if (!query.exec()) {
throw makeSqlException("Failed to read note by uuid", query.lastError().text());
}
if (query.next()) {
const QUuid noteId(query.value(0).toString());
const QString content = query.value(1).toString();
const bool finished = query.value(2).toBool();
const QUuid listUuid(query.value(3).toString());
return std::optional(Note(listUuid, noteId, content, finished));
}
return std::nullopt;
}

37
src/core/noteservice.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef NOTESERVICE_H
#define NOTESERVICE_H
#include <QObject>
#include <QList>
#include <QString>
#include <QUuid>
#include <optional>
#include "src/obj/note.h"
class NoteService : public QObject
{
Q_OBJECT
private:
NoteService();
static NoteService* _instance;
public:
static NoteService* getInstance();
QUuid create(QUuid listUuid, QString value);
QString update(QUuid listUuid, QUuid noteUuid, QString newValue);
void setFinishedValue(QUuid listUuid, QUuid noteUuid, bool isFinished);
void remove(QUuid listUuid, QUuid noteUuid);
QList<Note> getByList(QUuid listUuid);
std::optional<Note> getByUUID(QUuid noteUuid);
signals:
void onNoteCreated(Note value);
void onNoteUpdated(Note value);
void onNoteDeleted(QUuid listUuid, QUuid noteUuid);
};
#endif // NOTESERVICE_H

View File

@@ -0,0 +1,52 @@
#include "inputdialog.h"
#include "ui_inputdialog.h"
InputDialog::InputDialog(QWidget *parent, QString title, QString headline, QString message) : QDialog(parent), ui(new Ui::InputDialog)
{
ui->setupUi(this);
this->setWindowTitle(title);
ui->errorLabel->setVisible(false);
ui->headline->setText(headline);
ui->message->setText(message);
}
InputDialog::~InputDialog()
{
delete ui;
}
QString InputDialog::getInput()
{
if (this->result() != Accepted) {
return "";
}
return this->ui->lineEdit->text();
}
void InputDialog::on_buttonBox_accepted()
{
if (ui->lineEdit->text().isEmpty()) {
setErrorLabel("Empty name is not valid");
return;
}
accept();
}
void InputDialog::setErrorLabel(QString error)
{
ui->errorLabel->setText(error);
ui->errorLabel->setVisible(true);
}
void InputDialog::clearErrorLabel()
{
ui->errorLabel->setVisible(false);
ui->errorLabel->setText("error");
}
void InputDialog::on_buttonBox_clicked(QAbstractButton *button)
{
if (ui->buttonBox->standardButton(button) == QDialogButtonBox::Discard) {
reject();
}
}

View File

@@ -0,0 +1,33 @@
#ifndef INPUTDIALOG_H
#define INPUTDIALOG_H
#include <QDialog>
#include <QAbstractButton>
namespace Ui {
class InputDialog;
}
class InputDialog : public QDialog
{
Q_OBJECT
public:
InputDialog(QWidget *parent, QString title, QString headline, QString message);
~InputDialog();
QString getInput();
private slots:
void on_buttonBox_accepted();
void on_buttonBox_clicked(QAbstractButton *button);
private:
Ui::InputDialog *ui;
void setErrorLabel(QString error);
void clearErrorLabel();
};
#endif // INPUTDIALOG_H

View File

@@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InputDialog</class>
<widget class="QDialog" name="InputDialog">
<property name="windowModality">
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>454</width>
<height>176</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>454</width>
<height>176</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>454</width>
<height>176</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(249, 255, 251);</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="headline">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>22</pointsize>
<fontweight>Light</fontweight>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(138, 176, 106);
padding: 6px;
padding-left: 20 px;
padding-right: 20px;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="message">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(13, 13, 13);
padding: 6px;
padding-left: 20px;
padding-right: 20px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="styleSheet">
<string notr="true">margin: 6px;
margin-left: 20px;
margin-right: 20px;
background-color: rgb(243, 243, 243);
padding: 4px;
color: rgb(13, 13, 13);
border: 2px solid rgb(230, 230, 230);</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="errorLabel">
<property name="styleSheet">
<string notr="true">color: rgb(255, 89, 92);</string>
</property>
<property name="text">
<string>error</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="styleSheet">
<string notr="true">QPushButton {
border: none;
color: rgb(12, 12, 12);
background-color: rgb(217, 217, 217);
padding: 6px;
}
QPushButton::pressed {
background-color: ;
background-color: rgb(192, 192, 192);
}</string>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Discard|QDialogButtonBox::StandardButton::Save</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

183
src/gui/mainwindow.cpp Normal file
View File

@@ -0,0 +1,183 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "src/gui/dialog/input/inputdialog.h"
#include "src/core/listservice.h"
#include "src/core/noteservice.h"
#include <QDialogButtonBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
/*
* Events
*/
// ui
connect(ui->addListButton, &QPushButton::clicked, this, &MainWindow::openCreateListDialog);
connect(ui->lists, &QListWidget::currentRowChanged, this, &MainWindow::onListSelected);
connect(ui->saveNoteButton, &QPushButton::clicked, this, &MainWindow::onSaveNoteButtonClicked);
connect(ui->notes, &QListWidget::itemChanged, this, &MainWindow::onNoteChanged);
// services
connect(ListService::getInstance(), &ListService::onListCreated, this, &MainWindow::onListCreated);
connect(ListService::getInstance(), &ListService::onListUpdated, this, &MainWindow::onListCreated);
connect(NoteService::getInstance(), &NoteService::onNoteCreated, this, &MainWindow::onNoteCreated);
preload();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::openCreateListDialog(bool _)
{
// create the input dialog
InputDialog d = InputDialog(this, "Create a list", "New List", "Give a name to this list");
auto res = d.exec();
// execute, ignore if not saved
if (res != QDialog::Accepted) {
return;
}
QString newListName = d.getInput();
ListService::getInstance()->create(newListName);
}
void MainWindow::onListCreated(List value)
{
QListWidgetItem* item = new QListWidgetItem();
item->setText(value.getName());
item->setData(Qt::UserRole, QVariant(value.getUUID()));
this->ui->lists->addItem(item);
}
void MainWindow::onListUpdate(List value)
{
QListWidgetItem *item = nullptr;
for (int i = 0; i > ui->lists->count(); i++) {
item = ui->lists->item(i);
if (item->data(Qt::UserRole).toUuid() == value.getUUID()) {
item->setText(value.getName());
return;
}
}
}
void MainWindow::onNoteCreated(Note value)
{
QListWidgetItem* item = new QListWidgetItem();
item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsEditable);
item->setCheckState((value.isFinished() ? Qt::Checked : Qt::Unchecked));
item->setText(value.getContent());
item->setData(Qt::UserRole, QVariant(value.getUUID()));
this->ui->notes->addItem(item);
}
void MainWindow::onNoteUpdated(Note value)
{
QListWidgetItem *item = ui->lists->currentItem();
if (item == nullptr) {
qDebug() << "item null";
return;
}
if (item->data(Qt::UserRole).toUuid() != value.getParentUUID()) {
qDebug() << "item uuid not matching";
return;
}
for (int i = 0; i > ui->notes->count(); i++) {
item = ui->notes->item(i);
if (item->data(Qt::UserRole).toUuid() == value.getUUID()) {
item->setCheckState((value.isFinished() ? Qt::Checked : Qt::Unchecked));
item->setText(value.getContent());
return;
}
}
}
void MainWindow::onListSelected(int i)
{
if (i == -1) {
ui->notes->clear();
ui->newNoteEdit->setDisabled(true);
ui->saveNoteButton->setDisabled(true);
ui->notes->setDisabled(true);
return;
}
QListWidgetItem *item = ui->lists->item(i);
QVariant uuid = item->data(Qt::UserRole);
QList<Note> notes = NoteService::getInstance()->getByList(uuid.toUuid());
ui->notes->clear();
foreach (Note n, notes) {
onNoteCreated(n);
}
ui->newNoteEdit->setEnabled(true);
ui->saveNoteButton->setEnabled(true);
ui->notes->setEnabled(true);
}
void MainWindow::onSaveNoteButtonClicked(bool _)
{
QString content = ui->newNoteEdit->text();
if (content.isEmpty()) {
return;
}
int li = ui->lists->currentRow();
if (li == -1) {
return;
}
QListWidgetItem *item = ui->lists->item(li);
QVariant listUUID = item->data(Qt::UserRole);
NoteService::getInstance()->create(listUUID.toUuid(), content);
ui->newNoteEdit->clear();
}
void MainWindow::onNoteChanged(QListWidgetItem *item)
{
NoteService *service = NoteService::getInstance();
QString content = item->text();
QVariant uuid = item->data(Qt::UserRole);
std::optional<Note> opt = service->getByUUID(uuid.toUuid());
if (opt.has_value()) {
Note note = opt.value();
if (content.isEmpty()) {
service->remove(note.getParentUUID(), note.getUUID());
} else {
service->update(note.getParentUUID(), note.getUUID(), content);
service->setFinishedValue(note.getParentUUID(), note.getUUID(), item->checkState() == Qt::Checked ? true : false);
}
}
}
void MainWindow::preload()
{
QList<List> lists = ListService::getInstance()->getAll();
foreach (List l, lists) {
onListCreated(l);
}
if (lists.count() > 0) {
ui->lists->setCurrentItem(ui->lists->item(0));
}
}

40
src/gui/mainwindow.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QListWidgetItem>
#include "src/obj/list.h"
#include "src/obj/note.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
private slots:
void openCreateListDialog(bool);
void onListCreated(List value);
void onListUpdate(List value);
void onNoteCreated(Note value);
void onNoteUpdated(Note value);
void onListSelected(int i);
void onSaveNoteButtonClicked(bool);
void onNoteChanged(QListWidgetItem*);
private:
Ui::MainWindow *ui;
void preload();
};
#endif // MAINWINDOW_H

366
src/gui/mainwindow.ui Normal file
View File

@@ -0,0 +1,366 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1032</width>
<height>629</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1032</width>
<height>629</height>
</size>
</property>
<property name="windowTitle">
<string>ToDo</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(249, 255, 251);</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>50</height>
</size>
</property>
<property name="font">
<font>
<pointsize>28</pointsize>
<fontweight>DemiBold</fontweight>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(119, 167, 92);
border: none;
padding-left: 2px;
border-bottom: 2px solid rgb(242, 242, 242)</string>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>ToDo</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="lists">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
<fontweight>Light</fontweight>
</font>
</property>
<property name="styleSheet">
<string notr="true">QListView {
show-decoration-selected: 1; /* make the selection span the entire width of the view */
background-color: rgb(119, 167, 92);
border: none;
padding: 2px;
padding-top: 14px;
}
QListView::item {
padding: 8px;
border: none;
}
QListView::item:selected {
background-color: rgb(170, 216, 130);
}
QListView::item:selected:!active {
background-color: rgb(170, 216, 130);
}
QListView::item:selected:active {
background-color: rgb(170, 216, 130);
}
QListView::item:hover {
background-color: rgb(152, 193, 116);
}</string>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="autoScrollMargin">
<number>2</number>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::IgnoreAction</enum>
</property>
<property name="supportedDragActions">
<set>Qt::DropAction::IgnoreAction</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="horizontalWidget" native="true">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(138, 176, 106);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="addListButton">
<property name="toolTip">
<string>New list</string>
</property>
<property name="styleSheet">
<string notr="true">border: none;
padding: 6px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentNew"/>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="notes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QListView {
show-decoration-selected: 1; /* make the selection span the entire width of the view */
background-color: rgb(242, 242, 242);
border: none;
padding: 2px;
color: rgb(13, 13, 13);
}
QListView::item {
background-color: rgb(231, 231, 231);
margin: 10px;
padding: 10px;
border: none;
}
QListView::item:alternate {
background: #EEEEEE;
}
QListView::item:selected {
background-color: rgb(154, 154, 154);
}
QListView::item:selected:!active {
background: none;
}
QListView::item:hover {
background-color: rgb(204, 204, 204);
}</string>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::AllEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::IgnoreAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SelectionMode::MultiSelection</enum>
</property>
<property name="supportedDragActions">
<set>Qt::DropAction::IgnoreAction</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="newNoteEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">margin: 20px;
background-color: rgb(243, 243, 243);
padding: 8px;
color: rgb(13, 13, 13);
border: none;</string>
</property>
<property name="placeholderText">
<string>Enter your new note here</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveNoteButton">
<property name="styleSheet">
<string notr="true">background-color: rgb(170, 216, 130);
color: rgb(242, 242, 242);
border: none;
padding: 8px;</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="QIcon::ThemeIcon::MailSend"/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

23
src/main.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "src/gui/mainwindow.h"
#include <QApplication>
#include <QLocale>
#include <QTranslator>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTranslator translator;
const QStringList uiLanguages = QLocale::system().uiLanguages();
for (const QString &locale : uiLanguages) {
const QString baseName = "ToDo_" + QLocale(locale).name();
if (translator.load(":/i18n/" + baseName)) {
a.installTranslator(&translator);
break;
}
}
MainWindow w;
w.show();
return QCoreApplication::exec();
}

34
src/obj/list.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "list.h"
List::List(QUuid uuid, QString name)
{
this->uuid = uuid;
this->name = name;
}
List::List(QString name)
{
this->uuid = QUuid::createUuid();
this->name = name;
}
QUuid List::getUUID()
{
return this->uuid;
}
QString List::getName()
{
return this->name;
}
void List::setName(QString value)
{
this->name = value;
}
List List::duplicate()
{
List l = List(this->getName() + " (copy)");
return l;
}

26
src/obj/list.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef LIST_H
#define LIST_H
#include <QUuid>
#include <QString>
class List
{
private:
QUuid uuid;
QString name;
public:
List() {}
List(QUuid uuid, QString name);
List(QString name);
QUuid getUUID();
QString getName();
void setName(QString value);
List duplicate();
};
#endif // LIST_H

52
src/obj/note.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "note.h"
Note::Note(QUuid listParent, QUuid noteID, QString content, bool finished)
{
this->id = noteID;
this->parent = listParent;
this->content = content;
this->finished = finished;
}
Note::Note(QUuid listParent, QString content)
{
this->id = QUuid::createUuid();
this->parent = listParent;
this->content = content;
this->finished = false;
}
QUuid Note::getUUID()
{
return this->id;
}
QUuid Note::getParentUUID()
{
return this->parent;
}
QString Note::getContent()
{
return this->content;
}
bool Note::isFinished()
{
return this->finished;
}
void Note::setContent(QString content)
{
this->content = content;
}
void Note::setParentUUID(QUuid parent)
{
this->parent = parent;
}
void Note::setFinished(bool value)
{
this->finished = value;
}

30
src/obj/note.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef NOTE_H
#define NOTE_H
#include <QUuid>
#include <QString>
class Note
{
public:
Note() {};
Note(QUuid listParent, QUuid noteID, QString content, bool finished);
Note(QUuid listParent, QString content);
QUuid getUUID();
QUuid getParentUUID();
QString getContent();
bool isFinished();
void setContent(QString content);
void setParentUUID(QUuid parent);
void setFinished(bool value);
private:
QUuid id;
QUuid parent;
QString content;
bool finished;
};
#endif // NOTE_H