1use crate::utils::PrettyDuration;
29use chrono::Duration;
31use clock_core::timer::{Timer, TimerData};
32use cursive::{
33 event::{Callback, Event, EventResult, Key},
34 theme::ColorStyle,
35 view::View,
36 Cursive, Printer, Vec2, With,
37};
38use std::rc::Rc;
39
40#[derive(Copy, Clone)]
41enum TimerViewState {
42 Config,
43 Running,
44 Finished,
45}
46
47struct TimerViewConfig {
48 h: u8,
49 m: u8,
50 s: u8,
51 focus: u8, input_buffer: Vec<u8>,
53}
54
55pub struct TimerView {
56 timer: Timer,
57 remaining: Duration,
58 state: TimerViewState,
59 config: TimerViewConfig,
60 on_finish: Option<Rc<dyn Fn(&mut Cursive, TimerData)>>,
61}
62
63impl TimerView {
64 pub fn new(h: u8, m: u8, s: u8) -> Self {
65 let config = TimerViewConfig {
66 h,
67 m,
68 s,
69 focus: 1,
70 input_buffer: Vec::new(),
71 };
72 Self {
73 timer: Timer::new(Duration::zero()),
74 remaining: Duration::zero(),
75 config,
76 state: TimerViewState::Config,
77 on_finish: None,
78 }
79 }
80
81 pub fn start(&mut self) {
82 let seconds =
83 self.config.h as i64 * 3600 + self.config.m as i64 * 60 + self.config.s as i64;
84 self.timer = Timer::new(Duration::seconds(seconds));
85 self.state = TimerViewState::Running;
86 self.timer.pause_or_resume();
87 }
88
89 pub fn set_on_finish<F, R>(&mut self, cb: F)
95 where
96 F: 'static + Fn(&mut Cursive, TimerData) -> R,
97 {
98 self.on_finish = Some(Rc::new(move |s, t| {
99 cb(s, t);
100 }));
101 }
102
103 pub fn on_finish<F, R>(self, cb: F) -> Self
104 where
105 F: 'static + Fn(&mut Cursive, TimerData) -> R,
106 {
107 self.with(|s| s.set_on_finish(cb))
108 }
109
110 fn finish(&mut self) -> EventResult {
111 self.state = TimerViewState::Finished;
112 let data = self.timer.stop();
113 if self.on_finish.is_some() {
114 let cb = self.on_finish.clone().unwrap();
115 EventResult::Consumed(Some(Callback::from_fn_once(move |s| cb(s, data))))
116 } else {
117 EventResult::Consumed(None)
118 }
119 }
120
121 fn draw_running(&self, printer: &Printer) {
122 printer.print((0, 0), &self.remaining.pretty());
123 }
124
125 fn draw_finished(&self, printer: &Printer) {
126 printer.print((0, 0), "FINISHED!");
127 }
128
129 fn draw_config(&self, printer: &Printer) {
130 fn format(n: u8) -> String {
131 format!("{:02}", n)
132 }
133 let (h, m, s) = (
134 format(self.config.h),
135 format(self.config.m),
136 format(self.config.s),
137 );
138
139 if self.config.focus % 3 == 0 {
140 printer.with_color(ColorStyle::highlight(), |printer| printer.print((0, 0), &h));
141 } else {
142 printer.print((0, 0), &h);
143 }
144 printer.print((2, 0), ":");
145 if self.config.focus % 3 == 1 {
146 printer.with_color(ColorStyle::highlight(), |printer| printer.print((3, 0), &m));
147 } else {
148 printer.print((3, 0), &m);
149 }
150 printer.print((5, 0), ":");
151 if self.config.focus % 3 == 2 {
152 printer.with_color(ColorStyle::highlight(), |printer| printer.print((6, 0), &s));
153 } else {
154 printer.print((6, 0), &s);
155 }
156 }
157
158 fn set_selection(&mut self, v: u8) {
168 match self.config.focus % 3 {
169 0 => self.config.h = v,
170 1 => self.config.m = v,
171 2 => self.config.s = v,
172 _ => unreachable!(),
173 }
174 }
175
176 fn move_focus_right(&mut self) {
177 self.config.focus += 1;
178 self.config.input_buffer.clear();
179 }
180
181 fn move_focus_left(&mut self) {
182 self.config.focus -= 1;
183 self.config.input_buffer.clear();
184 }
185
186 fn read_buffer(&self) -> u8 {
187 let buffer = &self.config.input_buffer;
188 let n = match buffer.len() {
189 0 => 0,
190 1 => buffer[0],
191 2 => buffer[0] * 10 + buffer[1],
192 _ => unreachable!(),
193 };
194 match self.config.focus % 3 {
195 0 => n,
196 1 | 2 => {
197 if n < 60 {
198 n
199 } else {
200 59
201 }
202 }
203 _ => unreachable!(),
204 }
205 }
206}
207impl View for TimerView {
208 fn draw(&self, printer: &Printer) {
209 match self.state {
210 TimerViewState::Running => self.draw_running(printer),
211 TimerViewState::Config => self.draw_config(printer),
212 TimerViewState::Finished => self.draw_finished(printer),
213 }
214 }
215
216 fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
217 Vec2::new(12, 1) }
220
221 fn on_event(&mut self, event: Event) -> EventResult {
222 match self.state {
223 TimerViewState::Running => {
224 match event {
225 Event::Char(' ') => {
227 self.timer.pause_or_resume();
228 }
229 Event::Refresh => {
230 self.remaining = self.timer.read();
231 if self.remaining.num_milliseconds() < 10 {
232 return self.finish();
233 }
234 }
235 Event::Key(Key::Enter) => {
237 return self.finish();
238 }
239 _ => {
240 if self.timer.data.remaining.num_milliseconds() < 10 {
241 self.state = TimerViewState::Finished;
242 return self.finish();
243 }
244 } }
246 }
247 TimerViewState::Finished => match event {
248 Event::Char(' ') | Event::Key(Key::Enter) => {
249 self.state = TimerViewState::Config;
250 }
251 _ => return EventResult::Ignored,
252 },
253 TimerViewState::Config => match event {
254 Event::Char(c) => {
255 if c.is_numeric() {
256 self.config.input_buffer.push(c.to_digit(10).unwrap() as u8);
257 }
258 self.set_selection(self.read_buffer());
259 if self.config.input_buffer.len() == 2 {
260 self.move_focus_right();
261 }
262 }
263 Event::Key(Key::Right) | Event::Key(Key::Tab) => {
264 self.move_focus_right();
265 }
266 Event::Key(Key::Left) => self.move_focus_left(),
267 Event::Key(Key::Enter) => {
268 self.start();
269 }
270 _ => return EventResult::Ignored,
279 },
280 }
281 EventResult::Consumed(None)
282 }
283}