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(
52 &self,
53 evt: &Event,
54 settings: &mut Settings,
55 _bus: &mut Bus,
56 ) -> (Option<String>, bool) {
57 if let Event::Select(EntryId::SetLocale(ref 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(ref 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 ButtonScheme;
329
330impl SettingKind for ButtonScheme {
331 fn identity(&self) -> SettingIdentity {
332 SettingIdentity::ButtonScheme
333 }
334
335 fn label(&self, _settings: &Settings) -> String {
336 fl!("settings-general-button-scheme")
337 }
338
339 fn fetch(&self, settings: &Settings) -> SettingData {
340 let enabled = settings.button_scheme == crate::settings::ButtonScheme::Natural;
341 SettingData {
342 value: settings.button_scheme.to_i18n_string(),
343 widget: WidgetKind::Toggle {
344 left_label: crate::settings::ButtonScheme::Natural.to_i18n_string(),
345 right_label: crate::settings::ButtonScheme::Inverted.to_i18n_string(),
346 enabled,
347 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)),
348 },
349 }
350 }
351
352 fn handle(
353 &self,
354 evt: &Event,
355 settings: &mut Settings,
356 bus: &mut Bus,
357 ) -> (Option<String>, bool) {
358 let new_scheme = match evt {
359 Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)) => {
360 match settings.button_scheme {
361 crate::settings::ButtonScheme::Natural => {
362 Some(crate::settings::ButtonScheme::Inverted)
363 }
364 crate::settings::ButtonScheme::Inverted => {
365 Some(crate::settings::ButtonScheme::Natural)
366 }
367 }
368 }
369 Event::Select(EntryId::SetButtonScheme(scheme)) => Some(*scheme),
370 _ => None,
371 };
372
373 if let Some(scheme) = new_scheme {
374 settings.button_scheme = scheme;
375 bus.push_back(Event::Select(EntryId::SetButtonScheme(scheme)));
376 return (Some(settings.button_scheme.to_i18n_string()), true);
377 }
378 (None, false)
379 }
380}
381
382pub struct SettingsRetention;
384
385impl SettingKind for SettingsRetention {
386 fn identity(&self) -> SettingIdentity {
387 SettingIdentity::SettingsRetention
388 }
389
390 fn label(&self, _settings: &Settings) -> String {
391 fl!("settings-general-settings-retention")
392 }
393
394 fn fetch(&self, settings: &Settings) -> SettingData {
395 SettingData {
396 value: settings.settings_retention.to_string(),
397 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditSettingsRetention)),
398 }
399 }
400
401 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
402 Some(self)
403 }
404}
405
406impl InputSettingKind for SettingsRetention {
407 fn submit_view_id(&self) -> ViewId {
408 ViewId::SettingsRetentionInput
409 }
410
411 fn open_entry_id(&self) -> EntryId {
412 EntryId::EditSettingsRetention
413 }
414
415 fn input_label(&self) -> String {
416 fl!("settings-general-settings-retention")
417 }
418
419 fn input_max_chars(&self) -> usize {
420 3
421 }
422
423 fn current_text(&self, settings: &Settings) -> String {
424 settings.settings_retention.to_string()
425 }
426
427 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
428 if let Ok(value) = text.parse::<usize>() {
429 settings.settings_retention = value;
430 }
431 settings.settings_retention.to_string()
432 }
433}
434
435fn get_available_layouts() -> Result<Vec<String>, Error> {
437 let layouts_dir = Path::new("keyboard-layouts");
438 let mut layouts = Vec::new();
439
440 if layouts_dir.exists() {
441 for entry in fs::read_dir(layouts_dir)? {
442 let entry = entry?;
443 let path = entry.path();
444
445 if path.extension().and_then(|s| s.to_str()) == Some("json") {
446 if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
447 let layout_name = stem
448 .chars()
449 .enumerate()
450 .map(|(i, c)| {
451 if i == 0 {
452 c.to_uppercase().collect::<String>()
453 } else {
454 c.to_string()
455 }
456 })
457 .collect::<String>();
458 layouts.push(layout_name);
459 }
460 }
461 }
462 }
463
464 layouts.sort();
465 Ok(layouts)
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use crate::settings::Settings;
472 use crate::view::settings_editor::kinds::{InputSettingKind, ToggleSettings};
473 use crate::view::{Bus, EntryId, Event, ToggleEvent};
474 use std::collections::VecDeque;
475
476 mod locale {
477 use super::*;
478
479 #[test]
480 fn handle_set_locale_updates_settings() {
481 let setting = Locale;
482 let mut settings = Settings::default();
483 let mut bus: Bus = VecDeque::new();
484 let locale: Option<unic_langid::LanguageIdentifier> = Some("de-DE".parse().unwrap());
485 let event = Event::Select(EntryId::SetLocale(locale.clone()));
486
487 let result = setting.handle(&event, &mut settings, &mut bus);
488
489 assert!(result.0.is_some());
490 assert_eq!(result.0.unwrap(), "de-DE");
491 assert_eq!(settings.locale, locale);
492 }
493
494 #[test]
495 fn handle_returns_none_for_wrong_event() {
496 let setting = Locale;
497 let mut settings = Settings::default();
498 let mut bus: Bus = VecDeque::new();
499
500 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
501
502 assert!(result.0.is_none());
503 }
504 }
505
506 mod keyboard_layout {
507 use super::*;
508
509 #[test]
510 fn handle_set_layout_updates_settings() {
511 let setting = KeyboardLayout;
512 let mut settings = Settings::default();
513 let mut bus: Bus = VecDeque::new();
514 let event = Event::Select(EntryId::SetKeyboardLayout("German".to_string()));
515
516 let result = setting.handle(&event, &mut settings, &mut bus);
517
518 assert!(result.0.is_some());
519 assert_eq!(result.0.unwrap(), "German");
520 assert_eq!(settings.keyboard_layout, "German");
521 }
522
523 #[test]
524 fn handle_returns_none_for_wrong_event() {
525 let setting = KeyboardLayout;
526 let mut settings = Settings::default();
527 let mut bus: Bus = VecDeque::new();
528
529 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
530
531 assert!(result.0.is_none());
532 }
533 }
534
535 mod auto_suspend {
536 use super::*;
537
538 #[test]
539 fn apply_text_parses_and_updates() {
540 let setting = AutoSuspend;
541 let mut settings = Settings::default();
542
543 let display = setting.apply_text("60.0", &mut settings);
544
545 assert_eq!(display, "60.0");
546 assert_eq!(settings.auto_suspend, 60.0);
547 }
548
549 #[test]
550 fn apply_text_returns_never_for_zero() {
551 let setting = AutoSuspend;
552 let mut settings = Settings::default();
553
554 let display = setting.apply_text("0", &mut settings);
555
556 assert_eq!(display, "Never");
557 assert_eq!(settings.auto_suspend, 0.0);
558 }
559
560 #[test]
561 fn apply_text_ignores_invalid_input() {
562 let setting = AutoSuspend;
563 let mut settings = Settings {
564 auto_suspend: 30.0,
565 ..Default::default()
566 };
567
568 let display = setting.apply_text("invalid", &mut settings);
569
570 assert_eq!(settings.auto_suspend, 30.0);
571 assert_eq!(display, "30.0");
572 }
573 }
574
575 mod auto_power_off {
576 use super::*;
577
578 #[test]
579 fn apply_text_parses_and_updates() {
580 let setting = AutoPowerOff;
581 let mut settings = Settings::default();
582
583 let display = setting.apply_text("14.0", &mut settings);
584
585 assert_eq!(display, "14.0");
586 assert_eq!(settings.auto_power_off, 14.0);
587 }
588
589 #[test]
590 fn apply_text_returns_never_for_zero() {
591 let setting = AutoPowerOff;
592 let mut settings = Settings::default();
593
594 let display = setting.apply_text("0", &mut settings);
595
596 assert_eq!(display, "Never");
597 assert_eq!(settings.auto_power_off, 0.0);
598 }
599
600 #[test]
601 fn apply_text_ignores_invalid_input() {
602 let setting = AutoPowerOff;
603 let mut settings = Settings {
604 auto_power_off: 7.0,
605 ..Default::default()
606 };
607
608 let display = setting.apply_text("invalid", &mut settings);
609
610 assert_eq!(settings.auto_power_off, 7.0);
611 assert_eq!(display, "7.0");
612 }
613 }
614
615 mod sleep_cover {
616 use super::*;
617
618 #[test]
619 fn handle_toggle_event_toggles_value() {
620 let setting = SleepCover;
621 let mut settings = Settings {
622 sleep_cover: true,
623 ..Default::default()
624 };
625 let mut bus: Bus = VecDeque::new();
626 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover));
627
628 let result = setting.handle(&event, &mut settings, &mut bus);
629
630 assert!(result.0.is_some());
631 assert_eq!(result.0.unwrap(), "false");
632 assert!(!settings.sleep_cover);
633 }
634
635 #[test]
636 fn handle_returns_none_for_wrong_event() {
637 let setting = SleepCover;
638 let mut settings = Settings::default();
639 let mut bus: Bus = VecDeque::new();
640
641 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
642
643 assert!(result.0.is_none());
644 }
645 }
646
647 mod auto_share {
648 use super::*;
649
650 #[test]
651 fn handle_toggle_event_toggles_value() {
652 let setting = AutoShare;
653 let mut settings = Settings {
654 auto_share: false,
655 ..Default::default()
656 };
657 let mut bus: Bus = VecDeque::new();
658 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare));
659
660 let result = setting.handle(&event, &mut settings, &mut bus);
661
662 assert!(result.0.is_some());
663 assert_eq!(result.0.unwrap(), "true");
664 assert!(settings.auto_share);
665 }
666
667 #[test]
668 fn handle_returns_none_for_wrong_event() {
669 let setting = AutoShare;
670 let mut settings = Settings::default();
671 let mut bus: Bus = VecDeque::new();
672
673 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
674
675 assert!(result.0.is_none());
676 }
677 }
678
679 mod button_scheme {
680 use super::*;
681 use crate::settings::ButtonScheme;
682
683 #[test]
684 fn handle_toggle_event_switches_natural_to_inverted() {
685 let setting = ButtonScheme;
686 let mut settings = Settings {
687 button_scheme: ButtonScheme::Natural,
688 ..Default::default()
689 };
690 let mut bus: Bus = VecDeque::new();
691 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
692
693 let result = setting.handle(&event, &mut settings, &mut bus);
694
695 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
696 assert_eq!(bus.len(), 1);
697 assert!(result.0.is_some());
698 }
699
700 #[test]
701 fn handle_toggle_event_switches_inverted_to_natural() {
702 let setting = ButtonScheme;
703 let mut settings = Settings {
704 button_scheme: ButtonScheme::Inverted,
705 ..Default::default()
706 };
707 let mut bus: Bus = VecDeque::new();
708 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
709
710 let result = setting.handle(&event, &mut settings, &mut bus);
711
712 assert_eq!(settings.button_scheme, ButtonScheme::Natural);
713 assert_eq!(bus.len(), 1);
714 assert!(result.0.is_some());
715 }
716
717 #[test]
718 fn handle_set_scheme_event_applies_directly() {
719 let setting = ButtonScheme;
720 let mut settings = Settings {
721 button_scheme: ButtonScheme::Natural,
722 ..Default::default()
723 };
724 let mut bus: Bus = VecDeque::new();
725 let event = Event::Select(EntryId::SetButtonScheme(ButtonScheme::Inverted));
726
727 let result = setting.handle(&event, &mut settings, &mut bus);
728
729 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
730 assert!(result.0.is_some());
731 }
732
733 #[test]
734 fn handle_returns_none_for_wrong_event() {
735 let setting = ButtonScheme;
736 let mut settings = Settings::default();
737 let mut bus: Bus = VecDeque::new();
738
739 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
740
741 assert!(result.0.is_none());
742 }
743 }
744
745 mod settings_retention {
746 use super::*;
747
748 #[test]
749 fn apply_text_parses_and_updates() {
750 let setting = SettingsRetention;
751 let mut settings = Settings::default();
752
753 let display = setting.apply_text("10", &mut settings);
754
755 assert_eq!(display, "10");
756 assert_eq!(settings.settings_retention, 10);
757 }
758
759 #[test]
760 fn apply_text_ignores_invalid_input() {
761 let setting = SettingsRetention;
762 let mut settings = Settings {
763 settings_retention: 3,
764 ..Default::default()
765 };
766
767 let display = setting.apply_text("invalid", &mut settings);
768
769 assert_eq!(settings.settings_retention, 3);
770 assert_eq!(display, "3");
771 }
772 }
773}