1use super::{
4 InputSettingKind, SettingData, SettingIdentity, SettingKind, ToggleSettings, WidgetKind,
5};
6use crate::fl;
7use crate::i18n::I18nDisplay;
8use crate::settings::Settings;
9use crate::view::{Bus, EntryId, EntryKind, Event, ToggleEvent, ViewId};
10use anyhow::Error;
11use std::fs;
12use std::path::Path;
13
14pub struct Locale;
16
17impl SettingKind for Locale {
18 fn identity(&self) -> SettingIdentity {
19 SettingIdentity::Locale
20 }
21
22 fn label(&self, _settings: &Settings) -> String {
23 fl!("settings-general-language")
24 }
25
26 fn fetch(&self, settings: &Settings) -> SettingData {
27 let current = settings.locale.as_ref().map(|l| l.to_string());
28 let display = current
29 .as_deref()
30 .unwrap_or(crate::i18n::DEFAULT_LOCALE)
31 .to_string();
32
33 let entries = crate::i18n::AVAILABLE_LOCALES
34 .iter()
35 .map(|&tag| {
36 let lang_id: Option<unic_langid::LanguageIdentifier> = tag.parse().ok();
37 EntryKind::RadioButton(
38 tag.to_string(),
39 EntryId::SetLocale(lang_id),
40 current.as_deref() == Some(tag),
41 )
42 })
43 .collect::<Vec<_>>();
44
45 SettingData {
46 value: display,
47 widget: WidgetKind::SubMenu(entries),
48 }
49 }
50
51 fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
52 if let Event::Select(EntryId::SetLocale(ref locale)) = evt {
53 settings.locale = locale.clone();
54 crate::i18n::init(locale.as_ref());
55 let display = locale
56 .as_ref()
57 .map(|l| l.to_string())
58 .unwrap_or_else(|| crate::i18n::DEFAULT_LOCALE.to_string());
59 return Some(display);
60 }
61 None
62 }
63}
64
65pub struct KeyboardLayout;
67
68impl SettingKind for KeyboardLayout {
69 fn identity(&self) -> SettingIdentity {
70 SettingIdentity::KeyboardLayout
71 }
72
73 fn label(&self, _settings: &Settings) -> String {
74 fl!("settings-general-keyboard-layout")
75 }
76
77 fn fetch(&self, settings: &Settings) -> SettingData {
78 let current_layout = settings.keyboard_layout.clone();
79 let available_layouts = get_available_layouts().unwrap_or_default();
80
81 let entries = available_layouts
82 .iter()
83 .map(|layout| {
84 EntryKind::RadioButton(
85 layout.clone(),
86 EntryId::SetKeyboardLayout(layout.clone()),
87 current_layout == *layout,
88 )
89 })
90 .collect::<Vec<_>>();
91
92 SettingData {
93 value: current_layout,
94 widget: WidgetKind::SubMenu(entries),
95 }
96 }
97
98 fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
99 if let Event::Select(EntryId::SetKeyboardLayout(ref layout)) = evt {
100 settings.keyboard_layout = layout.clone();
101 return Some(layout.clone());
102 }
103 None
104 }
105}
106
107pub struct AutoSuspend;
109
110impl SettingKind for AutoSuspend {
111 fn identity(&self) -> SettingIdentity {
112 SettingIdentity::AutoSuspend
113 }
114
115 fn label(&self, _settings: &Settings) -> String {
116 fl!("settings-general-auto-suspend")
117 }
118
119 fn fetch(&self, settings: &Settings) -> SettingData {
120 let value = if settings.auto_suspend == 0.0 {
121 fl!("settings-general-never")
122 } else {
123 format!("{:.1}", settings.auto_suspend)
124 };
125
126 SettingData {
127 value,
128 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditAutoSuspend)),
129 }
130 }
131
132 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
133 Some(self)
134 }
135}
136
137impl InputSettingKind for AutoSuspend {
138 fn submit_view_id(&self) -> ViewId {
139 ViewId::AutoSuspendInput
140 }
141
142 fn open_entry_id(&self) -> EntryId {
143 EntryId::EditAutoSuspend
144 }
145
146 fn input_label(&self) -> String {
147 fl!("settings-general-auto-suspend-input")
148 }
149
150 fn input_max_chars(&self) -> usize {
151 10
152 }
153
154 fn current_text(&self, settings: &Settings) -> String {
155 if settings.auto_suspend == 0.0 {
156 "0".to_string()
157 } else {
158 format!("{:.1}", settings.auto_suspend)
159 }
160 }
161
162 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
163 if let Ok(value) = text.parse::<f32>() {
164 settings.auto_suspend = value;
165 }
166 if settings.auto_suspend == 0.0 {
167 fl!("settings-general-never")
168 } else {
169 format!("{:.1}", settings.auto_suspend)
170 }
171 }
172}
173
174pub struct AutoPowerOff;
176
177impl SettingKind for AutoPowerOff {
178 fn identity(&self) -> SettingIdentity {
179 SettingIdentity::AutoPowerOff
180 }
181
182 fn label(&self, _settings: &Settings) -> String {
183 fl!("settings-general-auto-power-off")
184 }
185
186 fn fetch(&self, settings: &Settings) -> SettingData {
187 let value = if settings.auto_power_off == 0.0 {
188 fl!("settings-general-never")
189 } else {
190 format!("{:.1}", settings.auto_power_off)
191 };
192
193 SettingData {
194 value,
195 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditAutoPowerOff)),
196 }
197 }
198
199 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
200 Some(self)
201 }
202}
203
204impl InputSettingKind for AutoPowerOff {
205 fn submit_view_id(&self) -> ViewId {
206 ViewId::AutoPowerOffInput
207 }
208
209 fn open_entry_id(&self) -> EntryId {
210 EntryId::EditAutoPowerOff
211 }
212
213 fn input_label(&self) -> String {
214 fl!("settings-general-auto-power-off-input")
215 }
216
217 fn input_max_chars(&self) -> usize {
218 10
219 }
220
221 fn current_text(&self, settings: &Settings) -> String {
222 if settings.auto_power_off == 0.0 {
223 "0".to_string()
224 } else {
225 format!("{:.1}", settings.auto_power_off)
226 }
227 }
228
229 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
230 if let Ok(value) = text.parse::<f32>() {
231 settings.auto_power_off = value;
232 }
233 if settings.auto_power_off == 0.0 {
234 fl!("settings-general-never")
235 } else {
236 format!("{:.1}", settings.auto_power_off)
237 }
238 }
239}
240
241pub struct SleepCover;
243
244impl SettingKind for SleepCover {
245 fn identity(&self) -> SettingIdentity {
246 SettingIdentity::SleepCover
247 }
248
249 fn label(&self, _settings: &Settings) -> String {
250 fl!("settings-general-enable-sleep-cover")
251 }
252
253 fn fetch(&self, settings: &Settings) -> SettingData {
254 SettingData {
255 value: settings.sleep_cover.to_string(),
256 widget: WidgetKind::Toggle {
257 left_label: fl!("settings-general-toggle-on"),
258 right_label: fl!("settings-general-toggle-off"),
259 enabled: settings.sleep_cover,
260 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover)),
261 },
262 }
263 }
264
265 fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
266 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover)) = evt {
267 settings.sleep_cover = !settings.sleep_cover;
268 return Some(settings.sleep_cover.to_string());
269 }
270 None
271 }
272}
273
274pub struct AutoShare;
276
277impl SettingKind for AutoShare {
278 fn identity(&self) -> SettingIdentity {
279 SettingIdentity::AutoShare
280 }
281
282 fn label(&self, _settings: &Settings) -> String {
283 fl!("settings-general-enable-auto-share")
284 }
285
286 fn fetch(&self, settings: &Settings) -> SettingData {
287 SettingData {
288 value: settings.auto_share.to_string(),
289 widget: WidgetKind::Toggle {
290 left_label: fl!("settings-general-toggle-on"),
291 right_label: fl!("settings-general-toggle-off"),
292 enabled: settings.auto_share,
293 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare)),
294 },
295 }
296 }
297
298 fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
299 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare)) = evt {
300 settings.auto_share = !settings.auto_share;
301 return Some(settings.auto_share.to_string());
302 }
303 None
304 }
305}
306
307pub struct ButtonScheme;
309
310impl SettingKind for ButtonScheme {
311 fn identity(&self) -> SettingIdentity {
312 SettingIdentity::ButtonScheme
313 }
314
315 fn label(&self, _settings: &Settings) -> String {
316 fl!("settings-general-button-scheme")
317 }
318
319 fn fetch(&self, settings: &Settings) -> SettingData {
320 let enabled = settings.button_scheme == crate::settings::ButtonScheme::Natural;
321 SettingData {
322 value: settings.button_scheme.to_i18n_string(),
323 widget: WidgetKind::Toggle {
324 left_label: crate::settings::ButtonScheme::Natural.to_i18n_string(),
325 right_label: crate::settings::ButtonScheme::Inverted.to_i18n_string(),
326 enabled,
327 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)),
328 },
329 }
330 }
331
332 fn handle(&self, evt: &Event, settings: &mut Settings, bus: &mut Bus) -> Option<String> {
333 let new_scheme = match evt {
334 Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)) => {
335 match settings.button_scheme {
336 crate::settings::ButtonScheme::Natural => {
337 Some(crate::settings::ButtonScheme::Inverted)
338 }
339 crate::settings::ButtonScheme::Inverted => {
340 Some(crate::settings::ButtonScheme::Natural)
341 }
342 }
343 }
344 Event::Select(EntryId::SetButtonScheme(scheme)) => Some(*scheme),
345 _ => None,
346 };
347
348 if let Some(scheme) = new_scheme {
349 settings.button_scheme = scheme;
350 bus.push_back(Event::Select(EntryId::SetButtonScheme(scheme)));
351 return Some(settings.button_scheme.to_i18n_string());
352 }
353 None
354 }
355}
356
357pub struct SettingsRetention;
359
360impl SettingKind for SettingsRetention {
361 fn identity(&self) -> SettingIdentity {
362 SettingIdentity::SettingsRetention
363 }
364
365 fn label(&self, _settings: &Settings) -> String {
366 fl!("settings-general-settings-retention")
367 }
368
369 fn fetch(&self, settings: &Settings) -> SettingData {
370 SettingData {
371 value: settings.settings_retention.to_string(),
372 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditSettingsRetention)),
373 }
374 }
375
376 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
377 Some(self)
378 }
379}
380
381impl InputSettingKind for SettingsRetention {
382 fn submit_view_id(&self) -> ViewId {
383 ViewId::SettingsRetentionInput
384 }
385
386 fn open_entry_id(&self) -> EntryId {
387 EntryId::EditSettingsRetention
388 }
389
390 fn input_label(&self) -> String {
391 fl!("settings-general-settings-retention")
392 }
393
394 fn input_max_chars(&self) -> usize {
395 3
396 }
397
398 fn current_text(&self, settings: &Settings) -> String {
399 settings.settings_retention.to_string()
400 }
401
402 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
403 if let Ok(value) = text.parse::<usize>() {
404 settings.settings_retention = value;
405 }
406 settings.settings_retention.to_string()
407 }
408}
409
410fn get_available_layouts() -> Result<Vec<String>, Error> {
412 let layouts_dir = Path::new("keyboard-layouts");
413 let mut layouts = Vec::new();
414
415 if layouts_dir.exists() {
416 for entry in fs::read_dir(layouts_dir)? {
417 let entry = entry?;
418 let path = entry.path();
419
420 if path.extension().and_then(|s| s.to_str()) == Some("json") {
421 if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
422 let layout_name = stem
423 .chars()
424 .enumerate()
425 .map(|(i, c)| {
426 if i == 0 {
427 c.to_uppercase().collect::<String>()
428 } else {
429 c.to_string()
430 }
431 })
432 .collect::<String>();
433 layouts.push(layout_name);
434 }
435 }
436 }
437 }
438
439 layouts.sort();
440 Ok(layouts)
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446 use crate::settings::Settings;
447 use crate::view::settings_editor::kinds::{InputSettingKind, ToggleSettings};
448 use crate::view::{Bus, EntryId, Event, ToggleEvent};
449 use std::collections::VecDeque;
450
451 mod locale {
452 use super::*;
453
454 #[test]
455 fn handle_set_locale_updates_settings() {
456 let setting = Locale;
457 let mut settings = Settings::default();
458 let mut bus: Bus = VecDeque::new();
459 let locale: Option<unic_langid::LanguageIdentifier> = Some("de-DE".parse().unwrap());
460 let event = Event::Select(EntryId::SetLocale(locale.clone()));
461
462 let result = setting.handle(&event, &mut settings, &mut bus);
463
464 assert!(result.is_some());
465 assert_eq!(result.unwrap(), "de-DE");
466 assert_eq!(settings.locale, locale);
467 }
468
469 #[test]
470 fn handle_returns_none_for_wrong_event() {
471 let setting = Locale;
472 let mut settings = Settings::default();
473 let mut bus: Bus = VecDeque::new();
474
475 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
476
477 assert!(result.is_none());
478 }
479 }
480
481 mod keyboard_layout {
482 use super::*;
483
484 #[test]
485 fn handle_set_layout_updates_settings() {
486 let setting = KeyboardLayout;
487 let mut settings = Settings::default();
488 let mut bus: Bus = VecDeque::new();
489 let event = Event::Select(EntryId::SetKeyboardLayout("German".to_string()));
490
491 let result = setting.handle(&event, &mut settings, &mut bus);
492
493 assert!(result.is_some());
494 assert_eq!(result.unwrap(), "German");
495 assert_eq!(settings.keyboard_layout, "German");
496 }
497
498 #[test]
499 fn handle_returns_none_for_wrong_event() {
500 let setting = KeyboardLayout;
501 let mut settings = Settings::default();
502 let mut bus: Bus = VecDeque::new();
503
504 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
505
506 assert!(result.is_none());
507 }
508 }
509
510 mod auto_suspend {
511 use super::*;
512
513 #[test]
514 fn apply_text_parses_and_updates() {
515 let setting = AutoSuspend;
516 let mut settings = Settings::default();
517
518 let display = setting.apply_text("60.0", &mut settings);
519
520 assert_eq!(display, "60.0");
521 assert_eq!(settings.auto_suspend, 60.0);
522 }
523
524 #[test]
525 fn apply_text_returns_never_for_zero() {
526 let setting = AutoSuspend;
527 let mut settings = Settings::default();
528
529 let display = setting.apply_text("0", &mut settings);
530
531 assert_eq!(display, "Never");
532 assert_eq!(settings.auto_suspend, 0.0);
533 }
534
535 #[test]
536 fn apply_text_ignores_invalid_input() {
537 let setting = AutoSuspend;
538 let mut settings = Settings {
539 auto_suspend: 30.0,
540 ..Default::default()
541 };
542
543 let display = setting.apply_text("invalid", &mut settings);
544
545 assert_eq!(settings.auto_suspend, 30.0);
546 assert_eq!(display, "30.0");
547 }
548 }
549
550 mod auto_power_off {
551 use super::*;
552
553 #[test]
554 fn apply_text_parses_and_updates() {
555 let setting = AutoPowerOff;
556 let mut settings = Settings::default();
557
558 let display = setting.apply_text("14.0", &mut settings);
559
560 assert_eq!(display, "14.0");
561 assert_eq!(settings.auto_power_off, 14.0);
562 }
563
564 #[test]
565 fn apply_text_returns_never_for_zero() {
566 let setting = AutoPowerOff;
567 let mut settings = Settings::default();
568
569 let display = setting.apply_text("0", &mut settings);
570
571 assert_eq!(display, "Never");
572 assert_eq!(settings.auto_power_off, 0.0);
573 }
574
575 #[test]
576 fn apply_text_ignores_invalid_input() {
577 let setting = AutoPowerOff;
578 let mut settings = Settings {
579 auto_power_off: 7.0,
580 ..Default::default()
581 };
582
583 let display = setting.apply_text("invalid", &mut settings);
584
585 assert_eq!(settings.auto_power_off, 7.0);
586 assert_eq!(display, "7.0");
587 }
588 }
589
590 mod sleep_cover {
591 use super::*;
592
593 #[test]
594 fn handle_toggle_event_toggles_value() {
595 let setting = SleepCover;
596 let mut settings = Settings {
597 sleep_cover: true,
598 ..Default::default()
599 };
600 let mut bus: Bus = VecDeque::new();
601 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover));
602
603 let result = setting.handle(&event, &mut settings, &mut bus);
604
605 assert!(result.is_some());
606 assert_eq!(result.unwrap(), "false");
607 assert!(!settings.sleep_cover);
608 }
609
610 #[test]
611 fn handle_returns_none_for_wrong_event() {
612 let setting = SleepCover;
613 let mut settings = Settings::default();
614 let mut bus: Bus = VecDeque::new();
615
616 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
617
618 assert!(result.is_none());
619 }
620 }
621
622 mod auto_share {
623 use super::*;
624
625 #[test]
626 fn handle_toggle_event_toggles_value() {
627 let setting = AutoShare;
628 let mut settings = Settings {
629 auto_share: false,
630 ..Default::default()
631 };
632 let mut bus: Bus = VecDeque::new();
633 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare));
634
635 let result = setting.handle(&event, &mut settings, &mut bus);
636
637 assert!(result.is_some());
638 assert_eq!(result.unwrap(), "true");
639 assert!(settings.auto_share);
640 }
641
642 #[test]
643 fn handle_returns_none_for_wrong_event() {
644 let setting = AutoShare;
645 let mut settings = Settings::default();
646 let mut bus: Bus = VecDeque::new();
647
648 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
649
650 assert!(result.is_none());
651 }
652 }
653
654 mod button_scheme {
655 use super::*;
656 use crate::settings::ButtonScheme;
657
658 #[test]
659 fn handle_toggle_event_switches_natural_to_inverted() {
660 let setting = ButtonScheme;
661 let mut settings = Settings {
662 button_scheme: ButtonScheme::Natural,
663 ..Default::default()
664 };
665 let mut bus: Bus = VecDeque::new();
666 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
667
668 let result = setting.handle(&event, &mut settings, &mut bus);
669
670 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
671 assert_eq!(bus.len(), 1);
672 assert!(result.is_some());
673 }
674
675 #[test]
676 fn handle_toggle_event_switches_inverted_to_natural() {
677 let setting = ButtonScheme;
678 let mut settings = Settings {
679 button_scheme: ButtonScheme::Inverted,
680 ..Default::default()
681 };
682 let mut bus: Bus = VecDeque::new();
683 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
684
685 let result = setting.handle(&event, &mut settings, &mut bus);
686
687 assert_eq!(settings.button_scheme, ButtonScheme::Natural);
688 assert_eq!(bus.len(), 1);
689 assert!(result.is_some());
690 }
691
692 #[test]
693 fn handle_set_scheme_event_applies_directly() {
694 let setting = ButtonScheme;
695 let mut settings = Settings {
696 button_scheme: ButtonScheme::Natural,
697 ..Default::default()
698 };
699 let mut bus: Bus = VecDeque::new();
700 let event = Event::Select(EntryId::SetButtonScheme(ButtonScheme::Inverted));
701
702 let result = setting.handle(&event, &mut settings, &mut bus);
703
704 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
705 assert!(result.is_some());
706 }
707
708 #[test]
709 fn handle_returns_none_for_wrong_event() {
710 let setting = ButtonScheme;
711 let mut settings = Settings::default();
712 let mut bus: Bus = VecDeque::new();
713
714 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
715
716 assert!(result.is_none());
717 }
718 }
719
720 mod settings_retention {
721 use super::*;
722
723 #[test]
724 fn apply_text_parses_and_updates() {
725 let setting = SettingsRetention;
726 let mut settings = Settings::default();
727
728 let display = setting.apply_text("10", &mut settings);
729
730 assert_eq!(display, "10");
731 assert_eq!(settings.settings_retention, 10);
732 }
733
734 #[test]
735 fn apply_text_ignores_invalid_input() {
736 let setting = SettingsRetention;
737 let mut settings = Settings {
738 settings_retention: 3,
739 ..Default::default()
740 };
741
742 let display = setting.apply_text("invalid", &mut settings);
743
744 assert_eq!(settings.settings_retention, 3);
745 assert_eq!(display, "3");
746 }
747 }
748}