1use std::{
2 collections::HashMap,
3 rc::{Rc, Weak},
4};
5
6use_RefCell!();
7use crate::{
8 node_interface::NodeLocal, ExpandedNode, LayoutHull, RuntimeContext,
9 RuntimePropertiesStackFrame, TransformAndBounds,
10};
11use kurbo::Affine;
12
13pub use pax_runtime_api::*;
14use pax_runtime_api::{cursor::CursorStyle, math::Point2, properties::UntypedProperty};
15
16use crate::node_interface::NodeInterface;
17#[cfg(feature = "designtime")]
18use {
19 pax_designtime::{
20 messages::{UserlandSourceUpdateRequest, UserlandSourceUpdateResponse},
21 DesigntimeManager,
22 },
23 pax_manifest::UniqueTemplateNodeIdentifier,
24};
25
26#[derive(Clone)]
27pub struct NodeContext {
42 pub expanded_node: Weak<ExpandedNode>,
43 pub slot_index: Property<Option<usize>>,
45 pub local_stack_frame: Rc<RuntimePropertiesStackFrame>,
47 pub containing_component: Weak<ExpandedNode>,
49 pub elapsed_frames: Property<u64>,
51 pub elapsed_millis: Property<u64>,
53 pub gyro: Property<Gyro>,
55 pub accel: Property<Accel>,
57 pub bounds_parent: Property<(f64, f64)>,
59 pub bounds_self: Property<(f64, f64)>,
61 pub measured_size: Property<Option<(f64, f64)>>,
63 pub subtree_layout_hull: Property<LayoutHull>,
65 pub platform: Platform,
67 pub os: OS,
69 pub target: Property<TargetInfo>,
71 pub viewport: Property<Viewport>,
73 pub projected_children_count: Property<usize>,
79 pub(crate) runtime_context: Rc<RuntimeContext>,
81 pub node_transform_and_bounds: TransformAndBounds<NodeLocal, Window>,
83 pub projected_children: Property<Vec<Rc<ExpandedNode>>>,
88 pub projected_children_changed: Property<()>,
90 pub received_children: Property<Vec<Rc<ExpandedNode>>>,
97 pub received_children_count: Property<usize>,
99 pub received_children_changed: Property<()>,
104 pub retained_received_children: Property<Vec<Rc<ExpandedNode>>>,
110 pub retained_received_children_changed: Property<()>,
112
113 #[cfg(feature = "designtime")]
114 pub designtime: Rc<RefCell<DesigntimeManager>>,
115 pub(crate) get_elapsed_millis: Rc<dyn Fn() -> u128>,
116}
117
118impl NodeContext {
119 pub fn push_local_store<T: Store>(&self, store: T) {
121 self.local_stack_frame.insert_stack_local_store(store);
122 }
123
124 pub fn peek_local_store<T: Store, V>(&self, f: impl FnOnce(&mut T) -> V) -> Result<V, String> {
126 self.local_stack_frame.peek_stack_local_store(f)
127 }
128
129 pub fn local_point(&self, p: Point2<Window>) -> Point2<NodeLocal> {
132 let presentation_transform = self
133 .expanded_node
134 .upgrade()
135 .map(|node| {
136 self.runtime_context
137 .presentation_scroll_transform_for_node(&node)
138 })
139 .unwrap_or(Affine::IDENTITY);
140 local_point_with_presentation_transform(
141 self.node_transform_and_bounds,
142 presentation_transform,
143 p,
144 )
145 }
146
147 pub fn get_node_interface(&self) -> Option<NodeInterface> {
149 Weak::upgrade(&self.containing_component).map(|v| v.into())
150 }
151
152 pub fn elapsed_time_millis(&self) -> u128 {
154 (self.get_elapsed_millis)()
155 }
156
157 pub fn subscribe(&self, dependencies: &[UntypedProperty], f: impl Fn() + 'static) {
159 match self.expanded_node.upgrade() {
160 Some(expanded_node) => {
161 let subscription_prop =
162 self.runtime_context
163 .register_node_effect(expanded_node.id, dependencies, f);
164 borrow_mut!(expanded_node.subscriptions).push(subscription_prop);
165 }
166 None => log::warn!("couldn't add subscription: node doesn't exist anymore"),
167 }
168 }
169
170 pub fn clear_subscriptions(&self) {
172 match self.expanded_node.upgrade() {
173 Some(expanded_node) => borrow_mut!(expanded_node.subscriptions).clear(),
174 None => log::warn!("couldn't clear subscriptions: node doesn't exist anymore"),
175 }
176 }
177
178 pub fn navigate_to(&self, url: &str, target: NavigationTarget) {
184 self.runtime_context
185 .enqueue_native_message(NativeMessage::Navigate(NavigationPatch {
186 url: url.to_string(),
187 target: match target {
188 NavigationTarget::Current => "current",
189 NavigationTarget::New => "new",
190 }
191 .to_string(),
192 }))
193 }
194
195 pub fn dispatch_event(&self, identifier: &'static str) -> Result<(), String> {
197 let component_origin = self
198 .containing_component
199 .upgrade()
200 .ok_or_else(|| "can't dispatch from root component".to_owned())?;
201
202 {
204 let component_origin_instance = borrow!(component_origin.instance_node);
205 let registry = component_origin_instance
206 .base()
207 .handler_registry
208 .as_ref()
209 .ok_or_else(|| "no registry present".to_owned())?;
210 borrow!(registry).handlers.get(identifier).ok_or_else(|| {
211 format!("no registered handler with name \"{}\" exists", identifier)
212 })?;
213 }
214
215 self.runtime_context
217 .queue_custom_event(Rc::clone(&component_origin), identifier);
218
219 Ok(())
220 }
221
222 pub fn set_cursor(&self, cursor: CursorStyle) {
224 self.runtime_context
225 .enqueue_native_message(NativeMessage::SetCursor(SetCursorPatch {
226 cursor: cursor.to_string(),
227 }));
228 }
229
230 pub fn screenshot(&self, id: u32) {
232 self.runtime_context
233 .enqueue_native_message(NativeMessage::Screenshot(ScreenshotPatch {
234 id,
235 scale: None,
236 }));
237 }
238
239 pub fn get_screenshot_map(&self) -> Rc<RefCell<HashMap<u32, ScreenshotData>>> {
241 self.runtime_context.get_screenshot_map()
242 }
243}
244
245fn local_point_with_presentation_transform(
246 node_transform_and_bounds: TransformAndBounds<NodeLocal, Window>,
247 presentation_transform: Affine,
248 point: Point2<Window>,
249) -> Point2<NodeLocal> {
250 node_transform_and_bounds.as_transform().inverse() * (presentation_transform.inverse() * point)
251}
252
253#[cfg(test)]
254mod local_point_tests {
255 use super::*;
256 use pax_runtime_api::math::{Transform2, Vector2};
257
258 #[test]
259 fn local_point_accounts_for_ancestor_scroller_presentation_offset() {
260 let node = TransformAndBounds {
261 transform: Transform2::<NodeLocal, Window>::translate(Vector2::new(50.0, 400.0)),
262 bounds: (200.0, 100.0),
263 };
264 let presentation_transform = Affine::translate((0.0, -300.0));
265
266 let local = local_point_with_presentation_transform(
267 node,
268 presentation_transform,
269 Point2::new(150.0, 140.0),
270 );
271
272 assert!((local.x - 0.5).abs() < 1e-9);
273 assert!((local.y - 0.4).abs() < 1e-9);
274 }
275}
276
277#[cfg(feature = "designtime")]
278impl NodeContext {
279 pub fn submit_userland_source_update(
281 &self,
282 request_id: String,
283 path: String,
284 contents: String,
285 ) -> Result<(), String> {
286 self.designtime
287 .borrow_mut()
288 .send_userland_source_update(UserlandSourceUpdateRequest {
289 request_id,
290 path,
291 contents,
292 })
293 .map_err(|err| err.to_string())
294 }
295
296 pub fn take_userland_source_update_responses(&self) -> Vec<UserlandSourceUpdateResponse> {
298 self.designtime
299 .borrow_mut()
300 .take_userland_source_update_responses()
301 }
302
303 pub fn raycast(&self, point: Point2<Window>, hit_invisible: bool) -> Vec<NodeInterface> {
304 let expanded_nodes = self.runtime_context.get_elements_beneath_ray(
305 self.runtime_context.get_userland_root_expanded_node(),
306 point,
307 false,
308 vec![],
309 hit_invisible,
310 );
311 expanded_nodes
312 .into_iter()
313 .map(Into::<NodeInterface>::into)
314 .collect()
315 }
316
317 pub fn get_nodes_by_global_id(&self, uni: UniqueTemplateNodeIdentifier) -> Vec<NodeInterface> {
318 let expanded_nodes = self.runtime_context.get_expanded_nodes_by_global_ids(&uni);
319 expanded_nodes
320 .into_iter()
321 .map(Into::<NodeInterface>::into)
322 .collect()
323 }
324
325 pub fn get_userland_root_expanded_node(&self) -> Option<NodeInterface> {
326 #[cfg(feature = "designtime")]
327 let expanded_node = self.runtime_context.get_userland_root_expanded_node()?;
328 #[cfg(not(feature = "designtime"))]
329 let expanded_node = self.runtime_context.get_root_expanded_node()?;
330 Some(expanded_node.into())
331 }
332
333 pub fn get_root_expanded_node(&self) -> Option<NodeInterface> {
334 let expanded_node = self.runtime_context.get_root_expanded_node()?;
335 Some(expanded_node.into())
336 }
337
338 pub fn get_nodes_by_id(&self, id: &str) -> Vec<NodeInterface> {
339 let expanded_nodes = self.runtime_context.get_expanded_nodes_by_id(id);
340 expanded_nodes
341 .into_iter()
342 .map(Into::<NodeInterface>::into)
343 .collect()
344 }
345}