shared/domain/module/body/
tapping_board.rs1use 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#[derive(Default, Clone, Serialize, Deserialize, Debug)]
16pub struct ModuleData {
17 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#[derive(Default, Clone, Serialize, Deserialize, Debug)]
104pub struct Content {
105 pub base: BaseContent,
107
108 pub editor_state: EditorState,
110
111 pub mode: Mode,
113
114 pub traces: Vec<Trace>,
116
117 pub play_settings: PlaySettings,
119}
120
121#[derive(Default, Clone, Serialize, Deserialize, Debug)]
123pub struct EditorState {
124 pub step: Step,
126
127 pub steps_completed: HashSet<Step>,
129}
130
131#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
132pub enum Mode {
134 Words,
136 Images,
138 Talk,
140 Read,
142 Scene,
144 PhotoAlbum,
146 Comic,
148 Timeline,
150 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
216pub enum Step {
217 One,
219 Two,
221 Three,
223 Four,
225 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}