Files
NimPulse/src/timer.nim
T

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