mirror of
https://codeberg.org/vyn/mirai.git
synced 2025-07-02 17:23:20 +00:00
Switch from Qt6 to Slint
This commit is contained in:
parent
f8be14bcf8
commit
63bf267a22
107 changed files with 27532 additions and 2896 deletions
433
src/UiState.cpp
Normal file
433
src/UiState.cpp
Normal 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 ¤tDay = 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 ¤tEvent = 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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue