shared/domain/module/body/
drag_drop.rs1use crate::domain::module::{
2 body::{
3 Audio, Body, BodyConvert, BodyExt, ModeExt, ModuleAssist, StepExt, ThemeId, Transform,
4 _groups::design::{Backgrounds, Sticker, Trace},
5 },
6 ModuleKind,
7};
8use serde::{Deserialize, Serialize};
9use std::collections::HashSet;
10use strum_macros::EnumIs;
11
12mod play_settings;
13pub use play_settings::*;
14
15use super::_groups::design::Text;
16
17#[derive(Default, Clone, Serialize, Deserialize, Debug)]
19pub struct ModuleData {
20 pub content: Option<Content>,
22}
23
24impl BodyExt<Mode, Step> for ModuleData {
25 fn as_body(&self) -> Body {
26 Body::DragDrop(self.clone())
27 }
28
29 fn is_complete(&self) -> bool {
30 self.content.is_some()
31 }
32
33 fn kind() -> ModuleKind {
34 ModuleKind::DragDrop
35 }
36
37 fn new_with_mode_and_theme(mode: Mode, theme: ThemeId) -> Self {
38 ModuleData {
39 content: Some(Content {
40 mode,
41 theme,
42 items: vec![Item {
43 sticker: Sticker::Text(Text::default()),
44 kind: ItemKind::Static,
45 }],
46 ..Default::default()
47 }),
48 }
49 }
50
51 fn mode(&self) -> Option<Mode> {
52 self.content.as_ref().map(|c| c.mode.clone())
53 }
54
55 fn requires_choose_mode(&self) -> bool {
56 self.content.is_none()
57 }
58
59 fn set_editor_state_step(&mut self, step: Step) {
60 if let Some(content) = self.content.as_mut() {
61 content.editor_state.step = step;
62 }
63 }
64 fn set_editor_state_steps_completed(&mut self, steps_completed: HashSet<Step>) {
65 if let Some(content) = self.content.as_mut() {
66 content.editor_state.steps_completed = steps_completed;
67 }
68 }
69
70 fn get_editor_state_step(&self) -> Option<Step> {
71 self.content
72 .as_ref()
73 .map(|content| content.editor_state.step)
74 }
75
76 fn get_editor_state_steps_completed(&self) -> Option<HashSet<Step>> {
77 self.content
78 .as_ref()
79 .map(|content| content.editor_state.steps_completed.clone())
80 }
81
82 fn set_theme(&mut self, theme_id: ThemeId) {
83 if let Some(content) = self.content.as_mut() {
84 content.theme = theme_id;
85 }
86 }
87
88 fn get_theme(&self) -> Option<ThemeId> {
89 self.content.as_ref().map(|content| content.theme)
90 }
91}
92
93impl BodyConvert for ModuleData {}
94
95impl TryFrom<Body> for ModuleData {
96 type Error = &'static str;
97
98 fn try_from(body: Body) -> Result<Self, Self::Error> {
99 match body {
100 Body::DragDrop(data) => Ok(data),
101 _ => Err("cannot convert body to drag & drop!"),
102 }
103 }
104}
105
106#[derive(Default, Clone, Serialize, Deserialize, Debug)]
108pub struct Content {
109 pub instructions: ModuleAssist,
111
112 pub theme: ThemeId,
114
115 pub backgrounds: Backgrounds,
117
118 pub items: Vec<Item>,
120
121 #[serde(default)]
125 #[serde(skip_serializing_if = "Vec::is_empty")]
126 pub item_targets: Vec<TargetTransform>,
127
128 pub editor_state: EditorState,
130
131 pub mode: Mode,
133
134 pub target_areas: Vec<TargetArea>,
136
137 pub play_settings: PlaySettings,
139
140 pub feedback: ModuleAssist,
142}
143
144#[derive(Default, Clone, Serialize, Deserialize, Debug)]
146pub struct EditorState {
147 pub step: Step,
149
150 pub steps_completed: HashSet<Step>,
152}
153
154#[derive(Clone, Serialize, Deserialize, Debug)]
156pub struct Item {
157 pub sticker: Sticker,
159
160 pub kind: ItemKind,
162}
163
164#[derive(Clone, Serialize, Deserialize, Debug)]
166pub struct TargetTransform {
167 pub sticker_idx: usize,
169 pub transform: Transform,
171 pub trace_idx: usize,
173}
174
175#[derive(Clone, Serialize, Deserialize, Debug, EnumIs)]
176pub enum ItemKind {
178 Static,
180 Interactive(Interactive),
182}
183
184#[derive(Clone, Serialize, Deserialize, Debug, Default)]
186pub struct Interactive {
187 pub audio: Option<Audio>,
189
190 pub target_transform: Option<Transform>,
192}
193
194#[derive(Clone, Serialize, Deserialize, Debug)]
196pub struct TargetArea {
197 pub trace: Trace,
199}
200
201#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
202pub enum Mode {
204 #[allow(missing_docs)]
205 SettingTable,
206 #[allow(missing_docs)]
207 Sorting,
208 #[allow(missing_docs)]
209 WordBuilder,
210 SentenceBuilder,
212 #[allow(missing_docs)]
213 Matching,
214 #[allow(missing_docs)]
215 DressUp,
216 SceneBuilder,
218}
219
220impl Default for Mode {
221 fn default() -> Self {
222 Self::SettingTable
223 }
224}
225
226impl ModeExt for Mode {
227 fn get_list() -> Vec<Self> {
228 vec![
229 Self::SettingTable,
230 Self::Sorting,
231 Self::WordBuilder,
232 Self::SentenceBuilder,
233 Self::Matching,
234 Self::DressUp,
235 Self::SceneBuilder,
236 ]
237 }
238
239 fn as_str_id(&self) -> &'static str {
240 match self {
241 Self::SettingTable => "setting-table",
242 Self::Sorting => "sorting",
243 Self::WordBuilder => "word-builder",
244 Self::SentenceBuilder => "sentence-builder",
245 Self::Matching => "matching",
246 Self::DressUp => "dress-up",
247 Self::SceneBuilder => "scene-builder",
248 }
249 }
250
251 fn label(&self) -> &'static str {
252 const STR_SETTING_TABLE: &'static str = "Set a Table";
253 const STR_SORTING: &'static str = "Sorting";
254 const STR_WORD_BUILDER: &'static str = "Build a Word";
255 const STR_SENTENCE_BUILDER: &'static str = "Build a Sentence";
256 const STR_MATCHING: &'static str = "Matching";
257 const STR_DRESS_UP: &'static str = "Dress Up";
258 const STR_SCENE_BUILDER: &'static str = "Build a Scene";
259
260 match self {
261 Self::SettingTable => STR_SETTING_TABLE,
262 Self::Sorting => STR_SORTING,
263 Self::WordBuilder => STR_WORD_BUILDER,
264 Self::SentenceBuilder => STR_SENTENCE_BUILDER,
265 Self::Matching => STR_MATCHING,
266 Self::DressUp => STR_DRESS_UP,
267 Self::SceneBuilder => STR_SCENE_BUILDER,
268 }
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
274pub enum Step {
275 One,
277 Two,
279 Three,
281 Four,
283 Five,
285}
286
287impl Default for Step {
288 fn default() -> Self {
289 Self::One
290 }
291}
292
293impl StepExt for Step {
294 fn next(&self) -> Option<Self> {
295 match self {
296 Self::One => Some(Self::Two),
297 Self::Two => Some(Self::Three),
298 Self::Three => Some(Self::Four),
299 Self::Four => Some(Self::Five),
300 Self::Five => None,
301 }
302 }
303
304 fn as_number(&self) -> usize {
305 match self {
306 Self::One => 1,
307 Self::Two => 2,
308 Self::Three => 3,
309 Self::Four => 4,
310 Self::Five => 5,
311 }
312 }
313
314 fn label(&self) -> &'static str {
315 const STR_1: &'static str = "Design";
317 const STR_2: &'static str = "Content";
318 const STR_3: &'static str = "Interaction";
319 const STR_4: &'static str = "Settings";
320 const STR_5: &'static str = "Preview";
321
322 match self {
323 Self::One => STR_1,
324 Self::Two => STR_2,
325 Self::Three => STR_3,
326 Self::Four => STR_4,
327 Self::Five => STR_5,
328 }
329 }
330
331 fn get_list() -> Vec<Self> {
332 vec![Self::One, Self::Two, Self::Three, Self::Four, Self::Five]
333 }
334 fn get_preview() -> Self {
335 Self::Five
336 }
337}