Add a Calendar view on the right side

This commit is contained in:
Vyn 2024-10-29 15:02:46 +01:00
parent a80515ff90
commit f1ac8a42d1
18 changed files with 406 additions and 130 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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
View 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

View 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;
}
}
}
}
}
}
}
}
}

View file

@ -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;
}
}

View file

@ -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() }
}
}*/
}
}
}