1use super::{SettingData, SettingIdentity, SettingKind, WidgetKind};
4use crate::fl;
5use crate::geom::Rectangle;
6use crate::i18n::I18nDisplay;
7use crate::settings::{FileExtension, FinishedAction, RefreshRatePair, Settings};
8use crate::view::{Bus, EntryId, EntryKind, Event, ViewId};
9
10pub struct FinishedActionSetting;
12
13impl SettingKind for FinishedActionSetting {
14 fn identity(&self) -> SettingIdentity {
15 SettingIdentity::FinishedAction
16 }
17
18 fn label(&self, _settings: &Settings) -> String {
19 fl!("settings-reader-end-of-book-action")
20 }
21
22 fn fetch(&self, settings: &Settings) -> SettingData {
23 let current = settings.reader.finished;
24
25 let entries = vec![
26 EntryKind::RadioButton(
27 FinishedAction::Notify.to_i18n_string(),
28 EntryId::SetFinishedAction(FinishedAction::Notify),
29 current == FinishedAction::Notify,
30 ),
31 EntryKind::RadioButton(
32 FinishedAction::Close.to_i18n_string(),
33 EntryId::SetFinishedAction(FinishedAction::Close),
34 current == FinishedAction::Close,
35 ),
36 EntryKind::RadioButton(
37 FinishedAction::GoToNext.to_i18n_string(),
38 EntryId::SetFinishedAction(FinishedAction::GoToNext),
39 current == FinishedAction::GoToNext,
40 ),
41 ];
42
43 SettingData {
44 value: current.to_i18n_string(),
45 widget: WidgetKind::SubMenu(entries),
46 }
47 }
48
49 fn handle(
50 &self,
51 evt: &Event,
52 settings: &mut Settings,
53 _bus: &mut Bus,
54 ) -> (Option<String>, bool) {
55 if let Event::Select(EntryId::SetFinishedAction(action)) = evt {
56 settings.reader.finished = *action;
57 return (Some(action.to_i18n_string()), true);
58 }
59 (None, false)
60 }
61}
62
63pub struct RefreshRateInfo;
65
66impl SettingKind for RefreshRateInfo {
67 fn identity(&self) -> SettingIdentity {
68 SettingIdentity::RefreshRate
69 }
70
71 fn label(&self, _settings: &Settings) -> String {
72 fl!("settings-reader-refresh-rate")
73 }
74
75 fn fetch(&self, settings: &Settings) -> SettingData {
76 let global = &settings.reader.refresh_rate.global;
77 let value = format!("{} / {}", global.regular, global.inverted);
78
79 SettingData {
80 value,
81 widget: WidgetKind::ActionLabel(Event::OpenRefreshRateEditor),
82 }
83 }
84
85 fn handle(
91 &self,
92 evt: &Event,
93 settings: &mut Settings,
94 _bus: &mut Bus,
95 ) -> (Option<String>, bool) {
96 let global = &settings.reader.refresh_rate.global;
97 match evt {
98 Event::Submit(ViewId::RefreshRateRegularInput, text) => {
99 let regular = text.parse::<u8>().unwrap_or(global.regular);
100 (
101 Some(fl!(
102 "settings-reader-refresh-rate-summary",
103 regular = regular,
104 inverted = global.inverted
105 )),
106 false,
107 )
108 }
109 Event::Submit(ViewId::RefreshRateInvertedInput, text) => {
110 let inverted = text.parse::<u8>().unwrap_or(global.inverted);
111 (
112 Some(fl!(
113 "settings-reader-refresh-rate-summary",
114 regular = global.regular,
115 inverted = inverted
116 )),
117 false,
118 )
119 }
120 _ => (None, false),
121 }
122 }
123}
124
125pub struct RefreshRateByKindInfo(pub FileExtension);
127
128impl SettingKind for RefreshRateByKindInfo {
129 fn identity(&self) -> SettingIdentity {
130 SettingIdentity::RefreshRateByKind(self.0.as_str().to_string())
131 }
132
133 fn label(&self, _settings: &Settings) -> String {
134 self.0.to_string().to_uppercase()
135 }
136
137 fn fetch(&self, settings: &Settings) -> SettingData {
138 let pair = settings
139 .reader
140 .refresh_rate
141 .by_kind
142 .get(self.0.as_str())
143 .cloned()
144 .unwrap_or(RefreshRatePair {
145 regular: 0,
146 inverted: 0,
147 });
148
149 let value = format!("{} / {}", pair.regular, pair.inverted);
150
151 SettingData {
152 value,
153 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditRefreshRateByKind(self.0))),
154 }
155 }
156
157 fn hold_event(&self, rect: Rectangle) -> Option<Event> {
158 let entries = vec![EntryKind::Command(
159 fl!("delete"),
160 EntryId::DeleteRefreshRateByKind(self.0),
161 )];
162
163 Some(Event::SubMenu(rect, entries))
164 }
165}
166
167pub struct RefreshRateRegularSetting;
169
170impl SettingKind for RefreshRateRegularSetting {
171 fn identity(&self) -> SettingIdentity {
172 SettingIdentity::RefreshRateRegular
173 }
174
175 fn label(&self, _settings: &Settings) -> String {
176 fl!("settings-reader-refresh-rate-regular")
177 }
178
179 fn fetch(&self, settings: &Settings) -> SettingData {
180 let value = settings.reader.refresh_rate.global.regular.to_string();
181
182 SettingData {
183 value,
184 widget: WidgetKind::ActionLabel(Event::OpenNamedInput {
185 view_id: crate::view::ViewId::RefreshRateRegularInput,
186 label: fl!("settings-reader-refresh-rate-regular-input"),
187 max_chars: 3,
188 initial_text: settings.reader.refresh_rate.global.regular.to_string(),
189 }),
190 }
191 }
192
193 fn handle(
194 &self,
195 evt: &Event,
196 settings: &mut Settings,
197 _bus: &mut Bus,
198 ) -> (Option<String>, bool) {
199 if let Event::Submit(crate::view::ViewId::RefreshRateRegularInput, ref text) = evt {
200 if let Ok(v) = text.parse::<u8>() {
201 settings.reader.refresh_rate.global.regular = v;
202 return (Some(v.to_string()), true);
203 }
204 }
205
206 (None, false)
207 }
208}
209
210pub struct RefreshRateInvertedSetting;
212
213impl SettingKind for RefreshRateInvertedSetting {
214 fn identity(&self) -> SettingIdentity {
215 SettingIdentity::RefreshRateInverted
216 }
217
218 fn label(&self, _settings: &Settings) -> String {
219 fl!("settings-reader-refresh-rate-inverted")
220 }
221
222 fn fetch(&self, settings: &Settings) -> SettingData {
223 let value = settings.reader.refresh_rate.global.inverted.to_string();
224
225 SettingData {
226 value,
227 widget: WidgetKind::ActionLabel(Event::OpenNamedInput {
228 view_id: crate::view::ViewId::RefreshRateInvertedInput,
229 label: fl!("settings-reader-refresh-rate-inverted-input"),
230 max_chars: 3,
231 initial_text: settings.reader.refresh_rate.global.inverted.to_string(),
232 }),
233 }
234 }
235
236 fn handle(
237 &self,
238 evt: &Event,
239 settings: &mut Settings,
240 _bus: &mut Bus,
241 ) -> (Option<String>, bool) {
242 if let Event::Submit(crate::view::ViewId::RefreshRateInvertedInput, ref text) = evt {
243 if let Ok(v) = text.parse::<u8>() {
244 settings.reader.refresh_rate.global.inverted = v;
245 return (Some(v.to_string()), true);
246 }
247 }
248
249 (None, false)
250 }
251}
252
253pub struct RefreshRateByKindRegular(pub FileExtension);
255
256impl SettingKind for RefreshRateByKindRegular {
257 fn identity(&self) -> SettingIdentity {
258 SettingIdentity::RefreshRateByKindRegular(self.0.as_str().to_string())
259 }
260
261 fn label(&self, _settings: &Settings) -> String {
262 fl!("settings-reader-refresh-rate-regular")
263 }
264
265 fn fetch(&self, settings: &Settings) -> SettingData {
266 let regular = settings
267 .reader
268 .refresh_rate
269 .by_kind
270 .get(self.0.as_str())
271 .map(|p| p.regular)
272 .unwrap_or(0);
273
274 SettingData {
275 value: regular.to_string(),
276 widget: WidgetKind::ActionLabel(Event::OpenNamedInput {
277 view_id: crate::view::ViewId::RefreshRateByKindRegularInput,
278 label: fl!(
279 "settings-reader-refresh-rate-by-kind-regular-input",
280 ext = self.0.as_str()
281 ),
282 max_chars: 3,
283 initial_text: regular.to_string(),
284 }),
285 }
286 }
287
288 fn handle(
289 &self,
290 evt: &Event,
291 settings: &mut Settings,
292 _bus: &mut Bus,
293 ) -> (Option<String>, bool) {
294 if let Event::Submit(crate::view::ViewId::RefreshRateByKindRegularInput, ref text) = evt {
295 if let Ok(v) = text.parse::<u8>() {
296 let pair = settings
297 .reader
298 .refresh_rate
299 .by_kind
300 .entry(self.0.as_str().to_string())
301 .or_insert(RefreshRatePair {
302 regular: 0,
303 inverted: 0,
304 });
305 pair.regular = v;
306 return (Some(v.to_string()), true);
307 }
308 }
309
310 (None, false)
311 }
312}
313
314pub struct RefreshRateByKindInverted(pub FileExtension);
316
317impl SettingKind for RefreshRateByKindInverted {
318 fn identity(&self) -> SettingIdentity {
319 SettingIdentity::RefreshRateByKindInverted(self.0.as_str().to_string())
320 }
321
322 fn label(&self, _settings: &Settings) -> String {
323 fl!("settings-reader-refresh-rate-inverted")
324 }
325
326 fn fetch(&self, settings: &Settings) -> SettingData {
327 let inverted = settings
328 .reader
329 .refresh_rate
330 .by_kind
331 .get(self.0.as_str())
332 .map(|p| p.inverted)
333 .unwrap_or(0);
334
335 SettingData {
336 value: inverted.to_string(),
337 widget: WidgetKind::ActionLabel(Event::OpenNamedInput {
338 view_id: crate::view::ViewId::RefreshRateByKindInvertedInput,
339 label: fl!(
340 "settings-reader-refresh-rate-by-kind-inverted-input",
341 ext = self.0.as_str()
342 ),
343 max_chars: 3,
344 initial_text: inverted.to_string(),
345 }),
346 }
347 }
348
349 fn handle(
350 &self,
351 evt: &Event,
352 settings: &mut Settings,
353 _bus: &mut Bus,
354 ) -> (Option<String>, bool) {
355 if let Event::Submit(crate::view::ViewId::RefreshRateByKindInvertedInput, ref text) = evt {
356 if let Ok(v) = text.parse::<u8>() {
357 let pair = settings
358 .reader
359 .refresh_rate
360 .by_kind
361 .entry(self.0.as_str().to_string())
362 .or_insert(RefreshRatePair {
363 regular: 0,
364 inverted: 0,
365 });
366 pair.inverted = v;
367 return (Some(v.to_string()), true);
368 }
369 }
370
371 (None, false)
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use crate::settings::{FinishedAction, Settings};
379 use crate::view::{Bus, EntryId, Event};
380 use std::collections::VecDeque;
381
382 mod finished_action_setting {
383 use super::*;
384
385 #[test]
386 fn handle_set_action_updates_settings() {
387 let setting = FinishedActionSetting;
388 let mut settings = Settings::default();
389 settings.reader.finished = FinishedAction::Close;
390 let mut bus: Bus = VecDeque::new();
391 let event = Event::Select(EntryId::SetFinishedAction(FinishedAction::GoToNext));
392
393 let result = setting.handle(&event, &mut settings, &mut bus);
394
395 assert!(result.0.is_some());
396 assert_eq!(settings.reader.finished, FinishedAction::GoToNext);
397 }
398
399 #[test]
400 fn handle_can_set_all_actions() {
401 let setting = FinishedActionSetting;
402 let mut settings = Settings::default();
403 let mut bus: Bus = VecDeque::new();
404
405 for action in [
406 FinishedAction::Notify,
407 FinishedAction::Close,
408 FinishedAction::GoToNext,
409 ] {
410 let event = Event::Select(EntryId::SetFinishedAction(action));
411 setting.handle(&event, &mut settings, &mut bus);
412 assert_eq!(settings.reader.finished, action);
413 }
414 }
415
416 #[test]
417 fn handle_returns_none_for_wrong_event() {
418 let setting = FinishedActionSetting;
419 let mut settings = Settings::default();
420 let mut bus: Bus = VecDeque::new();
421
422 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
423
424 assert!(result.0.is_none());
425 }
426
427 #[test]
428 fn handle_returns_none_for_per_library_entry_id() {
429 let setting = FinishedActionSetting;
430 let mut settings = Settings::default();
431 let mut bus: Bus = VecDeque::new();
432 let event = Event::Select(EntryId::SetLibraryFinishedAction(0, FinishedAction::Notify));
433
434 let result = setting.handle(&event, &mut settings, &mut bus);
435
436 assert!(result.0.is_none());
437 }
438 }
439
440 mod refresh_rate_info {
441 use super::*;
442
443 #[test]
444 fn handle_regular_submit_updates_display_without_writing_settings() {
445 let setting = RefreshRateInfo;
446 let mut settings = Settings::default();
447 settings.reader.refresh_rate.global.regular = 5;
448 settings.reader.refresh_rate.global.inverted = 10;
449 let mut bus: Bus = VecDeque::new();
450
451 let event = Event::Submit(ViewId::RefreshRateRegularInput, "3".to_string());
452 let (display, handled) = setting.handle(&event, &mut settings, &mut bus);
453
454 assert_eq!(
455 display.as_deref(),
456 Some(
457 fl!(
458 "settings-reader-refresh-rate-summary",
459 regular = 3u8,
460 inverted = 10u8
461 )
462 .as_str()
463 )
464 );
465 assert!(!handled);
466 assert_eq!(settings.reader.refresh_rate.global.regular, 5);
467 }
468
469 #[test]
470 fn handle_inverted_submit_updates_display_without_writing_settings() {
471 let setting = RefreshRateInfo;
472 let mut settings = Settings::default();
473 settings.reader.refresh_rate.global.regular = 5;
474 settings.reader.refresh_rate.global.inverted = 10;
475 let mut bus: Bus = VecDeque::new();
476
477 let event = Event::Submit(ViewId::RefreshRateInvertedInput, "7".to_string());
478 let (display, handled) = setting.handle(&event, &mut settings, &mut bus);
479
480 assert_eq!(
481 display.as_deref(),
482 Some(
483 fl!(
484 "settings-reader-refresh-rate-summary",
485 regular = 5u8,
486 inverted = 7u8
487 )
488 .as_str()
489 )
490 );
491 assert!(!handled);
492 assert_eq!(settings.reader.refresh_rate.global.inverted, 10);
493 }
494
495 #[test]
496 fn handle_invalid_text_falls_back_to_current_value() {
497 let setting = RefreshRateInfo;
498 let mut settings = Settings::default();
499 settings.reader.refresh_rate.global.regular = 5;
500 settings.reader.refresh_rate.global.inverted = 10;
501 let mut bus: Bus = VecDeque::new();
502
503 let event = Event::Submit(ViewId::RefreshRateRegularInput, "bad".to_string());
504 let (display, _) = setting.handle(&event, &mut settings, &mut bus);
505
506 assert_eq!(
507 display.as_deref(),
508 Some(
509 fl!(
510 "settings-reader-refresh-rate-summary",
511 regular = 5u8,
512 inverted = 10u8
513 )
514 .as_str()
515 )
516 );
517 }
518
519 #[test]
520 fn handle_unrelated_event_returns_none() {
521 let setting = RefreshRateInfo;
522 let mut settings = Settings::default();
523 let mut bus: Bus = VecDeque::new();
524
525 let (display, handled) =
526 setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
527
528 assert!(display.is_none());
529 assert!(!handled);
530 }
531 }
532}