Skip to main content

mittens_engine/engine/ecs/component/
stencil_clip.rs

1use crate::engine::ecs::component::Component;
2use crate::engine::ecs::{ComponentId, IntentValue, SignalEmitter};
3
4/// Declares a renderable-backed stencil clip boundary.
5///
6/// On `init()`, emits `RegisterStencilClip` so VisualWorld records the renderable that should be
7/// used as the clip source.
8///
9/// The renderer draws the referenced renderable into the stencil buffer
10/// (color write off, stencil INCR, ref = nesting depth) before drawing the TC's
11/// descendants, then restores stencil afterward with DECR. The same renderable
12/// also draws normally in the color pass — it does double duty.
13///
14/// ## Layout use
15///
16/// `sync_bg_quad` attaches a layout-owned `StencilClipComponent` as a sibling of the generated
17/// `__bg` helper whenever `overflow: Hidden | Scroll` is set on a style component. In that layout-
18/// owned case, the computed `__bg` renderable remains the clip shape.
19///
20/// ## Manual use
21///
22/// Attach somewhere under the renderable whose mesh should define the clip region.
23/// The nearest ancestor `RenderableComponent` determines the clip shape.
24pub struct StencilClipComponent {
25    /// Stencil reference depth. `0` = auto-assign based on ancestor nesting depth.
26    pub stencil_ref: u8,
27}
28
29impl StencilClipComponent {
30    pub fn new() -> Self {
31        Self { stencil_ref: 0 }
32    }
33}
34
35impl Default for StencilClipComponent {
36    fn default() -> Self {
37        Self::new()
38    }
39}
40
41impl Component for StencilClipComponent {
42    fn name(&self) -> &'static str {
43        "stencil_clip"
44    }
45
46    fn set_id(&mut self, _component: ComponentId) {}
47
48    fn as_any(&self) -> &dyn std::any::Any {
49        self
50    }
51
52    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
53        self
54    }
55
56    fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId) {
57        emit.push_intent_now(
58            component,
59            IntentValue::RegisterStencilClip {
60                component_ids: vec![component],
61            },
62        );
63    }
64
65    fn cleanup(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId) {
66        emit.push_intent_now(
67            component,
68            IntentValue::UnregisterStencilClip {
69                component_ids: vec![component],
70            },
71        );
72    }
73
74    fn to_mms_ast(
75        &self,
76        _world: &crate::engine::ecs::World,
77    ) -> crate::scripting::ast::ComponentExpression {
78        use crate::engine::ecs::component::ce_helpers::*;
79        if self.stencil_ref == 0 {
80            ce("StencilClip")
81        } else {
82            ce("StencilClip").with_call("stencil_ref", vec![num(self.stencil_ref as f64)])
83        }
84    }
85}