mirror of
https://codeberg.org/vyn/mirai.git
synced 2025-07-01 17:03:19 +00:00
Add a Calendar view on the right side
This commit is contained in:
parent
a80515ff90
commit
f1ac8a42d1
18 changed files with 406 additions and 130 deletions
|
@ -36,6 +36,8 @@ class MarkdownDataProvider : public DataProvider
|
|||
std::string toMarkdown();
|
||||
MarkdownData parseMarkdown(const std::string &content);
|
||||
|
||||
std::string path() const;
|
||||
|
||||
void save() override;
|
||||
void load() override;
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ class Source
|
|||
void load();
|
||||
|
||||
std::string name() const;
|
||||
std::string type() const;
|
||||
DataProvider *dataProvider();
|
||||
|
||||
void createTask(const createTaskParams &task);
|
||||
void removeTask(const Task &task);
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
namespace mirai
|
||||
{
|
||||
|
||||
std::string MarkdownDataProvider::path() const
|
||||
{
|
||||
return filePath_;
|
||||
}
|
||||
|
||||
std::string MarkdownDataProvider::name() const
|
||||
{
|
||||
return data.name;
|
||||
|
|
13
external/mirai-core/src/Source.cpp
vendored
13
external/mirai-core/src/Source.cpp
vendored
|
@ -82,6 +82,8 @@ void Source::createEvent(const createEventParams &eventToCreate)
|
|||
.id = generateUniqueId(),
|
||||
.dayId = day.value().id,
|
||||
.title = eventToCreate.title,
|
||||
.startsAt = eventToCreate.startsAt,
|
||||
.endsAt = eventToCreate.endsAt,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -144,4 +146,15 @@ std::string Source::name() const
|
|||
{
|
||||
return data->name();
|
||||
}
|
||||
|
||||
std::string Source::type() const
|
||||
{
|
||||
// There is only 1 type for now
|
||||
return "MarkdownFile";
|
||||
}
|
||||
|
||||
DataProvider *Source::dataProvider()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
} // namespace mirai
|
||||
|
|
3
external/mirai-core/src/View.cpp
vendored
3
external/mirai-core/src/View.cpp
vendored
|
@ -43,6 +43,9 @@ std::vector<Task> View::getTasksForDate(const Date &date)
|
|||
|
||||
std::vector<Event> View::getEventsForDate(const Date &date)
|
||||
{
|
||||
if (!dates.contains(date)) {
|
||||
return {};
|
||||
}
|
||||
return dates.at(date).events;
|
||||
}
|
||||
|
||||
|
|
2
external/selenite
vendored
2
external/selenite
vendored
|
@ -1 +1 @@
|
|||
Subproject commit cbab9dabe784bd3c799d23ead5d2b4d942ec4081
|
||||
Subproject commit 33d6a9dee8437979b5a9bf5a716a4053f3ebf2fa
|
|
@ -5,10 +5,13 @@
|
|||
*/
|
||||
|
||||
#include "AppWindowBackend.h"
|
||||
#include "AppWindow.h"
|
||||
#include "SeleniteSetup.h"
|
||||
#include "Utils.h"
|
||||
#include "appwindow.h"
|
||||
#include "mirai-core/DataProvider.h"
|
||||
#include "mirai-core/DateTime.h"
|
||||
#include "mirai-core/Day.h"
|
||||
#include "mirai-core/MarkdownDataProvider.h"
|
||||
#include "mirai-core/Mirai.h"
|
||||
#include "slint.h"
|
||||
#include "slint_string.h"
|
||||
|
@ -34,6 +37,7 @@ AppWindowBackend::AppWindowBackend(mirai::Mirai *miraiInstance)
|
|||
{
|
||||
sources_ = std::make_shared<slint::VectorModel<ui::Source>>();
|
||||
days_ = std::make_shared<slint::VectorModel<ui::Day>>();
|
||||
calendar_ = std::make_shared<slint::VectorModel<ui::CalendarDay>>();
|
||||
unscheduledTasks_ = std::make_shared<slint::VectorModel<ui::TaskData>>();
|
||||
tags_ = std::make_shared<slint::VectorModel<slint::SharedString>>();
|
||||
auto sourcesNames = std::make_shared<slint::MapModel<ui::Source, slint::SharedString>>(
|
||||
|
@ -43,14 +47,23 @@ AppWindowBackend::AppWindowBackend(mirai::Mirai *miraiInstance)
|
|||
}
|
||||
);
|
||||
|
||||
const auto palette =
|
||||
selenite::parseJson(std::string(getenv("HOME")) + "/.config/evalyte/theme.json");
|
||||
const auto palettePath = std::string(getenv("HOME")) + "/.config/evalyte/theme.json";
|
||||
const auto palette = selenite::parseJson(palettePath);
|
||||
if (palette.has_value()) {
|
||||
std::cerr << "Warning, no evalyte/theme.json found" << std::endl;
|
||||
setSelenitePalette(mainWindow_, palette.value());
|
||||
std::cerr << "Warning, no " << palettePath << " found" << std::endl;
|
||||
setSelenitePalette(mainWindow_->global<ui::Palette>(), palette.value());
|
||||
setSelenitePalette(settingsWindow_->global<ui::Palette>(), palette.value());
|
||||
}
|
||||
|
||||
mainWindow_->global<ui::Backend>().set_sources(sourcesNames);
|
||||
settingsWindow_->global<ui::Backend>().set_sources(sourcesNames);
|
||||
|
||||
mainWindow_->global<ui::Backend>().set_sources_selected(sources_);
|
||||
settingsWindow_->global<ui::Backend>().set_sources_selected(sources_);
|
||||
|
||||
mainWindow_->global<ui::Backend>().set_tags(tags_);
|
||||
mainWindow_->global<ui::Backend>().set_days(days_);
|
||||
mainWindow_->global<ui::Backend>().set_calendar(calendar_);
|
||||
|
||||
view_.setAllSources();
|
||||
view_.update();
|
||||
|
@ -92,10 +105,17 @@ void AppWindowBackend::setupUtilsCallbacks()
|
|||
};
|
||||
return std::format("{:%B %d}", chronoDate);
|
||||
});
|
||||
|
||||
mainWindow_->global<ui::Backend>().on_format_date_relative([&](const ui::Date &date) {
|
||||
return formatDateRelative(date);
|
||||
});
|
||||
}
|
||||
|
||||
void AppWindowBackend::setupCallbacks()
|
||||
{
|
||||
mainWindow_->global<ui::Backend>().on_settings_clicked([&]() {
|
||||
settingsWindow_->show();
|
||||
});
|
||||
mainWindow_->global<ui::Backend>().on_task_clicked([&](int sourceId, int taskId) {
|
||||
auto source = miraiInstance_->getSourceById(sourceId);
|
||||
assert(source);
|
||||
|
@ -142,10 +162,6 @@ void AppWindowBackend::setupCallbacks()
|
|||
reloadTasks();
|
||||
});
|
||||
|
||||
mainWindow_->global<ui::Backend>().set_sources_selected(sources_);
|
||||
mainWindow_->global<ui::Backend>().set_tags(tags_);
|
||||
mainWindow_->global<ui::Backend>().set_days(days_);
|
||||
|
||||
mainWindow_->global<ui::Backend>().on_save_task([&](ui::SaveTaskData newTaskData) {
|
||||
auto source = miraiInstance_->getSourceById(newTaskData.sourceId);
|
||||
assert(source);
|
||||
|
@ -326,6 +342,42 @@ void AppWindowBackend::reloadTasks()
|
|||
});
|
||||
}
|
||||
mainWindow_->global<ui::Backend>().set_unscheduled_tasks(unscheduledTasks_);
|
||||
|
||||
calendar_->clear();
|
||||
for (int dayIndex = 0; dayIndex < 7; ++dayIndex) {
|
||||
std::chrono::year_month_day nextDate =
|
||||
std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now()) +
|
||||
std::chrono::days{dayIndex};
|
||||
auto currentDate = mirai::Date{nextDate};
|
||||
auto events = view_.getEventsForDate(currentDate);
|
||||
auto slintEvents = std::make_shared<slint::VectorModel<ui::CalendarDayEvent>>();
|
||||
auto relativeDaysDiff = std::chrono::duration_cast<std::chrono::days>(
|
||||
std::chrono::sys_days(currentDate.toStdChrono()) -
|
||||
std::chrono::sys_days(todayDate.toStdChrono())
|
||||
)
|
||||
.count();
|
||||
if (relativeDaysDiff < 0 || relativeDaysDiff >= 3) {
|
||||
continue;
|
||||
}
|
||||
const std::vector<mirai::Event> eventsForDate = view_.getEventsForDate(currentDate);
|
||||
for (int eventIndex = 0; eventIndex < eventsForDate.size(); ++eventIndex) {
|
||||
auto ¤tEvent = eventsForDate.at(eventIndex);
|
||||
slintEvents->push_back(ui::CalendarDayEvent{
|
||||
.title = slint::SharedString(currentEvent.title()),
|
||||
.startsAt = MiraiTimeToSlintTime(currentEvent.startsAt()),
|
||||
.endsAt = MiraiTimeToSlintTime(currentEvent.endsAt()),
|
||||
});
|
||||
}
|
||||
|
||||
auto calendarDay = ui::CalendarDay{
|
||||
.events = slintEvents,
|
||||
.date = MiraiDateToSlintDate(currentDate),
|
||||
.header =
|
||||
slint::SharedString(capitalize(formatDateRelative(MiraiDateToSlintDate(currentDate))
|
||||
))
|
||||
};
|
||||
calendar_->push_back(calendarDay);
|
||||
}
|
||||
}
|
||||
|
||||
void AppWindowBackend::reloadSources()
|
||||
|
@ -334,9 +386,12 @@ void AppWindowBackend::reloadSources()
|
|||
bool noSourceSelected = miraiInstance_->getSources().size() == view_.activeSourceCount();
|
||||
for (const auto &source : miraiInstance_->getSources()) {
|
||||
bool isSourceSelected = view_.isSourceSelected(*source);
|
||||
mirai::MarkdownDataProvider *sourceProvider =
|
||||
dynamic_cast<mirai::MarkdownDataProvider *>(source->dataProvider());
|
||||
sources_->push_back(
|
||||
{.name = slint::SharedString(source->name()),
|
||||
.selected = isSourceSelected && !noSourceSelected}
|
||||
.selected = isSourceSelected && !noSourceSelected,
|
||||
.path = slint::SharedString(sourceProvider->path())}
|
||||
);
|
||||
}
|
||||
mainWindow_->global<ui::Backend>().set_no_source_selected(noSourceSelected);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "appwindow.h"
|
||||
#include "AppWindow.h"
|
||||
#include "mirai-core/Mirai.h"
|
||||
#include "mirai-core/View.h"
|
||||
#include "slint.h"
|
||||
|
@ -29,9 +29,11 @@ class AppWindowBackend
|
|||
std::shared_ptr<slint::VectorModel<ui::Source>> sources_;
|
||||
std::shared_ptr<slint::VectorModel<slint::SharedString>> tags_;
|
||||
std::shared_ptr<slint::VectorModel<ui::Day>> days_;
|
||||
std::shared_ptr<slint::VectorModel<ui::CalendarDay>> calendar_;
|
||||
std::shared_ptr<slint::VectorModel<ui::TaskData>> unscheduledTasks_;
|
||||
|
||||
slint::ComponentHandle<ui::AppWindow> mainWindow_ = ui::AppWindow::create();
|
||||
slint::ComponentHandle<ui::SettingsWindow> settingsWindow_ = ui::SettingsWindow::create();
|
||||
|
||||
mirai::Mirai *miraiInstance_;
|
||||
mirai::View view_;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "appwindow.h"
|
||||
#include "AppWindow.h"
|
||||
#include "selenite/palette.h"
|
||||
#include "slint_color.h"
|
||||
|
||||
|
@ -15,10 +15,8 @@ slint::Color seleniteColorToSlint(const selenite::Color &color)
|
|||
return slint::Color::from_rgb_uint8(color.r, color.g, color.b);
|
||||
}
|
||||
|
||||
void setSelenitePalette(slint::ComponentHandle<ui::AppWindow> ui, const selenite::Palette &palette)
|
||||
void setSelenitePalette(const ui::Palette &uiPalette, const selenite::Palette &palette)
|
||||
{
|
||||
auto &uiPalette = ui->global<ui::Palette>();
|
||||
|
||||
uiPalette.set_background(seleniteColorToSlint(palette.background));
|
||||
uiPalette.set_pane(seleniteColorToSlint(palette.pane));
|
||||
uiPalette.set_foreground(seleniteColorToSlint(palette.foreground));
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
*/
|
||||
|
||||
#include "Utils.h"
|
||||
#include <cctype>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
std::string formatZeroPadding(const int number)
|
||||
{
|
||||
|
@ -14,6 +17,29 @@ std::string formatZeroPadding(const int number)
|
|||
return std::to_string(number);
|
||||
}
|
||||
|
||||
std::string formatDateRelative(const ui::Date &date)
|
||||
{
|
||||
auto todayDate = mirai::Date(std::chrono::system_clock::now());
|
||||
auto relativeDaysDiff = std::chrono::duration_cast<std::chrono::days>(
|
||||
std::chrono::sys_days(SlintDateToMiraiDate(date).toStdChrono()) -
|
||||
std::chrono::sys_days(todayDate.toStdChrono())
|
||||
)
|
||||
.count();
|
||||
|
||||
if (relativeDaysDiff == 0) {
|
||||
return std::string("today");
|
||||
} else if (relativeDaysDiff == 1) {
|
||||
return std::string("tomorrow");
|
||||
}
|
||||
return std::format("in {} days", relativeDaysDiff);
|
||||
};
|
||||
|
||||
std::string capitalize(std::string str)
|
||||
{
|
||||
str[0] = static_cast<char>(toupper(str[0]));
|
||||
return str;
|
||||
};
|
||||
|
||||
std::string SlintDateToStdString(const ui::Date &date)
|
||||
{
|
||||
return std::to_string(date.year) + "-" + formatZeroPadding(date.month) + "-" +
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "appwindow.h"
|
||||
#include "AppWindow.h"
|
||||
#include "mirai-core/DateTime.h"
|
||||
#include <string>
|
||||
|
||||
std::string formatZeroPadding(const int number);
|
||||
std::string formatDateRelative(const ui::Date &date);
|
||||
std::string capitalize(std::string str);
|
||||
std::string SlintDateToStdString(const ui::Date &date);
|
||||
mirai::Date SlintDateToMiraiDate(const ui::Date &date);
|
||||
ui::Date MiraiDateToSlintDate(const mirai::Date &date);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Backend } from "Backend.slint";
|
|||
import { Button, VerticalBox, CheckBox } from "std-widgets.slint";
|
||||
import { SideBar } from "./components/SideBar.slint";
|
||||
import { MainView } from "MainView.slint";
|
||||
import { SettingsWindow } from "SettingsWindow.slint";
|
||||
import { Palette } from "@selenite";
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
|
@ -11,8 +12,6 @@ export component AppWindow inherits Window {
|
|||
max-height: 4000px; // needed, otherwise the window wants to fit the content (on Swaywm)
|
||||
default-font-size: 16px;
|
||||
|
||||
in-out property<string> test;
|
||||
|
||||
HorizontalLayout {
|
||||
SideBar {}
|
||||
MainView {
|
||||
|
@ -22,4 +21,4 @@ export component AppWindow inherits Window {
|
|||
|
||||
}
|
||||
|
||||
export { Backend, Palette } // Export to make it visible to the C++ backend
|
||||
export { Backend, Palette, SettingsWindow } // Export to make it visible to the C++ backend
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Date, Time } from "std-widgets.slint";
|
||||
import { CalendarDay } from "components/Calendar.slint";
|
||||
|
||||
export struct NewTaskData {
|
||||
sourceId: int,
|
||||
|
@ -35,7 +36,8 @@ export struct SaveEventParams {
|
|||
|
||||
export struct Source {
|
||||
name: string,
|
||||
selected: bool
|
||||
selected: bool,
|
||||
path: string
|
||||
}
|
||||
|
||||
export struct TaskData {
|
||||
|
@ -78,11 +80,13 @@ export global Backend {
|
|||
in-out property<bool> no-source-selected;
|
||||
in-out property<[string]> tags;
|
||||
in-out property<[Day]> days;
|
||||
in-out property<[CalendarDay]> calendar;
|
||||
in-out property<[TaskData]> unscheduled-tasks;
|
||||
|
||||
callback task-clicked(int, int);
|
||||
callback source-clicked(int);
|
||||
callback tag-clicked(int);
|
||||
callback settings-clicked();
|
||||
|
||||
callback open-new-task-form(OpenNewTaskFormParams);
|
||||
callback open-edit-task-form(int, int);
|
||||
|
@ -98,4 +102,6 @@ export global Backend {
|
|||
callback save-event(SaveEventParams);
|
||||
|
||||
pure callback format-date(Date) -> string;
|
||||
pure callback format-date-relative(Date) -> string;
|
||||
pure callback capitalize-string(string) -> string;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Backend, TaskData } from "Backend.slint";
|
|||
import { Button, VerticalBox, CheckBox, ScrollView, ComboBox } from "std-widgets.slint";
|
||||
import { TaskLine } from "./components/TaskLine.slint";
|
||||
import { EventGroup } from "./components/EventGroup.slint";
|
||||
import { Calendar } from "./components/Calendar.slint";
|
||||
import { VPopupIconMenu, VDatePicker, VTimePicker, VCheckBox, VButton, VTag, VText, VTextInput, Svg, Palette } from "@selenite";
|
||||
import { NewTaskData, SaveTaskData } from "Backend.slint";
|
||||
import { CreateTaskOrEvent } from "components/CreateTaskOrEvent.slint";
|
||||
|
@ -13,111 +14,113 @@ export component MainView inherits Rectangle {
|
|||
private property<string> icon-not-visible: Svg.not-visible;
|
||||
private property<bool> completed-tasks-visible: false;
|
||||
|
||||
VerticalLayout {
|
||||
horizontal-stretch: 1;
|
||||
padding: 16px;
|
||||
spacing: 16px;
|
||||
alignment: start;
|
||||
HorizontalLayout {
|
||||
horizontal-stretch: 1;
|
||||
alignment: start;
|
||||
spacing: 8px;
|
||||
VButton {
|
||||
text: "Show/Hide completed tasks";
|
||||
clicked => {
|
||||
Backend.toggle-show-completed-tasks();
|
||||
completed-tasks-visible = !completed-tasks-visible;
|
||||
}
|
||||
icon-svg: completed-tasks-visible ? icon-visible : icon-not-visible;
|
||||
icon-colorize: Palette.control-foreground;
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
horizontal-stretch: 1;
|
||||
background: Palette.background.brighter(0.2);
|
||||
height: 1px;
|
||||
}
|
||||
HorizontalLayout {
|
||||
|
||||
CreateTaskOrEvent {
|
||||
|
||||
}
|
||||
|
||||
Flickable {
|
||||
VerticalLayout {
|
||||
horizontal-stretch: 1;
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
spacing: 16px;
|
||||
HorizontalLayout {
|
||||
horizontal-stretch: 1;
|
||||
alignment: start;
|
||||
spacing: 16px;
|
||||
if Backend.days.length == 0 && Backend.unscheduled-tasks.length == 0 : VText {
|
||||
text: "There is no task to show";
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
spacing: 8px;
|
||||
VButton {
|
||||
text: "Show/Hide completed tasks";
|
||||
clicked => {
|
||||
Backend.toggle-show-completed-tasks();
|
||||
completed-tasks-visible = !completed-tasks-visible;
|
||||
}
|
||||
icon-svg: completed-tasks-visible ? icon-visible : icon-not-visible;
|
||||
icon-colorize: Palette.control-foreground;
|
||||
}
|
||||
for day[dayIndex] in Backend.days: VerticalLayout {
|
||||
Rectangle {
|
||||
background: Palette.card-background;
|
||||
border-radius: 8px;
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
HorizontalLayout {
|
||||
alignment: start;
|
||||
VText {
|
||||
text: Backend.format-date(day.date);
|
||||
color: day.isLate ? Palette.orange : Palette.foreground;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
}
|
||||
Rectangle {
|
||||
horizontal-stretch: 1;
|
||||
background: Palette.background.brighter(0.2);
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
CreateTaskOrEvent {
|
||||
|
||||
}
|
||||
|
||||
Flickable {
|
||||
horizontal-stretch: 1;
|
||||
VerticalLayout {
|
||||
alignment: start;
|
||||
spacing: 16px;
|
||||
if Backend.days.length == 0 && Backend.unscheduled-tasks.length == 0 : VText {
|
||||
text: "There is no task to show";
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
for day[dayIndex] in Backend.days: VerticalLayout {
|
||||
Rectangle {
|
||||
background: Palette.card-background;
|
||||
border-radius: 8px;
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
HorizontalLayout {
|
||||
alignment: start;
|
||||
VText {
|
||||
text: day.relativeDaysDiff == 0 ? " - today" :
|
||||
day.relativeDaysDiff == 1 ? " - tomorrow" :
|
||||
day.relativeDaysDiff == -1 ? " - yesterday" :
|
||||
day.relativeDaysDiff > 0 ? " - in \{day.relativeDaysDiff} days" :
|
||||
" - \{-day.relativeDaysDiff} days ago";
|
||||
color: Palette.foreground-hint;
|
||||
font-size: 1rem;
|
||||
text: Backend.format-date(day.date);
|
||||
color: day.isLate ? Palette.orange : Palette.foreground;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
VText {
|
||||
text: day.relativeDaysDiff == 0 ? " - today" :
|
||||
day.relativeDaysDiff == 1 ? " - tomorrow" :
|
||||
day.relativeDaysDiff == -1 ? " - yesterday" :
|
||||
day.relativeDaysDiff > 0 ? " - in \{day.relativeDaysDiff} days" :
|
||||
" - \{-day.relativeDaysDiff} days ago";
|
||||
color: Palette.foreground-hint;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
for event[eventIndex] in day.events: VerticalLayout {
|
||||
padding-top: 16px;
|
||||
EventGroup {
|
||||
event: event;
|
||||
}
|
||||
}
|
||||
for task[taskIndex] in day.tasks: VerticalLayout {
|
||||
padding-top: taskIndex == 0 ? 16px : 0px;
|
||||
padding-bottom: 8px;
|
||||
TaskLine {
|
||||
task: task;
|
||||
date: day.date;
|
||||
source-index: task.sourceId;
|
||||
task-index: task.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
for event[eventIndex] in day.events: VerticalLayout {
|
||||
padding-top: 16px;
|
||||
EventGroup {
|
||||
event: event;
|
||||
}
|
||||
}
|
||||
for task[taskIndex] in day.tasks: VerticalLayout {
|
||||
padding-top: taskIndex == 0 ? 16px : 0px;
|
||||
padding-bottom: 8px;
|
||||
TaskLine {
|
||||
task: task;
|
||||
date: day.date;
|
||||
source-index: task.sourceId;
|
||||
task-index: task.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if Backend.unscheduled-tasks.length > 0 : VerticalLayout {
|
||||
Rectangle {
|
||||
background: Palette.card-background;
|
||||
border-radius: 8px;
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
HorizontalLayout {
|
||||
alignment: start;
|
||||
VText {
|
||||
text: "Unscheduled";
|
||||
color: Palette.foreground;
|
||||
font-size: 1.2rem;
|
||||
if Backend.unscheduled-tasks.length > 0 : VerticalLayout {
|
||||
Rectangle {
|
||||
background: Palette.card-background;
|
||||
border-radius: 8px;
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
HorizontalLayout {
|
||||
alignment: start;
|
||||
VText {
|
||||
text: "Unscheduled";
|
||||
color: Palette.foreground;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
for task[taskIndex] in Backend.unscheduled-tasks: VerticalLayout {
|
||||
padding-top: taskIndex == 0 ? 16px : 0px;
|
||||
padding-bottom: 8px;
|
||||
TaskLine {
|
||||
task: task;
|
||||
source-index: task.sourceId;
|
||||
task-index: task.id;
|
||||
for task[taskIndex] in Backend.unscheduled-tasks: VerticalLayout {
|
||||
padding-top: taskIndex == 0 ? 16px : 0px;
|
||||
padding-bottom: 8px;
|
||||
TaskLine {
|
||||
task: task;
|
||||
source-index: task.sourceId;
|
||||
task-index: task.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,5 +128,10 @@ export component MainView inherits Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
Calendar {
|
||||
init => { debug("cal len", Backend.calendar.length) }
|
||||
days: Backend.calendar;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
29
ui/SettingsWindow.slint
Normal file
29
ui/SettingsWindow.slint
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { Backend } from "Backend.slint";
|
||||
import { Button, VerticalBox, CheckBox } from "std-widgets.slint";
|
||||
import { SideBar } from "./components/SideBar.slint";
|
||||
import { MainView } from "MainView.slint";
|
||||
import { VText, VTextInput, Palette } from "@selenite";
|
||||
|
||||
export component SettingsWindow inherits Window {
|
||||
|
||||
title: "Mirai - Settings";
|
||||
min-height: 100px;
|
||||
max-height: 4000px; // needed, otherwise the window wants to fit the content (on Swaywm)
|
||||
default-font-size: 16px;
|
||||
background: Palette.background;
|
||||
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
spacing: 8px;
|
||||
for source[source-index] in Backend.sources-selected: VerticalLayout {
|
||||
VText {
|
||||
text: source.name;
|
||||
}
|
||||
VTextInput {
|
||||
text: source.path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Backend, Palette } // Export to make it visible to the C++ backend
|
112
ui/components/Calendar.slint
Normal file
112
ui/components/Calendar.slint
Normal file
|
@ -0,0 +1,112 @@
|
|||
import { ScrollView, Date, Time } from "std-widgets.slint";
|
||||
import { VCheckBox, VButton, VActionButton, Svg, VTag, VPopupIconMenu, VText, Palette } from "@selenite";
|
||||
import { Utils } from "../Utils.slint";
|
||||
|
||||
export struct CalendarDayEvent {
|
||||
title: string,
|
||||
startsAt: Time,
|
||||
endsAt: Time
|
||||
}
|
||||
|
||||
export struct CalendarDay {
|
||||
events: [CalendarDayEvent],
|
||||
date: Date,
|
||||
header: string,
|
||||
}
|
||||
|
||||
export enum CalendarDateDisplayFormat {
|
||||
Relative,
|
||||
Normal
|
||||
}
|
||||
|
||||
export component Calendar inherits Rectangle {
|
||||
in property<[CalendarDay]> days;
|
||||
in property <CalendarDateDisplayFormat> format;
|
||||
private property <length> header-height: 64px;
|
||||
private property <length> available-day-space: self.height - header-height;
|
||||
private property <length> day-start-y: header-height;
|
||||
private property <length> hour-spacing: available-day-space / 24;
|
||||
background: Palette.pane;
|
||||
|
||||
HorizontalLayout {
|
||||
Rectangle {
|
||||
//background: red;
|
||||
width: 48px;
|
||||
VerticalLayout {
|
||||
y: 0;
|
||||
height: header-height;
|
||||
padding-right: 8px;
|
||||
VText {
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: right;
|
||||
text: "";
|
||||
}
|
||||
}
|
||||
|
||||
for index in 24: VerticalLayout {
|
||||
y: day-start-y + index * hour-spacing - (hour-spacing / 2);
|
||||
height: hour-spacing;
|
||||
padding-right: 8px;
|
||||
VText {
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: right;
|
||||
text: "\{index}h";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Rectangle {
|
||||
//background: green;
|
||||
HorizontalLayout {
|
||||
for day[day-index] in root.days: Rectangle {
|
||||
if day-index > 0 : Rectangle {
|
||||
x: 0;
|
||||
y: header-height - 32px;
|
||||
width: 1px;
|
||||
height: parent.height;
|
||||
background: Palette.card-background.transparentize(0.5);
|
||||
}
|
||||
VerticalLayout {
|
||||
y: 0;
|
||||
height: header-height;
|
||||
padding-right: 8px;
|
||||
VText {
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: center;
|
||||
text: day.header;
|
||||
}
|
||||
}
|
||||
for hour[hour-index] in 24 : Rectangle {
|
||||
background: Palette.card-background.transparentize(0.5);
|
||||
x: 0px;
|
||||
width: parent.width;
|
||||
y: day-start-y + hour-spacing * hour-index;
|
||||
height: 1px;
|
||||
}
|
||||
for event[event-index] in day.events : Rectangle {
|
||||
background: Palette.card-background;
|
||||
border-radius: 4px;
|
||||
x: 8px;
|
||||
width: parent.width - 16px;
|
||||
y: day-start-y + hour-spacing * event.startsAt.hour;
|
||||
height: hour-spacing * (event.endsAt.hour - event.startsAt.hour) - 2px;
|
||||
clip: true;
|
||||
HorizontalLayout {
|
||||
Rectangle {
|
||||
width: 4px;
|
||||
background: Palette.accent;
|
||||
}
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
VText {
|
||||
text: event.title;
|
||||
wrap: TextWrap.word-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ export component EventGroup {
|
|||
VText {
|
||||
text: Utils.time-to-string(event.endsAt);
|
||||
color: Palette.accent;
|
||||
font-size: 0.8rem;
|
||||
font-size: 0.9rem;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Backend } from "../Backend.slint";
|
||||
import { ToggleButton, VText, Palette } from "@selenite";
|
||||
import { VButton, ToggleButton, VText, Svg, Palette } from "@selenite";
|
||||
|
||||
export component SideBar inherits Rectangle {
|
||||
background: Palette.pane;
|
||||
|
||||
VerticalLayout {
|
||||
alignment: start;
|
||||
height: parent.height;
|
||||
padding: 16px;
|
||||
spacing: 16px;
|
||||
VText {
|
||||
|
@ -13,19 +13,33 @@ export component SideBar inherits Rectangle {
|
|||
font-size: 1.5rem;
|
||||
}
|
||||
VerticalLayout {
|
||||
spacing: 4px;
|
||||
ToggleButton {
|
||||
text: "All";
|
||||
text-alignment: left;
|
||||
active: Backend.no-source-selected;
|
||||
clicked => { Backend.source-clicked(-1) }
|
||||
}
|
||||
for item[index] in Backend.sources-selected: ToggleButton {
|
||||
text: item.name;
|
||||
text-alignment: left;
|
||||
active: item.selected;
|
||||
clicked => { Backend.source-clicked(index) }
|
||||
alignment: space-between;
|
||||
vertical-stretch: 1;
|
||||
VerticalLayout {
|
||||
vertical-stretch: 1;
|
||||
spacing: 4px;
|
||||
ToggleButton {
|
||||
text: "All";
|
||||
text-alignment: left;
|
||||
active: Backend.no-source-selected;
|
||||
clicked => { Backend.source-clicked(-1) }
|
||||
}
|
||||
for item[index] in Backend.sources-selected: ToggleButton {
|
||||
text: item.name;
|
||||
text-alignment: left;
|
||||
active: item.selected;
|
||||
clicked => { Backend.source-clicked(index) }
|
||||
}
|
||||
}
|
||||
/*VerticalLayout {
|
||||
spacing: 4px;
|
||||
VButton {
|
||||
icon-svg: Svg.cog;
|
||||
text: "Settings";
|
||||
background: transparent;
|
||||
clicked => { Backend.settings-clicked() }
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue