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)]
34pub struct NodeContext {
54 pub expanded_node: Weak<ExpandedNode>,
55 pub slot_index: LocalProperty<Option<usize>>,
57 pub expression_stack: Rc<RuntimePropertiesStackFrame>,
58 pub(crate) store_generation: u64,
59 pub containing_component: Weak<ExpandedNode>,
61 pub elapsed_frames: LocalProperty<u64>,
63 pub elapsed_millis: LocalProperty<u64>,
65 pub gyro: LocalProperty<Gyro>,
67 pub accel: LocalProperty<Accel>,
69 pub bounds_parent: LocalProperty<(f64, f64)>,
71 pub bounds_self: LocalProperty<(f64, f64)>,
73 pub measured_size: LocalProperty<Option<(f64, f64)>>,
75 pub subtree_layout_hull: LocalProperty<LayoutHull>,
77 pub platform: Platform,
79 pub os: OS,
81 pub target: LocalProperty<TargetInfo>,
83 pub viewport: LocalProperty<Viewport>,
85 pub projected_children_count: LocalProperty<usize>,
91 pub(crate) runtime_context: Rc<RuntimeContext>,
93 pub node_transform_and_bounds: TransformAndBounds<NodeLocal, Window>,
95 pub projected_children: LocalProperty<Vec<Rc<ExpandedNode>>>,
100 pub projected_children_changed: LocalProperty<()>,
102 pub received_children: LocalProperty<Vec<Rc<ExpandedNode>>>,
109 pub received_children_count: LocalProperty<usize>,
111 pub received_children_changed: LocalProperty<()>,
116 pub retained_received_children: LocalProperty<Vec<Rc<ExpandedNode>>>,
122 pub retained_received_children_changed: LocalProperty<()>,
124
125 #[cfg(feature = "designtime")]
126 pub designtime: Rc<RefCell<DesigntimeManager>>,
127 pub(crate) get_elapsed_millis: Rc<dyn Fn() -> u128>,
128}
129
130impl NodeContext {
131 pub fn async_scope(&self) -> Result<AsyncScope, AsyncError> {
135 self.expanded_node
136 .upgrade()
137 .filter(|node| node.stores.generation() == self.store_generation)
138 .ok_or(AsyncError::Closed)?
139 .async_scope(&self.runtime_context)
140 }
141
142 pub fn application(&self) -> AppContext {
144 self.runtime_context.application.context()
145 }
146
147 pub fn local_point(&self, p: Point2<Window>) -> Point2<NodeLocal> {
150 let presentation_transform = self
151 .expanded_node
152 .upgrade()
153 .map(|node| {
154 self.runtime_context
155 .presentation_scroll_transform_for_node(&node)
156 })
157 .unwrap_or(Affine::IDENTITY);
158 local_point_with_presentation_transform(
159 self.node_transform_and_bounds,
160 presentation_transform,
161 p,
162 )
163 }
164
165 pub fn get_node_interface(&self) -> Option<NodeInterface> {
167 Weak::upgrade(&self.containing_component).map(|v| v.into())
168 }
169
170 pub fn elapsed_time_millis(&self) -> u128 {
172 (self.get_elapsed_millis)()
173 }
174
175 pub fn subscribe(&self, dependencies: &[UntypedProperty], f: impl Fn() + 'static) {
177 match self.expanded_node.upgrade() {
178 Some(expanded_node) => {
179 let subscription_prop =
180 self.runtime_context
181 .register_node_effect(expanded_node.id, dependencies, f);
182 borrow_mut!(expanded_node.subscriptions).push(subscription_prop);
183 }
184 None => log::warn!("couldn't add subscription: node doesn't exist anymore"),
185 }
186 }
187
188 pub fn clear_subscriptions(&self) {
190 match self.expanded_node.upgrade() {
191 Some(expanded_node) => borrow_mut!(expanded_node.subscriptions).clear(),
192 None => log::warn!("couldn't clear subscriptions: node doesn't exist anymore"),
193 }
194 }
195
196 pub fn navigate_to(&self, url: &str, target: NavigationTarget) {
205 self.runtime_context
206 .enqueue_native_message(NativeMessage::Navigate(NavigationPatch {
207 url: url.to_string(),
208 target: match target {
209 NavigationTarget::Current => "current",
210 NavigationTarget::New => "new",
211 }
212 .to_string(),
213 }))
214 }
215
216 pub fn dispatch_event(&self, identifier: &'static str) -> Result<(), String> {
218 let component_origin = self
219 .containing_component
220 .upgrade()
221 .ok_or_else(|| "can't dispatch from root component".to_owned())?;
222
223 {
225 let component_origin_instance = borrow!(component_origin.instance_node);
226 let registry = component_origin_instance
227 .base()
228 .handler_registry
229 .as_ref()
230 .ok_or_else(|| "no registry present".to_owned())?;
231 borrow!(registry).handlers.get(identifier).ok_or_else(|| {
232 format!("no registered handler with name \"{}\" exists", identifier)
233 })?;
234 }
235
236 self.runtime_context
238 .queue_custom_event(Rc::clone(&component_origin), identifier);
239
240 Ok(())
241 }
242
243 pub fn set_cursor(&self, cursor: CursorStyle) {
245 self.runtime_context
246 .enqueue_native_message(NativeMessage::SetCursor(SetCursorPatch {
247 cursor: cursor.to_string(),
248 }));
249 }
250
251 pub fn screenshot(&self, id: u32) {
253 self.runtime_context
254 .enqueue_native_message(NativeMessage::Screenshot(ScreenshotPatch {
255 id,
256 scale: None,
257 }));
258 }
259
260 pub fn get_screenshot_map(&self) -> Rc<RefCell<HashMap<u32, ScreenshotData>>> {
262 self.runtime_context.get_screenshot_map()
263 }
264}
265
266fn local_point_with_presentation_transform(
267 node_transform_and_bounds: TransformAndBounds<NodeLocal, Window>,
268 presentation_transform: Affine,
269 point: Point2<Window>,
270) -> Point2<NodeLocal> {
271 node_transform_and_bounds.as_transform().inverse() * (presentation_transform.inverse() * point)
272}
273
274#[cfg(test)]
275mod local_point_tests {
276 use super::*;
277 use pax_runtime_api::math::{Transform2, Vector2};
278
279 #[test]
280 fn local_point_accounts_for_ancestor_scroller_presentation_offset() {
281 let node = TransformAndBounds {
282 transform: Transform2::<NodeLocal, Window>::translate(Vector2::new(50.0, 400.0)),
283 bounds: (200.0, 100.0),
284 };
285 let presentation_transform = Affine::translate((0.0, -300.0));
286
287 let local = local_point_with_presentation_transform(
288 node,
289 presentation_transform,
290 Point2::new(150.0, 140.0),
291 );
292
293 assert!((local.x - 0.5).abs() < 1e-9);
294 assert!((local.y - 0.4).abs() < 1e-9);
295 }
296}
297
298#[cfg(feature = "designtime")]
299impl NodeContext {
300 pub fn submit_userland_source_update(
302 &self,
303 request_id: String,
304 path: String,
305 contents: String,
306 ) -> Result<(), String> {
307 self.designtime
308 .borrow_mut()
309 .send_userland_source_update(UserlandSourceUpdateRequest {
310 request_id,
311 path,
312 contents,
313 })
314 .map_err(|err| err.to_string())
315 }
316
317 pub fn take_userland_source_update_responses(&self) -> Vec<UserlandSourceUpdateResponse> {
319 self.designtime
320 .borrow_mut()
321 .take_userland_source_update_responses()
322 }
323
324 pub fn raycast(&self, point: Point2<Window>, hit_invisible: bool) -> Vec<NodeInterface> {
325 let expanded_nodes = self.runtime_context.get_elements_beneath_ray(
326 self.runtime_context.get_userland_root_expanded_node(),
327 point,
328 false,
329 vec![],
330 hit_invisible,
331 );
332 expanded_nodes
333 .into_iter()
334 .map(Into::<NodeInterface>::into)
335 .collect()
336 }
337
338 pub fn get_nodes_by_global_id(&self, uni: UniqueTemplateNodeIdentifier) -> Vec<NodeInterface> {
339 let expanded_nodes = self.runtime_context.get_expanded_nodes_by_global_ids(&uni);
340 expanded_nodes
341 .into_iter()
342 .map(Into::<NodeInterface>::into)
343 .collect()
344 }
345
346 pub fn get_userland_root_expanded_node(&self) -> Option<NodeInterface> {
347 #[cfg(feature = "designtime")]
348 let expanded_node = self.runtime_context.get_userland_root_expanded_node()?;
349 #[cfg(not(feature = "designtime"))]
350 let expanded_node = self.runtime_context.get_root_expanded_node()?;
351 Some(expanded_node.into())
352 }
353
354 pub fn get_root_expanded_node(&self) -> Option<NodeInterface> {
355 let expanded_node = self.runtime_context.get_root_expanded_node()?;
356 Some(expanded_node.into())
357 }
358
359 pub fn get_nodes_by_id(&self, id: &str) -> Vec<NodeInterface> {
360 let expanded_nodes = self.runtime_context.get_expanded_nodes_by_id(id);
361 expanded_nodes
362 .into_iter()
363 .map(Into::<NodeInterface>::into)
364 .collect()
365 }
366}
367
368impl pax_runtime_api::async_runtime::AsyncScopeSource for NodeContext {
369 fn work_scope(&self) -> Result<AsyncScope, AsyncError> {
370 self.async_scope()
371 }
372}