Switch from Qt6 to Slint

This commit is contained in:
Vyn 2024-08-16 21:35:12 +02:00
parent f8be14bcf8
commit 63bf267a22
107 changed files with 27532 additions and 2896 deletions

View file

@ -1,61 +0,0 @@
/*
* 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_ANDROID_FILE_RESOURCE_H
#define MIRAI_ANDROID_FILE_RESOURCE_H
#include "QDebug"
#include "QFile"
#include "core/BaseFileResource.h"
#include "core/TodoMd.h"
namespace mirai
{
class AndroidFileResource : public BaseFileResource
{
public:
AndroidFileResource(BaseFileResourceConstructor data) : BaseFileResource(data){};
AndroidFileResource(AndroidFileResource &) = delete;
AndroidFileResource(AndroidFileResource &&) = delete;
AndroidFileResource operator=(AndroidFileResource &) = delete;
AndroidFileResource operator=(AndroidFileResource &&) = delete;
~AndroidFileResource() override = default;
void save() override
{
QFile file(QString::fromStdString(getPath()));
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
throw std::runtime_error("can't create " + getPath());
}
const std::string content = TodoMdFormat::stringifyTasks(getName(), getTasks());
file.write(content.c_str());
file.close();
};
void load() override
{
qDebug() << "Reading " << QString::fromStdString(getPath());
QFile file(QString::fromStdString(getPath()));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Can't read";
return;
}
QString content = file.readAll();
file.close();
auto result = TodoMdFormat::parse(content.toStdString());
for (const auto &task : result.tasks) {
addTask(task);
}
setName(result.name);
};
};
} // namespace mirai
#endif

View file

@ -1,381 +0,0 @@
/*
* 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"
#include "AndroidFileResource.h"
#include "TaskItem.h"
#include "core/StdFileResource.h"
#include "core/TaskItem.h"
#include "core/TasksView.h"
#include "core/TodoMd.h"
#include "cpp-utils/debug.h"
#include <algorithm>
#include <exception>
#include <iostream>
#include <memory>
#include <ostream>
#include <qjsonarray.h>
#include <qjsonvalue.h>
#include <qlogging.h>
#include <qstandardpaths.h>
#include <qvariant.h>
#include <sstream>
Backend::Backend() : todoView(&mirai)
{
cpputils::debug::Timer startDuration;
std::cout << "Backend created" << std::endl;
cpputils::debug::Timer readConfigDuration;
QFile loadFile(getConfigFilePath());
readConfigDuration.printTimeElapsed("Read config duration");
if (loadFile.open(QIODevice::ReadOnly)) {
QByteArray loadData = loadFile.readAll();
configJson = QJsonDocument::fromJson(loadData);
loadFile.close();
if (!configJson.isObject()) {
qWarning() << "config.json is not a valid config file";
exit(1);
}
QJsonObject jsonRootObject = configJson.object();
auto jsonFilesPath = configJson["files"];
if (!jsonFilesPath.isArray()) {
qWarning() << "config.json should contains a 'files' string array";
exit(1);
}
for (const QJsonValueRef &filePath : jsonFilesPath.toArray()) {
cpputils::debug::Timer loadingFileDuration;
auto fileResource =
std::make_unique<mirai::AndroidFileResource>(mirai::BaseFileResourceConstructor{
.name = "[Can't load path]", .path = filePath.toString().toStdString()
});
mirai.loadResource(std::move(fileResource));
loadingFileDuration.printTimeElapsed(
"Loading file duration of " + filePath.toString().toStdString()
);
}
auto jsonTagsConfig = configJson["tags"];
if (jsonTagsConfig.isObject()) {
for (auto &jsonTagConfigKey : jsonTagsConfig.toObject().keys()) {
tagsConfig[jsonTagConfigKey] =
jsonTagsConfig.toObject()[jsonTagConfigKey].toObject()["color"].toString();
}
}
} else {
qWarning() << "Couldn't find existing config file";
}
cpputils::debug::Timer updatingViewDuration;
todoView.update();
updatingViewDuration.printTimeElapsed("Updating view duration");
cpputils::debug::Timer rebuildQMLTasksListDuration;
rebuildQMLTasksList();
rebuildQMLTasksListDuration.printTimeElapsed("Rebuilding QML duration");
startDuration.printTimeElapsed("Start duration");
}
void Backend::addTodoFromRawFormat(QString filePath, QString text, QString date)
{
auto resource = std::ranges::find_if(
mirai.getResources(),
[&](std::unique_ptr<mirai::BaseResource> &resource) {
auto fsResource = dynamic_cast<mirai::BaseFileResource *>(resource.get());
if (!fsResource) {
return false;
}
return fsResource->getPath() == filePath.toStdString();
}
);
if (resource == mirai.getResources().end()) {
qWarning() << "File path '" << filePath << "' doesn't exist" << Qt::endl;
return;
}
resource->get()->addTask(
mirai::TodoMdFormat::stringToTask(text.toStdString(), date.toStdString())
);
mirai.save();
todoView.update();
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::addTagFilter(QString tag)
{
todoView.addTagFilter(tag.toStdString());
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::removeTagFilter(QString tag)
{
todoView.removeTagFilter(tag.toStdString());
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::addResourceFilter(QString fileName)
{
todoView.addResourceFilter(fileName.toStdString());
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::removeResourceFilter(QString fileName)
{
todoView.removeResourceFilter(fileName.toStdString());
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::removeFilters()
{
todoView.removeFilters();
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::updateTodoFromRawFormat(int todoIndex, QString text, QString date)
{
QMLTaskItem &taskItem = QMLTasks[todoIndex];
taskItem.taskItem->setText(taskItem.taskItem->getText());
*(taskItem.taskItem) =
mirai::TodoMdFormat::stringToTask(text.toStdString(), date.toStdString());
mirai.save();
todoView.update();
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::updateTodo(int todoIndex, QString state, QString text, QString date)
{
QMLTaskItem &taskItem = QMLTasks[todoIndex];
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();
todoView.update();
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::removeTodo(int todoIndex)
{
QMLTaskItem &taskItem = QMLTasks[todoIndex];
mirai.removeTask(taskItem.taskItem);
mirai.save();
todoView.update();
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::hideCompletedTasks(bool shouldHide)
{
shouldHideCompletedTasks_ = shouldHide;
rebuildQMLTasksList();
emit tasksChanged();
}
void Backend::rebuildQMLTasksList()
{
// TODO MOVE TO ANOTHER FILE
QMLResources.clear();
for (auto &resource : mirai.getResources()) {
QMLResources.push_back({.tasksFile = resource.get()});
}
// ----
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 < todoView.count(); ++i) {
mirai::TaskItem &task = todoView[i];
if (shouldHideCompletedTasks_ && task.getState() == mirai::DONE && task.hasDate() &&
task.getDate() < currentDate.str()) {
continue;
}
bool shouldShowDate = false;
if (lastDate != task.getDate()) {
lastDate = task.getDate();
shouldShowDate = true;
}
QList<QString> qStringTags;
for (auto &tag : task.getTags()) {
qStringTags.push_back(QString::fromStdString(tag));
}
QMLTasks.push_back(
{.taskItem = &task, .shouldShowDate = shouldShowDate, .tags = qStringTags}
);
}
QMLTags.clear();
for (auto &tag : mirai.getTags()) {
QMLTags.push_back({
.name = QString::fromStdString(tag),
.color = getTagColor(QString::fromStdString(tag)),
});
}
QMLActiveTagsFilter.clear();
for (auto &activeTagFilter : todoView.getActiveTagsFilter()) {
QMLActiveTagsFilter.push_back(QString::fromStdString(activeTagFilter));
}
QMLActiveResourcesFilter.clear();
for (auto &activeFileFilter : todoView.getActiveFilesFilter()) {
QMLActiveResourcesFilter.push_back(QString::fromStdString(activeFileFilter));
}
}
QVariant Backend::getTasks()
{
return QVariant::fromValue(QMLTasks);
}
QVariant Backend::getTags()
{
return QVariant::fromValue(QMLTags);
}
QVariant Backend::getActiveTagsFilter()
{
return QVariant::fromValue(QMLActiveTagsFilter);
}
QVariant Backend::getActiveResourcesFilter()
{
return QVariant::fromValue(QMLActiveResourcesFilter);
}
bool Backend::shouldHideCompletedTasks()
{
return shouldHideCompletedTasks_;
}
QVariant Backend::getResources()
{
return QVariant::fromValue(QMLResources);
}
QString Backend::getTagColor(QString tag)
{
if (!tagsConfig.contains(tag)) {
return "#c6d0f5"; // TODO: currently hard coded but should match the default Text color.
}
return tagsConfig[tag];
}
void Backend::saveTagsColor(QVariant modifiedTags)
{
auto aa = modifiedTags.toList();
for (auto bb : aa) {
auto cc = bb.toMap();
std::cout << cc["name"].toString().toStdString() << ": "
<< cc["color"].toString().toStdString() << std::endl;
tagsConfig[cc["name"].toString()] = cc["color"].toString();
}
rebuildQMLTasksList();
emit tagsChanged();
emit tasksChanged();
saveConfig();
}
void Backend::saveFilesPath(QVariant modifiedFilesPath)
{
// TODO: Make something better, rework rebuildQMLTasksList ?
auto paths = modifiedFilesPath.toStringList();
QMLTags.clear();
QMLTasks.clear();
QMLResources.clear();
emit tagsChanged();
emit tasksChanged();
emit filesChanged();
mirai.unloadAllResources();
for (auto path : paths) {
if (path == "") {
continue;
}
auto fileResource =
std::make_unique<mirai::AndroidFileResource>(mirai::BaseFileResourceConstructor{
.name = "[Can't load path]", .path = path.toStdString()
});
mirai.loadResource(std::move(fileResource));
}
todoView.update();
rebuildQMLTasksList();
emit tagsChanged();
emit tasksChanged();
emit filesChanged();
saveConfig();
}
void Backend::saveConfig()
{
QJsonObject rootJson;
QJsonArray filesJson;
for (auto &file : mirai.getResources()) {
auto fsResource = dynamic_cast<mirai::BaseFileResource *>(file.get());
if (fsResource) {
filesJson.append(QString::fromStdString(fsResource->getPath()));
}
}
rootJson["files"] = filesJson;
QJsonObject tagsJson;
for (auto &tag : tagsConfig.keys()) {
QJsonObject tagConfig;
tagConfig["color"] = getTagColor(tag);
tagsJson[tag] = tagConfig;
}
rootJson["tags"] = tagsJson;
rootJson["theme"] = configJson["theme"];
QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
QFile configFile(getConfigFilePath());
if (!configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qWarning() << "Cannot save config: " << getConfigFilePath();
return;
}
configFile.write(QJsonDocument(rootJson).toJson());
configFile.close();
qDebug() << "Config file saved";
}
QString Backend::getConfigFilePath() const
{
QString configFolder = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
return configFolder + "/config.json";
}
QString Backend::getThemeColor(QString themeElementName)
{
if (configJson["theme"].isNull()) {
std::cerr << "Warning: theme in config is null" << std::endl;
return "";
}
if (!configJson["theme"].isObject()) {
std::cerr << "Warning: theme in config is not an object" << std::endl;
return "";
}
if (!configJson["theme"].toObject()[themeElementName].isString()) {
std::cerr << "Warning: element " << themeElementName.toStdString()
<< " in theme in config is not valid" << std::endl;
return "";
}
return configJson["theme"].toObject()[themeElementName].toString();
}

View file

@ -1,95 +0,0 @@
/*
* 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 "Tag.h"
#include "TasksFile.h"
#include "core/Mirai.h"
#include "core/TasksView.h"
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtQml/qqmlregistration.h>
#include <memory>
#include <qjsondocument.h>
#include <qmap.h>
#include "TaskItem.h"
class Backend : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QVariant tasks READ getTasks NOTIFY tasksChanged)
Q_PROPERTY(QVariant files READ getResources NOTIFY filesChanged)
Q_PROPERTY(QVariant tags READ getTags NOTIFY tagsChanged)
Q_PROPERTY(QVariant activeTagsFilter READ getActiveTagsFilter NOTIFY tasksChanged)
Q_PROPERTY(QVariant activeResourcesFilter READ getActiveResourcesFilter NOTIFY tasksChanged)
Q_PROPERTY(bool shouldHideCompletedTasks READ shouldHideCompletedTasks WRITE hideCompletedTasks
NOTIFY tasksChanged)
public:
Backend();
Q_INVOKABLE void addTodoFromRawFormat(QString filePath, QString text, QString date);
Q_INVOKABLE void addTagFilter(QString tag);
Q_INVOKABLE void removeTagFilter(QString tag);
Q_INVOKABLE void addResourceFilter(QString fileName);
Q_INVOKABLE void removeResourceFilter(QString fileName);
Q_INVOKABLE void removeFilters();
Q_INVOKABLE void updateTodoFromRawFormat(int todoIndex, QString text, QString date);
Q_INVOKABLE void updateTodo(int todoIndex, QString state, QString text, QString date);
Q_INVOKABLE void removeTodo(int todoIndex);
Q_INVOKABLE void hideCompletedTasks(bool shouldHide);
Q_INVOKABLE QString getTagColor(QString tag);
Q_INVOKABLE void saveTagsColor(QVariant tags);
Q_INVOKABLE void saveFilesPath(QVariant filesPath);
Q_INVOKABLE QString getThemeColor(QString themeElementName);
private:
void rebuildQMLTasksList();
QVariant getTasks();
QVariant getResources();
QVariant getTags();
QVariant getActiveTagsFilter();
QVariant getActiveResourcesFilter();
bool shouldHideCompletedTasks();
void saveConfig();
QString getConfigFilePath() const;
mirai::Mirai mirai;
// Both Todo and Calendar view use the todoView for now.
mirai::TasksView todoView;
QJsonDocument configJson;
QList<QMLTaskItem> QMLTasks;
QList<QMLTasksFile> QMLResources;
QList<QMLTag> QMLTags;
QList<QString> QMLActiveTagsFilter;
QList<QString> QMLActiveResourcesFilter;
QMap<QString, QString> tagsConfig;
bool shouldHideCompletedTasks_ = true;
signals:
void tasksChanged();
void tagsChanged();
void filesChanged();
};
#endif

View file

@ -1,17 +0,0 @@
/*
* 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 "Tag.h"
QString QMLTag::getName()
{
return name;
}
QString QMLTag::getColor()
{
return color;
}

View file

@ -1,29 +0,0 @@
/*
* 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_TAG_H
#define QML_TAG_H
#include <QtCore/QString>
#include <QtQml/qqmlregistration.h>
#include <qobjectdefs.h>
struct QMLTag {
Q_GADGET
Q_PROPERTY(QString name READ getName)
Q_PROPERTY(QString color READ getColor)
QML_VALUE_TYPE(tag)
public:
QString getName();
QString getColor();
QString name;
QString color;
};
#endif

View file

@ -1,56 +0,0 @@
/*
* 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"
#include "core/TodoMd.h"
QString QMLTaskItem::getRawFormat()
{
return QString::fromStdString(mirai::TodoMdFormat::taskToString(*taskItem));
}
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());
}
QString QMLTaskItem::getStartTime()
{
return QString::fromStdString(taskItem->getStartTime());
}
QString QMLTaskItem::getEndTime()
{
return QString::fromStdString(taskItem->getEndTime());
}
QString QMLTaskItem::getTime()
{
if (taskItem->getStartTime() != "" && taskItem->getEndTime() != "") {
return QString::fromStdString(taskItem->getStartTime() + "-" + taskItem->getEndTime());
}
return "";
}
QList<QString> QMLTaskItem::getTags()
{
return tags;
}
bool QMLTaskItem::getShouldShowDate()
{
return shouldShowDate;
}

View file

@ -1,46 +0,0 @@
/*
* 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 <QtCore/QString>
#include <QtQml/qqmlregistration.h>
#include "core/TaskItem.h"
struct QMLTaskItem {
Q_GADGET
Q_PROPERTY(QString rawFormat READ getRawFormat)
Q_PROPERTY(QString text READ getText)
Q_PROPERTY(QString state READ getState)
Q_PROPERTY(QString date READ getDate)
Q_PROPERTY(QList<QString> tags READ getTags)
Q_PROPERTY(bool shouldShowDate READ getShouldShowDate)
Q_PROPERTY(QString time READ getTime)
Q_PROPERTY(QString startTime READ getStartTime)
Q_PROPERTY(QString endTime READ getEndTime)
QML_VALUE_TYPE(taskItem)
public:
QString getText();
QString getRawFormat();
QString getState();
QString getDate();
QString getTime();
QString getStartTime();
QString getEndTime();
QList<QString> getTags();
bool getShouldShowDate();
mirai::TaskItem *taskItem = nullptr;
bool shouldShowDate = false;
QList<QString> tags;
};
#endif

View file

@ -1,22 +0,0 @@
/*
* 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 "core/BaseFileResource.h"
QString QMLTasksFile::getName()
{
return QString::fromStdString(tasksFile->getName());
}
QString QMLTasksFile::getPath()
{
auto fileResource = dynamic_cast<mirai::BaseFileResource *>(tasksFile);
if (!fileResource) {
return "";
}
return QString::fromStdString(fileResource->getPath());
}

View file

@ -1,29 +0,0 @@
/*
* 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_TASKSFILE_H
#define QML_TASKSFILE_H
#include "QtCore/qvariant.h"
#include "core/BaseFileResource.h"
#include <QtCore/QString>
#include <QtQml/qqmlregistration.h>
struct QMLTasksFile {
Q_GADGET
Q_PROPERTY(QString name READ getName)
Q_PROPERTY(QString path READ getPath)
QML_VALUE_TYPE(tasksFile)
public:
QString getName();
QString getPath();
mirai::BaseResource *tasksFile;
};
#endif

433
src/UiState.cpp Normal file
View file

@ -0,0 +1,433 @@
/*
* 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 "UiState.h"
#include "Utils.h"
#include "appwindow.h"
#include "mirai-core/Mirai.h"
#include "mirai-core/TaskItem.h"
#include "mirai-core/TodoMd.h"
#include "slint.h"
#include "slint_sharedvector.h"
#include "slint_string.h"
#include <algorithm>
#include <bits/chrono.h>
#include <cassert>
#include <charconv>
#include <chrono>
#include <ctime>
#include <format>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <memory>
#include <optional>
#include <ostream>
#include <ranges>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
UiState::UiState(mirai::Mirai *miraiInstance) : miraiInstance_(miraiInstance), view_(miraiInstance)
{
resources_ = std::make_shared<slint::VectorModel<slint::SharedString>>();
days_ = std::make_shared<slint::VectorModel<Day>>();
tags_ = std::make_shared<slint::VectorModel<slint::SharedString>>();
taskEditWindow_->global<Backend>().set_resources(resources_);
taskEditWindow_->global<Backend>().set_tags(tags_);
eventWindow_->global<Backend>().set_resources(resources_);
eventWindow_->global<Backend>().set_tags(tags_);
view_.update();
reloadResources();
reloadTags();
reloadTasks();
setupCallbacks();
}
std::optional<Date> stringToDate(const std::string &dateStr)
{
using std::operator""sv;
auto dateSplitView =
dateStr | std::views::split("-"sv) | std::views::transform([](auto v) -> int {
int i = 0;
std::from_chars(v.data(), v.data() + v.size(), i);
return i;
});
std::vector<int> dateSplit{dateSplitView.begin(), dateSplitView.end()};
if (dateSplit.size() != 3) {
return std::nullopt;
}
auto year = dateSplit[0];
auto month = dateSplit[1];
auto day = dateSplit[2];
return Date{.year = year, .month = month, .day = day};
}
void UiState::setupEventWindowCallbacks()
{
mainWindow_->global<Backend>().on_open_new_event_form([&]() {
auto todayDate = std::chrono::year_month_day{
std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())
};
eventWindow_->set_eventId(-1);
eventWindow_->set_taskTitle("");
eventWindow_->set_taskDate({
.year = static_cast<int>(todayDate.year()),
// Try to directly use `unsigned`
.month = static_cast<int>(static_cast<unsigned>(todayDate.month())),
.day = static_cast<int>(static_cast<unsigned>(todayDate.day())),
});
eventWindow_->show();
});
mainWindow_->global<Backend>().on_open_edit_event_form([&](int sourceId, int eventId) {
auto resource = miraiInstance_->getResourceById(sourceId);
assert(resource);
auto event = resource->getEventById(eventId);
assert(event);
eventWindow_->global<Backend>().set_resources(resources_);
eventWindow_->global<Backend>().set_tags(tags_);
eventWindow_->set_sourceId(sourceId);
eventWindow_->set_eventId(eventId);
eventWindow_->set_taskTitle(slint::SharedString(event->getText()));
eventWindow_->set_taskDate(MiraiDateToSlintDate(event->getDate()));
eventWindow_->set_startsAt(MiraiTimeToSlintTime(event->getStartTime()));
eventWindow_->set_endsAt(MiraiTimeToSlintTime(event->getEndTime()));
eventWindow_->show();
});
mainWindow_->global<Backend>().on_delete_event_clicked([&](int sourceId, int eventId) {
auto resource = miraiInstance_->getResourceById(sourceId);
assert(resource);
auto event = resource->getEventById(eventId);
assert(event);
auto day = event->day();
assert(day);
day->deleteEvent(*event);
miraiInstance_->save();
view_.update();
reloadTasks();
});
eventWindow_->on_create([&](NewEventParams newEventParams) {
const Date &date = newEventParams.date;
const std::string dateStr = SlintDateToStdString(date);
auto resource = miraiInstance_->getResourceById(newEventParams.sourceId);
auto day = resource->day(SlintDateToMiraiDate(date));
day->createEvent({
.description = std::string(newEventParams.title),
.date = SlintDateToMiraiDate(newEventParams.date),
.startTime = SlintTimeToMiraiTime(newEventParams.startsAt),
.endTime = SlintTimeToMiraiTime(newEventParams.endsAt),
});
miraiInstance_->save();
view_.update();
reloadTasks();
eventWindow_->hide();
});
eventWindow_->on_save([&](SaveEventParams newEventParams) {
const Date &date = newEventParams.date;
const std::string dateStr = SlintDateToStdString(date);
auto resource = miraiInstance_->getResourceById(newEventParams.sourceId);
assert(resource);
auto event = resource->getEventById(newEventParams.id);
assert(event);
event->setText(std::string(newEventParams.title));
event->setStartTime(SlintTimeToMiraiTime(newEventParams.startsAt));
event->setEndTime(SlintTimeToMiraiTime(newEventParams.endsAt));
// TODO we can't change the date of the event for now.
miraiInstance_->save();
view_.update();
reloadTasks();
eventWindow_->hide();
});
}
void UiState::setupUtilsCallbacks()
{
mainWindow_->global<Backend>().on_formatDate([&](const Date &date) {
std::chrono::year_month_day chronoDate{
std::chrono::year(date.year),
std::chrono::month(date.month),
std::chrono::day(date.day),
};
return std::format("{:%d %B}", chronoDate);
});
}
void UiState::setupCallbacks()
{
mainWindow_->global<Backend>().on_task_clicked([&](int sourceId, int taskId) {
auto resource = miraiInstance_->getResourceById(sourceId);
assert(resource);
auto task = resource->getTaskById(taskId);
assert(task);
task->getState() == mirai::DONE ? task->markAsUndone() : task->markAsDone();
miraiInstance_->save();
});
mainWindow_->global<Backend>().on_source_clicked([&](int index) {
const auto &source = miraiInstance_->getResourceById(index);
const auto &sourceName = source->getName();
if (std::ranges::find(view_.getActiveFilesFilter(), sourceName) ==
view_.getActiveFilesFilter().end()) {
view_.addResourceFilter(sourceName);
} else {
view_.removeResourceFilter(sourceName);
}
view_.update();
reloadTasks();
});
mainWindow_->global<Backend>().on_tag_clicked([&](int index) {
const std::string &tag = miraiInstance_->getTags().at(index);
if (std::ranges::find(view_.getActiveTagsFilter(), tag) ==
view_.getActiveTagsFilter().end()) {
view_.addTagFilter(tag);
} else {
view_.removeTagFilter(tag);
}
view_.update();
reloadTasks();
});
mainWindow_->global<Backend>().on_delete_task_clicked([&](int sourceId, int taskId) {
auto resource = miraiInstance_->getResourceById(sourceId);
assert(resource);
auto task = resource->getTaskById(taskId);
assert(task);
resource->deleteTask(*task);
miraiInstance_->save();
view_.update();
reloadTasks();
});
mainWindow_->global<Backend>().on_open_edit_task_form([&](int sourceId, int taskId) {
auto resource = miraiInstance_->getResourceById(sourceId);
assert(resource);
auto task = resource->getTaskById(taskId);
assert(task);
taskEditWindow_->set_taskResourceIndex(sourceId);
taskEditWindow_->set_taskId(task->id());
taskEditWindow_->set_taskTitle(slint::SharedString(task->getText()));
taskEditWindow_->set_taskDate(MiraiDateToSlintDate(task->day()->getDate()));
taskEditWindow_->on_save([&](SaveTaskData newTaskData) {
auto resource = miraiInstance_->getResourceById(newTaskData.sourceId);
assert(resource);
auto task = resource->getTaskById(newTaskData.id);
assert(task);
const Date &date = newTaskData.date;
const std::string dateStr = SlintDateToStdString(date);
auto taskData = mirai::TodoMdFormat::stringToTask(
"- [ ] " + std::string(newTaskData.title), dateStr
);
task->setText(taskData.text);
if (task->day()->getDate() != SlintDateToMiraiDate(date)) {
auto newDate = resource->day(SlintDateToMiraiDate(date));
newDate->createTask({
.text = taskData.text,
.state = task->getState(),
.tags = taskData.tags,
});
auto oldDate = task->day();
resource->deleteTask(*task);
}
miraiInstance_->save();
view_.update();
reloadTasks();
taskEditWindow_->hide();
});
taskEditWindow_->show();
});
mainWindow_->global<Backend>().on_open_new_task_form([&](OpenNewTaskFormParams params) {
taskEditWindow_->global<Backend>().set_resources(resources_);
taskEditWindow_->global<Backend>().set_tags(tags_);
auto todayDate = std::chrono::year_month_day{
std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())
};
taskEditWindow_->set_taskId(-1);
taskEditWindow_->set_eventId(params.eventId);
taskEditWindow_->set_taskResourceIndex(
params.eventSourceId == -1 ? 0 : params.eventSourceId
);
taskEditWindow_->set_taskTitle("");
if (params.eventId == -1) {
taskEditWindow_->set_taskDate({
.year = static_cast<int>(todayDate.year()),
// Try to directly use `unsigned`
.month = static_cast<int>(static_cast<unsigned>(todayDate.month())),
.day = static_cast<int>(static_cast<unsigned>(todayDate.day())),
});
} else {
auto resource = miraiInstance_->getResourceById(params.eventSourceId);
assert(resource);
auto event = resource->getEventById(params.eventId);
assert(event);
taskEditWindow_->set_taskDate(MiraiDateToSlintDate(event->getDate()));
}
taskEditWindow_->on_create([&](NewTaskData newTaskData) {
const Date &date = newTaskData.date;
const std::string dateStr = SlintDateToStdString(date);
auto resource = miraiInstance_->getResourceById(newTaskData.sourceId);
auto task = mirai::TodoMdFormat::stringToTask(
"- [ ] " + std::string(newTaskData.title), dateStr
);
if (newTaskData.eventId == -1) {
auto day = resource->day(SlintDateToMiraiDate(date));
assert(day);
day->createTask(task);
} else {
auto event = resource->getEventById(newTaskData.eventId);
assert(event);
event->createTask(task);
}
miraiInstance_->save();
view_.update();
reloadTasks();
taskEditWindow_->hide();
});
taskEditWindow_->show();
});
mainWindow_->global<Backend>().on_toggle_show_completed_tasks([&] {
view_.hideCompletedTasks(!view_.shouldHideCompletedTasks());
view_.update();
reloadTasks();
});
mainWindow_->global<Backend>().set_resources(resources_);
mainWindow_->global<Backend>().set_tags(tags_);
mainWindow_->global<Backend>().set_visible_tasks(days_);
setupEventWindowCallbacks();
setupUtilsCallbacks();
}
std::shared_ptr<slint::VectorModel<slint::SharedString>>
stdToSlintStringVector(const std::vector<std::string> &stdVector)
{
auto slintVector = std::make_shared<slint::VectorModel<slint::SharedString>>();
for (const auto &item : stdVector) {
slintVector->push_back(slint::SharedString(item));
}
return slintVector;
}
void UiState::reloadTasks()
{
days_->clear();
if (miraiInstance_->getResources().size() == 0) {
return;
}
auto todayDate = mirai::Date(std::chrono::system_clock::now());
auto &days = view_;
auto slintDays = std::make_shared<slint::VectorModel<Day>>();
for (int dayIndex = 0; dayIndex < days.count(); ++dayIndex) {
auto &currentDay = view_[dayIndex];
auto slintEvents = std::make_shared<slint::VectorModel<Event>>();
auto slintDayTasks = std::make_shared<slint::VectorModel<TaskData>>();
slintDays->push_back(Day{
.sourceId = currentDay.day->source()->id(),
.id = dayIndex,
.date = MiraiDateToSlintDate(currentDay.day->getDate()),
.events = slintEvents,
.tasks = slintDayTasks,
.isLate = currentDay.day->getDate() < todayDate,
.isToday = currentDay.day->getDate() == todayDate,
});
for (int taskIndex = 0; taskIndex < currentDay.filteredTasks.size(); ++taskIndex) {
auto &task = currentDay.filteredTasks.at(taskIndex);
std::vector<slint::SharedString> tags;
std::transform(
task->getTags().begin(), task->getTags().end(), std::back_inserter(tags),
[&](const std::string &tag) {
return slint::SharedString(tag);
}
);
slintDayTasks->push_back({
.sourceId = task->source()->id(),
.id = task->id(),
.title = slint::SharedString(task->getText()),
.checked = task->getState() == mirai::DONE,
.tags = std::make_shared<slint::VectorModel<slint::SharedString>>(tags),
});
}
for (int eventIndex = 0; eventIndex < currentDay.filteredEvents.size(); ++eventIndex) {
auto &currentEvent = currentDay.filteredEvents.at(eventIndex);
auto slintTasks = std::make_shared<slint::VectorModel<TaskData>>();
slintEvents->push_back(Event{
.sourceId = currentEvent.event->day()->source()->id(),
.id = currentEvent.event->id(),
.title = slint::SharedString(currentEvent.event->getText()),
.startsAt = MiraiTimeToSlintTime(currentEvent.event->getStartTime()),
.endsAt = MiraiTimeToSlintTime(currentEvent.event->getEndTime()),
.tasks = slintTasks,
});
for (int taskIndex = 0; taskIndex < currentEvent.filteredTasks.size(); ++taskIndex) {
auto &task = currentEvent.filteredTasks.at(taskIndex);
std::vector<slint::SharedString> tags;
std::transform(
task->getTags().begin(), task->getTags().end(), std::back_inserter(tags),
[&](const std::string &tag) {
return slint::SharedString(tag);
}
);
slintTasks->push_back({
.sourceId = task->source()->id(),
.id = task->id(),
.title = slint::SharedString(task->getText()),
.checked = task->getState() == mirai::DONE,
.tags = std::make_shared<slint::VectorModel<slint::SharedString>>(tags),
});
}
}
}
days_ = slintDays;
mainWindow_->global<Backend>().set_visible_tasks(days_);
}
void UiState::reloadResources()
{
resources_->clear();
for (const auto &resource : miraiInstance_->getResources()) {
resources_->push_back(slint::SharedString(resource->getName()));
}
}
void UiState::reloadTags()
{
tags_->clear();
for (const auto &tag : miraiInstance_->getTags()) {
tags_->push_back(slint::SharedString(tag));
}
}
void UiState::run()
{
mainWindow_->run();
}

41
src/UiState.h Normal file
View 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
*/
#pragma once
#include "appwindow.h"
#include "mirai-core/Mirai.h"
#include "mirai-core/TasksView.h"
#include "slint.h"
class UiState
{
public:
UiState(mirai::Mirai *mirai);
void run();
void reloadResources();
void reloadTags();
void reloadTasks();
private:
void setupCallbacks();
void setupEventWindowCallbacks();
void setupUtilsCallbacks();
std::shared_ptr<slint::VectorModel<slint::SharedString>> resources_;
std::shared_ptr<slint::VectorModel<slint::SharedString>> tags_;
std::shared_ptr<slint::VectorModel<Day>> days_;
slint::ComponentHandle<AppWindow> mainWindow_ = AppWindow::create();
slint::ComponentHandle<TaskEdit> taskEditWindow_ = TaskEdit::create();
slint::ComponentHandle<EventWindow> eventWindow_ = EventWindow::create();
mirai::Mirai *miraiInstance_;
mirai::TasksView view_;
};

47
src/Utils.cpp Normal file
View file

@ -0,0 +1,47 @@
/*
* 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"
std::string formatZeroPadding(const int number)
{
if (number < 10) {
return "0" + std::to_string(number);
}
return std::to_string(number);
}
std::string SlintDateToStdString(const Date &date)
{
return std::to_string(date.year) + "-" + formatZeroPadding(date.month) + "-" +
formatZeroPadding(date.day);
}
mirai::Date SlintDateToMiraiDate(const Date &date)
{
return mirai::Date(
date.year, static_cast<unsigned>(date.month), static_cast<unsigned>(date.day)
);
}
Date MiraiDateToSlintDate(const mirai::Date &date)
{
return {
.year = date.year,
.month = static_cast<int>(date.month),
.day = static_cast<int>(date.day),
};
}
Time MiraiTimeToSlintTime(const mirai::Time &time)
{
return {.hour = time.hour, .minute = time.minute, .second = 0};
}
mirai::Time SlintTimeToMiraiTime(const Time &time)
{
return {.hour = time.hour, .minute = time.minute};
}

18
src/Utils.h Normal file
View file

@ -0,0 +1,18 @@
/*
* 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 once
#include "appwindow.h"
#include "mirai-core/DateTime.h"
#include <string>
std::string formatZeroPadding(const int number);
std::string SlintDateToStdString(const Date &date);
mirai::Date SlintDateToMiraiDate(const Date &date);
Date MiraiDateToSlintDate(const mirai::Date &date);
Time MiraiTimeToSlintTime(const mirai::Time &time);
mirai::Time SlintTimeToMiraiTime(const Time &time);

View file

@ -1,39 +0,0 @@
/*
* 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_BASE_FILE_RESOURCE_H
#define MIRAI_BASE_FILE_RESOURCE_H
#include "core/BaseResource.h"
#include <string>
namespace mirai
{
struct BaseFileResourceConstructor {
std::string name;
std::string path;
};
class BaseFileResource : public BaseResource
{
public:
BaseFileResource(BaseFileResourceConstructor data) : BaseResource(data.name), path(data.path){};
BaseFileResource(BaseFileResource &) = delete;
BaseFileResource(BaseFileResource &&) = delete;
~BaseFileResource() override = default;
const std::string &getPath() const
{
return path;
}
private:
std::string path;
};
} // namespace mirai
#endif

View file

@ -1,52 +0,0 @@
/*
* 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 "BaseResource.h"
#include "TaskItem.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <memory>
#include <ostream>
namespace mirai
{
void BaseResource::setDirty(bool shouldBeDirty)
{
isDirty_ = shouldBeDirty;
}
bool BaseResource::isDirty() const
{
return isDirty_;
}
std::vector<std::unique_ptr<TaskItem>> &BaseResource::getTasks()
{
return tasks;
}
void BaseResource::addTask(TaskItemData taskItem)
{
tasks.push_back(std::make_unique<TaskItem>(this, taskItem));
setDirty(true);
}
void BaseResource::removeTask(const TaskItem *taskToRemove)
{
auto findFunction = [&](const std::unique_ptr<TaskItem> &taskInFilter) {
return taskInFilter.get() == taskToRemove;
};
auto taskToDelete = std::remove_if(tasks.begin(), tasks.end(), findFunction);
if (taskToDelete == tasks.end()) {
return;
}
tasks.erase(taskToDelete);
setDirty(true);
}
} // namespace mirai

View file

@ -1,53 +0,0 @@
/*
* 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_BASE_RESOURCE_H
#define MIRAI_BASE_RESOURCE_H
#include "core/TaskItem.h"
#include <memory>
#include <string>
#include <vector>
namespace mirai
{
class BaseResource
{
public:
BaseResource(std::string name) : name(name) {};
BaseResource(BaseResource &) = delete;
BaseResource(BaseResource &&) = delete;
virtual ~BaseResource() = default;
virtual void save() = 0;
virtual void load() = 0;
void setName(std::string name)
{
this->name = name;
}
const std::string &getName() const
{
return name;
}
void addTask(TaskItemData TaskItem);
void removeTask(const TaskItem *taskToRemove);
std::vector<std::unique_ptr<TaskItem>> &getTasks();
void setDirty(bool shouldBeDirty);
bool isDirty() const;
private:
std::string name;
std::vector<std::unique_ptr<TaskItem>> tasks;
bool isDirty_ = false;
};
} // namespace mirai
#endif

View file

@ -1,91 +0,0 @@
/*
* 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"
#include "TaskItem.h"
#include "cpp-utils/debug.h"
#include "utils.h"
#include <algorithm>
#include <iostream>
#include <memory>
#include <optional>
#include <ostream>
#include <vector>
namespace mirai
{
void Mirai::loadResource(std::unique_ptr<BaseResource> &&resource)
{
resource->load();
resources.push_back(std::move(resource));
reloadTags();
};
void Mirai::unloadAllResources()
{
resources.clear();
}
void Mirai::save()
{
for (auto &resource : resources) {
if (resource->isDirty()) {
resource->save();
resource->setDirty(false);
}
}
reloadTags();
}
void Mirai::removeTask(const TaskItem *taskItem)
{
for (auto &resource : resources) {
resource->removeTask(taskItem);
}
}
std::vector<std::unique_ptr<BaseResource>> &Mirai::getResources()
{
return resources;
}
std::optional<std::reference_wrapper<BaseResource>> Mirai::getResourceByName(const std::string &name
)
{
auto resourceIterator =
std::ranges::find_if(resources, [&](const std::unique_ptr<BaseResource> &resource) {
return resource->getName() == name;
});
if (resourceIterator == resources.end()) {
return std::nullopt;
}
return *(resourceIterator->get());
}
const std::vector<std::string> &Mirai::getTags()
{
return tags;
}
void Mirai::reloadTags()
{
cpputils::debug::Timer reloadingTagsDuration;
tags.clear();
for (auto &resource : resources) {
for (auto &task : resource->getTasks()) {
for (auto &tag : task->getTags()) {
if (!vectorUtils::contains(tags, tag)) {
tags.push_back(tag);
}
}
}
}
reloadingTagsDuration.printTimeElapsed("ReloadingTags");
}
} // namespace mirai

View file

@ -1,42 +0,0 @@
/*
* 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 "TodoMd.h"
#include "core/BaseFileResource.h"
#include <algorithm>
#include <functional>
#include <memory>
#include <optional>
#include <string>
namespace mirai
{
class Mirai
{
public:
void loadResource(std::unique_ptr<BaseResource> &&resource);
void unloadAllResources();
void save();
void removeTask(const TaskItem *taskItem);
std::optional<std::reference_wrapper<BaseResource>> getResourceByName(const std::string &name);
std::vector<std::unique_ptr<BaseResource>> &getResources();
const std::vector<std::string> &getTags();
void reloadTags();
private:
std::vector<std::unique_ptr<BaseResource>> resources;
std::vector<std::string> tags;
};
} // namespace mirai
#endif

View file

@ -1,63 +0,0 @@
/*
* 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_STD_FILE_RESOURCE_H
#define MIRAI_STD_FILE_RESOURCE_H
#include "core/BaseFileResource.h"
#include "core/TodoMd.h"
#include <fstream>
#include <string>
namespace mirai
{
class StdFileResource : public BaseFileResource
{
public:
StdFileResource(BaseFileResourceConstructor data) : BaseFileResource(data){};
StdFileResource(StdFileResource &) = delete;
StdFileResource(StdFileResource &&) = delete;
StdFileResource operator=(StdFileResource &) = delete;
StdFileResource operator=(StdFileResource &&) = delete;
~StdFileResource() override = default;
void save() override
{
std::ofstream file(getPath());
if (!file.is_open()) {
throw std::runtime_error("can't create " + getPath());
}
const std::string content = TodoMdFormat::stringifyTasks(getName(), getTasks());
file << content;
file.close();
};
void load() override
{
std::ifstream file(getPath());
if (!file.is_open()) {
return;
}
std::string content = "";
std::string line;
while (std::getline(file, line)) {
content += line + "\n";
}
file.close();
auto result = TodoMdFormat::parse(content);
for (const auto &task : result.tasks) {
addTask(task);
}
setName(result.name);
};
};
} // namespace mirai
#endif

View file

@ -1,95 +0,0 @@
/*
* 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"
#include "BaseResource.h"
#include "core/utils.h"
#include <iostream>
namespace mirai
{
TaskItem::TaskItem(BaseResource *parent, TaskItemData data) : parent(parent), data(data)
{
}
void TaskItem::markAsDone()
{
data.state = DONE;
onChange();
}
void TaskItem::markAsUndone()
{
data.state = TODO;
onChange();
}
void TaskItem::setDate(const std::string &date)
{
this->data.date = date;
onChange();
}
void TaskItem::setText(const std::string &text)
{
this->data.text = text;
onChange();
}
const std::string &TaskItem::getText() const
{
return data.text;
}
const TaskItemState &TaskItem::getState() const
{
return data.state;
}
const std::string &TaskItem::getDate() const
{
return data.date;
}
const std::string &TaskItem::getStartTime() const
{
return data.startTime;
}
const std::string &TaskItem::getEndTime() const
{
return data.endTime;
}
const Tags &TaskItem::getTags() const
{
return data.tags;
}
bool TaskItem::hasDate() const
{
return isDate(data.date);
}
bool TaskItem::hasTime() const
{
return getStartTime() != "" && getEndTime() != "";
}
bool TaskItem::hasTag(const std::string &tag) const
{
return vectorUtils::contains(data.tags, tag);
}
void TaskItem::onChange()
{
if (parent) {
parent->setDirty(true);
}
}
} // namespace mirai

View file

@ -1,70 +0,0 @@
/*
* 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 "using.h"
#include <functional>
#include <string>
namespace mirai
{
enum TaskItemState { TODO, DONE };
struct TaskItemData {
std::string text;
TaskItemState state;
std::string date;
std::string startTime;
std::string endTime;
Tags tags;
};
class BaseResource;
class TaskItem
{
friend BaseResource;
private:
public:
TaskItem(BaseResource *parent, const TaskItemData data);
TaskItem &operator=(const TaskItemData &newData)
{
data = newData;
onChange();
return *this;
};
void markAsDone();
void markAsUndone();
void setDate(const std::string &date);
void setText(const std::string &text);
const std::string &getLine() const;
const std::string &getText() const;
const TaskItemState &getState() const;
const std::string &getDate() const;
const std::string &getStartTime() const;
const std::string &getEndTime() const;
const Tags &getTags() const;
bool hasTag(const std::string &tag) const;
bool hasDate() const;
bool hasTime() const;
private:
void onChange();
BaseResource *parent;
TaskItemData data;
};
} // namespace mirai
#endif

View file

@ -1,124 +0,0 @@
/*
* 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"
#include "core/Mirai.h"
#include "utils.h"
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
namespace mirai
{
TasksView::TasksView(Mirai *miraiInstance) : mirai(miraiInstance)
{
if (!miraiInstance) {
throw std::runtime_error("NULL pointer passed in TasksView");
}
update();
}
TaskItem &TasksView::operator[](int index)
{
return *(tasksToShow.at(index));
}
size_t TasksView::count() const
{
return tasksToShow.size();
}
void TasksView::update()
{
tasksToShow.clear();
for (auto &file : mirai->getResources()) {
if (resourcesFilter.size() > 0 &&
!vectorUtils::contains(resourcesFilter, file->getName())) {
continue;
}
for (auto &task : file->getTasks()) {
if (tagsFilter.size() > 0 && !vectorUtils::containsAll(tagsFilter, task->getTags())) {
continue;
}
tasksToShow.push_back(task.get());
}
}
std::ranges::sort(tasksToShow, [](const TaskItem *t1, const TaskItem *t2) {
if (t1->hasDate() && !t2->hasDate()) {
return true;
} else if (!t1->hasDate() && t2->hasDate()) {
return false;
}
if (t1->getDate() < t2->getDate()) {
return true;
} else if (t1->getDate() > t2->getDate()) {
return false;
}
if (t1->hasTime() && !t2->hasTime()) {
return true;
} else if (!t1->hasTime() && t2->hasTime()) {
return false;
}
return t1->getStartTime() < t2->getStartTime();
});
}
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::addResourceFilter(const std::string &fileName)
{
resourcesFilter.push_back(fileName);
update();
}
void TasksView::removeResourceFilter(const std::string &fileName)
{
resourcesFilter.erase(std::remove_if(
resourcesFilter.begin(), resourcesFilter.end(),
[&](const auto &fileInFilter) {
return fileName == fileInFilter;
}
));
update();
}
void TasksView::removeFilters()
{
tagsFilter.clear();
update();
}
const std::vector<std::string> &TasksView::getActiveTagsFilter()
{
return tagsFilter;
}
const std::vector<std::string> &TasksView::getActiveFilesFilter()
{
return resourcesFilter;
}
} // namespace mirai

View file

@ -1,42 +0,0 @@
/*
* 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 "core/Mirai.h"
#include "using.h"
#include <cstddef>
#include <string>
namespace mirai
{
class TasksView
{
public:
TasksView(Mirai *mirai);
TaskItem &operator[](int index);
size_t count() const;
void update();
void addTagFilter(const std::string &tag);
void removeTagFilter(const std::string &tag);
void addResourceFilter(const std::string &fileName);
void removeResourceFilter(const std::string &fileName);
void removeFilters();
const Tags &getActiveTagsFilter();
const std::vector<std::string> &getActiveFilesFilter();
private:
Mirai *mirai;
std::vector<TaskItem *> tasksToShow;
Tags tagsFilter;
std::vector<std::string> resourcesFilter;
};
} // namespace mirai
#endif

View file

@ -1,88 +0,0 @@
/*
* 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"
#include "TaskItem.h"
#include "cpp-utils/debug.h"
#include "utils.h"
#include <fstream>
#include <iostream>
#include <memory>
#include <string_view>
namespace mirai
{
Tags TodoMdFormat::extractTagsFromMetadata(std::string metadata)
{
Tags tags;
std::regex regex("(#([a-zA-Z0-1]+))");
std::smatch matches;
while (std::regex_search(metadata, matches, regex)) {
if (!vectorUtils::contains(tags, matches[2].str())) {
tags.push_back(matches[2]);
}
metadata = matches.suffix();
}
return tags;
}
std::string TodoMdFormat::fieldWithSpace(const std::string &field)
{
if (field.length() == 0) {
return "";
}
return (field + " ");
}
TaskItemData TodoMdFormat::stringToTask(const std::string &str, const std::string &date)
{
std::smatch matches;
std::regex regex("- \\[(\\s|X)\\] (([0-9]{2}:[0-9]{2})-([0-9]{2}:[0-9]{2}) > )?(.*?)( -- (.*))?"
);
std::regex_match(str, matches, regex);
/*std::cout << "line " << str << std::endl;*/
/*std::cout << "M 0 " << matches[0] << std::endl;*/
/*std::cout << "M 1 " << matches[1] << std::endl;*/
/*std::cout << "M 2 " << matches[2] << std::endl;*/
/*std::cout << "M 3 " << matches[3] << std::endl;*/
/*std::cout << "M 4 " << matches[4] << std::endl;*/
/*std::cout << "M 5 " << matches[5] << std::endl;*/
/*std::cout << "M 6 " << matches[6] << std::endl;*/
/*std::cout << "M 7 " << matches[7] << std::endl;*/
std::string text = stringUtils::trim(matches[5]);
TaskItemData taskItem{
.text = text,
.state = str.substr(0, 5) == "- [X]" ? DONE : TODO,
.date = date,
.startTime = matches[3],
.endTime = matches[4],
.tags = extractTagsFromMetadata(matches[7])
};
return taskItem;
}
std::string TodoMdFormat::taskToString(const TaskItem &task)
{
std::string str = task.getText();
if (task.getTags().size() > 0) {
str += " --";
for (const std::string &tag : task.getTags()) {
str += " #" + tag;
}
}
if (task.getStartTime() != "" && task.getEndTime() != "") {
str = task.getStartTime() + "-" + task.getEndTime() + " > " + str;
}
str = (task.getState() == DONE ? "- [X] " : "- [ ] ") + str;
return str;
}
} // namespace mirai

View file

@ -1,92 +0,0 @@
/*
* 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 "cpp-utils/debug.h"
#include <sstream>
#include <string>
#include <vector>
namespace mirai
{
struct MiraiMarkdownFormatParseResult {
std::string name;
std::vector<TaskItemData> tasks;
};
class TodoMdFormat
{
public:
static std::string
stringifyTasks(const std::string &name, const std::vector<std::unique_ptr<TaskItem>> &tasks)
{
std::string result = "";
std::string currentDate = "";
result += "# " + name + "\n";
for (const auto &task : tasks) {
if (currentDate != task->getDate()) {
currentDate = task->getDate();
result += "\n## " + (task->getDate() != "" ? task->getDate() : "No date") + "\n\n";
}
result += taskToString(*task) + '\n';
}
return result;
};
static MiraiMarkdownFormatParseResult parse(const std::string &content)
{
cpputils::debug::Timer readMdFormatDuration;
std::vector<TaskItem> taskItems;
std::string line;
std::stringstream contentStream(content);
if (!std::getline(contentStream, 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 = "";
std::vector<TaskItemData> result;
cpputils::debug::Timer stringToTaskDuration;
stringToTaskDuration.reset();
cpputils::debug::Timer gelinesDuration;
while (std::getline(contentStream, line)) {
if (std::string_view{line.data(), 3} == "## ") {
currentDate = line.substr(3);
} else if (std::string_view{line.data(), 5} == "- [ ]" || std::string_view{line.data(), 5} == "- [X]") {
stringToTaskDuration.start();
TaskItemData taskItemData = stringToTask(line, currentDate);
stringToTaskDuration.stop();
taskItemData.date = currentDate;
result.push_back(taskItemData);
}
}
gelinesDuration.printTimeElapsed("getlinesDuration");
stringToTaskDuration.printTimeElapsed("stringToTaskDuration");
readMdFormatDuration.printTimeElapsed("Reading MD File duration");
return {.name = name, .tasks = result};
}
static std::string taskToString(const TaskItem &task);
static TaskItemData stringToTask(const std::string &str, const std::string &date);
private:
static std::string fieldWithSpace(const std::string &field);
static TaskItem parseLine(const std::string &line);
static Tags extractTagsFromMetadata(std::string metadata);
};
} // namespace mirai
#endif

View file

@ -1,94 +0,0 @@
/*
* 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

View file

@ -1,19 +0,0 @@
/*
* 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 <memory>
#include <string>
#include <vector>
namespace mirai
{
using Tags = std::vector<std::string>;
}
#endif

View file

@ -1,38 +0,0 @@
/*
* 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"
#include <cctype>
namespace mirai
{
bool isDate(const std::string &dateStr)
{
// std regex are really slow
/*std::regex regex("[0-9]{4}-[0-9]{2}-[0-9]{2}");*/
/*return std::regex_match(dateStr, regex);*/
// Quick hand made check instead of regex until I rework date to use timestamp or std::chrono
if (dateStr.length() != 10) {
return false;
}
if (dateStr[4] != '-' || dateStr[7] != '-') {
return false;
}
for (int i = 0; i < dateStr.length(); ++i) {
if (i == 4 || i == 7) {
// These are the '-' characters
continue;
}
if (!isdigit(dateStr[i])) {
return false;
}
}
return true;
}
} // namespace mirai

View file

@ -1,25 +0,0 @@
/*
* 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 <cpp-utils/string.h>
#include <cpp-utils/vector.h>
#include <locale>
#include <regex>
namespace mirai
{
namespace stringUtils = cpputils::string;
namespace vectorUtils = cpputils::vector;
bool isDate(const std::string &dateStr);
} // namespace mirai
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -4,48 +4,29 @@
* The license can be found in the LICENSE file or at https://www.gnu.org/licenses/gpl-3.0.txt
*/
#include <QtCore/QRect>
#include <QtGui/QFont>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
#include <QtQml/QQmlApplicationEngine>
#include <exception>
#include <iostream>
#include <ostream>
#include "UiState.h"
#include "mirai-core/Config.h"
#include "mirai-core/Mirai.h"
#include "mirai-core/StdFileResource.h"
#include "nlohmann/json.hpp"
#include <cstdlib>
#include <string>
int main(int argc, char *argv[])
int main(int argc, char **argv)
{
try {
std::cout << "Mirai started" << std::endl;
QGuiApplication app(argc, argv);
mirai::Config config(std::string(getenv("HOME")) + "/.config/mirai/config.json");
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));
mirai::Mirai mirai;
QFont font("Helvetica");
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
for (const auto &sourceFilePath : config.sources()) {
auto file = std::make_unique<mirai::StdFileResource>(
mirai::BaseFileResourceConstructor{.name = sourceFilePath, .path = sourceFilePath}
);
engine.load(url);
return app.exec();
} catch (const std::exception &e) {
std::cout << e.what() << std::endl;
mirai.loadResource(std::move(file));
}
UiState uiState{&mirai};
uiState.run();
return 0;
}

View file

@ -1,129 +0,0 @@
/*
* 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 bool isPhone: root.width < root.height
property string selectedView: "list"
Backend {
id: backend
}
ThemeLoader {
id: colorPalette
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
function openSettings() {
filesForm.reset()
filesFormPopup.open()
}
function newTask() {
taskForm.taskToEdit = undefined
taskForm.taskToEditIndex = -1
taskFormPopup.open()
}
function editTask(task, taskIndex) {
taskForm.taskToEdit = task
taskForm.taskToEditIndex = taskIndex
taskFormPopup.open()
}
Component {
id: sideMenuComponent
SideMenu {
id: sideMenu
}
}
Component {
id: mainPanelComponent
MainPanel {
}
}
RowLayout {
id: desktopLayout
anchors.fill: parent
spacing: 0
Loader {
sourceComponent: sideMenuComponent
Layout.fillHeight: true
Layout.fillWidth: root.isPhone
}
Loader {
sourceComponent: mainPanelComponent
Layout.fillWidth: true
Layout.fillHeight: true
}
}
SwipeView {
id: phoneLayout
anchors.fill: parent
spacing: 0
Loader {
sourceComponent: sideMenuComponent
Layout.fillHeight: true
}
Loader {
sourceComponent: mainPanelComponent
Layout.fillWidth: true
Layout.fillHeight: true
}
}
function setFittingLayout() {
if (root.isPhone) {
desktopLayout.visible = false
phoneLayout.visible = true
phoneLayout.setCurrentIndex(1)
} else {
desktopLayout.visible = true
phoneLayout.visible = false
}
}
onWidthChanged: setFittingLayout()
Component.onCompleted: setFittingLayout()
Modal {
id: taskFormPopup
fullScreen: root.isPhone
TaskForm {
id: taskForm
width: parent.width
onConfirmed: {
taskFormPopup.close()
}
onCanceled: {
taskFormPopup.close()
}
}
}
}

View file

@ -1,103 +0,0 @@
/*
* 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 {
color: colorPalette.selected.background
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 16
TabSelector {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
tabs: [
{
label: "Todo",
onClick: () => {
backend.hideCompletedTasks(true) // Little workaround for now.
root.selectedView = "list"
},
selected: root.selectedView === "list"
},
{
label: "Calendar",
onClick: () => {
backend.hideCompletedTasks(false) // Little workaround for now.
root.selectedView = "calendar"
},
selected: root.selectedView === "calendar"
}
]
}
RowLayout {
AppButton {
icon.source: "qrc:/qt/qml/Mirai/src/images/add.png"
icon.color: colorPalette.selected.green
text: "Add task"
onClicked: {
root.newTask()
}
}
AppButton {
icon.source: backend.shouldHideCompletedTasks ? "qrc:/qt/qml/Mirai/src/images/not-visible.png" : "qrc:/qt/qml/Mirai/src/images/visible.png"
text: `Completed tasks`
onClicked: {
backend.hideCompletedTasks(!backend.shouldHideCompletedTasks)
}
}
}
Component {
id: listViewComponent
ListView {
}
}
Component {
id: calendarViewComponent
CalendarView {
}
}
Component {
id: gettingStartedComponent
AppText {
text: "You currently have no files loaded, you can add them by clicking on the cog icon on the left pane"
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Loader {
sourceComponent: backend.tasks.length === 0 ? gettingStartedComponent
: selectedView === "list" ? listViewComponent
: selectedView === "calendar" ? calendarViewComponent
: undefined
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View file

@ -1,159 +0,0 @@
/*
* 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
Rectangle {
color: colorPalette.selected.pane
implicitWidth: childrenRect.width + 20 + 30
ColumnLayout {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 10
RowLayout {
AppText {
text: "Files"
font.pixelSize: 32
}
Item { Layout.fillWidth: true }
AppIcon {
icon.source: "qrc:/qt/qml/Mirai/src/images/settings.png"
icon.color: colorPalette.selected.textPlaceholder
onClicked: {
filesForm.reset();
filesFormPopup.open();
}
}
}
Item { Layout.preferredHeight: 16 }
Repeater {
model: backend.files
Rectangle {
Layout.preferredHeight: childrenRect.height
Layout.fillWidth: true
color: backend.activeResourcesFilter.includes(modelData.name) ? colorPalette.selected.filterSelected : mouse.hovered ? colorPalette.selected.filterHovered : "transparent"
radius: 4
AppText {
text: modelData.name
padding: 4
}
MouseArea {
anchors.fill: parent
onClicked: {
if (backend.activeResourcesFilter.includes(modelData.name)) {
backend.removeResourceFilter(modelData.name)
} else {
backend.addResourceFilter(modelData.name)
}
}
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
}
}
Item { Layout.preferredHeight: 16 }
RowLayout {
AppText {
text: "Tags"
font.pixelSize: 32
}
Item { Layout.fillWidth: true }
AppIcon {
icon.source: "qrc:/qt/qml/Mirai/src/images/settings.png"
icon.color: colorPalette.selected.textPlaceholder
onClicked: {
tagsForm.reset();
tagsFormPopup.open();
}
}
}
Item { Layout.preferredHeight: 16 }
Repeater {
model: backend.tags
Rectangle {
Layout.preferredHeight: childrenRect.height
Layout.fillWidth: true
color: backend.activeTagsFilter.includes(modelData.name) ? colorPalette.selected.filterSelected : mouse.hovered ? colorPalette.selected.filterHovered : "transparent"
radius: 4
QtObject {
id: internal
}
AppText {
text: modelData.name
color: {
return modelData.color
}
padding: 4
}
MouseArea {
anchors.fill: parent
onClicked: {
if (backend.activeTagsFilter.includes(modelData.name)) {
backend.removeTagFilter(modelData.name)
} else {
backend.addTagFilter(modelData.name)
}
}
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
}
}
Item {
Layout.fillHeight: true
}
Modal {
id: filesFormPopup
fullScreen: root.isPhone
FilesForm {
id: filesForm
width: parent.width
onConfirmed: (filesPath) => {
filesFormPopup.close()
console.log(filesPath)
backend.saveFilesPath(filesPath)
}
}
}
Modal {
id: tagsFormPopup
fullScreen: root.isPhone
TagsConfigForm {
id: tagsForm
width: parent.width
onConfirmed: (tags) => {
tagsFormPopup.close()
backend.saveTagsColor(tags)
}
}
}
}
}

View file

@ -1,49 +0,0 @@
/*
* 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 Mirai
Item {
property QtObject selected: OneDark
function applyCustomThemeFromConfig() {
//console.log(backend.getThemeColor("text"))
console.log(Object.keys(selected))
selected.background = getThemeColor("background")
selected.pane = getThemeColor("pane")
selected.text = getThemeColor("text")
selected.textPlaceholder = getThemeColor("textPlaceholder")
selected.accent = getThemeColor("accent")
selected.fieldBackground = getThemeColor("fieldBackground")
selected.buttonIcon = getThemeColor("buttonIcon")
selected.buttonBackground = getThemeColor("buttonBackground")
selected.buttonHovered = getThemeColor("buttonHovered")
selected.filterHovered = getThemeColor("filterHovered")
selected.filterSelected = getThemeColor("filterSelected")
selected.modalBorder = getThemeColor("modalBorder")
selected.calendarLines = getThemeColor("calendarLines")
selected.calendarCurrentTime = getThemeColor("calendarCurrentTime")
selected.calendarEvent = getThemeColor("calendarEvent")
selected.green = getThemeColor("green")
selected.red = getThemeColor("red")
}
// Proxy function for backend.getThemeColor that do not override the value if
// no custom color are defined for this element
function getThemeColor(element) {
let elementColor = backend.getThemeColor(element)
if (!elementColor) {
return selected[element];
}
return elementColor;
}
Component.onCompleted: {
applyCustomThemeFromConfig()
}
}

View file

@ -1,44 +0,0 @@
/*
* 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
property bool noBackgroundColor: false
icon.color: colorPalette.selected.buttonIcon
// I have a different behavior when setting padding for Android
padding: root.isPhone ? undefined : 8
contentItem: IconLabel {
spacing: control.spacing
mirrored: control.mirrored
display: control.display
icon: control.icon
text: control.text
font: control.font
color: colorPalette.selected.text
}
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
}
}

View file

@ -1,41 +0,0 @@
/*
* 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: colorPalette.selected.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.green : colorPalette.selected.text
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 10
implicitHeight: 10
radius: 999
color: control.checked ? colorPalette.selected.green : colorPalette.selected.fieldBackground
}
}
}

View file

@ -1,27 +0,0 @@
/*
* 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
ComboBox {
id: control
contentItem: AppText {
anchors.fill: parent
text: control.currentText
verticalAlignment: Text.AlignVCenter
leftPadding: 10
}
background: Rectangle {
implicitWidth: 200
implicitHeight: 32
color: colorPalette.selected.fieldBackground
radius: 4
}
}

View file

@ -1,22 +0,0 @@
/*
* 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: colorPalette.selected.buttonIcon
background: Rectangle {
color: "transparent"
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
}

View file

@ -1,21 +0,0 @@
/*
* 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
TextField {
color: colorPalette.selected.text
placeholderTextColor: colorPalette.selected.textPlaceholder
leftPadding: 10
implicitHeight: 32
background: Rectangle {
color: colorPalette.selected.fieldBackground
radius: 4
}
}

View file

@ -1,13 +0,0 @@
/*
* 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: colorPalette.selected.text
}

View file

@ -1,194 +0,0 @@
/*
* 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 {
property int numberOfDaysPerPage: 7
id: control
spacing: 32
QtObject {
id: internal
property date weekStartDate: {
const firstDayOfTheWeek = 1 // 1 = Monday, hardcoded for now
const date = new Date()
let firstDayOfTheWeekDelta = date.getDay() - firstDayOfTheWeek
if (firstDayOfTheWeekDelta < 0) {
firstDayOfTheWeekDelta += 7
}
date.setDate(date.getDate() - firstDayOfTheWeekDelta)
return date
}
}
RowLayout {
spacing: 12
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
AppButton {
text: "<"
onClicked: {
internal.weekStartDate.setDate(internal.weekStartDate.getDate() - control.numberOfDaysPerPage)
}
}
AppText {
text: capitalize(internal.weekStartDate.toLocaleDateString(Qt.locale(), "MMMM"))
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
font.pointSize: 24
}
AppButton {
text: ">"
onClicked: {
internal.weekStartDate.setDate(internal.weekStartDate.getDate() + control.numberOfDaysPerPage)
}
}
}
RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
Layout.fillHeight: true
spacing: 0
AppText {
text: ""
}
Rectangle {
color: "transparent"
Layout.preferredWidth: childrenRect.width + 10
Layout.fillHeight: true
Repeater {
model: 23 // Skip 00:00
AppText {
text: `${index + 1}:00`
y: (parent.height / 24 * (index + 1)) - (height / 2)
verticalAlignment: Text.AlignVCenter
}
}
}
}
Repeater {
model: control.numberOfDaysPerPage
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
property date day: new Date(new Date(internal.weekStartDate).setDate(internal.weekStartDate.getDate() + index))
color: "transparent"
Rectangle {
color: colorPalette.selected.calendarLines
height: parent.height
width: 1
anchors.left: parent.left
}
ColumnLayout {
anchors.fill: parent
spacing: 0
AppText {
Layout.fillWidth: true
text: capitalize(day.toLocaleDateString(Qt.locale(), "dddd dd"))
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
Rectangle {
color: "transparent"
Layout.fillWidth: true
Layout.fillHeight: true
id: daysSurface
property real hourHeight: daysSurface.height / 24
Repeater {
model: 23 // Skip 00:00
Rectangle {
color: colorPalette.selected.calendarLines
height: 1
width: daysSurface.width
y: daysSurface.hourHeight * (index + 1)
}
}
Repeater {
model: backend.tasks.filter(task => {
if (task.startTime == "" || task.endTime == "") {
return false
}
const date = new Date(internal.weekStartDate)
date.setDate(internal.weekStartDate.getDate() + index)
return task.date === date.toLocaleDateString(Qt.locale(), "yyyy-MM-dd")
})
Rectangle {
property string name: modelData.text
property int startTime: parseInt(modelData.startTime)
property int endTime: parseInt(modelData.endTime)
color: colorPalette.selected.calendarEvent
anchors.right: parent.right
anchors.left: parent.left
anchors.rightMargin: 2
anchors.leftMargin: 2
radius: 4
y: daysSurface.hourHeight * startTime
height: (endTime - startTime) * daysSurface.hourHeight - 4
Rectangle {
anchors.fill: parent
color: backend.getTagColor(modelData.tags[0])
opacity: 0.2
}
Rectangle {
color: backend.getTagColor(modelData.tags[0])
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
topLeftRadius: parent.radius
bottomLeftRadius: parent.radius
width: 6
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 8
anchors.leftMargin: 12
AppText {
color: backend.getTagColor(modelData.tags[0])
Layout.fillWidth: true
Layout.fillHeight: true
text: name
wrapMode: Text.Wrap
}
}
}
}
Rectangle {
color: day.getFullYear() === new Date().getFullYear() &&
day.getMonth() === new Date().getMonth() &&
day.getDate() === new Date().getDate() ? colorPalette.selected.calendarCurrentTime : "transparent"
height: 2
width: parent.width
y: daysSurface.hourHeight * (new Date().getHours() + (new Date().getMinutes() / 60))
}
}
}
}
}
}
}

View file

@ -1,76 +0,0 @@
/*
* 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: colorPalette.selected.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')
newTodoDate.text = formattedDate
datePicker.close()
}
onReset: {
newTodoDate.text = ""
datePicker.close()
}
}
}
}
}

View file

@ -1,124 +0,0 @@
/*
* 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)
}

View file

@ -1,26 +0,0 @@
/*
* 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
Popup {
property bool fullScreen: false
parent: Overlay.overlay
width: parent.width * (fullScreen ? 1 : 0.75)
height: fullScreen ? parent.height : undefined
x: Math.round((parent.width - width) / 2)
y: fullScreen ? 0 : 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
}
}

View file

@ -1,58 +0,0 @@
/*
* 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 ? colorPalette.selected.filterHovered : "transparent"
implicitHeight: 2
}
}
MouseArea {
anchors.fill: parent
onClicked: {
modelData?.onClick()
}
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
}
}
}

View file

@ -1,32 +0,0 @@
/*
* 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 Mirai
Rectangle {
id: control
property string text
property string backgroundColor
property string textColor
color: backgroundColor
implicitWidth: childrenRect.width
implicitHeight: childrenRect.height
radius: 8
QtObject {
id: internal
}
AppText {
color: backend.getTagColor(control.text)
padding: 2
leftPadding: 6
rightPadding: 6
text: control.text
}
}

View file

@ -1,45 +0,0 @@
/*
* 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 taskItem task
function getFormatedText() {
if (task?.time && task.time != "") {
return `<font color=\"${colorPalette.selected.textPlaceholder}\">${task.time} \></font> ${task.text}`
}
return task.text
}
AppCheckbox {
id: checkbox
text: control.getFormatedText()
checked: task.state === 'DONE'
//textComponent.font.pointSize: 14
textComponent.color: task.date < internal.todayDate ? colorPalette.selected.red
// : task.date === internal.todayDate ? colorPalette.selected.palette.sapphire
: colorPalette.selected.text
onClicked: {
backend.updateTodo(index, modelData.state === 'DONE' ? "TODO" : "DONE", modelData.text, modelData.date)
}
}
Repeater {
model: task.tags
Tag {
Layout.alignment: Qt.AlignVCenter
text: modelData
backgroundColor: colorPalette.selected.fieldBackground
textColor: colorPalette.selected.accent
}
}
}

View file

@ -1,79 +0,0 @@
/*
* 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 QtQuick.Dialogs
import Mirai
// WIP
ColumnLayout {
id: form
spacing: 6
signal confirmed(paths: var)
function reset() {
internal.paths = []
internal.paths = backend.files.map(file => file.path)
}
QtObject {
id: internal
property var paths
}
AppText {
text: "Files"
font.pixelSize: 32
}
Item {
Layout.preferredHeight: 32
}
Repeater {
model: internal.paths
ColumnLayout {
AppLineEdit {
text: modelData
onTextChanged: {
internal.paths[index] = text
}
}
}
}
AppButton {
text: "Add"
icon.source: "qrc:/qt/qml/Mirai/src/images/add.png"
icon.color: colorPalette.selected.green
onClicked: {
fileDialog.open()
}
}
FileDialog {
id: fileDialog
onAccepted: {
console.log(selectedFile.toString())
internal.paths = [...internal.paths, selectedFile.toString().replace(/^file:\/\//, "")]
}
}
Item {
Layout.preferredHeight: 32
}
AppButton {
text: "Save"
onClicked: {
form.confirmed(internal.paths)
}
}
}

View file

@ -1,77 +0,0 @@
/*
* 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 QtQuick.Dialogs
import Mirai
ColumnLayout {
id: form
spacing: 6
signal confirmed(tags: var)
function reset() {
internal.tags = Qt.binding(function () { return [] })
internal.tags = Qt.binding(function () { return backend.tags.map(tag => {
return {name: tag.name, color: tag.color}
})})
}
QtObject {
id: internal
property var tags: []
}
Repeater {
id: tagsList
model: internal.tags
RowLayout {
Rectangle {
id: newTagColor
color: colorDialog.selectedColor
Layout.fillHeight: true
Layout.preferredWidth: 32
MouseArea {
anchors.fill: parent
onClicked: {
colorDialog.open()
}
HoverHandler {
id: mouse
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
cursorShape: Qt.PointingHandCursor
}
}
}
ColorDialog {
id: colorDialog
selectedColor: modelData.color
onAccepted: {
colorDialog.selectedColor = selectedColor
internal.tags[index].color = selectedColor
}
}
AppText {
text: modelData.name
color: colorDialog.selectedColor
}
}
}
AppButton {
text: "Save"
onClicked: {
form.confirmed(internal.tags)
}
}
}

View file

@ -1,95 +0,0 @@
/*
* 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
signal canceled
onTaskToEditChanged: {
newTodoContent.text = taskToEdit?.rawFormat ?? "- [ ] "
newTodoDate.text = taskToEdit?.date ?? ""
}
function createTask() {
if (taskToEdit && taskToEditIndex !== undefined) {
backend.updateTodoFromRawFormat(taskToEditIndex, newTodoContent.text, newTodoDate.text)
} else {
backend.addTodoFromRawFormat(
file.currentValue,
newTodoContent.text,
newTodoDate.text != "" ? newTodoDate.text : "No date"
)
}
form.confirmed()
}
AppText {
text: "New/Edit task"
}
AppComboBox {
id: file
textRole: "text"
valueRole: "value"
// Set the initial currentIndex to the value stored in the backend.
Component.onCompleted: currentIndex = 0
model: backend.files.map(file => (
{ value: file.path, text: qsTr(file.name) }
))
onActivated: {
console.log(currentValue)
}
}
DateField {
id: newTodoDate
text: taskToEdit?.date ?? ""
textFieldComponent.placeholderText: "No date"
Layout.fillWidth: true
}
AppLineEdit {
id: newTodoContent
Layout.fillWidth: true
placeholderText: "Enter your new task..."
text: taskToEdit?.rawFormat ?? ""
Keys.onReturnPressed: {
if (newTodoContent.text == "") {
return
}
createTask()
}
}
RowLayout {
spacing: 8
AppButton {
text: "Create"
onClicked: {
createTask()
}
}
AppButton {
text: "Cancel"
onClicked: {
form.canceled()
}
}
}
}

View file

@ -1,38 +0,0 @@
/*
* 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"
}

View file

@ -1,27 +0,0 @@
/*
* 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: OneDark
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
property string calendarLines: CatppuccinFrappe.surface0
property string calendarCurrentTime: CatppuccinFrappe.red
property string calendarEvent: CatppuccinFrappe.overlay0
}

View file

@ -1,55 +0,0 @@
/*
* 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
//black = "#181a1f",
//bg0 = "#282c34",
//bg1 = "#31353f",
//bg2 = "#393f4a",
//bg3 = "#3b3f4c",
//bg_d = "#21252b",
//bg_blue = "#73b8f1",
//bg_yellow = "#ebd09c",
//fg = "#abb2bf",
//purple = "#c678dd",
//green = "#98c379",
//orange = "#d19a66",
//blue = "#61afef",
//yellow = "#e5c07b",
//cyan = "#56b6c2",
//red = "#e86671",
//grey = "#5c6370",
//light_grey = "#848b98",
//dark_cyan = "#2b6f77",
//dark_red = "#993939",
//dark_yellow = "#93691d",
//dark_purple = "#8a3fa0",
//diff_add = "#31392b",
//diff_delete = "#382b2c",
//diff_change = "#1c3448",
//diff_text = "#2c5372",
QtObject {
property string background: "#282c34"
property string pane: "#21252b"
property string text: "#abb2bf"
property string textPlaceholder: "#5c6370"
property string accent: "#2b6f77"
property string fieldBackground: "#393f4a"
property string buttonIcon: "#2b6f77"
property string buttonBackground: "#31353f"
property string buttonHovered: "#3b3f4c"
property string filterHovered: "#5c6370"
property string filterSelected: "#3b3f4c"
property string modalBorder: "#8a3fa0"
property string calendarLines: "#3b3f4c"
property string calendarCurrentTime: "#e86671"
property string calendarEvent: "#3b3f4c"
property string green: "#98c379"
property string red: "#e86671"
}

View file

@ -1,24 +0,0 @@
/*
* 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 {
Rectangle {
color: "transparent"
Layout.preferredWidth: parent.width
Layout.fillHeight: true
Calendar {
width: parent.width
height: parent.height
numberOfDaysPerPage: root.isPhone ? 1 : 7
}
}
}

View file

@ -1,99 +0,0 @@
/*
* 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
ScrollView {
id: scroll
clip: true
contentHeight: layout.height
ColumnLayout {
id: layout
anchors.right: parent.right
anchors.left: parent.left
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.red
// : 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
onClicked: {
contextMenu.popup()
}
}
}
}
}
}