pax_runtime/
api.rs

1use std::rc::{Rc, Weak};
2
3use_RefCell!();
4use crate::{
5    node_interface::NodeLocal, ExpandedNode, RuntimeContext, RuntimePropertiesStackFrame,
6    TransformAndBounds,
7};
8
9pub use pax_runtime_api::*;
10use pax_runtime_api::{cursor::CursorStyle, math::Point2, properties::UntypedProperty};
11
12use crate::node_interface::NodeInterface;
13#[cfg(feature = "designtime")]
14use {pax_designtime::DesigntimeManager, pax_manifest::UniqueTemplateNodeIdentifier};
15
16#[derive(Clone)]
17pub struct NodeContext {
18    pub expanded_node: Weak<ExpandedNode>,
19    /// slot index of this node in its container
20    pub slot_index: Property<Option<usize>>,
21    /// Stack frame of this component, used to look up stores
22    pub local_stack_frame: Rc<RuntimePropertiesStackFrame>,
23    /// Reference to the ExpandedNode of the component containing this node
24    pub containing_component: Weak<ExpandedNode>,
25    /// The current global engine tick count
26    pub frames_elapsed: Property<u64>,
27    /// The bounds of this element's immediate container (parent) in px
28    pub bounds_parent: Property<(f64, f64)>,
29    /// The bounds of this element in px
30    pub bounds_self: Property<(f64, f64)>,
31    /// Current platform (Web/Native) this app is running on
32    pub platform: Platform,
33    /// Current os (Android/Windows/Mac/Linux) this app is running on
34    pub os: OS,
35    /// The number of slot children provided to this component template
36    pub slot_children_count: Property<usize>,
37    /// Borrow of the RuntimeContext, used at least for exposing raycasting to userland
38    pub(crate) runtime_context: Rc<RuntimeContext>,
39    /// The transform of this node in the global coordinate space
40    pub node_transform_and_bounds: TransformAndBounds<NodeLocal, Window>,
41    /// Slot children of this node
42    pub slot_children: Property<Vec<Rc<ExpandedNode>>>,
43    /// A property that can be depended on to dirty when a slot child is attached
44    pub slot_children_attached_listener: Property<()>,
45
46    #[cfg(feature = "designtime")]
47    pub designtime: Rc<RefCell<DesigntimeManager>>,
48    pub(crate) get_elapsed_millis: Rc<dyn Fn() -> u128>,
49}
50
51impl NodeContext {
52    pub fn push_local_store<T: Store>(&self, store: T) {
53        self.local_stack_frame.insert_stack_local_store(store);
54    }
55
56    pub fn peek_local_store<T: Store, V>(&self, f: impl FnOnce(&mut T) -> V) -> Result<V, String> {
57        self.local_stack_frame.peek_stack_local_store(f)
58    }
59
60    pub fn local_point(&self, p: Point2<Window>) -> Point2<NodeLocal> {
61        self.node_transform_and_bounds.as_transform().inverse() * p
62    }
63
64    pub fn get_node_interface(&self) -> Option<NodeInterface> {
65        Weak::upgrade(&self.containing_component).map(|v| v.into())
66    }
67
68    /// Get std::time::Instant::now()
69    pub fn elapsed_time_millis(&self) -> u128 {
70        (self.get_elapsed_millis)()
71    }
72
73    pub fn subscribe(&self, dependencies: &[UntypedProperty], f: impl Fn() + 'static) {
74        let subscription_prop = Property::computed(f, dependencies);
75        match self.expanded_node.upgrade() {
76            Some(expanded_node) => borrow_mut!(expanded_node.subscriptions).push(subscription_prop),
77            None => log::warn!("couldn't add subscription: node doesn't exist anymore"),
78        }
79    }
80
81    pub fn clear_subscriptions(&self) {
82        match self.expanded_node.upgrade() {
83            Some(expanded_node) => borrow_mut!(expanded_node.subscriptions).clear(),
84            None => log::warn!("couldn't clear subscriptions: node doesn't exist anymore"),
85        }
86    }
87
88    pub fn navigate_to(&self, url: &str, target: NavigationTarget) {
89        self.runtime_context
90            .enqueue_native_message(NativeMessage::Navigate(NavigationPatch {
91                url: url.to_string(),
92                target: match target {
93                    NavigationTarget::Current => "current",
94                    NavigationTarget::New => "new",
95                }
96                .to_string(),
97            }))
98    }
99
100    pub fn dispatch_event(&self, identifier: &'static str) -> Result<(), String> {
101        let component_origin = self
102            .containing_component
103            .upgrade()
104            .ok_or_else(|| "can't dispatch from root component".to_owned())?;
105
106        // Check that this is a valid custom event to trigger
107        {
108            let component_origin_instance = borrow!(component_origin.instance_node);
109            let registry = component_origin_instance
110                .base()
111                .handler_registry
112                .as_ref()
113                .ok_or_else(|| "no registry present".to_owned())?;
114            borrow!(registry).handlers.get(identifier).ok_or_else(|| {
115                format!("no registered handler with name \"{}\" exists", identifier)
116            })?;
117        }
118
119        // ok now we know it's a valid thing to dispatch, queue it for end of tick
120        self.runtime_context
121            .queue_custom_event(Rc::clone(&component_origin), identifier);
122
123        Ok(())
124    }
125
126    pub fn set_cursor(&self, cursor: CursorStyle) {
127        self.runtime_context
128            .enqueue_native_message(NativeMessage::SetCursor(SetCursorPatch {
129                cursor: cursor.to_string(),
130            }));
131    }
132}
133
134#[cfg(feature = "designtime")]
135impl NodeContext {
136    pub fn raycast(&self, point: Point2<Window>, hit_invisible: bool) -> Vec<NodeInterface> {
137        let expanded_nodes = self.runtime_context.get_elements_beneath_ray(
138            self.runtime_context.get_userland_root_expanded_node(),
139            point,
140            false,
141            vec![],
142            hit_invisible,
143        );
144        expanded_nodes
145            .into_iter()
146            .map(Into::<NodeInterface>::into)
147            .collect()
148    }
149
150    pub fn get_nodes_by_global_id(&self, uni: UniqueTemplateNodeIdentifier) -> Vec<NodeInterface> {
151        let expanded_nodes = self.runtime_context.get_expanded_nodes_by_global_ids(&uni);
152        expanded_nodes
153            .into_iter()
154            .map(Into::<NodeInterface>::into)
155            .collect()
156    }
157
158    pub fn get_userland_root_expanded_node(&self) -> Option<NodeInterface> {
159        #[cfg(feature = "designtime")]
160        let expanded_node = self.runtime_context.get_userland_root_expanded_node()?;
161        #[cfg(not(feature = "designtime"))]
162        let expanded_node = self.runtime_context.get_root_expanded_node()?;
163        Some(expanded_node.into())
164    }
165
166    pub fn get_root_expanded_node(&self) -> Option<NodeInterface> {
167        let expanded_node = self.runtime_context.get_root_expanded_node()?;
168        Some(expanded_node.into())
169    }
170
171    pub fn get_nodes_by_id(&self, id: &str) -> Vec<NodeInterface> {
172        let expanded_nodes = self.runtime_context.get_expanded_nodes_by_id(id);
173        expanded_nodes
174            .into_iter()
175            .map(Into::<NodeInterface>::into)
176            .collect()
177    }
178}