1use super::{
4 InputSettingKind, SettingData, SettingIdentity, SettingKind, ToggleSettings, WidgetKind,
5};
6use crate::device::CURRENT_DEVICE;
7use crate::fl;
8use crate::frontlight::LightLevel;
9use crate::geolocation::Coordinates;
10use crate::i18n::I18nDisplay;
11use crate::settings::Settings;
12use crate::view::{Bus, EntryId, EntryKind, Event, ToggleEvent, ViewId};
13use anyhow::Error;
14use std::fs;
15
16pub struct Locale;
18
19impl SettingKind for Locale {
20 fn identity(&self) -> SettingIdentity {
21 SettingIdentity::Locale
22 }
23
24 fn label(&self, _settings: &Settings) -> String {
25 fl!("settings-general-language")
26 }
27
28 fn fetch(&self, settings: &Settings) -> SettingData {
29 let current = settings.locale.as_ref().map(|l| l.to_string());
30 let display = current
31 .as_deref()
32 .unwrap_or(crate::i18n::DEFAULT_LOCALE)
33 .to_string();
34
35 let entries = crate::i18n::AVAILABLE_LOCALES
36 .iter()
37 .map(|&tag| {
38 let lang_id: Option<unic_langid::LanguageIdentifier> = tag.parse().ok();
39 EntryKind::RadioButton(
40 tag.to_string(),
41 EntryId::SetLocale(lang_id),
42 current.as_deref() == Some(tag),
43 )
44 })
45 .collect::<Vec<_>>();
46
47 SettingData {
48 value: display,
49 widget: WidgetKind::SubMenu(entries),
50 }
51 }
52
53 fn handle(
54 &self,
55 evt: &Event,
56 settings: &mut Settings,
57 _bus: &mut Bus,
58 ) -> (Option<String>, bool) {
59 if let Event::Select(EntryId::SetLocale(locale)) = evt {
60 settings.locale = locale.clone();
61 crate::i18n::init(locale.as_ref());
62 let display = locale
63 .as_ref()
64 .map(|l| l.to_string())
65 .unwrap_or_else(|| crate::i18n::DEFAULT_LOCALE.to_string());
66 return (Some(display), true);
67 }
68 (None, false)
69 }
70}
71
72pub struct KeyboardLayout;
74
75impl SettingKind for KeyboardLayout {
76 fn identity(&self) -> SettingIdentity {
77 SettingIdentity::KeyboardLayout
78 }
79
80 fn label(&self, _settings: &Settings) -> String {
81 fl!("settings-general-keyboard-layout")
82 }
83
84 fn fetch(&self, settings: &Settings) -> SettingData {
85 let current_layout = settings.keyboard_layout.clone();
86 let available_layouts = get_available_layouts().unwrap_or_default();
87
88 let entries = available_layouts
89 .iter()
90 .map(|layout| {
91 EntryKind::RadioButton(
92 layout.clone(),
93 EntryId::SetKeyboardLayout(layout.clone()),
94 current_layout == *layout,
95 )
96 })
97 .collect::<Vec<_>>();
98
99 SettingData {
100 value: current_layout,
101 widget: WidgetKind::SubMenu(entries),
102 }
103 }
104
105 fn handle(
106 &self,
107 evt: &Event,
108 settings: &mut Settings,
109 _bus: &mut Bus,
110 ) -> (Option<String>, bool) {
111 if let Event::Select(EntryId::SetKeyboardLayout(layout)) = evt {
112 settings.keyboard_layout = layout.clone();
113 return (Some(layout.clone()), true);
114 }
115 (None, false)
116 }
117}
118
119pub struct AutoSuspend;
121
122impl SettingKind for AutoSuspend {
123 fn identity(&self) -> SettingIdentity {
124 SettingIdentity::AutoSuspend
125 }
126
127 fn label(&self, _settings: &Settings) -> String {
128 fl!("settings-general-auto-suspend")
129 }
130
131 fn fetch(&self, settings: &Settings) -> SettingData {
132 let value = if settings.auto_suspend == 0.0 {
133 fl!("settings-general-never")
134 } else {
135 format!("{:.1}", settings.auto_suspend)
136 };
137
138 SettingData {
139 value,
140 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditAutoSuspend)),
141 }
142 }
143
144 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
145 Some(self)
146 }
147}
148
149impl InputSettingKind for AutoSuspend {
150 fn submit_view_id(&self) -> ViewId {
151 ViewId::AutoSuspendInput
152 }
153
154 fn open_entry_id(&self) -> EntryId {
155 EntryId::EditAutoSuspend
156 }
157
158 fn input_label(&self) -> String {
159 fl!("settings-general-auto-suspend-input")
160 }
161
162 fn input_max_chars(&self) -> usize {
163 10
164 }
165
166 fn current_text(&self, settings: &Settings) -> String {
167 if settings.auto_suspend == 0.0 {
168 "0".to_string()
169 } else {
170 format!("{:.1}", settings.auto_suspend)
171 }
172 }
173
174 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
175 if let Ok(value) = text.parse::<f32>() {
176 settings.auto_suspend = value;
177 }
178 if settings.auto_suspend == 0.0 {
179 fl!("settings-general-never")
180 } else {
181 format!("{:.1}", settings.auto_suspend)
182 }
183 }
184}
185
186pub struct AutoPowerOff;
188
189impl SettingKind for AutoPowerOff {
190 fn identity(&self) -> SettingIdentity {
191 SettingIdentity::AutoPowerOff
192 }
193
194 fn label(&self, _settings: &Settings) -> String {
195 fl!("settings-general-auto-power-off")
196 }
197
198 fn fetch(&self, settings: &Settings) -> SettingData {
199 let value = if settings.auto_power_off == 0.0 {
200 fl!("settings-general-never")
201 } else {
202 format!("{:.1}", settings.auto_power_off)
203 };
204
205 SettingData {
206 value,
207 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditAutoPowerOff)),
208 }
209 }
210
211 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
212 Some(self)
213 }
214}
215
216impl InputSettingKind for AutoPowerOff {
217 fn submit_view_id(&self) -> ViewId {
218 ViewId::AutoPowerOffInput
219 }
220
221 fn open_entry_id(&self) -> EntryId {
222 EntryId::EditAutoPowerOff
223 }
224
225 fn input_label(&self) -> String {
226 fl!("settings-general-auto-power-off-input")
227 }
228
229 fn input_max_chars(&self) -> usize {
230 10
231 }
232
233 fn current_text(&self, settings: &Settings) -> String {
234 if settings.auto_power_off == 0.0 {
235 "0".to_string()
236 } else {
237 format!("{:.1}", settings.auto_power_off)
238 }
239 }
240
241 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
242 if let Ok(value) = text.parse::<f32>() {
243 settings.auto_power_off = value;
244 }
245 if settings.auto_power_off == 0.0 {
246 fl!("settings-general-never")
247 } else {
248 format!("{:.1}", settings.auto_power_off)
249 }
250 }
251}
252
253pub struct SleepCover;
255
256impl SettingKind for SleepCover {
257 fn identity(&self) -> SettingIdentity {
258 SettingIdentity::SleepCover
259 }
260
261 fn label(&self, _settings: &Settings) -> String {
262 fl!("settings-general-enable-sleep-cover")
263 }
264
265 fn fetch(&self, settings: &Settings) -> SettingData {
266 SettingData {
267 value: settings.sleep_cover.to_string(),
268 widget: WidgetKind::Toggle {
269 left_label: fl!("settings-general-toggle-on"),
270 right_label: fl!("settings-general-toggle-off"),
271 enabled: settings.sleep_cover,
272 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover)),
273 },
274 }
275 }
276
277 fn handle(
278 &self,
279 evt: &Event,
280 settings: &mut Settings,
281 _bus: &mut Bus,
282 ) -> (Option<String>, bool) {
283 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover)) = evt {
284 settings.sleep_cover = !settings.sleep_cover;
285 return (Some(settings.sleep_cover.to_string()), true);
286 }
287 (None, false)
288 }
289}
290
291pub struct AutoShare;
293
294impl SettingKind for AutoShare {
295 fn identity(&self) -> SettingIdentity {
296 SettingIdentity::AutoShare
297 }
298
299 fn label(&self, _settings: &Settings) -> String {
300 fl!("settings-general-enable-auto-share")
301 }
302
303 fn fetch(&self, settings: &Settings) -> SettingData {
304 SettingData {
305 value: settings.auto_share.to_string(),
306 widget: WidgetKind::Toggle {
307 left_label: fl!("settings-general-toggle-on"),
308 right_label: fl!("settings-general-toggle-off"),
309 enabled: settings.auto_share,
310 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare)),
311 },
312 }
313 }
314
315 fn handle(
316 &self,
317 evt: &Event,
318 settings: &mut Settings,
319 _bus: &mut Bus,
320 ) -> (Option<String>, bool) {
321 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare)) = evt {
322 settings.auto_share = !settings.auto_share;
323 return (Some(settings.auto_share.to_string()), true);
324 }
325 (None, false)
326 }
327}
328
329pub struct AutoTime;
331
332impl SettingKind for AutoTime {
333 fn identity(&self) -> SettingIdentity {
334 SettingIdentity::AutoTime
335 }
336
337 fn label(&self, _settings: &Settings) -> String {
338 fl!("settings-general-auto-time")
339 }
340
341 fn fetch(&self, settings: &Settings) -> SettingData {
342 SettingData {
343 value: settings.auto_time.to_string(),
344 widget: WidgetKind::Toggle {
345 left_label: fl!("settings-general-toggle-on"),
346 right_label: fl!("settings-general-toggle-off"),
347 enabled: settings.auto_time,
348 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoTime)),
349 },
350 }
351 }
352
353 fn handle(
354 &self,
355 evt: &Event,
356 settings: &mut Settings,
357 _bus: &mut Bus,
358 ) -> (Option<String>, bool) {
359 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoTime)) = evt {
360 settings.auto_time = !settings.auto_time;
361 return (Some(settings.auto_time.to_string()), true);
362 }
363 (None, false)
364 }
365}
366
367pub struct ButtonScheme;
369
370impl SettingKind for ButtonScheme {
371 fn identity(&self) -> SettingIdentity {
372 SettingIdentity::ButtonScheme
373 }
374
375 fn label(&self, _settings: &Settings) -> String {
376 fl!("settings-general-button-scheme")
377 }
378
379 fn fetch(&self, settings: &Settings) -> SettingData {
380 let enabled = settings.button_scheme == crate::settings::ButtonScheme::Natural;
381 SettingData {
382 value: settings.button_scheme.to_i18n_string(),
383 widget: WidgetKind::Toggle {
384 left_label: crate::settings::ButtonScheme::Natural.to_i18n_string(),
385 right_label: crate::settings::ButtonScheme::Inverted.to_i18n_string(),
386 enabled,
387 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)),
388 },
389 }
390 }
391
392 fn handle(
393 &self,
394 evt: &Event,
395 settings: &mut Settings,
396 bus: &mut Bus,
397 ) -> (Option<String>, bool) {
398 let new_scheme = match evt {
399 Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme)) => {
400 match settings.button_scheme {
401 crate::settings::ButtonScheme::Natural => {
402 Some(crate::settings::ButtonScheme::Inverted)
403 }
404 crate::settings::ButtonScheme::Inverted => {
405 Some(crate::settings::ButtonScheme::Natural)
406 }
407 }
408 }
409 Event::Select(EntryId::SetButtonScheme(scheme)) => Some(*scheme),
410 _ => None,
411 };
412
413 if let Some(scheme) = new_scheme {
414 settings.button_scheme = scheme;
415 bus.push_back(Event::Select(EntryId::SetButtonScheme(scheme)));
416 return (Some(settings.button_scheme.to_i18n_string()), true);
417 }
418 (None, false)
419 }
420}
421
422pub struct AutoFrontlight;
427
428impl SettingKind for AutoFrontlight {
429 fn identity(&self) -> SettingIdentity {
430 SettingIdentity::AutoFrontlight
431 }
432
433 fn label(&self, _settings: &Settings) -> String {
434 fl!("settings-general-auto-frontlight")
435 }
436
437 fn fetch(&self, settings: &Settings) -> SettingData {
438 SettingData {
439 value: settings.auto_frontlight.to_string(),
440 widget: WidgetKind::Toggle {
441 left_label: fl!("settings-general-toggle-on"),
442 right_label: fl!("settings-general-toggle-off"),
443 enabled: settings.auto_frontlight,
444 tap_event: Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoFrontlight)),
445 },
446 }
447 }
448
449 fn handle(
450 &self,
451 evt: &Event,
452 settings: &mut Settings,
453 bus: &mut Bus,
454 ) -> (Option<String>, bool) {
455 if let Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoFrontlight)) = evt {
456 settings.auto_frontlight = !settings.auto_frontlight;
457 bus.push_back(Event::AutoFrontlightConfigChanged);
458 return (Some(settings.auto_frontlight.to_string()), true);
459 }
460 (None, false)
461 }
462}
463
464pub struct AutoFrontlightBrightness;
469
470impl SettingKind for AutoFrontlightBrightness {
471 fn identity(&self) -> SettingIdentity {
472 SettingIdentity::AutoFrontlightBrightness
473 }
474
475 fn label(&self, _settings: &Settings) -> String {
476 fl!("settings-general-auto-frontlight-brightness")
477 }
478
479 fn fetch(&self, settings: &Settings) -> SettingData {
480 SettingData {
481 value: self.display_value(settings),
482 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditAutoFrontlightBrightness)),
483 }
484 }
485
486 fn handle(
487 &self,
488 evt: &Event,
489 settings: &mut Settings,
490 bus: &mut Bus,
491 ) -> (Option<String>, bool) {
492 if let Event::Submit(ViewId::AutoFrontlightBrightnessInput, text) = evt {
493 let display = self.apply_text(text, settings);
494 bus.push_back(Event::AutoFrontlightConfigChanged);
495 return (Some(display), true);
496 }
497
498 (None, false)
499 }
500
501 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
502 Some(self)
503 }
504}
505
506impl AutoFrontlightBrightness {
507 fn display_value(&self, settings: &Settings) -> String {
508 settings
509 .auto_frontlight_night_brightness
510 .map(|brightness| brightness.to_string())
511 .unwrap_or_else(|| LightLevel::default().to_string())
512 }
513}
514
515impl InputSettingKind for AutoFrontlightBrightness {
516 fn submit_view_id(&self) -> ViewId {
517 ViewId::AutoFrontlightBrightnessInput
518 }
519
520 fn open_entry_id(&self) -> EntryId {
521 EntryId::EditAutoFrontlightBrightness
522 }
523
524 fn input_label(&self) -> String {
525 fl!("settings-general-auto-frontlight-brightness-input")
526 }
527
528 fn input_max_chars(&self) -> usize {
529 3
530 }
531
532 fn current_text(&self, settings: &Settings) -> String {
533 settings
534 .auto_frontlight_night_brightness
535 .map(|b| b.into())
536 .unwrap_or_else(|| LightLevel::default().into())
537 }
538
539 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
540 if let Ok(value) = text.trim().parse::<f32>() {
541 settings.auto_frontlight_night_brightness = Some(value.into());
542 }
543 self.display_value(settings)
544 }
545}
546
547pub struct AutoFrontlightManualCoordinates;
552
553impl SettingKind for AutoFrontlightManualCoordinates {
554 fn identity(&self) -> SettingIdentity {
555 SettingIdentity::AutoFrontlightManualCoordinates
556 }
557
558 fn label(&self, _settings: &Settings) -> String {
559 fl!("settings-general-auto-frontlight-manual-coordinates")
560 }
561
562 fn fetch(&self, settings: &Settings) -> SettingData {
563 SettingData {
564 value: self.display_value(settings),
565 widget: WidgetKind::ActionLabel(Event::Select(
566 EntryId::EditAutoFrontlightManualCoordinates,
567 )),
568 }
569 }
570
571 fn handle(
572 &self,
573 evt: &Event,
574 settings: &mut Settings,
575 bus: &mut Bus,
576 ) -> (Option<String>, bool) {
577 if let Event::Submit(ViewId::AutoFrontlightManualCoordinatesInput, text) = evt {
578 let display = self.apply_text(text, settings);
579 bus.push_back(Event::AutoFrontlightConfigChanged);
580 return (Some(display), true);
581 }
582
583 (None, false)
584 }
585
586 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
587 Some(self)
588 }
589}
590
591impl AutoFrontlightManualCoordinates {
592 fn display_value(&self, settings: &Settings) -> String {
593 settings
594 .auto_frontlight_manual_coordinates
595 .map(|coordinates| {
596 format!(
597 "{:.4}, {:.4}",
598 coordinates.latitude(),
599 coordinates.longitude()
600 )
601 })
602 .unwrap_or_else(|| fl!("settings-general-not-set"))
603 }
604}
605
606impl InputSettingKind for AutoFrontlightManualCoordinates {
607 fn submit_view_id(&self) -> ViewId {
608 ViewId::AutoFrontlightManualCoordinatesInput
609 }
610
611 fn open_entry_id(&self) -> EntryId {
612 EntryId::EditAutoFrontlightManualCoordinates
613 }
614
615 fn input_label(&self) -> String {
616 fl!("settings-general-auto-frontlight-manual-coordinates-input")
617 }
618
619 fn input_max_chars(&self) -> usize {
620 32
621 }
622
623 fn current_text(&self, settings: &Settings) -> String {
624 settings
625 .auto_frontlight_manual_coordinates
626 .map(|coordinates| coordinates.to_string())
627 .unwrap_or_default()
628 }
629
630 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
631 let trimmed = text.trim();
632 if trimmed.is_empty() {
633 settings.auto_frontlight_manual_coordinates = None;
634 return self.display_value(settings);
635 }
636
637 let mut parts = trimmed.split(',').map(str::trim);
638 let parsed_coordinates =
639 parts
640 .next()
641 .zip(parts.next())
642 .and_then(|(latitude, longitude)| {
643 let latitude = latitude.parse::<f64>().ok()?;
644 let longitude = longitude.parse::<f64>().ok()?;
645 Coordinates::new(latitude, longitude).ok()
646 });
647
648 if parsed_coordinates.is_some() && parts.next().is_none() {
649 settings.auto_frontlight_manual_coordinates = parsed_coordinates;
650 }
651
652 self.display_value(settings)
653 }
654}
655
656pub struct SettingsRetention;
658
659impl SettingKind for SettingsRetention {
660 fn identity(&self) -> SettingIdentity {
661 SettingIdentity::SettingsRetention
662 }
663
664 fn label(&self, _settings: &Settings) -> String {
665 fl!("settings-general-settings-retention")
666 }
667
668 fn fetch(&self, settings: &Settings) -> SettingData {
669 SettingData {
670 value: settings.settings_retention.to_string(),
671 widget: WidgetKind::ActionLabel(Event::Select(EntryId::EditSettingsRetention)),
672 }
673 }
674
675 fn as_input_kind(&self) -> Option<&dyn InputSettingKind> {
676 Some(self)
677 }
678}
679
680impl InputSettingKind for SettingsRetention {
681 fn submit_view_id(&self) -> ViewId {
682 ViewId::SettingsRetentionInput
683 }
684
685 fn open_entry_id(&self) -> EntryId {
686 EntryId::EditSettingsRetention
687 }
688
689 fn input_label(&self) -> String {
690 fl!("settings-general-settings-retention")
691 }
692
693 fn input_max_chars(&self) -> usize {
694 3
695 }
696
697 fn current_text(&self, settings: &Settings) -> String {
698 settings.settings_retention.to_string()
699 }
700
701 fn apply_text(&self, text: &str, settings: &mut Settings) -> String {
702 if let Ok(value) = text.parse::<usize>() {
703 settings.settings_retention = value;
704 }
705 settings.settings_retention.to_string()
706 }
707}
708
709fn get_available_layouts() -> Result<Vec<String>, Error> {
711 let layouts_dir = CURRENT_DEVICE.install_path("keyboard-layouts");
712 let mut layouts = Vec::new();
713
714 if layouts_dir.exists() {
715 for entry in fs::read_dir(&layouts_dir)? {
716 let entry = entry?;
717 let path = entry.path();
718
719 if path.extension().and_then(|s| s.to_str()) == Some("json") {
720 if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
721 let layout_name = stem
722 .chars()
723 .enumerate()
724 .map(|(i, c)| {
725 if i == 0 {
726 c.to_uppercase().collect::<String>()
727 } else {
728 c.to_string()
729 }
730 })
731 .collect::<String>();
732 layouts.push(layout_name);
733 }
734 }
735 }
736 }
737
738 layouts.sort();
739 Ok(layouts)
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745 use crate::settings::Settings;
746 use crate::view::settings_editor::kinds::{InputSettingKind, ToggleSettings};
747 use crate::view::{Bus, EntryId, Event, ToggleEvent};
748 use std::collections::VecDeque;
749
750 mod locale {
751 use super::*;
752
753 #[test]
754 fn handle_set_locale_updates_settings() {
755 let setting = Locale;
756 let mut settings = Settings::default();
757 let mut bus: Bus = VecDeque::new();
758 let locale: Option<unic_langid::LanguageIdentifier> = Some("de-DE".parse().unwrap());
759 let event = Event::Select(EntryId::SetLocale(locale.clone()));
760
761 let result = setting.handle(&event, &mut settings, &mut bus);
762
763 assert!(result.0.is_some());
764 assert_eq!(result.0.unwrap(), "de-DE");
765 assert_eq!(settings.locale, locale);
766 }
767
768 #[test]
769 fn handle_returns_none_for_wrong_event() {
770 let setting = Locale;
771 let mut settings = Settings::default();
772 let mut bus: Bus = VecDeque::new();
773
774 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
775
776 assert!(result.0.is_none());
777 }
778 }
779
780 mod keyboard_layout {
781 use super::*;
782
783 #[test]
784 fn handle_set_layout_updates_settings() {
785 let setting = KeyboardLayout;
786 let mut settings = Settings::default();
787 let mut bus: Bus = VecDeque::new();
788 let event = Event::Select(EntryId::SetKeyboardLayout("German".to_string()));
789
790 let result = setting.handle(&event, &mut settings, &mut bus);
791
792 assert!(result.0.is_some());
793 assert_eq!(result.0.unwrap(), "German");
794 assert_eq!(settings.keyboard_layout, "German");
795 }
796
797 #[test]
798 fn handle_returns_none_for_wrong_event() {
799 let setting = KeyboardLayout;
800 let mut settings = Settings::default();
801 let mut bus: Bus = VecDeque::new();
802
803 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
804
805 assert!(result.0.is_none());
806 }
807 }
808
809 mod auto_suspend {
810 use super::*;
811
812 #[test]
813 fn apply_text_parses_and_updates() {
814 let setting = AutoSuspend;
815 let mut settings = Settings::default();
816
817 let display = setting.apply_text("60.0", &mut settings);
818
819 assert_eq!(display, "60.0");
820 assert_eq!(settings.auto_suspend, 60.0);
821 }
822
823 #[test]
824 fn apply_text_returns_never_for_zero() {
825 let setting = AutoSuspend;
826 let mut settings = Settings::default();
827
828 let display = setting.apply_text("0", &mut settings);
829
830 assert_eq!(display, "Never");
831 assert_eq!(settings.auto_suspend, 0.0);
832 }
833
834 #[test]
835 fn apply_text_ignores_invalid_input() {
836 let setting = AutoSuspend;
837 let mut settings = Settings {
838 auto_suspend: 30.0,
839 ..Default::default()
840 };
841
842 let display = setting.apply_text("invalid", &mut settings);
843
844 assert_eq!(settings.auto_suspend, 30.0);
845 assert_eq!(display, "30.0");
846 }
847 }
848
849 mod auto_power_off {
850 use super::*;
851
852 #[test]
853 fn apply_text_parses_and_updates() {
854 let setting = AutoPowerOff;
855 let mut settings = Settings::default();
856
857 let display = setting.apply_text("14.0", &mut settings);
858
859 assert_eq!(display, "14.0");
860 assert_eq!(settings.auto_power_off, 14.0);
861 }
862
863 #[test]
864 fn apply_text_returns_never_for_zero() {
865 let setting = AutoPowerOff;
866 let mut settings = Settings::default();
867
868 let display = setting.apply_text("0", &mut settings);
869
870 assert_eq!(display, "Never");
871 assert_eq!(settings.auto_power_off, 0.0);
872 }
873
874 #[test]
875 fn apply_text_ignores_invalid_input() {
876 let setting = AutoPowerOff;
877 let mut settings = Settings {
878 auto_power_off: 7.0,
879 ..Default::default()
880 };
881
882 let display = setting.apply_text("invalid", &mut settings);
883
884 assert_eq!(settings.auto_power_off, 7.0);
885 assert_eq!(display, "7.0");
886 }
887 }
888
889 mod sleep_cover {
890 use super::*;
891
892 #[test]
893 fn handle_toggle_event_toggles_value() {
894 let setting = SleepCover;
895 let mut settings = Settings {
896 sleep_cover: true,
897 ..Default::default()
898 };
899 let mut bus: Bus = VecDeque::new();
900 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::SleepCover));
901
902 let result = setting.handle(&event, &mut settings, &mut bus);
903
904 assert!(result.0.is_some());
905 assert_eq!(result.0.unwrap(), "false");
906 assert!(!settings.sleep_cover);
907 }
908
909 #[test]
910 fn handle_returns_none_for_wrong_event() {
911 let setting = SleepCover;
912 let mut settings = Settings::default();
913 let mut bus: Bus = VecDeque::new();
914
915 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
916
917 assert!(result.0.is_none());
918 }
919 }
920
921 mod auto_share {
922 use super::*;
923
924 #[test]
925 fn handle_toggle_event_toggles_value() {
926 let setting = AutoShare;
927 let mut settings = Settings {
928 auto_share: false,
929 ..Default::default()
930 };
931 let mut bus: Bus = VecDeque::new();
932 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::AutoShare));
933
934 let result = setting.handle(&event, &mut settings, &mut bus);
935
936 assert!(result.0.is_some());
937 assert_eq!(result.0.unwrap(), "true");
938 assert!(settings.auto_share);
939 }
940
941 #[test]
942 fn handle_returns_none_for_wrong_event() {
943 let setting = AutoShare;
944 let mut settings = Settings::default();
945 let mut bus: Bus = VecDeque::new();
946
947 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
948
949 assert!(result.0.is_none());
950 }
951 }
952
953 mod auto_frontlight {
954 use super::*;
955
956 #[test]
957 fn brightness_apply_text_parses_and_updates() {
958 let setting = AutoFrontlightBrightness;
959 let mut settings = Settings::default();
960 let mut bus: Bus = VecDeque::new();
961
962 let display = setting.apply_text("25", &mut settings);
963 let result = setting.handle(
964 &Event::Submit(ViewId::AutoFrontlightBrightnessInput, "25".to_string()),
965 &mut settings,
966 &mut bus,
967 );
968
969 assert_eq!(display, "25%");
970 assert_eq!(settings.auto_frontlight_night_brightness, Some(25.0.into()));
971 assert_eq!(result, (Some("25%".to_string()), true));
972 assert!(matches!(
973 bus.pop_front(),
974 Some(Event::AutoFrontlightConfigChanged)
975 ));
976 }
977
978 #[test]
979 fn brightness_apply_text_ignores_invalid_input() {
980 let setting = AutoFrontlightBrightness;
981 let mut settings = Settings {
982 auto_frontlight_night_brightness: Some(10.0.into()),
983 ..Default::default()
984 };
985 let mut bus: Bus = VecDeque::new();
986
987 let display = setting.apply_text("invalid", &mut settings);
988 let result = setting.handle(
989 &Event::Submit(ViewId::AutoFrontlightBrightnessInput, "invalid".to_string()),
990 &mut settings,
991 &mut bus,
992 );
993
994 assert_eq!(display, "10%");
995 assert_eq!(settings.auto_frontlight_night_brightness, Some(10.0.into()));
996 assert_eq!(result, (Some("10%".to_string()), true));
997 assert!(matches!(
998 bus.pop_front(),
999 Some(Event::AutoFrontlightConfigChanged)
1000 ));
1001 }
1002
1003 #[test]
1004 fn manual_coordinates_apply_text_parses_and_updates() {
1005 let setting = AutoFrontlightManualCoordinates;
1006 let mut settings = Settings::default();
1007 let mut bus: Bus = VecDeque::new();
1008
1009 let display = setting.apply_text("51.5074, -0.1278", &mut settings);
1010 let result = setting.handle(
1011 &Event::Submit(
1012 ViewId::AutoFrontlightManualCoordinatesInput,
1013 "51.5074, -0.1278".to_string(),
1014 ),
1015 &mut settings,
1016 &mut bus,
1017 );
1018
1019 assert_eq!(display, "51.5074, -0.1278");
1020 assert_eq!(
1021 settings.auto_frontlight_manual_coordinates,
1022 Some(Coordinates::new(51.5074, -0.1278).unwrap())
1023 );
1024 assert_eq!(result, (Some("51.5074, -0.1278".to_string()), true));
1025 assert!(matches!(
1026 bus.pop_front(),
1027 Some(Event::AutoFrontlightConfigChanged)
1028 ));
1029 }
1030
1031 #[test]
1032 fn manual_coordinates_apply_text_clears_on_empty_input() {
1033 let setting = AutoFrontlightManualCoordinates;
1034 let mut settings = Settings {
1035 auto_frontlight_manual_coordinates: Some(
1036 Coordinates::new(51.5074, -0.1278).unwrap(),
1037 ),
1038 ..Default::default()
1039 };
1040 let mut bus: Bus = VecDeque::new();
1041
1042 let display = setting.apply_text("", &mut settings);
1043 let result = setting.handle(
1044 &Event::Submit(ViewId::AutoFrontlightManualCoordinatesInput, "".to_string()),
1045 &mut settings,
1046 &mut bus,
1047 );
1048
1049 assert_eq!(display, "Not set");
1050 assert_eq!(settings.auto_frontlight_manual_coordinates, None);
1051 assert_eq!(result, (Some("Not set".to_string()), true));
1052 assert!(matches!(
1053 bus.pop_front(),
1054 Some(Event::AutoFrontlightConfigChanged)
1055 ));
1056 }
1057
1058 #[test]
1059 fn manual_coordinates_apply_text_ignores_invalid_input() {
1060 let setting = AutoFrontlightManualCoordinates;
1061 let mut settings = Settings {
1062 auto_frontlight_manual_coordinates: Some(
1063 Coordinates::new(51.5074, -0.1278).unwrap(),
1064 ),
1065 ..Default::default()
1066 };
1067 let mut bus: Bus = VecDeque::new();
1068
1069 let display = setting.apply_text("invalid", &mut settings);
1070 let result = setting.handle(
1071 &Event::Submit(
1072 ViewId::AutoFrontlightManualCoordinatesInput,
1073 "invalid".to_string(),
1074 ),
1075 &mut settings,
1076 &mut bus,
1077 );
1078
1079 assert_eq!(display, "51.5074, -0.1278");
1080 assert_eq!(
1081 settings.auto_frontlight_manual_coordinates,
1082 Some(Coordinates::new(51.5074, -0.1278).unwrap())
1083 );
1084 assert_eq!(result, (Some("51.5074, -0.1278".to_string()), true));
1085 assert!(matches!(
1086 bus.pop_front(),
1087 Some(Event::AutoFrontlightConfigChanged)
1088 ));
1089 }
1090 }
1091
1092 mod button_scheme {
1093 use super::*;
1094 use crate::settings::ButtonScheme;
1095
1096 #[test]
1097 fn handle_toggle_event_switches_natural_to_inverted() {
1098 let setting = ButtonScheme;
1099 let mut settings = Settings {
1100 button_scheme: ButtonScheme::Natural,
1101 ..Default::default()
1102 };
1103 let mut bus: Bus = VecDeque::new();
1104 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
1105
1106 let result = setting.handle(&event, &mut settings, &mut bus);
1107
1108 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
1109 assert_eq!(bus.len(), 1);
1110 assert!(result.0.is_some());
1111 }
1112
1113 #[test]
1114 fn handle_toggle_event_switches_inverted_to_natural() {
1115 let setting = ButtonScheme;
1116 let mut settings = Settings {
1117 button_scheme: ButtonScheme::Inverted,
1118 ..Default::default()
1119 };
1120 let mut bus: Bus = VecDeque::new();
1121 let event = Event::Toggle(ToggleEvent::Setting(ToggleSettings::ButtonScheme));
1122
1123 let result = setting.handle(&event, &mut settings, &mut bus);
1124
1125 assert_eq!(settings.button_scheme, ButtonScheme::Natural);
1126 assert_eq!(bus.len(), 1);
1127 assert!(result.0.is_some());
1128 }
1129
1130 #[test]
1131 fn handle_set_scheme_event_applies_directly() {
1132 let setting = ButtonScheme;
1133 let mut settings = Settings {
1134 button_scheme: ButtonScheme::Natural,
1135 ..Default::default()
1136 };
1137 let mut bus: Bus = VecDeque::new();
1138 let event = Event::Select(EntryId::SetButtonScheme(ButtonScheme::Inverted));
1139
1140 let result = setting.handle(&event, &mut settings, &mut bus);
1141
1142 assert_eq!(settings.button_scheme, ButtonScheme::Inverted);
1143 assert!(result.0.is_some());
1144 }
1145
1146 #[test]
1147 fn handle_returns_none_for_wrong_event() {
1148 let setting = ButtonScheme;
1149 let mut settings = Settings::default();
1150 let mut bus: Bus = VecDeque::new();
1151
1152 let result = setting.handle(&Event::Select(EntryId::About), &mut settings, &mut bus);
1153
1154 assert!(result.0.is_none());
1155 }
1156 }
1157
1158 mod settings_retention {
1159 use super::*;
1160
1161 #[test]
1162 fn apply_text_parses_and_updates() {
1163 let setting = SettingsRetention;
1164 let mut settings = Settings::default();
1165
1166 let display = setting.apply_text("10", &mut settings);
1167
1168 assert_eq!(display, "10");
1169 assert_eq!(settings.settings_retention, 10);
1170 }
1171
1172 #[test]
1173 fn apply_text_ignores_invalid_input() {
1174 let setting = SettingsRetention;
1175 let mut settings = Settings {
1176 settings_retention: 3,
1177 ..Default::default()
1178 };
1179
1180 let display = setting.apply_text("invalid", &mut settings);
1181
1182 assert_eq!(settings.settings_retention, 3);
1183 assert_eq!(display, "3");
1184 }
1185 }
1186}