shared/domain/module/body/
tapping_board.rs

1use crate::domain::module::{
2    body::{
3        Body, BodyConvert, BodyExt, ModeExt, StepExt, ThemeId,
4        _groups::design::{BaseContent, Trace},
5    },
6    ModuleKind,
7};
8use serde::{Deserialize, Serialize};
9use std::collections::HashSet;
10
11mod play_settings;
12pub use play_settings::*;
13
14/// The body for [`TappingBoard`](crate::domain::module::ModuleKind::TappingBoard) modules.
15#[derive(Default, Clone, Serialize, Deserialize, Debug)]
16pub struct ModuleData {
17    /// The content
18    pub content: Option<Content>,
19}
20
21impl BodyExt<Mode, Step> for ModuleData {
22    fn as_body(&self) -> Body {
23        Body::TappingBoard(self.clone())
24    }
25
26    fn is_complete(&self) -> bool {
27        self.content.is_some()
28    }
29
30    fn kind() -> ModuleKind {
31        ModuleKind::TappingBoard
32    }
33
34    fn new_with_mode_and_theme(mode: Mode, theme: ThemeId) -> Self {
35        ModuleData {
36            content: Some(Content {
37                mode,
38                base: BaseContent {
39                    theme,
40                    ..Default::default()
41                },
42                ..Default::default()
43            }),
44        }
45    }
46
47    fn mode(&self) -> Option<Mode> {
48        self.content.as_ref().map(|c| c.mode.clone())
49    }
50
51    fn requires_choose_mode(&self) -> bool {
52        self.content.is_none()
53    }
54
55    fn set_editor_state_step(&mut self, step: Step) {
56        if let Some(content) = self.content.as_mut() {
57            content.editor_state.step = step;
58        }
59    }
60    fn set_editor_state_steps_completed(&mut self, steps_completed: HashSet<Step>) {
61        if let Some(content) = self.content.as_mut() {
62            content.editor_state.steps_completed = steps_completed;
63        }
64    }
65
66    fn get_editor_state_step(&self) -> Option<Step> {
67        self.content
68            .as_ref()
69            .map(|content| content.editor_state.step)
70    }
71
72    fn get_editor_state_steps_completed(&self) -> Option<HashSet<Step>> {
73        self.content
74            .as_ref()
75            .map(|content| content.editor_state.steps_completed.clone())
76    }
77
78    fn set_theme(&mut self, theme_id: ThemeId) {
79        if let Some(content) = self.content.as_mut() {
80            content.base.theme = theme_id;
81        }
82    }
83
84    fn get_theme(&self) -> Option<ThemeId> {
85        self.content.as_ref().map(|content| content.base.theme)
86    }
87}
88
89impl BodyConvert for ModuleData {}
90
91impl TryFrom<Body> for ModuleData {
92    type Error = &'static str;
93
94    fn try_from(body: Body) -> Result<Self, Self::Error> {
95        match body {
96            Body::TappingBoard(data) => Ok(data),
97            _ => Err("cannot convert body to Listen & Learn!"),
98        }
99    }
100}
101
102/// The body for [`TappingBoard`](crate::domain::module::ModuleKind::TappingBoard) modules.
103#[derive(Default, Clone, Serialize, Deserialize, Debug)]
104pub struct Content {
105    /// The base content for all design modules
106    pub base: BaseContent,
107
108    /// The editor state
109    pub editor_state: EditorState,
110
111    /// The mode
112    pub mode: Mode,
113
114    /// Traces
115    pub traces: Vec<Trace>,
116
117    /// play settings
118    pub play_settings: PlaySettings,
119}
120
121/// Editor state
122#[derive(Default, Clone, Serialize, Deserialize, Debug)]
123pub struct EditorState {
124    /// the current step
125    pub step: Step,
126
127    /// the completed steps
128    pub steps_completed: HashSet<Step>,
129}
130
131#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
132/// The mode
133pub enum Mode {
134    /// Words mode
135    Words,
136    /// Images mode
137    Images,
138    /// Talk mode
139    Talk,
140    /// Read mode
141    Read,
142    /// Scene mode
143    Scene,
144    /// Photo album mode
145    PhotoAlbum,
146    /// Comic mode
147    Comic,
148    /// Timeline mode
149    Timeline,
150    /// Family Tree mode
151    FamilyTree,
152}
153
154impl Default for Mode {
155    fn default() -> Self {
156        Self::Words
157    }
158}
159
160impl ModeExt for Mode {
161    fn get_list() -> Vec<Self> {
162        vec![
163            Self::Words,
164            Self::Images,
165            Self::Talk,
166            Self::Read,
167            Self::Scene,
168            Self::PhotoAlbum,
169            Self::Comic,
170            Self::Timeline,
171            Self::FamilyTree,
172        ]
173    }
174
175    fn as_str_id(&self) -> &'static str {
176        match self {
177            Self::Words => "words",
178            Self::Images => "images",
179            Self::Talk => "talk",
180            Self::Read => "read",
181            Self::Scene => "scene",
182            Self::PhotoAlbum => "photo-album",
183            Self::Comic => "comic",
184            Self::Timeline => "timeline",
185            Self::FamilyTree => "family-tree",
186        }
187    }
188
189    fn label(&self) -> &'static str {
190        const STR_WORDS_LABEL: &'static str = "Words";
191        const STR_IMAGES_LABEL: &'static str = "Images";
192        const STR_TALK_LABEL: &'static str = "Talking Pictures";
193        const STR_READ_LABEL: &'static str = "Read Along";
194        const STR_SCENE_LABEL: &'static str = "Scene";
195        const STR_PHOTO_ALBUM_LABEL: &'static str = "Photo Album";
196        const STR_COMIC_LABEL: &'static str = "Comics";
197        const STR_TIMELINE_LABEL: &'static str = "Timeline";
198        const STR_FAMILY_TREE_LABEL: &'static str = "Family Tree";
199
200        match self {
201            Self::Words => STR_WORDS_LABEL,
202            Self::Images => STR_IMAGES_LABEL,
203            Self::Talk => STR_TALK_LABEL,
204            Self::Read => STR_READ_LABEL,
205            Self::Scene => STR_SCENE_LABEL,
206            Self::PhotoAlbum => STR_PHOTO_ALBUM_LABEL,
207            Self::Comic => STR_COMIC_LABEL,
208            Self::Timeline => STR_TIMELINE_LABEL,
209            Self::FamilyTree => STR_FAMILY_TREE_LABEL,
210        }
211    }
212}
213
214/// The Steps
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
216pub enum Step {
217    /// Step 1
218    One,
219    /// Step 2
220    Two,
221    /// Step 3
222    Three,
223    /// Step 4
224    Four,
225    /// Step 5
226    Five,
227}
228
229impl Default for Step {
230    fn default() -> Self {
231        Self::One
232    }
233}
234
235impl StepExt for Step {
236    fn next(&self) -> Option<Self> {
237        match self {
238            Self::One => Some(Self::Two),
239            Self::Two => Some(Self::Three),
240            Self::Three => Some(Self::Four),
241            Self::Four => Some(Self::Five),
242            Self::Five => None,
243        }
244    }
245
246    fn as_number(&self) -> usize {
247        match self {
248            Self::One => 1,
249            Self::Two => 2,
250            Self::Three => 3,
251            Self::Four => 4,
252            Self::Five => 5,
253        }
254    }
255
256    fn label(&self) -> &'static str {
257        const STR_BACKGROUND: &'static str = "Design";
258        const STR_CONTENT: &'static str = "Content";
259        const STR_INTERACTION: &'static str = "Interaction";
260        const STR_SETTINGS: &'static str = "Settings";
261        const STR_PREVIEW: &'static str = "Preview";
262        match self {
263            Self::One => STR_BACKGROUND,
264            Self::Two => STR_CONTENT,
265            Self::Three => STR_INTERACTION,
266            Self::Four => STR_SETTINGS,
267            Self::Five => STR_PREVIEW,
268        }
269    }
270
271    fn get_list() -> Vec<Self> {
272        vec![Self::One, Self::Two, Self::Three, Self::Four, Self::Five]
273    }
274    fn get_preview() -> Self {
275        Self::Five
276    }
277}