mirror of
https://codeberg.org/vyn/mirai.git
synced 2025-07-03 01:33:19 +00:00
first commit
This commit is contained in:
commit
3e7d8b4b70
43 changed files with 2681 additions and 0 deletions
142
src/Backend.cpp
Normal file
142
src/Backend.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "Backend.h"
|
||||
|
||||
Backend::Backend() {
|
||||
std::cout << "Backend created" << std::endl;
|
||||
QDir().mkdir(QDir::homePath() + "/.config/mirai");
|
||||
QFile loadFile(QDir::homePath() + "/.config/mirai/config.json");
|
||||
|
||||
if (!loadFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Couldn't find existing config file";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QByteArray loadData = loadFile.readAll();
|
||||
QJsonDocument json = QJsonDocument::fromJson(loadData);
|
||||
loadFile.close();
|
||||
if (!json.isObject()) {
|
||||
qWarning() << "config.json is not a valid config file";
|
||||
exit(1);
|
||||
}
|
||||
QJsonObject jsonRootObject = json.object();
|
||||
|
||||
auto jsonFilesPath = json["files"];
|
||||
if (!jsonFilesPath.isArray()) {
|
||||
qWarning() << "config.json should contains a 'files' string array";
|
||||
exit(1);
|
||||
}
|
||||
QString filePath = jsonFilesPath.toArray()[0].toString();
|
||||
mirai.loadFile(filePath.toStdString());
|
||||
view = mirai.getTasks();
|
||||
rebuildQMLTasksList();
|
||||
}
|
||||
|
||||
void Backend::addTodo(QString newTodo, QString date) {
|
||||
mirai.addTask(newTodo.toStdString(), date.toStdString());
|
||||
mirai.save();
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::addTagFilter(QString tag) {
|
||||
view.lock()->addTagFilter(tag.toStdString());
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::removeTagFilter(QString tag) {
|
||||
view.lock()->removeTagFilter(tag.toStdString());
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::removeFilters() {
|
||||
view.lock()->removeFilters();
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::updateTodo(int todoIndex, QString state, QString text, QString date) {
|
||||
QMLTaskItem& taskItem = QMLTasks[todoIndex];
|
||||
taskItem.taskItem->state = state == "TODO" ? mirai::TODO : mirai::DONE;
|
||||
if (state == "DONE") {
|
||||
taskItem.taskItem->markAsDone();
|
||||
} else if (state == "TODO") {
|
||||
taskItem.taskItem->markAsUndone();
|
||||
}
|
||||
taskItem.taskItem->setText(text.toStdString());
|
||||
taskItem.taskItem->setDate(date.toStdString());
|
||||
mirai.save();
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::removeTodo(int todoIndex) {
|
||||
QMLTaskItem& taskItem = QMLTasks[todoIndex];
|
||||
mirai.removeTask(taskItem.taskItem);
|
||||
mirai.save();
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::hideCompletedTasks(bool shouldHide) {
|
||||
shouldHideCompletedTasks_ = shouldHide;
|
||||
rebuildQMLTasksList();
|
||||
emit tasksChanged();
|
||||
}
|
||||
|
||||
void Backend::rebuildQMLTasksList() {
|
||||
QMLTasks.clear();
|
||||
std::string lastDate = "";
|
||||
std::time_t t = std::time(nullptr);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
std::stringstream currentDate;
|
||||
currentDate << std::put_time(&tm, "%Y-%m-%d");
|
||||
for (int i = 0; i < view.lock()->count(); ++i) {
|
||||
mirai::TaskItem& task = (*view.lock())[i];
|
||||
if (shouldHideCompletedTasks_ && task.getState() == mirai::DONE && task.hasDate() && task.getDate() < currentDate.str()) {
|
||||
continue;
|
||||
}
|
||||
bool shouldShowDate = false;
|
||||
if (lastDate != task.date) {
|
||||
lastDate = task.date;
|
||||
shouldShowDate = true;
|
||||
}
|
||||
QMLTasks.push_back({
|
||||
.taskItem = &task,
|
||||
.shouldShowDate = shouldShowDate
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
QMLTags.clear();
|
||||
for (auto& tag : mirai.getTags()) {
|
||||
QMLTags.push_back(QString::fromStdString(tag));
|
||||
}
|
||||
|
||||
QMLActiveTagsFilter.clear();
|
||||
for (auto& activeTagFilter: view.lock()->getActiveTagsFilter()) {
|
||||
QMLActiveTagsFilter.push_back(QString::fromStdString(activeTagFilter));
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Backend::getTasks() {
|
||||
return QVariant::fromValue(QMLTasks);
|
||||
}
|
||||
|
||||
QVariant Backend::getTags() {
|
||||
return QVariant::fromValue(QMLTags);
|
||||
}
|
||||
|
||||
QVariant Backend::getActiveTagsFilter() {
|
||||
return QVariant::fromValue(QMLActiveTagsFilter);
|
||||
}
|
||||
|
||||
bool Backend::shouldHideCompletedTasks() {
|
||||
return shouldHideCompletedTasks_;
|
||||
}
|
75
src/Backend.h
Normal file
75
src/Backend.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef BACKEND_H
|
||||
#define BACKEND_H
|
||||
|
||||
#include "QtCore/qcontainerfwd.h"
|
||||
#include "QtCore/qtmetamacros.h"
|
||||
#include "QtCore/qvariant.h"
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "core/TaskItem.h"
|
||||
#include "core/TasksView.h"
|
||||
#include "core/TodoMd.h"
|
||||
#include "core/Mirai.h"
|
||||
|
||||
#include "TaskItem.h"
|
||||
|
||||
class Backend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_PROPERTY(QVariant tasks READ getTasks NOTIFY tasksChanged)
|
||||
Q_PROPERTY(QVariant tags READ getTags NOTIFY tasksChanged)
|
||||
Q_PROPERTY(QVariant activeTagsFilter READ getActiveTagsFilter NOTIFY tasksChanged)
|
||||
Q_PROPERTY(bool shouldHideCompletedTasks READ shouldHideCompletedTasks WRITE hideCompletedTasks NOTIFY tasksChanged)
|
||||
|
||||
public:
|
||||
|
||||
Backend();
|
||||
|
||||
Q_INVOKABLE void addTodo(QString newTodo, QString date);
|
||||
Q_INVOKABLE void addTagFilter(QString tag);
|
||||
Q_INVOKABLE void removeTagFilter(QString tag);
|
||||
Q_INVOKABLE void removeFilters();
|
||||
Q_INVOKABLE void updateTodo(int todoIndex, QString state, QString text, QString date);
|
||||
Q_INVOKABLE void removeTodo(int todoIndex);
|
||||
Q_INVOKABLE void hideCompletedTasks(bool shouldHide);
|
||||
|
||||
private:
|
||||
|
||||
void rebuildQMLTasksList();
|
||||
|
||||
QVariant getTasks();
|
||||
QVariant getTags();
|
||||
QVariant getActiveTagsFilter();
|
||||
bool shouldHideCompletedTasks();
|
||||
|
||||
mirai::Mirai mirai;
|
||||
std::weak_ptr<mirai::TasksView> view;
|
||||
|
||||
QList<QMLTaskItem> QMLTasks;
|
||||
QList<QString> QMLTags;
|
||||
QList<QString> QMLActiveTagsFilter;
|
||||
|
||||
bool shouldHideCompletedTasks_ = true;
|
||||
|
||||
signals:
|
||||
|
||||
void tasksChanged();
|
||||
};
|
||||
|
||||
#endif
|
23
src/TaskItem.cpp
Normal file
23
src/TaskItem.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "TaskItem.h"
|
||||
|
||||
QString QMLTaskItem::getText() {
|
||||
return QString::fromStdString(taskItem->getText());
|
||||
}
|
||||
|
||||
QString QMLTaskItem::getState() {
|
||||
return QString::fromStdString(taskItem->getState() == mirai::TODO ? "TODO" : "DONE");
|
||||
}
|
||||
|
||||
QString QMLTaskItem::getDate() {
|
||||
return QString::fromStdString(taskItem->getDate());
|
||||
}
|
||||
|
||||
bool QMLTaskItem::getShouldShowDate() {
|
||||
return shouldShowDate;
|
||||
}
|
37
src/TaskItem.h
Normal file
37
src/TaskItem.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef QML_TASKITEM_H
|
||||
#define QML_TASKITEM_H
|
||||
|
||||
#include "QtCore/qvariant.h"
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtCore/QString>
|
||||
#include <memory>
|
||||
|
||||
#include "core/TaskItem.h"
|
||||
|
||||
struct QMLTaskItem {
|
||||
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString text READ getText)
|
||||
Q_PROPERTY(QString state READ getState)
|
||||
Q_PROPERTY(QString date READ getDate)
|
||||
Q_PROPERTY(bool shouldShowDate READ getShouldShowDate)
|
||||
QML_VALUE_TYPE(taskItem)
|
||||
|
||||
public:
|
||||
|
||||
QString getText();
|
||||
QString getState();
|
||||
QString getDate();
|
||||
bool getShouldShowDate();
|
||||
|
||||
mirai::TaskItem* taskItem;
|
||||
bool shouldShowDate = false;
|
||||
};
|
||||
|
||||
#endif
|
55
src/core/Mirai.cpp
Normal file
55
src/core/Mirai.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "Mirai.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
void Mirai::loadFile(const std::string& path) {
|
||||
auto tasksFile = TodoMdFormat::readFile(path);
|
||||
files->push_back(std::move(tasksFile));
|
||||
tags.clear();
|
||||
for (auto& file : *files) {
|
||||
for (auto& tag : file.getTags()) {
|
||||
tags.push_back(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mirai::save() {
|
||||
for (auto& file : *files) {
|
||||
TodoMdFormat::writeFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
void Mirai::addTask(std::string text, std::string date) {
|
||||
/*std::time_t t = std::time(nullptr);*/
|
||||
/*std::tm tm = *std::localtime(&t);*/
|
||||
/*std::stringstream ssCreationDate;*/
|
||||
/*ssCreationDate << std::put_time(&tm, "%Y-%m-%d");*/
|
||||
(*files)[0].addTask(text, date);
|
||||
for (auto& view : views) {
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Mirai::removeTask(const TaskItem* taskItem) {
|
||||
(*files)[0].removeTask(taskItem);
|
||||
for (auto& view : views) {
|
||||
view->update();
|
||||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<TasksView> Mirai::getTasks() {
|
||||
auto view = std::make_shared<TasksView>(files);
|
||||
views.push_back(view);
|
||||
return view;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& Mirai::getTags() {
|
||||
return tags;
|
||||
}
|
||||
}
|
45
src/core/Mirai.h
Normal file
45
src/core/Mirai.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_MIRAI_H
|
||||
#define MIRAI_MIRAI_H
|
||||
|
||||
#include "TaskItem.h"
|
||||
#include "TasksFile.h"
|
||||
#include "TasksView.h"
|
||||
#include "TodoMd.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
class Mirai {
|
||||
|
||||
public:
|
||||
|
||||
void loadFile(const std::string& path);
|
||||
void save();
|
||||
void addTask(std::string text, std::string date);
|
||||
void removeTask(const TaskItem* taskItem);
|
||||
|
||||
std::weak_ptr<TasksView> getTasks();
|
||||
const std::vector<std::string>& getTags();
|
||||
|
||||
private:
|
||||
|
||||
// The `TasksFile`s are shared to the views, their lifetime can outlive
|
||||
// this (Mirai) object
|
||||
// because we can't control if the caller will keep the main object alive.
|
||||
std::shared_ptr<std::vector<TasksFile>> files = std::make_shared<std::vector<TasksFile>>();
|
||||
|
||||
// We keep a vector of shared_ptr because we need the ref counting mechanism to know
|
||||
// if we have to remove the view (not used) or tell the view to update() on
|
||||
// some changes.
|
||||
std::vector<std::shared_ptr<TasksView>> views;
|
||||
std::vector<std::string> tags;
|
||||
};
|
||||
}
|
||||
#endif
|
32
src/core/TaskItem.cpp
Normal file
32
src/core/TaskItem.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
|
||||
#include "TaskItem.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
void TaskItem::markAsDone() {
|
||||
state = DONE;
|
||||
}
|
||||
|
||||
void TaskItem::markAsUndone() {
|
||||
state = TODO;
|
||||
}
|
||||
|
||||
void TaskItem::setDate(const std::string& date) {
|
||||
this->date = date;
|
||||
}
|
||||
|
||||
void TaskItem::setText(const std::string& text) {
|
||||
this->text = text;
|
||||
}
|
||||
|
||||
const std::string& TaskItem::getText() const { return text; }
|
||||
const TaskItemState& TaskItem::getState() const { return state; }
|
||||
const std::string& TaskItem::getDate() const { return date; }
|
||||
bool TaskItem::hasDate() const { return isDate(date); }
|
||||
}
|
49
src/core/TaskItem.h
Normal file
49
src/core/TaskItem.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_TASKITEM_H
|
||||
#define MIRAI_TASKITEM_H
|
||||
|
||||
#include "utils.h"
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mirai {
|
||||
enum TaskItemState {
|
||||
TODO,
|
||||
DONE
|
||||
};
|
||||
|
||||
struct TaskItem {
|
||||
public:
|
||||
|
||||
void markAsDone();
|
||||
|
||||
void markAsUndone();
|
||||
void setDate(const std::string& date);
|
||||
|
||||
void setText(const std::string& text);
|
||||
|
||||
const std::string& getText() const;
|
||||
const TaskItemState& getState() const;
|
||||
const std::string& getDate() const;
|
||||
bool hasDate() const;
|
||||
|
||||
std::string text;
|
||||
TaskItemState state;
|
||||
std::string date;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
71
src/core/TasksFile.cpp
Normal file
71
src/core/TasksFile.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "TasksFile.h"
|
||||
#include "using.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
TasksFile::TasksFile(TasksFileConstructor params) : name(params.name), path(params.path) {
|
||||
for (const auto& task : params.tasks) {
|
||||
processTaskMetaData(task);
|
||||
tasks.push_back(std::make_unique<TaskItem>(task));
|
||||
}
|
||||
sortByDate();
|
||||
}
|
||||
|
||||
const std::string& TasksFile::getName() const { return name; }
|
||||
const std::string& TasksFile::getPath() const { return path; }
|
||||
TasksPtrs& TasksFile::getTasks() { return tasks; }
|
||||
Tags& TasksFile::getTags() { return tags; }
|
||||
|
||||
void TasksFile::addTask(std::string text, std::string date) {
|
||||
auto newTask = std::make_unique<TaskItem>(TaskItem{
|
||||
.text = text,
|
||||
.state = TODO,
|
||||
.date = date == "" ? "No date" : date
|
||||
});
|
||||
tasks.push_back(std::move(newTask));
|
||||
sortByDate();
|
||||
}
|
||||
|
||||
void TasksFile::removeTask(const TaskItem* taskToRemove) {
|
||||
tasks.erase(std::remove_if(tasks.begin(), tasks.end(), [&] (const std::unique_ptr<TaskItem>& taskInFilter){
|
||||
return taskInFilter.get() == taskToRemove;
|
||||
}));
|
||||
}
|
||||
|
||||
void TasksFile::processTaskMetaData(const TaskItem& task) {
|
||||
const std::string metaDataSeparator = " -- ";
|
||||
auto separatorIndex = task.text.find(metaDataSeparator);
|
||||
if (separatorIndex == std::string::npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto metaData = task.text.substr(separatorIndex + metaDataSeparator.length());
|
||||
std::regex regex("(#[a-zA-Z0-1]+)");
|
||||
std::smatch matches;
|
||||
|
||||
while (std::regex_search(metaData, matches, regex))
|
||||
{
|
||||
if (std::find(tags.begin(), tags.end(), matches[0]) == tags.end()) {
|
||||
tags.push_back(matches[0]);
|
||||
}
|
||||
metaData = matches.suffix();
|
||||
}
|
||||
}
|
||||
|
||||
void TasksFile::sortByDate() {
|
||||
std::sort(tasks.begin(), tasks.end(), [] (const std::unique_ptr<TaskItem>& t1, const std::unique_ptr<TaskItem>& t2) {
|
||||
if (t1->hasDate() && !t2->hasDate()) {
|
||||
return true;
|
||||
} else if (!t1->hasDate() && t2->hasDate()) {
|
||||
return false;
|
||||
}
|
||||
return t1->date < t2->date;
|
||||
});
|
||||
}
|
||||
}
|
58
src/core/TasksFile.h
Normal file
58
src/core/TasksFile.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_TASKSFILE_H
|
||||
#define MIRAI_TASKSFILE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "TaskItem.h"
|
||||
#include "using.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
struct TasksFileConstructor {
|
||||
const std::string& name;
|
||||
const std::string& path;
|
||||
std::vector<TaskItem> tasks;
|
||||
};
|
||||
|
||||
class TasksFile {
|
||||
|
||||
public:
|
||||
|
||||
TasksFile(TasksFileConstructor params);
|
||||
|
||||
void addTask(std::string text, std::string date);
|
||||
void removeTask(const TaskItem* taskToRemove);
|
||||
void processTaskMetaData(const TaskItem& task);
|
||||
void sortByDate();
|
||||
|
||||
const std::string& getName() const;
|
||||
const std::string& getPath() const;
|
||||
TasksPtrs& getTasks();
|
||||
Tags& getTags();
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
std::string path;
|
||||
TasksPtrs tasks;
|
||||
Tags tags;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
56
src/core/TasksView.cpp
Normal file
56
src/core/TasksView.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "TasksView.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
|
||||
TasksView::TasksView(std::shared_ptr<std::vector<TasksFile>> files) : files(files) {
|
||||
update();
|
||||
}
|
||||
|
||||
TaskItem& TasksView::operator[](int index) {
|
||||
return *(tasksToShow.at(index));
|
||||
}
|
||||
|
||||
int TasksView::count() const { return tasksToShow.size(); }
|
||||
|
||||
void TasksView::update() {
|
||||
tasksToShow.clear();
|
||||
for (auto& file : *files) {
|
||||
for (auto& task : file.getTasks()) {
|
||||
if (tagsFilter.size() != 0
|
||||
&& std::find_if(tagsFilter.begin(), tagsFilter.end(), [&](const std::string& tag) {
|
||||
return task->getText().find(tag) != std::string::npos;
|
||||
}) == tagsFilter.end())
|
||||
continue;
|
||||
tasksToShow.push_back(task.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TasksView::addTagFilter(const std::string& tag) {
|
||||
tagsFilter.push_back(tag);
|
||||
update();
|
||||
}
|
||||
|
||||
void TasksView::removeTagFilter(const std::string& tag) {
|
||||
tagsFilter.erase(std::remove_if(tagsFilter.begin(), tagsFilter.end(), [&] (const auto& tagInFilter){
|
||||
return tag == tagInFilter;
|
||||
}));
|
||||
update();
|
||||
}
|
||||
|
||||
void TasksView::removeFilters() {
|
||||
tagsFilter.clear();
|
||||
update();
|
||||
}
|
||||
|
||||
const std::vector<std::string>& TasksView::getActiveTagsFilter() {
|
||||
return tagsFilter;
|
||||
}
|
||||
}
|
41
src/core/TasksView.h
Normal file
41
src/core/TasksView.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_TASKSVIEW_H
|
||||
#define MIRAI_TASKSVIEW_H
|
||||
#include "TasksFile.h"
|
||||
#include "using.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
class TasksView {
|
||||
|
||||
public:
|
||||
|
||||
TasksView(std::shared_ptr<std::vector<TasksFile>> files);
|
||||
|
||||
TaskItem& operator[](int index);
|
||||
|
||||
int count() const;
|
||||
void update();
|
||||
void addTagFilter(const std::string& tag);
|
||||
void removeTagFilter(const std::string& tag);
|
||||
void removeFilters();
|
||||
const Tags& getActiveTagsFilter();
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<std::vector<TasksFile>> files;
|
||||
std::vector<TaskItem*> tasksToShow;
|
||||
Tags tagsFilter;
|
||||
};
|
||||
}
|
||||
#endif
|
78
src/core/TodoMd.cpp
Normal file
78
src/core/TodoMd.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "TodoMd.h"
|
||||
|
||||
namespace mirai {
|
||||
|
||||
TasksFile TodoMdFormat::readFile(const std::string& path) {
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("todo.txt file not found");
|
||||
}
|
||||
std::vector<TaskItem> taskItems;
|
||||
std::string line;
|
||||
|
||||
if (!std::getline(file, line) || line.substr(0, 2) != "# ") {
|
||||
std::cerr << "Couldn't find the task list name" << std::endl;
|
||||
}
|
||||
|
||||
std::string name = line.substr(2);
|
||||
std::string currentDate = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
if (line.substr(0, 3) == "## ") {
|
||||
currentDate = line.substr(3);
|
||||
} else if (line.substr(0, 5) == "- [ ]" || line.substr(0, 5) == "- [X]") {
|
||||
std::string text = line.substr(5);
|
||||
trim(text);
|
||||
TaskItem taskItem = {
|
||||
.text = text,
|
||||
.state = line.substr(0, 5) == "- [X]" ? DONE : TODO,
|
||||
.date = currentDate
|
||||
};
|
||||
taskItems.push_back(taskItem);
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
TasksFile tasks({
|
||||
.name = name,
|
||||
.path = path,
|
||||
.tasks = taskItems
|
||||
});
|
||||
return tasks;
|
||||
}
|
||||
|
||||
void TodoMdFormat::writeFile(TasksFile& tasks) {
|
||||
std::ofstream file(tasks.getPath());
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("can't create todo.txt");
|
||||
}
|
||||
std::string currentDate = "";
|
||||
|
||||
file << "# " << tasks.getName() << "\n";
|
||||
for (const auto& task : tasks.getTasks()) {
|
||||
if (currentDate != task->date) {
|
||||
currentDate = task->date;
|
||||
file << "\n## " << (task->date != "" ? task->date : "No date") << "\n\n";
|
||||
}
|
||||
if (task->getState() == TODO) {
|
||||
file << "- [ ] ";
|
||||
} else if (task->getState() == DONE) {
|
||||
file << "- [X] ";
|
||||
}
|
||||
file << task->getText() << '\n';
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
std::string TodoMdFormat::fieldWithSpace(const std::string& field) {
|
||||
if (field.length() == 0)
|
||||
return "";
|
||||
return (field + " ");
|
||||
}
|
||||
}
|
||||
|
38
src/core/TodoMd.h
Normal file
38
src/core/TodoMd.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_TODOMD_H
|
||||
#define MIRAI_TODOMD_H
|
||||
|
||||
#include "TaskItem.h"
|
||||
#include "TasksFile.h"
|
||||
#include "utils.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
class TodoMdFormat {
|
||||
public:
|
||||
|
||||
static TasksFile readFile(const std::string& path);
|
||||
static void writeFile(TasksFile& tasks);
|
||||
|
||||
private:
|
||||
|
||||
static std::string fieldWithSpace(const std::string& field);
|
||||
static TaskItem parseLine(const std::string& line);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
95
src/core/TodoTxt.h
Normal file
95
src/core/TodoTxt.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_TODOTXT_H
|
||||
#define MIRAI_TODOTXT_H
|
||||
|
||||
#include "TaskItem.h"
|
||||
#include "utils.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mirai {
|
||||
|
||||
class TodoTxtFormat {
|
||||
public:
|
||||
|
||||
static TaskList readFile() {
|
||||
std::ifstream file("../newqml/todo.txt");
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("todo.txt file not found");
|
||||
}
|
||||
std::vector<TaskItem> taskItems;
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
auto taskItem = parseLine(line);
|
||||
taskItems.push_back(taskItem);
|
||||
}
|
||||
file.close();
|
||||
TaskList tasks({
|
||||
.tasks = taskItems
|
||||
});
|
||||
return tasks;
|
||||
}
|
||||
|
||||
static void writeFile(TaskList& tasks) {
|
||||
std::ofstream file("../newqml/todo.txt");
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("can't create todo.txt");
|
||||
}
|
||||
for (const auto& task : tasks.getTasks()) {
|
||||
file << fieldWithSpace(task->state == DONE ? "x" : task->priority);
|
||||
file << fieldWithSpace(task->state == DONE ? task->getDate() : "");
|
||||
file << fieldWithSpace(task->getCreationDate());
|
||||
file << task->getText() << '\n';
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static std::string fieldWithSpace(const std::string& field) {
|
||||
if (field.length() == 0)
|
||||
return "";
|
||||
return (field + " ");
|
||||
}
|
||||
|
||||
static TaskItem parseLine(const std::string& line) {
|
||||
std::smatch matches;
|
||||
std::regex regex("(^x )?(\\([A-Z]\\) )?([0-9]{4}-[0-9]{2}-[0-9]{2} )?([0-9]{4}-[0-9]{2}-[0-9]{2} )?(.*)");
|
||||
std::regex_match(line, matches, regex);
|
||||
|
||||
for (size_t i = 0; i < matches.size(); ++i) {
|
||||
std::ssub_match sub_match = matches[i];
|
||||
std::string piece = sub_match.str();
|
||||
}
|
||||
|
||||
const TaskItemState taskState = trim_copy(matches[1].str()) == "x" ? DONE : TODO;
|
||||
const std::string priority = trim_copy(matches[2].str());
|
||||
const std::string firstDate = trim_copy(matches[3].str());
|
||||
const std::string secondDate = trim_copy(matches[4].str());
|
||||
const std::string text = trim_copy(matches[5].str());
|
||||
|
||||
const std::string date = taskState == DONE ? firstDate : "";
|
||||
|
||||
|
||||
|
||||
return {
|
||||
.text = text,
|
||||
.state = taskState,
|
||||
.date = date,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
20
src/core/using.h
Normal file
20
src/core/using.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_USING_H
|
||||
#define MIRAI_USING_H
|
||||
|
||||
#include "TaskItem.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mirai {
|
||||
using Tags = std::vector<std::string>;
|
||||
using TasksPtrs = std::vector<std::unique_ptr<TaskItem>>;
|
||||
}
|
||||
|
||||
#endif
|
44
src/core/utils.cpp
Normal file
44
src/core/utils.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
void ltrim(std::string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
}
|
||||
|
||||
void rtrim(std::string& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
void trim(std::string& s) {
|
||||
rtrim(s);
|
||||
ltrim(s);
|
||||
}
|
||||
|
||||
std::string ltrim_copy(std::string s) {
|
||||
ltrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string rtrim_copy(std::string s) {
|
||||
rtrim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string trim_copy(std::string s) {
|
||||
trim(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool isDate(const std::string& dateStr) {
|
||||
std::regex regex("[0-9]{4}-[0-9]{2}-[0-9]{2}");
|
||||
return std::regex_match(dateStr, regex);
|
||||
}
|
25
src/core/utils.h
Normal file
25
src/core/utils.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#ifndef MIRAI_UTILS_H
|
||||
#define MIRAI_UTILS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <locale>
|
||||
#include <regex>
|
||||
|
||||
void ltrim(std::string& s);
|
||||
void rtrim(std::string& s);
|
||||
void trim(std::string& s);
|
||||
|
||||
std::string ltrim_copy(std::string s);
|
||||
std::string rtrim_copy(std::string s);
|
||||
std::string trim_copy(std::string s);
|
||||
|
||||
bool isDate(const std::string& dateStr);
|
||||
|
||||
#endif
|
BIN
src/images/add.png
Normal file
BIN
src/images/add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
src/images/calendar.png
Normal file
BIN
src/images/calendar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
src/images/task.png
Normal file
BIN
src/images/task.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
45
src/main.cpp
Normal file
45
src/main.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QFont>
|
||||
#include <QtCore/QRect>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtQml/QQmlApplicationEngine>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
try {
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
qreal refDpi = 54.;
|
||||
qreal refHeight = 1440.;
|
||||
qreal refWidth = 2560.;
|
||||
QRect rect = QGuiApplication::primaryScreen()->geometry();
|
||||
qreal height = qMax(rect.width(), rect.height());
|
||||
qreal width = qMin(rect.width(), rect.height());
|
||||
qreal dpi = QGuiApplication::primaryScreen()->logicalDotsPerInch();
|
||||
//auto m_ratio = qMin(height/refHeight, width/refWidth);
|
||||
auto m_ratioFont = qMin(height*refDpi/(dpi*refHeight), width*refDpi/(dpi*refWidth));
|
||||
|
||||
QFont font("Helvetica", m_ratioFont);
|
||||
app.setFont(font);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
const QUrl url(u"qrc:/qt/qml/Mirai/src/qml/Main.qml"_qs);
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||
&app, []() { QCoreApplication::exit(-1); },
|
||||
Qt::QueuedConnection);
|
||||
engine.load(url);
|
||||
|
||||
return app.exec();
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
}
|
51
src/qml/AppButton.qml
Normal file
51
src/qml/AppButton.qml
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
Button {
|
||||
id: control
|
||||
icon.color: MiraiColorPalette.buttonIcon
|
||||
property bool noBackgroundColor: false
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: control.icon.source != "" ? 4 : 0
|
||||
Component {
|
||||
id: buttonIcon
|
||||
AppIcon {
|
||||
icon.source: control.icon.source
|
||||
icon.color: control?.icon?.color
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: control.icon.source != "" ? buttonIcon : undefined
|
||||
}
|
||||
|
||||
Text {
|
||||
text: control.text
|
||||
color: MiraiColorPalette.text
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: control.noBackgroundColor ? "transparent"
|
||||
: mouse.hovered ? colorPalette.selected.buttonHovered
|
||||
: colorPalette.selected.buttonBackground
|
||||
radius: 4
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: mouse
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
41
src/qml/AppCheckbox.qml
Normal file
41
src/qml/AppCheckbox.qml
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Mirai
|
||||
|
||||
CheckBox {
|
||||
id: control
|
||||
property alias textComponent: text
|
||||
|
||||
contentItem: Text {
|
||||
id: text
|
||||
text: control.text
|
||||
color: MiraiColorPalette.text
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: control.indicator.width + control.spacing
|
||||
}
|
||||
|
||||
indicator: Rectangle {
|
||||
x: control.leftPadding
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
implicitWidth: 12
|
||||
implicitHeight: 12
|
||||
radius: 999
|
||||
color: control.checked ? colorPalette.selected.palette.green : colorPalette.selected.text
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitWidth: 10
|
||||
implicitHeight: 10
|
||||
radius: 999
|
||||
color: control.checked ? colorPalette.selected.palette.green : colorPalette.selected.fieldBackground
|
||||
}
|
||||
}
|
||||
}
|
15
src/qml/AppIcon.qml
Normal file
15
src/qml/AppIcon.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Mirai
|
||||
|
||||
Button {
|
||||
icon.color: MiraiColorPalette.buttonIcon
|
||||
|
||||
background: Rectangle { color: "transparent" }
|
||||
}
|
22
src/qml/AppLineEdit.qml
Normal file
22
src/qml/AppLineEdit.qml
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import Mirai
|
||||
|
||||
TextField {
|
||||
color: MiraiColorPalette.text
|
||||
placeholderTextColor: MiraiColorPalette.textPlaceholder
|
||||
leftPadding: 10
|
||||
implicitHeight: 32
|
||||
|
||||
background: Rectangle {
|
||||
color: MiraiColorPalette.fieldBackground
|
||||
radius: 4
|
||||
}
|
||||
}
|
13
src/qml/AppText.qml
Normal file
13
src/qml/AppText.qml
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Mirai
|
||||
|
||||
Text {
|
||||
color: MiraiColorPalette.text
|
||||
}
|
77
src/qml/DateField.qml
Normal file
77
src/qml/DateField.qml
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
Rectangle {
|
||||
|
||||
id: datePickerRoot
|
||||
property alias text: newTodoDate.text
|
||||
property alias textFieldComponent: newTodoDate
|
||||
color: MiraiColorPalette.fieldBackground
|
||||
radius: 4
|
||||
implicitHeight: 32
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
AppIcon {
|
||||
icon.source: "qrc:/qt/qml/Mirai/src/images/calendar.png"
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
AppLineEdit {
|
||||
id: newTodoDate
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
readOnly: true
|
||||
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
datePicker.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: datePicker
|
||||
implicitWidth: 300
|
||||
implicitHeight: 300
|
||||
x: datePickerRoot.x
|
||||
y: datePickerRoot.y - 300 - 10
|
||||
background: Rectangle {
|
||||
color: colorPalette.selected.pane
|
||||
border.color: colorPalette.selected.modalBorder
|
||||
border.width: 2
|
||||
}
|
||||
DatePicker {
|
||||
anchors.fill: parent
|
||||
Component.onCompleted: set(new Date()) // today
|
||||
onClicked: {
|
||||
const formattedDate = Qt.formatDate(date, 'yyyy-MM-dd')
|
||||
print('onClicked', formattedDate)
|
||||
newTodoDate.text = formattedDate
|
||||
datePicker.close()
|
||||
}
|
||||
onReset: {
|
||||
newTodoDate.text = ""
|
||||
datePicker.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
124
src/qml/DatePicker.qml
Normal file
124
src/qml/DatePicker.qml
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import Mirai
|
||||
import QtQuick
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
|
||||
function set(date) {
|
||||
selectedDate = new Date(date)
|
||||
positionViewAtIndex((selectedDate.getFullYear()) * 12 + selectedDate.getMonth(), ListView.Center) // index from month year
|
||||
}
|
||||
|
||||
signal clicked(date date);
|
||||
signal reset();
|
||||
|
||||
property date selectedDate: new Date()
|
||||
|
||||
width: 500
|
||||
height: 100
|
||||
snapMode: ListView.SnapOneItem
|
||||
orientation: Qt.Horizontal
|
||||
clip: true
|
||||
|
||||
model: 3000 * 12 // index == months since January of the year 0
|
||||
|
||||
delegate: Item {
|
||||
property int year: Math.floor(index / 12)
|
||||
property int month: index % 12 // 0 January
|
||||
property int firstDay: new Date(year, month, 1).getDay() // 0 Sunday to 6 Saturday
|
||||
|
||||
width: root.width
|
||||
height: root.height
|
||||
|
||||
Column {
|
||||
Item { // month year header
|
||||
width: root.width
|
||||
height: root.height - grid.height
|
||||
|
||||
AppText { // month year
|
||||
anchors.centerIn: parent
|
||||
text: ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'][month] + ' ' + year
|
||||
font {pixelSize: 0.5 * grid.cellHeight}
|
||||
}
|
||||
|
||||
AppButton {
|
||||
text: "Remove"
|
||||
onClicked: {
|
||||
root.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grid { // 1 month calender
|
||||
id: grid
|
||||
|
||||
width: root.width
|
||||
height: 0.875 * root.height
|
||||
property real cellWidth: width / columns;
|
||||
property real cellHeight: height / rows // width and height of each cell in the grid.
|
||||
|
||||
columns: 7 // days
|
||||
rows: 7
|
||||
|
||||
Repeater {
|
||||
model: grid.columns * grid.rows // 49 cells per month
|
||||
|
||||
delegate: Rectangle { // index is 0 to 48
|
||||
property int day: index - 7 // 0 = top left below Sunday (-7 to 41)
|
||||
property int date: day - firstDay + 2 // 1-31
|
||||
|
||||
color: internal.selected ? colorPalette.selected.filterSelected
|
||||
: mouseArea.containsMouse ? colorPalette.selected.filterHovered
|
||||
: "transparent"
|
||||
width: grid.cellWidth; height: grid.cellHeight
|
||||
radius: 0.02 * root.height
|
||||
opacity: !mouseArea.pressed? 1: 0.3
|
||||
|
||||
QtObject {
|
||||
id: internal
|
||||
property bool selected: new Date(year, month, date).toDateString() == selectedDate.toDateString() && text.text && day >= 0
|
||||
}
|
||||
|
||||
AppText {
|
||||
id: text
|
||||
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: 0.5 * parent.height
|
||||
font.bold: new Date(year, month, date).toDateString() == new Date().toDateString() // today
|
||||
text: {
|
||||
if(day < 0)
|
||||
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][index] // Su-Sa
|
||||
else if(new Date(year, month, date).getMonth() == month)
|
||||
date // 1-31
|
||||
else
|
||||
''
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: text.text && day >= 0
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
selectedDate = new Date(year, month, date)
|
||||
root.clicked(selectedDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Component.onCompleted: set(new Date()) // today (otherwise Jan 0000)
|
||||
}
|
145
src/qml/Main.qml
Normal file
145
src/qml/Main.qml
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
Window {
|
||||
id: root
|
||||
width: 640
|
||||
height: 480
|
||||
visible: true
|
||||
title: qsTr("Hello World")
|
||||
|
||||
property string selectedView: "list"
|
||||
|
||||
Backend {
|
||||
id: backend
|
||||
}
|
||||
|
||||
Item {
|
||||
id: colorPalette
|
||||
property QtObject selected: MiraiColorPalette
|
||||
}
|
||||
|
||||
function newTask() {
|
||||
taskForm.taskToEdit = undefined
|
||||
taskForm.taskToEditIndex = -1
|
||||
taskFormPopup.open()
|
||||
}
|
||||
|
||||
function editTask(task, taskIndex) {
|
||||
taskForm.taskToEdit = task
|
||||
taskForm.taskToEditIndex = taskIndex
|
||||
taskFormPopup.open()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: MiraiColorPalette.pane
|
||||
Layout.preferredWidth: childrenRect.width + 20
|
||||
Layout.fillHeight: true
|
||||
|
||||
SideMenu {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 10
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: colorPalette.selected.background
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
spacing: 16
|
||||
|
||||
|
||||
|
||||
TabSelector {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
|
||||
tabs: [
|
||||
{
|
||||
label: "List",
|
||||
onClick: () => { root.selectedView = "list" },
|
||||
selected: root.selectedView === "list"
|
||||
},
|
||||
{
|
||||
label: "Calendar",
|
||||
onClick: () => { root.selectedView = "calendar" },
|
||||
selected: root.selectedView === "calendar"
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
AppButton {
|
||||
icon.source: "qrc:/qt/qml/Mirai/src/images/add.png"
|
||||
icon.color: colorPalette.selected.palette.green
|
||||
text: "Add task"
|
||||
onClicked: {
|
||||
root.newTask()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: listViewComponent
|
||||
ListView {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: calendarViewComponent
|
||||
CalendarView {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: selectedView === "list" ? listViewComponent
|
||||
: selectedView === "calendar" ? calendarViewComponent
|
||||
: undefined
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: taskFormPopup
|
||||
width: parent.width * 0.75
|
||||
implicitHeight: taskForm.height + padding * 2
|
||||
x: Math.round((parent.width - width) / 2)
|
||||
y: Math.round((parent.height * 0.4) / 2)
|
||||
padding: 8
|
||||
background: Rectangle {
|
||||
border.color: colorPalette.selected.modalBorder
|
||||
border.width: 2
|
||||
color: colorPalette.selected.pane
|
||||
radius: 4
|
||||
}
|
||||
TaskForm {
|
||||
id: taskForm
|
||||
width: parent.width
|
||||
onConfirmed: {
|
||||
taskFormPopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
61
src/qml/SideMenu.qml
Normal file
61
src/qml/SideMenu.qml
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
AppText {
|
||||
text: "Tags"
|
||||
font.pixelSize: 32
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 16 }
|
||||
|
||||
Repeater {
|
||||
model: backend.tags
|
||||
Rectangle {
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
Layout.fillWidth: true
|
||||
color: backend.activeTagsFilter.includes(modelData) ? MiraiColorPalette.filterSelected : mouse.hovered ? MiraiColorPalette.filterHovered : "transparent"
|
||||
radius: 4
|
||||
AppText {
|
||||
text: modelData
|
||||
padding: 4
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
if (backend.activeTagsFilter.includes(modelData)) {
|
||||
backend.removeTagFilter(modelData)
|
||||
} else {
|
||||
backend.addTagFilter(modelData)
|
||||
}
|
||||
}
|
||||
HoverHandler {
|
||||
id: mouse
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
AppButton {
|
||||
text: `Hide completed tasks: ${backend.shouldHideCompletedTasks ? "ON" : "OFF"}`
|
||||
onClicked: {
|
||||
backend.hideCompletedTasks(!backend.shouldHideCompletedTasks)
|
||||
}
|
||||
}
|
||||
}
|
27
src/qml/TaskItem.qml
Normal file
27
src/qml/TaskItem.qml
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Mirai
|
||||
|
||||
RowLayout {
|
||||
property taskItem task
|
||||
|
||||
AppCheckbox {
|
||||
id: checkbox
|
||||
text: task.text
|
||||
checked: task.state === 'DONE'
|
||||
textComponent.color: task.date < internal.todayDate ? colorPalette.selected.palette.pink
|
||||
// : task.date === internal.todayDate ? colorPalette.selected.palette.sapphire
|
||||
: colorPalette.selected.text
|
||||
onClicked: {
|
||||
backend.updateTodo(index, modelData.state === 'DONE' ? "TODO" : "DONE", modelData.text, modelData.date)
|
||||
}
|
||||
}
|
||||
}
|
58
src/qml/components/TabSelector.qml
Normal file
58
src/qml/components/TabSelector.qml
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
RowLayout {
|
||||
id: control
|
||||
property var tabs: []
|
||||
|
||||
Repeater {
|
||||
model: tabs
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
AppText {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
id: tabText
|
||||
text: modelData.label
|
||||
font.pointSize: 24
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
color: modelData.selected ? "white" : mouse.hovered ? MiraiColorPalette.filterHovered : "transparent"
|
||||
implicitHeight: 2
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
modelData?.onClick()
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: mouse
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
55
src/qml/forms/TaskForm.qml
Normal file
55
src/qml/forms/TaskForm.qml
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
ColumnLayout {
|
||||
id: form
|
||||
property var taskToEdit
|
||||
property int taskToEditIndex
|
||||
spacing: 6
|
||||
signal confirmed
|
||||
|
||||
onTaskToEditChanged: {
|
||||
newTodoContent.text = taskToEdit?.text ?? ""
|
||||
newTodoDate.text = taskToEdit?.date ?? ""
|
||||
}
|
||||
|
||||
AppText {
|
||||
text: "New task"
|
||||
}
|
||||
|
||||
AppLineEdit {
|
||||
id: newTodoContent
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter your new task..."
|
||||
text: taskToEdit?.text ?? ""
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (newTodoContent.text == "") {
|
||||
return
|
||||
}
|
||||
if (taskToEdit && taskToEditIndex !== undefined) {
|
||||
backend.updateTodo(taskToEditIndex, taskToEdit.state, newTodoContent.text, newTodoDate.text)
|
||||
} else {
|
||||
backend.addTodo(newTodoContent.text, newTodoDate.text)
|
||||
}
|
||||
form.confirmed()
|
||||
}
|
||||
}
|
||||
|
||||
DateField {
|
||||
id: newTodoDate
|
||||
text: taskToEdit?.date ?? ""
|
||||
textFieldComponent.placeholderText: "No date"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
38
src/qml/styles/CatppuccinFrappe.qml
Normal file
38
src/qml/styles/CatppuccinFrappe.qml
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
pragma Singleton
|
||||
import QtQuick 2.5
|
||||
|
||||
// Catppuccin Frappe theme (https://github.com/catppuccin/catppuccin)
|
||||
QtObject {
|
||||
property string rosewater: "#f2d5cf"
|
||||
property string flamingo: "#eebebe"
|
||||
property string pink: "#f4b8e4"
|
||||
property string mauve: "#ca9ee6"
|
||||
property string red: "#e78284"
|
||||
property string maroon: "#ea999c"
|
||||
property string peach: "#ef9f76"
|
||||
property string yellow: "#e5c890"
|
||||
property string green: "#a6d189"
|
||||
property string teal: "#81c8be"
|
||||
property string sky: "#99d1db"
|
||||
property string sapphire: "#85c1dc"
|
||||
property string blue: "#8caaee"
|
||||
property string lavender: "#babbf1"
|
||||
property string text: "#c6d0f5"
|
||||
property string subtext1: "#b5bfe2"
|
||||
property string subtext0: "#a5adce"
|
||||
property string overlay2: "#949cbb"
|
||||
property string overlay1: "#838ba7"
|
||||
property string overlay0: "#737994"
|
||||
property string surface2: "#626880"
|
||||
property string surface1: "#51576d"
|
||||
property string surface0: "#414559"
|
||||
property string base: "#303446"
|
||||
property string mantle: "#292c3c"
|
||||
property string crust: "#232634"
|
||||
}
|
24
src/qml/styles/MiraiColorPalette.qml
Normal file
24
src/qml/styles/MiraiColorPalette.qml
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
pragma Singleton
|
||||
import QtQuick 2.5
|
||||
import Mirai
|
||||
|
||||
QtObject {
|
||||
property QtObject palette: CatppuccinFrappe
|
||||
property string background: CatppuccinFrappe.base
|
||||
property string pane: CatppuccinFrappe.mantle
|
||||
property string text: CatppuccinFrappe.text
|
||||
property string textPlaceholder: CatppuccinFrappe.overlay1
|
||||
property string fieldBackground: CatppuccinFrappe.surface0
|
||||
property string buttonIcon: CatppuccinFrappe.sapphire
|
||||
property string buttonBackground: CatppuccinFrappe.surface0
|
||||
property string buttonHovered: CatppuccinFrappe.surface1
|
||||
property string filterHovered: CatppuccinFrappe.surface0
|
||||
property string filterSelected: CatppuccinFrappe.surface1
|
||||
property string modalBorder: CatppuccinFrappe.lavender
|
||||
}
|
17
src/qml/views/CalendarView.qml
Normal file
17
src/qml/views/CalendarView.qml
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
ColumnLayout {
|
||||
AppText {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
text: "Not implemented yet :)"
|
||||
}
|
||||
}
|
98
src/qml/views/ListView.qml
Normal file
98
src/qml/views/ListView.qml
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Mirai. Copyright (C) 2024 Vyn
|
||||
* This file is licensed under version 3 of the GNU General Public License (GPL-3.0-only)
|
||||
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Mirai
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
QtObject {
|
||||
id: internal
|
||||
property string todayDate: Qt.formatDate(new Date(), 'yyyy-MM-dd')
|
||||
property string tomorrowDate: Qt.formatDate(new Date(new Date().setDate(new Date().getDate() + 1)), 'yyyy-MM-dd')
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: backend.tasks
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
id: task
|
||||
required property var modelData
|
||||
required property int index
|
||||
color: "transparent"
|
||||
|
||||
HoverHandler {
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Component {
|
||||
id: dateForTasks
|
||||
AppText {
|
||||
topPadding: 32
|
||||
bottomPadding: 16
|
||||
text: task.modelData.date === internal.todayDate ? "Today"
|
||||
: task.modelData.date === internal.tomorrowDate ? "Tomorrow"
|
||||
: task.modelData.date
|
||||
color: task.modelData.date < internal.todayDate ? colorPalette.selected.palette.pink
|
||||
// : task.modelData.date === internal.todayDate ? colorPalette.selected.palette.sapphire
|
||||
: colorPalette.selected.text
|
||||
font.pointSize: 24
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
sourceComponent: task.modelData.shouldShowDate ? dateForTasks : undefined
|
||||
}
|
||||
|
||||
TaskItem {
|
||||
id: taskItem
|
||||
task: task.modelData
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
MenuItem {
|
||||
text: "Edit"
|
||||
onClicked: {
|
||||
root.editTask(task.modelData, task.index)
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: "Delete"
|
||||
onClicked: {
|
||||
backend.removeTodo(task.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
propagateComposedEvents: true
|
||||
onClicked: {
|
||||
contextMenu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue