Skip to main content

scal_core/
anim_obj.rs

1use std::ops::Range;
2
3use glam::Vec2;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7use crate::anim_builders::{
8    CodeAddLinesBuilder, CodeHighlightBuilder, CodeModifyLineBuilder, CodeRemoveLinesBuilder,
9};
10use crate::anim_op::{AnimOP, CodeAnimationStyle};
11use crate::color::Color;
12use crate::ease::Ease;
13use crate::theme::Theme;
14use crate::transform::Transform;
15
16#[derive(Clone, Debug, Serialize, Deserialize)]
17/// Generic for all object type. Conversion is done using ``IntoAnimOp`` trait.
18pub struct AnimObj {
19    /// Each object has it's UUID so that you can apply animations over IPC to this object's clone in the
20    /// runtime.
21    pub id: Uuid,
22    #[allow(missing_docs)]
23    pub transform: Transform,
24    #[allow(missing_docs)]
25    pub kind: AnimObjKind,
26}
27
28impl AnimObj {
29    /// Returns an animation that instantly adds that object to the scene.
30    /// ```
31    /// let pointer = svg()
32    ///     .path("./pointer-tool.svg")
33    ///     .build();
34    /// Project {
35    ///    scene_settings,
36    ///    timeline: timeline![
37    ///        cw.instantiate(),
38    ///     ]
39    /// }
40    /// ```
41    #[must_use]
42    pub fn instantiate(&self) -> AnimOP {
43        AnimOP::Instantiate(Box::new(self.clone()), None)
44    }
45}
46
47#[derive(Clone, Debug, Serialize, Deserialize)]
48#[allow(missing_docs)]
49pub enum AnimObjKind {
50    Rectangle {
51        size: Vec2,
52        corner_radius: f32,
53        color: Color,
54    },
55    Circle {
56        radius: f32,
57        color: Color,
58    },
59    Polygon {
60        radius: f32,
61        sides: u32,
62        color: Color,
63    },
64    Text {
65        value: String,
66        font_family: String,
67        alignment: TextAlign,
68        color: Color,
69        font_size: f32,
70    },
71    Code {
72        source_code: String,
73        font_family: String,
74        font_size: f32,
75        syntax: Syntax,
76        theme: Option<Theme>,
77        padding: f32,
78        show_line_numbers: bool,
79        line_number_color: Color,
80    },
81    Image {
82        path: String,
83        size: Vec2,
84        color: Color,
85        stretch: StretchMode,
86    },
87    Svg {
88        path: String,
89        size: Vec2,
90        tint: Color,
91        fill: Option<Color>,
92        stroke: Option<Color>,
93        stroke_width: Option<f32>,
94        stretch: StretchMode,
95    },
96    CodeWindow {
97        source_code: String,
98        font_family: String,
99        font_size: f32,
100        syntax: Syntax,
101        theme: Option<Theme>,
102        title: String,
103        title_font_size: f32,
104        width: f32,
105        height: f32,
106        background_color: Color,
107        code_id: Uuid,
108        close_btn_id: Uuid,
109        minimize_btn_id: Uuid,
110        maximize_btn_id: Uuid,
111        title_id: Uuid,
112        bg_id: Uuid,
113        container_id: Uuid,
114        title_bar_bg_id: Uuid,
115        show_line_numbers: bool,
116        line_number_color: Color,
117    },
118    Group {
119        children: Vec<AnimObj>,
120    },
121}
122
123#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
124#[allow(missing_docs)]
125pub enum TextAlign {
126    Center,
127    Left,
128    Right,
129}
130
131#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
132#[allow(missing_docs)]
133pub enum Syntax {
134    Rust,
135    Nix,
136    Python,
137    JS,
138    Zig,
139}
140
141#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
142#[allow(missing_docs)]
143pub enum StretchMode {
144    Fit,
145    Fill,
146}
147
148/// Wrapper around the ``AnimObj`` struct that allows for clean animation construction for specific
149/// ``AnimObj`` kind.
150pub struct CodeHandle(pub AnimObj);
151
152impl std::ops::Deref for CodeHandle {
153    type Target = AnimObj;
154    fn deref(&self) -> &AnimObj {
155        &self.0
156    }
157}
158
159impl CodeHandle {
160    /// Returns an animation that instantly adds that object to the scene.
161    /// ```
162    /// let pointer = svg()
163    ///     .path("./pointer-tool.svg")
164    ///     .build();
165    /// Project {
166    ///    scene_settings,
167    ///    timeline: timeline![
168    ///        cw.instantiate(),
169    ///     ]
170    /// }
171    /// ```
172    #[must_use]
173    pub fn instantiate(&self) -> AnimOP {
174        AnimOP::Instantiate(Box::new(self.0.clone()), None)
175    }
176
177    /// Returns a builder for an animation of adding code lines to the code block.
178    /// ```
179    ///                code.add_lines()
180    ///                    .str(
181    ///                        r"
182    ///fn fib(n: u32) -> u32 {
183    ///    match n {
184    ///        0 => 0,
185    ///        1 => 1,
186    ///        _ => fib(n - 1) + fib(n - 2),
187    ///    }
188    ///}
189    ///                "
190    ///                    )
191    ///                    .over(5.s())
192    ///                    .style(CodeAnimationStyle::TypeWriter),
193    /// ```
194    #[must_use]
195    pub const fn add_lines(&self) -> CodeAddLinesBuilder {
196        CodeAddLinesBuilder {
197            uuid: self.0.id,
198            text: String::new(),
199            from_line: 0,
200            duration: 1.0,
201            ease: Ease::Linear,
202            style: CodeAnimationStyle::TypeWriter,
203        }
204    }
205
206    /// Returns a builder for an animation of modifying a line of code.
207    /// ```
208    ///                code.modify_line()
209    ///                    .str("New Line Contents")
210    ///                    .line(25)
211    ///                    .over(5.s())
212    ///                    .style(CodeAnimationStyle::TypeWriter),
213    /// ```
214    #[must_use]
215    pub const fn modify_line(&self) -> CodeModifyLineBuilder {
216        CodeModifyLineBuilder {
217            uuid: self.0.id,
218            line: 0,
219            text: String::new(),
220            duration: 1.0,
221            ease: Ease::Linear,
222            style: CodeAnimationStyle::TypeWriter,
223        }
224    }
225
226    /// Returns a builder for an animation of highlighting code by line range or regex pattern.
227    /// ```
228    ///                code.highlight()
229    ///                    .lines(3..6)
230    ///                    .color(Color::new(1.0, 1.0, 0.0, 0.3))
231    ///                    .over(1.s())
232    ///                    .ease(Ease::InOutCubic),
233    /// ```
234    #[must_use]
235    pub fn highlight(&self) -> CodeHighlightBuilder {
236        CodeHighlightBuilder {
237            uuid: self.0.id,
238            ranges: Vec::new(),
239            regex: None,
240            color: Color::new(1.0, 1.0, 0.0, 0.3),
241            duration: 1.0,
242            ease: Ease::Linear,
243        }
244    }
245
246    /// Returns a builder for an animation of removing lines form a code block.
247    /// ```
248    ///                code.remove_lines()
249    ///                    .range(0..25)
250    ///                    .over(5.s())
251    ///                    .style(CodeAnimationStyle::TypeWriter),
252    /// ```
253    #[must_use]
254    pub const fn remove_lines(&self) -> CodeRemoveLinesBuilder {
255        CodeRemoveLinesBuilder {
256            uuid: self.0.id,
257            range: 0..0,
258            duration: 1.0,
259            ease: Ease::Linear,
260            style: CodeAnimationStyle::TypeWriter,
261        }
262    }
263}
264
265/// Wrapper around the ``AnimObj`` struct that allows for clean animation construction for specific
266/// ``AnimObj`` kind.
267pub struct CodeWindowHandle(pub AnimObj);
268
269impl std::ops::Deref for CodeWindowHandle {
270    type Target = AnimObj;
271    fn deref(&self) -> &AnimObj {
272        &self.0
273    }
274}
275
276impl CodeWindowHandle {
277    /// Returns an animation that instantly adds that object to the scene.
278    /// ```
279    /// let pointer = svg()
280    ///     .path("./pointer-tool.svg")
281    ///     .build();
282    /// Project {
283    ///    scene_settings,
284    ///    timeline: timeline![
285    ///        cw.instantiate(),
286    ///     ]
287    /// }
288    /// ```
289    #[must_use]
290    pub fn instantiate(&self) -> AnimOP {
291        AnimOP::Instantiate(Box::new(self.0.clone()), None)
292    }
293    /// Returns a builder for an animation of adding code lines to the code block.
294    /// ```
295    ///                code.add_lines()
296    ///                    .str(
297    ///                        r"
298    ///fn fib(n: u32) -> u32 {
299    ///    match n {
300    ///        0 => 0,
301    ///        1 => 1,
302    ///        _ => fib(n - 1) + fib(n - 2),
303    ///    }
304    ///}
305    ///                "
306    ///                    )
307    ///                    .over(5.s())
308    ///                    .style(CodeAnimationStyle::TypeWriter),
309    /// ```
310    #[must_use]
311    pub const fn add_lines(&self) -> CodeAddLinesBuilder {
312        let code_id = if let AnimObjKind::CodeWindow { code_id, .. } = &self.0.kind {
313            *code_id
314        } else {
315            self.0.id
316        };
317        CodeAddLinesBuilder {
318            uuid: code_id,
319            text: String::new(),
320            from_line: 0,
321            duration: 1.0,
322            ease: Ease::Linear,
323            style: CodeAnimationStyle::TypeWriter,
324        }
325    }
326
327    /// Returns a builder for an animation of modifying a line of code.
328    /// ```
329    ///                code.modify_line()
330    ///                    .str("New Line Contents")
331    ///                    .line(25)
332    ///                    .over(5.s())
333    ///                    .style(CodeAnimationStyle::TypeWriter),
334    /// ```
335    #[must_use]
336    pub const fn modify_line(&self, line: u32) -> CodeModifyLineBuilder {
337        let code_id = if let AnimObjKind::CodeWindow { code_id, .. } = &self.0.kind {
338            *code_id
339        } else {
340            self.0.id
341        };
342        CodeModifyLineBuilder {
343            uuid: code_id,
344            line,
345            text: String::new(),
346            duration: 1.0,
347            ease: Ease::Linear,
348            style: CodeAnimationStyle::TypeWriter,
349        }
350    }
351
352    /// Returns a builder for an animation of highlighting code by line range or regex pattern.
353    /// ```
354    ///                cw.highlight()
355    ///                    .lines(3..6)
356    ///                    .color(Color::new(1.0, 1.0, 0.0, 0.3))
357    ///                    .over(1.s())
358    ///                    .ease(Ease::InOutCubic),
359    /// ```
360    #[must_use]
361    pub fn highlight(&self) -> CodeHighlightBuilder {
362        let code_id = if let AnimObjKind::CodeWindow { code_id, .. } = &self.0.kind {
363            *code_id
364        } else {
365            self.0.id
366        };
367        CodeHighlightBuilder {
368            uuid: code_id,
369            ranges: Vec::new(),
370            regex: None,
371            color: Color::new(1.0, 1.0, 0.0, 0.3),
372            duration: 1.0,
373            ease: Ease::Linear,
374        }
375    }
376
377    /// Returns a builder for an animation of removing lines form a code block.
378    /// ```
379    ///                code.remove_lines()
380    ///                    .range(0..25)
381    ///                    .over(5.s())
382    ///                    .style(CodeAnimationStyle::TypeWriter),
383    /// ```
384    #[must_use]
385    pub const fn remove_lines(&self, range: Range<u32>) -> CodeRemoveLinesBuilder {
386        let code_id = if let AnimObjKind::CodeWindow { code_id, .. } = &self.0.kind {
387            *code_id
388        } else {
389            self.0.id
390        };
391        CodeRemoveLinesBuilder {
392            uuid: code_id,
393            range,
394            duration: 1.0,
395            ease: Ease::Linear,
396            style: CodeAnimationStyle::TypeWriter,
397        }
398    }
399
400    #[must_use]
401    /// Returns handle to one of the objects that are used inside of the code window. This allows
402    /// you to animate them or use the as a reference point for movement.
403    pub const fn close_button(&self) -> CircleHandle {
404        if let AnimObjKind::CodeWindow { close_btn_id, .. } = &self.0.kind {
405            CircleHandle(*close_btn_id)
406        } else {
407            CircleHandle(self.0.id)
408        }
409    }
410    #[must_use]
411    /// Returns handle to one of the objects that are used inside of the code window. This allows
412    /// you to animate them or use the as a reference point for movement.
413    pub const fn minimize_button(&self) -> CircleHandle {
414        if let AnimObjKind::CodeWindow {
415            minimize_btn_id, ..
416        } = &self.0.kind
417        {
418            CircleHandle(*minimize_btn_id)
419        } else {
420            CircleHandle(self.0.id)
421        }
422    }
423    #[must_use]
424    /// Returns handle to one of the objects that are used inside of the code window. This allows
425    /// you to animate them or use the as a reference point for movement.
426    pub const fn maximize_button(&self) -> CircleHandle {
427        if let AnimObjKind::CodeWindow {
428            maximize_btn_id, ..
429        } = &self.0.kind
430        {
431            CircleHandle(*maximize_btn_id)
432        } else {
433            CircleHandle(self.0.id)
434        }
435    }
436    #[must_use]
437    /// Returns handle to one of the objects that are used inside of the code window. This allows
438    /// you to animate them or use the as a reference point for movement.
439    pub const fn title_text(&self) -> TextHandle {
440        if let AnimObjKind::CodeWindow { title_id, .. } = &self.0.kind {
441            TextHandle(*title_id)
442        } else {
443            TextHandle(self.0.id)
444        }
445    }
446    #[must_use]
447    /// Returns handle to one of the objects that are used inside of the code window. This allows
448    /// you to animate them or use the as a reference point for movement.
449    pub const fn window_background(&self) -> RectangleHandle {
450        RectangleHandle(self.0.id)
451    }
452    #[must_use]
453    /// Returns handle to one of the objects that are used inside of the code window. This allows
454    /// you to animate them or use the as a reference point for movement.
455    pub const fn container(&self) -> RectangleHandle {
456        if let AnimObjKind::CodeWindow { container_id, .. } = &self.0.kind {
457            RectangleHandle(*container_id)
458        } else {
459            RectangleHandle(self.0.id)
460        }
461    }
462    #[must_use]
463    /// Returns handle to one of the objects that are used inside of the code window. This allows
464    /// you to animate them or use the as a reference point for movement.
465    pub const fn title_bar_background(&self) -> RectangleHandle {
466        if let AnimObjKind::CodeWindow {
467            title_bar_bg_id, ..
468        } = &self.0.kind
469        {
470            RectangleHandle(*title_bar_bg_id)
471        } else {
472            RectangleHandle(self.0.id)
473        }
474    }
475}
476
477macro_rules! impl_handle {
478    ($type:ty) => {
479        impl $type {
480            /// Returns a builder for an animation that moves this object to a target position.
481            /// ```
482            ///                handle.position()
483            ///                    .to(Vec2::new(100.0, 200.0))
484            ///                    .over(5.s())
485            ///                    .ease(Ease::InOutCubic),
486            /// ```
487            #[must_use]
488            pub const fn position(&self) -> crate::transform::PositionBuilder {
489                crate::transform::PositionBuilder {
490                    uuid: self.0,
491                    target: None,
492                    object: None,
493                    duration: 1.0,
494                    ease: Ease::Linear,
495                }
496            }
497            /// Returns a builder for an animation that scales this object.
498            /// ```
499            ///                handle.scale()
500            ///                    .to(Vec2::new(2.0, 2.0))
501            ///                    .over(5.s())
502            ///                    .ease(Ease::InOutCubic),
503            /// ```
504            #[must_use]
505            pub const fn scale(&self) -> crate::transform::ScaleBuilder {
506                crate::transform::ScaleBuilder {
507                    uuid: self.0,
508                    target: None,
509                    object: None,
510                    duration: 1.0,
511                    ease: Ease::Linear,
512                }
513            }
514            /// Returns a builder for an animation that rotates this object.
515            /// ```
516            ///                handle.rotation()
517            ///                    .to(360.0)
518            ///                    .over(5.s())
519            ///                    .ease(Ease::InOutCubic),
520            /// ```
521            #[must_use]
522            pub const fn rotation(&self) -> crate::transform::RotateBuilder {
523                crate::transform::RotateBuilder {
524                    uuid: self.0,
525                    target: None,
526                    duration: 1.0,
527                    ease: Ease::Linear,
528                }
529            }
530        }
531        impl From<$type> for Uuid {
532            fn from(h: $type) -> Uuid {
533                h.0
534            }
535        }
536    };
537}
538
539/// Handle to a Circle sub-object of a code window, for use in animation builders.
540pub struct CircleHandle(pub Uuid);
541/// Handle to a Text sub-object of a code window, for use in animation builders.
542pub struct TextHandle(pub Uuid);
543/// Handle to a Rectangle sub-object of a code window, for use in animation builders.
544pub struct RectangleHandle(pub Uuid);
545
546impl_handle!(CircleHandle);
547impl_handle!(TextHandle);
548impl_handle!(RectangleHandle);