First commit

This commit is contained in:
Vyn 2024-10-16 12:17:51 +02:00
commit 8585f5741f
13 changed files with 505 additions and 0 deletions

54
ui/app-window.slint Normal file
View file

@ -0,0 +1,54 @@
import { State, SessionStep, CountdownStatus } from "./state.slint";
import { VText, VButton, VActionButton, Svg, Palette } from "@selenite";
import { CountdownView } from "./countdown-view.slint";
import { SettingsView } from "settings-view.slint";
enum CurrentView {
Countdown,
Settings
}
export component AppWindow inherits Window {
title: "Focus";
callback start-stop;
property <CurrentView> current-view: CurrentView.Countdown;
background: Palette.background;
default-font-size: 16px;
padding: 0px;
VerticalLayout {
width: parent.width;
height: parent.height;
HorizontalLayout {
padding: 8px;
vertical-stretch: 0;
alignment: start;
settingsButtons := VActionButton {
icon-svg: Svg.burger;
background: Palette.background.transparentize(1);
clicked => {
if (current-view == CurrentView.Countdown) {
current-view = CurrentView.Settings;
} else if (current-view == CurrentView.Settings) {
current-view = CurrentView.Countdown;
State.config-changed();
}
}
}
}
if current-view == CurrentView.Countdown : countdown-view := CountdownView {
start-stop => { root.start-stop() }
vertical-stretch: 1;
padding: 32px;
padding-top: 0px;
}
if current-view == CurrentView.Settings : settings-view := SettingsView {
vertical-stretch: 1;
}
}
}
export { State }

68
ui/countdown-view.slint Normal file
View file

@ -0,0 +1,68 @@
import { State, SessionStep, CountdownStatus } from "./state.slint";
import { VText, VButton, VActionButton, Svg, Palette } from "@selenite";
import { Utils } from "utils.slint";
export component CountdownView inherits VerticalLayout {
callback start-stop <=> startStopButton.clicked;
pure function format-session-step(session-step: SessionStep) -> string {
if (session-step == SessionStep.Setup) {
return "Setup";
}
if (session-step == SessionStep.Focus) {
return "Focus";
}
if (session-step == SessionStep.Break) {
return "Break";
}
if (session-step == SessionStep.Finished) {
return "Finished";
}
return "Not implemented";
}
VerticalLayout {
spacing: 32px;
alignment: center;
VerticalLayout {
VText {
font-size: 3rem;
letter-spacing: 0.2rem;
font-weight: 600;
text: "\{Utils.format-countdown(State.countdown)}";
horizontal-alignment: center;
}
VText {
color: Palette.foreground-hint;
text: format-session-step(State.sessions-step);
horizontal-alignment: center;
}
}
HorizontalLayout {
alignment: space-between;
spacing: 8px;
for step[step-index] in State.max-session-count: Rectangle {
preferred-width: 16px;
preferred-height: 16px;
border-radius: 32px;
background: step-index + 1 == State.current-session
? Palette.accent.transparentize(0.5)
: step-index + 1 < State.current-session ? Palette.accent
: Palette.control-background;
animate background {
duration: 0.5s;
}
}
}
HorizontalLayout {
alignment: center;
startStopButton := VActionButton {
background: Palette.foreground.transparentize(1);
icon-size: 2rem;
icon-svg: State.countdown-status != CountdownStatus.Running ? Svg.play : Svg.pause;
}
}
}
}

44
ui/settings-view.slint Normal file
View file

@ -0,0 +1,44 @@
import { State, SessionStep, CountdownStatus } from "./state.slint";
import { VText, VButton, VSlider, Palette } from "@selenite";
import { Utils } from "utils.slint";
export component SettingsView inherits Rectangle {
VerticalLayout {
padding: 32px;
spacing: 32px;
alignment: center;
VSlider {
label: "Session duration";
label-size: 1.25rem;
label-alignment: TextHorizontalAlignment.center;
no-background: true;
minimum: 1;
maximum: 60 * 60;
value: State.focus-countdown-duration;
format-value(value) => { return Utils.format-countdown(value); }
released(value) => { State.focus-countdown-duration = value; }
}
VSlider {
label: "Break duration";
label-size: 1.25rem;
label-alignment: TextHorizontalAlignment.center;
no-background: true;
minimum: 1;
maximum: 60 * 60;
value: State.break-countdown-duration;
format-value(value) => { return Utils.format-countdown(value); }
released(value) => { State.break-countdown-duration = value; }
}
VSlider {
label: "Number of session";
label-size: 1.25rem;
label-alignment: TextHorizontalAlignment.center;
no-background: true;
minimum: 1;
maximum: 8;
value: State.max-session-count;
format-value(value) => { return Math.round(value); }
released(value) => { State.max-session-count = Math.round(value); }
}
}
}

24
ui/state.slint Normal file
View file

@ -0,0 +1,24 @@
export enum SessionStep {
Setup,
Focus,
Break,
Finished,
}
export enum CountdownStatus {
Running,
Paused,
NotStarted
}
export global State {
in-out property <int> countdown;
in-out property <int> focus-countdown-duration;
in-out property <int> break-countdown-duration;
in-out property <SessionStep> sessions-step;
in-out property <CountdownStatus> countdown-status;
in-out property <int> current-session;
in-out property <int> max-session-count;
callback config-changed();
}

12
ui/utils.slint Normal file
View file

@ -0,0 +1,12 @@
export global Utils {
public pure function format-zero-padding(number: int) -> string {
if (number < 10) {
return "0\{number}";
}
return number;
}
public pure function format-countdown(countdown: int) -> string {
return "\{format-zero-padding(countdown / 60)}:\{format-zero-padding(Math.mod(countdown, 60))}";
}
}