pax_runtime/
rendering.rs

1use std::collections::HashMap;
2
3use std::iter;
4use std::rc::Rc;
5use_RefCell!();
6use crate::api::{CommonProperties, RenderContext};
7use pax_manifest::UniqueTemplateNodeIdentifier;
8use pax_message::NativeInterrupt;
9use pax_runtime_api::pax_value::PaxAny;
10use pax_runtime_api::{borrow, use_RefCell, Variable};
11use piet::{Color, StrokeStyle};
12
13use crate::api::{Layer, Scroll};
14
15use crate::{ExpandedNode, HandlerRegistry, RuntimeContext, RuntimePropertiesStackFrame};
16
17/// Type aliases to make it easier to work with nested Rcs and
18/// RefCells for instance nodes.
19pub type InstanceNodePtr = Rc<dyn InstanceNode>;
20pub type InstanceNodePtrList = RefCell<Vec<InstanceNodePtr>>;
21
22pub struct InstantiationArgs {
23    pub prototypical_common_properties_factory: Box<
24        dyn Fn(
25            Rc<RuntimePropertiesStackFrame>,
26            Option<Rc<ExpandedNode>>,
27        ) -> Option<Rc<RefCell<CommonProperties>>>,
28    >,
29    pub prototypical_properties_factory: Box<
30        dyn Fn(
31            Rc<RuntimePropertiesStackFrame>,
32            Option<Rc<ExpandedNode>>,
33        ) -> Option<Rc<RefCell<PaxAny>>>,
34    >,
35    pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
36    pub children: Option<InstanceNodePtrList>,
37    pub component_template: Option<InstanceNodePtrList>,
38
39    pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
40    // Used by RuntimePropertyStackFrame to pull out struct's properties based on their names
41    pub properties_scope_factory:
42        Option<Box<dyn Fn(Rc<RefCell<PaxAny>>) -> HashMap<String, Variable>>>,
43}
44
45pub struct ReusableInstanceNodeArgs {
46    pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
47    pub children: InstanceNodePtrList,
48    pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
49}
50
51impl ReusableInstanceNodeArgs {
52    pub fn new(base: &BaseInstance) -> Self {
53        ReusableInstanceNodeArgs {
54            handler_registry: base.handler_registry.clone(),
55            children: base.instance_children.clone(),
56            template_node_identifier: base.template_node_identifier.clone(),
57        }
58    }
59}
60
61#[derive(Clone)]
62pub enum NodeType {
63    Component,
64    Primitive,
65}
66
67impl std::fmt::Debug for dyn InstanceNode {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        self.resolve_debug(f, None)
70    }
71}
72
73/// Central runtime representation of a properties-computable and renderable node.
74/// `InstanceNode`s are conceptually stateless, and rely on [`ExpandedNode`]s for stateful representations.
75///
76/// An `InstanceNode` sits in between a [`pax_compiler::TemplateNodeDefinition`], the
77/// compile-time `definition` analogue to this `instance`, and [`ExpandedNode`].
78///
79/// There is a 1:1 relationship between [`pax_compiler::TemplateNodeDefinition`]s and `InstanceNode`s.
80/// There is a one-to-many relationship between one `InstanceNode` and possibly many variant [`ExpandedNode`]s,
81/// due to duplication via `for`.
82///
83/// `InstanceNode`s are architecturally "type-aware" — they can perform type-specific operations e.g. on the state stored in [`ExpandedNode`], while
84/// [`ExpandedNode`]s are "type-blind".  The latter store polymorphic data but cannot operate on it without the type-aware assistance of their linked `InstanceNode`.
85///
86/// (See [`RepeatInstance#expand_node`] where we visit a singular `InstanceNode` several times, producing multiple [`ExpandedNode`]s.)
87pub trait InstanceNode {
88    ///Retrieves the base instance, containing common functionality that all instances share
89    fn base(&self) -> &BaseInstance;
90
91    fn instantiate(args: InstantiationArgs) -> Rc<Self>
92    where
93        Self: Sized;
94
95    fn resolve_debug(
96        &self,
97        f: &mut std::fmt::Formatter,
98        expanded_node: Option<&ExpandedNode>,
99    ) -> std::fmt::Result;
100
101    /// Updates the expanded node, recomputing its properties and possibly updating its children
102    fn update(self: Rc<Self>, _expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {}
103
104    /// Second lifecycle method during each render loop, occurs after
105    /// properties have been computed, but before rendering
106    /// Example use-case: perform side-effects to the drawing contexts.
107    /// This is how [`Frame`] performs clipping, for example.
108    /// Occurs in a pre-order traversal of the render tree.
109    #[allow(unused_variables)]
110    fn handle_pre_render(
111        &self,
112        expanded_node: &ExpandedNode,
113        context: &Rc<RuntimeContext>,
114        rcs: &mut dyn RenderContext,
115    ) {
116        //no-op default implementation
117    }
118
119    /// Third lifecycle method during each render loop, occurs
120    /// after all descendents have been rendered.
121    /// Occurs in a post-order traversal of the render tree. Most primitives
122    /// are expected to draw their contents to the rendering context during this event.
123    #[allow(unused_variables)]
124    fn render(
125        &self,
126        expanded_node: &ExpandedNode,
127        context: &Rc<RuntimeContext>,
128        rcs: &mut dyn RenderContext,
129    ) {
130    }
131
132    /// Fourth and final lifecycle method during each render loop, occurs
133    /// after all descendents have been rendered AND the current node has been rendered.
134    /// Useful for clean-up, e.g. this is where `Frame` cleans up the drawing contexts
135    /// to stop clipping.
136    /// Occurs in a post-order traversal of the render tree.
137    #[allow(unused_variables)]
138    fn handle_post_render(
139        &self,
140        expanded_node: &ExpandedNode,
141        context: &Rc<RuntimeContext>,
142        rcs: &mut dyn RenderContext,
143    ) {
144        //no-op default implementation
145    }
146
147    /// Fires during the tick when a node is first attached to the render tree.  For example,
148    /// this event fires by all nodes on the global first tick, and by all nodes in a subtree
149    /// when a `Conditional` subsequently turns on a subtree (i.e. when the `Conditional`s criterion becomes `true` after being `false` through the end of at least 1 frame.)
150    /// A use-case: send a message to native renderers that a `Text` element should be rendered and tracked
151    #[allow(unused_variables)]
152    fn handle_mount(
153        self: Rc<Self>,
154        expanded_node: &Rc<ExpandedNode>,
155        context: &Rc<RuntimeContext>,
156    ) {
157        let env = Rc::clone(&expanded_node.stack);
158        let children = borrow!(self.base().get_instance_children());
159        let children_with_envs = children.iter().cloned().zip(iter::repeat(env));
160
161        let new_children = expanded_node.generate_children(
162            children_with_envs,
163            context,
164            &expanded_node.parent_frame,
165            true,
166        );
167        expanded_node.children.set(new_children);
168    }
169
170    /// Fires during element unmount, when an element is about to be removed from the render tree (e.g. by a `Conditional`)
171    /// A use-case: send a message to native renderers that a `Text` element should be removed
172    #[allow(unused_variables)]
173    fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, context: &Rc<RuntimeContext>) {
174        // let new_children =
175        //     expanded_node.generate_children(vec![], context, &expanded_node.parent_frame);
176        // expanded_node.children.set(new_children);
177    }
178
179    /// Invoked by event interrupts to pass scroll information to render node
180    #[allow(unused_variables)]
181    fn handle_scroll(&self, args_scroll: Scroll) {
182        //no-op default implementation
183    }
184
185    fn get_template(&self) -> Option<&InstanceNodePtrList> {
186        None
187    }
188
189    /// Used by frame to control content clipping
190    fn clips_content(&self, _expanded_node: &ExpandedNode) -> bool {
191        false
192    }
193
194    fn handle_native_interrupt(
195        &self,
196        _expanded_node: &Rc<ExpandedNode>,
197        _interrupt: &NativeInterrupt,
198    ) {
199        // no-op for many
200    }
201
202    fn handle_control_flow_node_expansion(
203        self: Rc<Self>,
204        _expanded_node: &Rc<ExpandedNode>,
205        _context: &Rc<RuntimeContext>,
206    ) {
207    }
208
209    fn handle_setup_slot_children(
210        self: Rc<Self>,
211        _expanded_node: &Rc<ExpandedNode>,
212        _context: &Rc<RuntimeContext>,
213    ) {
214    }
215}
216
217pub struct BaseInstance {
218    pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
219    pub instance_prototypical_properties_factory: Box<
220        dyn Fn(
221            Rc<RuntimePropertiesStackFrame>,
222            Option<Rc<ExpandedNode>>,
223        ) -> Option<Rc<RefCell<PaxAny>>>,
224    >,
225    pub instance_prototypical_common_properties_factory: Box<
226        dyn Fn(
227            Rc<RuntimePropertiesStackFrame>,
228            Option<Rc<ExpandedNode>>,
229        ) -> Option<Rc<RefCell<CommonProperties>>>,
230    >,
231    pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
232    pub properties_scope_factory:
233        Option<Box<dyn Fn(Rc<RefCell<PaxAny>>) -> HashMap<String, Variable>>>,
234    instance_children: InstanceNodePtrList,
235    flags: InstanceFlags,
236}
237
238#[derive(Clone)]
239pub struct InstanceFlags {
240    /// Used for exotic tree traversals for `Slot`, e.g. for `Stacker` > `Repeat` > `Rectangle`
241    /// where the repeated `Rectangle`s need to be be considered direct children of `Stacker`.
242    /// `Repeat` and `Conditional` override `is_invisible_to_slot` to return true
243    pub invisible_to_slot: bool,
244    /// Certain elements, such as Groups and Components, are invisible to ray-casting.
245    /// Since these container elements are on top of the elements they contain,
246    /// this is needed otherwise the containers would intercept rays that should hit their contents.
247    pub invisible_to_raycasting: bool,
248    /// The layer type (`Layer::Native` or `Layer::Canvas`) for this RenderNode.
249    /// Default is `Layer::Canvas`, and must be overwritten for `InstanceNode`s that manage native
250    /// content.
251    pub layer: Layer,
252
253    /// Only true for ComponentInstance
254    pub is_component: bool,
255
256    /// Is this node a `Slot`?
257    pub is_slot: bool,
258}
259
260impl BaseInstance {
261    pub fn new(args: InstantiationArgs, flags: InstanceFlags) -> Self {
262        BaseInstance {
263            handler_registry: args.handler_registry,
264            instance_prototypical_common_properties_factory: args
265                .prototypical_common_properties_factory,
266            instance_prototypical_properties_factory: args.prototypical_properties_factory,
267            instance_children: args.children.unwrap_or_default(),
268            flags,
269            template_node_identifier: args.template_node_identifier,
270            properties_scope_factory: args.properties_scope_factory,
271        }
272    }
273
274    /// Returns a handle to a node-managed HandlerRegistry, a mapping between event types and handlers.
275    /// Each node that can handle events is responsible for implementing this; Component instances generate
276    /// the necessary code to wire up userland events like `<SomeNode @click=self.handler>`. Primitives must handle
277    /// this explicitly, see e.g. `[pax_std::drawing::rectangle::RectangleInstance#get_handler_registry]`.
278    pub fn get_handler_registry(&self) -> Option<Rc<RefCell<HandlerRegistry>>> {
279        match &self.handler_registry {
280            Some(registry) => Some(Rc::clone(registry)),
281            _ => None,
282        }
283    }
284
285    /// Return the list of instance nodes that are children of this one.  Intuitively, this will return
286    /// instance nodes mapping exactly to the template node definitions.
287    /// For `Component`s, `get_instance_children` returns the root(s) of its template, not its `slot_children`.
288    /// (see [`get_slot_children`] for the way to retrieve the latter.)
289    pub fn get_instance_children(&self) -> &InstanceNodePtrList {
290        &self.instance_children
291    }
292
293    pub fn flags(&self) -> &InstanceFlags {
294        &self.flags
295    }
296}
297
298/// Represents the outer stroke of a drawable element
299pub struct StrokeInstance {
300    pub color: Color,
301    pub width: f64,
302    pub style: StrokeStyle,
303}