110 lines
3.4 KiB
Nim
110 lines
3.4 KiB
Nim
## timer.nim
|
|
## =========
|
|
## Machine à états du minuteur Pomodoro. Aucune dépendance Win32 : le module
|
|
## ne fait que compter les secondes et décider des transitions de phase.
|
|
##
|
|
## Phases : idle, working, shortBreak, longBreak, paused.
|
|
|
|
import config
|
|
|
|
type
|
|
TimerPhase* = enum
|
|
phIdle, phWorking, phShortBreak, phLongBreak, phPaused
|
|
|
|
## Événement renvoyé par tick() à la fin d'une période.
|
|
TickEvent* = enum
|
|
evNone ## Rien à signaler.
|
|
evWorkCompleted ## Une période de travail vient de se terminer.
|
|
evBreakCompleted ## Une pause vient de se terminer.
|
|
|
|
PomodoroTimer* = object
|
|
phase*: TimerPhase
|
|
prevPhase*: TimerPhase ## Phase avant mise en pause (pour reprendre).
|
|
remainingSecs*: int
|
|
cycleCount*: int ## Pomodoros complétés dans le cycle courant.
|
|
|
|
proc formatTime*(secs: int): string =
|
|
## Formate une durée en secondes sous la forme M:SS ou H:MM:SS.
|
|
let m = secs div 60
|
|
let s = secs mod 60
|
|
if m >= 60:
|
|
let h = m div 60
|
|
let mm = m mod 60
|
|
result = $h & ":" & (if mm < 10: "0" else: "") & $mm &
|
|
":" & (if s < 10: "0" else: "") & $s
|
|
else:
|
|
result = $m & ":" & (if s < 10: "0" else: "") & $s
|
|
|
|
proc initTimer*(): PomodoroTimer =
|
|
PomodoroTimer(phase: phIdle, prevPhase: phIdle, remainingSecs: 0, cycleCount: 0)
|
|
|
|
proc startWork*(t: var PomodoroTimer, p: Profile) =
|
|
t.phase = phWorking
|
|
t.prevPhase = phWorking
|
|
t.remainingSecs = p.workMin * 60
|
|
|
|
proc startBreak*(t: var PomodoroTimer, p: Profile, longBreak: bool) =
|
|
if longBreak:
|
|
t.phase = phLongBreak
|
|
t.remainingSecs = p.longBreakMin * 60
|
|
else:
|
|
t.phase = phShortBreak
|
|
t.remainingSecs = p.shortBreakMin * 60
|
|
t.prevPhase = t.phase
|
|
|
|
proc pause*(t: var PomodoroTimer) =
|
|
if t.phase in {phWorking, phShortBreak, phLongBreak}:
|
|
t.prevPhase = t.phase
|
|
t.phase = phPaused
|
|
|
|
proc resume*(t: var PomodoroTimer) =
|
|
if t.phase == phPaused:
|
|
t.phase = t.prevPhase
|
|
|
|
proc stop*(t: var PomodoroTimer) =
|
|
t.phase = phIdle
|
|
t.remainingSecs = 0
|
|
t.prevPhase = phIdle
|
|
# On remet le cycle à zéro : après un arrêt, le prochain démarrage repart
|
|
# d'un cycle complet (sinon la pause longue arrivait au mauvais moment).
|
|
t.cycleCount = 0
|
|
|
|
proc isBreak*(t: PomodoroTimer): bool =
|
|
## Vrai si le minuteur est dans une pause (courte ou longue).
|
|
t.phase in {phShortBreak, phLongBreak}
|
|
|
|
proc skipBreak*(t: var PomodoroTimer, p: Profile) =
|
|
## Saute la pause courante et repart immédiatement en travail.
|
|
if t.isBreak():
|
|
t.startWork(p)
|
|
|
|
proc extendBreak*(t: var PomodoroTimer, minutes: int) =
|
|
## Prolonge la pause courante de `minutes` minutes.
|
|
if t.isBreak() and minutes > 0:
|
|
t.remainingSecs += minutes * 60
|
|
|
|
proc isRunning*(t: PomodoroTimer): bool =
|
|
t.phase in {phWorking, phShortBreak, phLongBreak}
|
|
|
|
proc tick*(t: var PomodoroTimer, p: Profile): TickEvent =
|
|
## Décrémente le temps restant d'une seconde. Retourne un événement quand
|
|
## une période se termine et enchaîne automatiquement sur la suivante.
|
|
if t.phase notin {phWorking, phShortBreak, phLongBreak}:
|
|
return evNone
|
|
if t.remainingSecs > 0:
|
|
dec t.remainingSecs
|
|
if t.remainingSecs > 0:
|
|
return evNone
|
|
|
|
case t.phase
|
|
of phWorking:
|
|
inc t.cycleCount
|
|
let longBreak = (p.cycles > 0) and (t.cycleCount mod p.cycles == 0)
|
|
t.startBreak(p, longBreak)
|
|
return evWorkCompleted
|
|
of phShortBreak, phLongBreak:
|
|
t.startWork(p)
|
|
return evBreakCompleted
|
|
else:
|
|
return evNone
|