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