cadmus_core/view/settings_editor/kinds/
intermission.rs

1//! Setting kinds for the Intermissions category.
2
3use super::{SettingData, SettingIdentity, SettingKind, WidgetKind};
4use crate::fl;
5use crate::i18n::I18nDisplay;
6use crate::settings::{IntermKind, IntermissionDisplay, Settings};
7use crate::view::{Bus, EntryId, EntryKind, Event};
8
9/// Fetches the intermission display setting data for the given intermission kind
10fn fetch_intermission(kind: IntermKind, settings: &Settings) -> SettingData {
11    let display = &settings.intermissions[kind];
12
13    let (value, is_logo, is_cover) = match display {
14        IntermissionDisplay::Logo => (display.to_i18n_string(), true, false),
15        IntermissionDisplay::Cover => (display.to_i18n_string(), false, true),
16        IntermissionDisplay::Image(path) => {
17            let i18n_display = fl!("settings-intermission-custom");
18            let display_name = path
19                .file_name()
20                .and_then(|n| n.to_str())
21                .unwrap_or(i18n_display.as_str())
22                .to_string();
23            (display_name, false, false)
24        }
25    };
26
27    let entries = vec![
28        EntryKind::RadioButton(
29            IntermissionDisplay::Logo.to_i18n_string(),
30            EntryId::SetIntermission(kind, IntermissionDisplay::Logo),
31            is_logo,
32        ),
33        EntryKind::RadioButton(
34            IntermissionDisplay::Cover.to_i18n_string(),
35            EntryId::SetIntermission(kind, IntermissionDisplay::Cover),
36            is_cover,
37        ),
38        EntryKind::Command(
39            fl!("settings-intermission-custom-image"),
40            EntryId::EditIntermissionImage(kind),
41        ),
42    ];
43
44    SettingData {
45        value,
46        widget: WidgetKind::SubMenu(entries),
47    }
48}
49
50/// Extracts the display name from an [`IntermissionDisplay`] value.
51///
52/// Uses [`IntermissionDisplay`]'s [`I18nDisplay`] implementation for
53/// `Logo` and `Cover`. For `Image`, the filename is used instead of the full path since
54/// the built-in `Display` impl only yields `"Custom"` for that variant.
55fn intermission_display_name(display: &IntermissionDisplay) -> String {
56    let i18n_display = fl!("settings-intermission-custom");
57    match display {
58        IntermissionDisplay::Image(path) => path
59            .file_name()
60            .and_then(|n| n.to_str())
61            .unwrap_or(i18n_display.as_str())
62            .to_string(),
63        _ => display.to_i18n_string(),
64    }
65}
66
67/// Suspend screen display setting
68pub struct IntermissionSuspend;
69
70impl SettingKind for IntermissionSuspend {
71    fn identity(&self) -> SettingIdentity {
72        SettingIdentity::IntermissionSuspend
73    }
74
75    fn label(&self, _settings: &Settings) -> String {
76        fl!("settings-intermission-suspend-screen")
77    }
78
79    fn fetch(&self, settings: &Settings) -> SettingData {
80        fetch_intermission(IntermKind::Suspend, settings)
81    }
82
83    fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
84        if let Event::Select(EntryId::SetIntermission(IntermKind::Suspend, ref display)) = evt {
85            settings.intermissions[IntermKind::Suspend] = display.clone();
86            return Some(intermission_display_name(display));
87        }
88
89        if let Event::FileChooserClosed(Some(ref path)) = evt {
90            let display = IntermissionDisplay::Image(path.clone());
91            settings.intermissions[IntermKind::Suspend] = display.clone();
92            return Some(intermission_display_name(&display));
93        }
94
95        None
96    }
97
98    fn file_chooser_entry_id(&self) -> Option<EntryId> {
99        Some(EntryId::EditIntermissionImage(IntermKind::Suspend))
100    }
101}
102
103/// Power off screen display setting
104pub struct IntermissionPowerOff;
105
106impl SettingKind for IntermissionPowerOff {
107    fn identity(&self) -> SettingIdentity {
108        SettingIdentity::IntermissionPowerOff
109    }
110
111    fn label(&self, _settings: &Settings) -> String {
112        fl!("settings-intermission-power-off-screen")
113    }
114
115    fn fetch(&self, settings: &Settings) -> SettingData {
116        fetch_intermission(IntermKind::PowerOff, settings)
117    }
118
119    fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
120        if let Event::Select(EntryId::SetIntermission(IntermKind::PowerOff, ref display)) = evt {
121            settings.intermissions[IntermKind::PowerOff] = display.clone();
122            return Some(intermission_display_name(display));
123        }
124
125        if let Event::FileChooserClosed(Some(ref path)) = evt {
126            let display = IntermissionDisplay::Image(path.clone());
127            settings.intermissions[IntermKind::PowerOff] = display.clone();
128            return Some(intermission_display_name(&display));
129        }
130
131        None
132    }
133
134    fn file_chooser_entry_id(&self) -> Option<EntryId> {
135        Some(EntryId::EditIntermissionImage(IntermKind::PowerOff))
136    }
137}
138
139/// Share screen display setting
140pub struct IntermissionShare;
141
142impl SettingKind for IntermissionShare {
143    fn identity(&self) -> SettingIdentity {
144        SettingIdentity::IntermissionShare
145    }
146
147    fn label(&self, _settings: &Settings) -> String {
148        fl!("settings-intermission-share-screen")
149    }
150
151    fn fetch(&self, settings: &Settings) -> SettingData {
152        fetch_intermission(IntermKind::Share, settings)
153    }
154
155    fn handle(&self, evt: &Event, settings: &mut Settings, _bus: &mut Bus) -> Option<String> {
156        if let Event::Select(EntryId::SetIntermission(IntermKind::Share, ref display)) = evt {
157            settings.intermissions[IntermKind::Share] = display.clone();
158            return Some(intermission_display_name(display));
159        }
160
161        if let Event::FileChooserClosed(Some(ref path)) = evt {
162            let display = IntermissionDisplay::Image(path.clone());
163            settings.intermissions[IntermKind::Share] = display.clone();
164            return Some(intermission_display_name(&display));
165        }
166
167        None
168    }
169
170    fn file_chooser_entry_id(&self) -> Option<EntryId> {
171        Some(EntryId::EditIntermissionImage(IntermKind::Share))
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::settings::{IntermissionDisplay, Settings};
179    use crate::view::{Bus, EntryId, Event};
180    use std::collections::VecDeque;
181    use std::path::PathBuf;
182
183    mod intermission_suspend {
184        use super::*;
185
186        #[test]
187        fn handle_set_intermission_updates_settings() {
188            let setting = IntermissionSuspend;
189            let mut settings = Settings::default();
190            let mut bus: Bus = VecDeque::new();
191            let event = Event::Select(EntryId::SetIntermission(
192                IntermKind::Suspend,
193                IntermissionDisplay::Cover,
194            ));
195
196            let result = setting.handle(&event, &mut settings, &mut bus);
197
198            assert!(result.is_some());
199            assert_eq!(
200                settings.intermissions[IntermKind::Suspend],
201                IntermissionDisplay::Cover
202            );
203        }
204
205        #[test]
206        fn handle_file_chooser_closed_updates_settings() {
207            let setting = IntermissionSuspend;
208            let mut settings = Settings::default();
209            let mut bus: Bus = VecDeque::new();
210            let path = PathBuf::from("/selected/image.jpg");
211            let event = Event::FileChooserClosed(Some(path));
212
213            let result = setting.handle(&event, &mut settings, &mut bus);
214
215            assert!(result.is_some());
216            assert_eq!(
217                settings.intermissions[IntermKind::Suspend],
218                IntermissionDisplay::Image(PathBuf::from("/selected/image.jpg"))
219            );
220        }
221
222        #[test]
223        fn handle_returns_none_for_wrong_kind() {
224            let setting = IntermissionSuspend;
225            let mut settings = Settings::default();
226            let mut bus: Bus = VecDeque::new();
227            let event = Event::Select(EntryId::SetIntermission(
228                IntermKind::PowerOff,
229                IntermissionDisplay::Cover,
230            ));
231
232            let result = setting.handle(&event, &mut settings, &mut bus);
233
234            assert!(result.is_none());
235        }
236
237        #[test]
238        fn handle_returns_none_for_cancelled_file_chooser() {
239            let setting = IntermissionSuspend;
240            let mut settings = Settings::default();
241            let mut bus: Bus = VecDeque::new();
242
243            let result = setting.handle(&Event::FileChooserClosed(None), &mut settings, &mut bus);
244
245            assert!(result.is_none());
246        }
247    }
248
249    mod intermission_power_off {
250        use super::*;
251
252        #[test]
253        fn handle_set_intermission_updates_settings() {
254            let setting = IntermissionPowerOff;
255            let mut settings = Settings::default();
256            let mut bus: Bus = VecDeque::new();
257            let event = Event::Select(EntryId::SetIntermission(
258                IntermKind::PowerOff,
259                IntermissionDisplay::Cover,
260            ));
261
262            let result = setting.handle(&event, &mut settings, &mut bus);
263
264            assert!(result.is_some());
265            assert_eq!(
266                settings.intermissions[IntermKind::PowerOff],
267                IntermissionDisplay::Cover
268            );
269        }
270
271        #[test]
272        fn handle_file_chooser_closed_updates_settings() {
273            let setting = IntermissionPowerOff;
274            let mut settings = Settings::default();
275            let mut bus: Bus = VecDeque::new();
276            let path = PathBuf::from("/selected/poweroff.png");
277            let event = Event::FileChooserClosed(Some(path));
278
279            let result = setting.handle(&event, &mut settings, &mut bus);
280
281            assert!(result.is_some());
282            assert_eq!(
283                settings.intermissions[IntermKind::PowerOff],
284                IntermissionDisplay::Image(PathBuf::from("/selected/poweroff.png"))
285            );
286        }
287
288        #[test]
289        fn handle_returns_none_for_wrong_kind() {
290            let setting = IntermissionPowerOff;
291            let mut settings = Settings::default();
292            let mut bus: Bus = VecDeque::new();
293            let event = Event::Select(EntryId::SetIntermission(
294                IntermKind::Suspend,
295                IntermissionDisplay::Logo,
296            ));
297
298            let result = setting.handle(&event, &mut settings, &mut bus);
299
300            assert!(result.is_none());
301        }
302
303        #[test]
304        fn handle_returns_none_for_cancelled_file_chooser() {
305            let setting = IntermissionPowerOff;
306            let mut settings = Settings::default();
307            let mut bus: Bus = VecDeque::new();
308
309            let result = setting.handle(&Event::FileChooserClosed(None), &mut settings, &mut bus);
310
311            assert!(result.is_none());
312        }
313    }
314
315    mod intermission_share {
316        use super::*;
317
318        #[test]
319        fn handle_set_intermission_updates_settings() {
320            let setting = IntermissionShare;
321            let mut settings = Settings::default();
322            let mut bus: Bus = VecDeque::new();
323            let event = Event::Select(EntryId::SetIntermission(
324                IntermKind::Share,
325                IntermissionDisplay::Cover,
326            ));
327
328            let result = setting.handle(&event, &mut settings, &mut bus);
329
330            assert!(result.is_some());
331            assert_eq!(
332                settings.intermissions[IntermKind::Share],
333                IntermissionDisplay::Cover
334            );
335        }
336
337        #[test]
338        fn handle_file_chooser_closed_updates_settings() {
339            let setting = IntermissionShare;
340            let mut settings = Settings::default();
341            let mut bus: Bus = VecDeque::new();
342            let path = PathBuf::from("/selected/share.jpg");
343            let event = Event::FileChooserClosed(Some(path));
344
345            let result = setting.handle(&event, &mut settings, &mut bus);
346
347            assert!(result.is_some());
348            assert_eq!(
349                settings.intermissions[IntermKind::Share],
350                IntermissionDisplay::Image(PathBuf::from("/selected/share.jpg"))
351            );
352        }
353
354        #[test]
355        fn handle_returns_none_for_wrong_kind() {
356            let setting = IntermissionShare;
357            let mut settings = Settings::default();
358            let mut bus: Bus = VecDeque::new();
359            let event = Event::Select(EntryId::SetIntermission(
360                IntermKind::PowerOff,
361                IntermissionDisplay::Cover,
362            ));
363
364            let result = setting.handle(&event, &mut settings, &mut bus);
365
366            assert!(result.is_none());
367        }
368
369        #[test]
370        fn handle_returns_none_for_cancelled_file_chooser() {
371            let setting = IntermissionShare;
372            let mut settings = Settings::default();
373            let mut bus: Bus = VecDeque::new();
374
375            let result = setting.handle(&Event::FileChooserClosed(None), &mut settings, &mut bus);
376
377            assert!(result.is_none());
378        }
379    }
380}