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