Skip to main content

mittens_engine/engine/ecs/component/
text_shadow.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3
4/// Text shadow styling.
5///
6/// If a `TextShadowComponent` is parented to a `TextComponent`, `TextSystem` will spawn additional
7/// shadow renderables for every glyph.
8#[derive(Debug, Clone, Copy)]
9pub struct TextShadowComponent {
10    /// Shadow color (RGBA). Default: black.
11    pub rgba: [f32; 4],
12
13    /// Shadow scale multiplier. Default: 1.25.
14    pub scale: f32,
15
16    /// Shadow XYZ offset in glyph-local space.
17    ///
18    /// For Z: `TextSystem` uses this as a *magnitude* to nudge the shadow behind the main glyph
19    /// to avoid z-fighting.
20    /// Default: [0.0, 0.0, 0.001].
21    pub offset: [f32; 3],
22
23    component: Option<ComponentId>,
24}
25
26impl TextShadowComponent {
27    pub const DEFAULT_RGBA: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
28    pub const DEFAULT_SCALE: f32 = 1.25;
29    pub const DEFAULT_OFFSET: [f32; 3] = [0.0, 0.0, 0.001];
30
31    pub fn new() -> Self {
32        Self {
33            rgba: Self::DEFAULT_RGBA,
34            scale: Self::DEFAULT_SCALE,
35            offset: Self::DEFAULT_OFFSET,
36            component: None,
37        }
38    }
39
40    pub fn with_rgba(mut self, rgba: [f32; 4]) -> Self {
41        self.rgba = rgba;
42        self
43    }
44
45    pub fn with_scale(mut self, scale: f32) -> Self {
46        self.scale = scale;
47        self
48    }
49
50    pub fn with_offset(mut self, offset: [f32; 3]) -> Self {
51        self.offset = offset;
52        self
53    }
54
55    pub fn with_offset_xy(mut self, offset: [f32; 2]) -> Self {
56        self.offset[0] = offset[0];
57        self.offset[1] = offset[1];
58        self
59    }
60
61    pub fn with_z_offset(mut self, z_offset: f32) -> Self {
62        self.offset[2] = z_offset;
63        self
64    }
65}
66
67impl Default for TextShadowComponent {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73impl Component for TextShadowComponent {
74    fn name(&self) -> &'static str {
75        "text_shadow"
76    }
77
78    fn set_id(&mut self, component: ComponentId) {
79        self.component = Some(component);
80    }
81
82    fn as_any(&self) -> &dyn std::any::Any {
83        self
84    }
85
86    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
87        self
88    }
89
90    fn init(&mut self, _emit: &mut dyn crate::engine::ecs::SignalEmitter, _component: ComponentId) {
91        // TextShadow is consumed by TextSystem at TextComponent expansion time.
92    }
93
94    fn to_mms_ast(
95        &self,
96        _world: &crate::engine::ecs::World,
97    ) -> crate::scripting::ast::ComponentExpression {
98        use crate::engine::ecs::component::ce_helpers::*;
99        ce("TextShadow")
100            .with_call(
101                "rgba",
102                vec![array(nums(self.rgba.iter().map(|&v| v as f64)))],
103            )
104            .with_call("scale", vec![num(self.scale as f64)])
105            .with_call(
106                "offset",
107                vec![array(nums(self.offset.iter().map(|&v| v as f64)))],
108            )
109    }
110}