shared/domain/module/body/
drag_drop.rs

1use 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/// The body for [`DragDrop`](crate::domain::module::ModuleKind::DragDrop) modules.
18#[derive(Default, Clone, Serialize, Deserialize, Debug)]
19pub struct ModuleData {
20    /// The content
21    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/// The body for [`DragDrop`](crate::domain::module::ModuleKind::DragDrop) modules.
107#[derive(Default, Clone, Serialize, Deserialize, Debug)]
108pub struct Content {
109    /// The instructions for the module.
110    pub instructions: ModuleAssist,
111
112    /// The module's theme.
113    pub theme: ThemeId,
114
115    /// Backgrounds
116    pub backgrounds: Backgrounds,
117
118    /// Items (wrapper around Sticker and metadata)
119    pub items: Vec<Item>,
120
121    /// List of targets for items
122    ///
123    /// Each item can possibly have multiple targets
124    #[serde(default)]
125    #[serde(skip_serializing_if = "Vec::is_empty")]
126    pub item_targets: Vec<TargetTransform>,
127
128    /// The editor state
129    pub editor_state: EditorState,
130
131    /// The mode
132    pub mode: Mode,
133
134    /// target areas
135    pub target_areas: Vec<TargetArea>,
136
137    /// play settings
138    pub play_settings: PlaySettings,
139
140    /// The feedback for the module.
141    pub feedback: ModuleAssist,
142}
143
144/// Editor state
145#[derive(Default, Clone, Serialize, Deserialize, Debug)]
146pub struct EditorState {
147    /// the current step
148    pub step: Step,
149
150    /// the completed steps
151    pub steps_completed: HashSet<Step>,
152}
153
154/// drag & drop sticker w/ metadata
155#[derive(Clone, Serialize, Deserialize, Debug)]
156pub struct Item {
157    /// the sticker
158    pub sticker: Sticker,
159
160    /// the kind
161    pub kind: ItemKind,
162}
163
164/// Represents a possible placement for a sticker
165#[derive(Clone, Serialize, Deserialize, Debug)]
166pub struct TargetTransform {
167    /// Index of the sticker in the list of stickers
168    pub sticker_idx: usize,
169    /// Target transform for this sticker for rendering during edit
170    pub transform: Transform,
171    /// Index of the trace in which this item can be placed
172    pub trace_idx: usize,
173}
174
175#[derive(Clone, Serialize, Deserialize, Debug, EnumIs)]
176/// The mode
177pub enum ItemKind {
178    /// Just part of the scene
179    Static,
180    /// One of the draggables
181    Interactive(Interactive),
182}
183
184/// drag & drop sticker w/ metadata
185#[derive(Clone, Serialize, Deserialize, Debug, Default)]
186pub struct Interactive {
187    /// audio
188    pub audio: Option<Audio>,
189
190    /// target transform
191    pub target_transform: Option<Transform>,
192}
193
194/// drag & drop trace w/ metadata
195#[derive(Clone, Serialize, Deserialize, Debug)]
196pub struct TargetArea {
197    /// the trace
198    pub trace: Trace,
199}
200
201#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq, Eq, Hash)]
202/// The mode
203pub enum Mode {
204    #[allow(missing_docs)]
205    SettingTable,
206    #[allow(missing_docs)]
207    Sorting,
208    #[allow(missing_docs)]
209    WordBuilder,
210    /// Build a Sentence
211    SentenceBuilder,
212    #[allow(missing_docs)]
213    Matching,
214    #[allow(missing_docs)]
215    DressUp,
216    /// Build a Scene
217    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/// The Steps
273#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
274pub enum Step {
275    /// Step 1
276    One,
277    /// Step 2
278    Two,
279    /// Step 3
280    Three,
281    /// Step 4
282    Four,
283    /// Step 5
284    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        //TODO - localizaton
316        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}