1use crate::{
2 api::Property, ExpandedNodeIdentifier, RuntimePropertiesStackFrame, TransformAndBounds,
3};
4use_RefCell!();
5use std::collections::HashMap;
6use std::rc::Rc;
7
8use kurbo::Affine;
9use pax_message::NativeMessage;
10use pax_runtime_api::{
11 pax_value::PaxAny, use_RefCell, Event, Focus, SelectStart, Variable, Window, OS,
12};
13
14use crate::api::{KeyDown, KeyPress, KeyUp, NodeContext, RenderContext};
15use piet::InterpolationMode;
16
17use crate::{ComponentInstance, RuntimeContext};
18use pax_runtime_api::Platform;
19use std::time::Instant;
20
21pub mod node_interface;
22pub mod occlusion;
23
24mod expanded_node;
31pub use expanded_node::ExpandedNode;
32
33use self::node_interface::NodeLocal;
34
35#[cfg(feature = "designtime")]
36use {
37 crate::InstanceNode,
38 pax_designtime::DesigntimeManager,
39 pax_runtime_api::{borrow, borrow_mut},
40};
41
42#[derive(Clone)]
43pub struct Globals {
44 pub frames_elapsed: Property<u64>,
45 pub viewport: Property<TransformAndBounds<NodeLocal, Window>>,
46 pub platform: Platform,
47 pub os: OS,
48 #[cfg(feature = "designtime")]
49 pub designtime: Rc<RefCell<DesigntimeManager>>,
50 pub get_elapsed_millis: Rc<dyn Fn() -> u128>,
51}
52
53impl Globals {
54 pub fn stack_frame(&self) -> Rc<RuntimePropertiesStackFrame> {
55 let mobile = Property::new(self.os.is_mobile());
56 let desktop = Property::new(self.os.is_desktop());
57
58 let cloned_viewport = self.viewport.clone();
59 let deps = [cloned_viewport.untyped()];
60 let viewport = Property::computed(
61 move || {
62 let viewport = cloned_viewport.get();
63 pax_runtime_api::Viewport {
64 width: viewport.bounds.0,
65 height: viewport.bounds.1,
66 }
67 },
68 &deps,
69 );
70
71 let mobile_var = Variable::new_from_typed_property(mobile);
72 let desktop_var = Variable::new_from_typed_property(desktop);
73 let viewport_var = Variable::new_from_typed_property(viewport);
74 let frames_elapsed_var = Variable::new_from_typed_property(self.frames_elapsed.clone());
75
76 let global_scope = vec![
77 ("$mobile".to_string(), mobile_var),
78 ("$desktop".to_string(), desktop_var),
79 ("$viewport".to_string(), viewport_var),
80 ("$frames_elapsed".to_string(), frames_elapsed_var),
81 ]
82 .into_iter()
83 .collect();
84
85 let root_env = RuntimePropertiesStackFrame::new(global_scope);
86 root_env
87 }
88}
89
90impl std::fmt::Debug for Globals {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("Globals")
93 .field("frames_elapsed", &self.frames_elapsed)
94 .field("viewport", &self.viewport)
95 .finish_non_exhaustive()
96 }
97}
98
99pub struct PaxEngine {
101 pub runtime_context: Rc<RuntimeContext>,
102 pub root_expanded_node: Rc<ExpandedNode>,
103}
104
105pub enum HandlerLocation {
106 Inline,
107 Component,
108}
109
110pub struct Handler {
111 pub function: fn(Rc<RefCell<PaxAny>>, &NodeContext, Option<PaxAny>),
112 pub location: HandlerLocation,
113}
114
115impl Handler {
116 pub fn new_inline_handler(
117 function: fn(Rc<RefCell<PaxAny>>, &NodeContext, Option<PaxAny>),
118 ) -> Self {
119 Handler {
120 function,
121 location: HandlerLocation::Inline,
122 }
123 }
124
125 pub fn new_component_handler(
126 function: fn(Rc<RefCell<PaxAny>>, &NodeContext, Option<PaxAny>),
127 ) -> Self {
128 Handler {
129 function,
130 location: HandlerLocation::Component,
131 }
132 }
133}
134
135pub struct HandlerRegistry {
136 pub handlers: HashMap<String, Vec<Handler>>,
137}
138
139impl Default for HandlerRegistry {
140 fn default() -> Self {
141 HandlerRegistry {
142 handlers: HashMap::new(),
143 }
144 }
145}
146
147struct ImgData<R: piet::RenderContext> {
148 img: R::Image,
149 size: (usize, usize),
150}
151
152pub struct Renderer<R: piet::RenderContext> {
153 backends: Vec<R>,
154 image_map: HashMap<String, ImgData<R>>,
155}
156
157impl<R: piet::RenderContext> Renderer<R> {
158 pub fn new() -> Self {
159 Self {
160 backends: Vec::new(),
161 image_map: HashMap::new(),
162 }
163 }
164
165 pub fn add_context(&mut self, id: usize, context: R) {
166 self.backends.insert(id.to_owned(), context);
167 }
168
169 pub fn remove_context(&mut self, id: usize) {
170 self.backends.remove(id);
171 }
172
173 pub fn image_loaded(&self, path: &str) -> bool {
174 self.image_map.contains_key(path)
175 }
176}
177
178impl<R: piet::RenderContext> crate::api::RenderContext for Renderer<R> {
179 fn fill(&mut self, layer: usize, path: kurbo::BezPath, brush: &piet_common::PaintBrush) {
180 if let Some(layer) = self.backends.get_mut(layer) {
181 layer.fill(path, brush);
182 }
183 }
184
185 fn stroke(
186 &mut self,
187 layer: usize,
188 path: kurbo::BezPath,
189 brush: &piet_common::PaintBrush,
190 width: f64,
191 ) {
192 if let Some(layer) = self.backends.get_mut(layer) {
193 layer.stroke(path, brush, width);
194 }
195 }
196
197 fn save(&mut self, layer: usize) {
198 if let Some(layer) = self.backends.get_mut(layer) {
199 let _ = layer.save();
200 }
201 }
202
203 fn transform(&mut self, layer: usize, affine: Affine) {
204 if let Some(layer) = self.backends.get_mut(layer) {
205 layer.transform(affine);
206 }
207 }
208
209 fn clip(&mut self, layer: usize, path: kurbo::BezPath) {
210 if let Some(layer) = self.backends.get_mut(layer) {
211 layer.clip(path);
212 }
213 }
214
215 fn restore(&mut self, layer: usize) {
216 if let Some(layer) = self.backends.get_mut(layer) {
217 let _ = layer.restore();
218 }
219 }
220
221 fn load_image(&mut self, path: &str, buf: &[u8], width: usize, height: usize) {
222 let render_context = self.backends.first_mut().unwrap();
224 let img = render_context
225 .make_image(width, height, buf, piet::ImageFormat::RgbaSeparate)
226 .expect("image creation successful");
227 self.image_map.insert(
228 path.to_owned(),
229 ImgData {
230 img,
231 size: (width, height),
232 },
233 );
234 }
235
236 fn get_image_size(&mut self, image_path: &str) -> Option<(usize, usize)> {
237 self.image_map.get(image_path).map(|img| (img.size))
238 }
239
240 fn draw_image(&mut self, layer: usize, image_path: &str, rect: kurbo::Rect) {
241 let Some(data) = self.image_map.get(image_path) else {
242 return;
243 };
244 if let Some(layer) = self.backends.get_mut(layer) {
245 layer.draw_image(&data.img, rect, InterpolationMode::Bilinear);
246 }
247 }
248
249 fn layers(&self) -> usize {
250 self.backends.len()
251 }
252}
253
254impl PaxEngine {
258 #[cfg(not(feature = "designtime"))]
259 pub fn new(
260 main_component_instance: Rc<ComponentInstance>,
261 viewport_size: (f64, f64),
262 platform: Platform,
263 os: OS,
264 get_elapsed_millis: Box<dyn Fn() -> u128>,
265 ) -> Self {
266 use crate::api::math::Transform2;
267 use pax_runtime_api::{properties, Functions};
268 Functions::register_all_functions();
269
270 let frames_elapsed = Property::new(0);
271 properties::register_time(&frames_elapsed);
272 let globals = Globals {
273 frames_elapsed,
274 viewport: Property::new(TransformAndBounds {
275 transform: Transform2::identity(),
276 bounds: viewport_size,
277 }),
278 platform,
279 os,
280 get_elapsed_millis: Rc::from(get_elapsed_millis),
281 };
282 let runtime_context = Rc::new(RuntimeContext::new(globals));
283 let root_node =
284 ExpandedNode::initialize_root(Rc::clone(&main_component_instance), &runtime_context);
285 runtime_context.register_root_expanded_node(&root_node);
286
287 PaxEngine {
288 runtime_context,
289 root_expanded_node: root_node,
290 }
291 }
292
293 #[cfg(feature = "designtime")]
294 pub fn new_with_designtime(
295 designer_main_component_instance: Rc<ComponentInstance>,
296 userland_main_component_instance: Rc<ComponentInstance>,
297 viewport_size: (f64, f64),
298 designtime: Rc<RefCell<DesigntimeManager>>,
299 platform: Platform,
300 os: OS,
301 get_elapsed_millis: Box<dyn Fn() -> u128>,
302 ) -> Self {
303 use pax_runtime_api::{math::Transform2, properties, Functions};
304 Functions::register_all_functions();
305
306 let frames_elapsed = Property::new(0);
307 properties::register_time(&frames_elapsed);
308 let globals = Globals {
309 frames_elapsed,
310 viewport: Property::new(TransformAndBounds {
311 transform: Transform2::identity(),
312 bounds: viewport_size,
313 }),
314 platform,
315 os,
316 designtime: designtime.clone(),
317 get_elapsed_millis: Rc::from(get_elapsed_millis),
318 };
319
320 let mut runtime_context = Rc::new(RuntimeContext::new(
321 globals,
322 userland_main_component_instance,
323 ));
324
325 let root_expanded_node = ExpandedNode::initialize_root(
326 Rc::clone(&designer_main_component_instance),
327 &mut runtime_context,
328 );
329 runtime_context.register_root_expanded_node(&root_expanded_node);
330
331 PaxEngine {
332 runtime_context,
333 root_expanded_node,
334 }
335 }
336
337 #[cfg(feature = "designtime")]
338 pub fn partial_update_expanded_node(&mut self, new_instance: Rc<dyn InstanceNode>) {
339 let unique_id = new_instance
341 .base()
342 .template_node_identifier
343 .clone()
344 .expect("new instance node has unique identifier");
345
346 let nodes = self
347 .runtime_context
348 .get_expanded_nodes_by_global_ids(&unique_id);
349 for node in nodes {
350 node.recreate_with_new_data(new_instance.clone(), &self.runtime_context);
351 }
352 }
353
354 #[cfg(feature = "designtime")]
355 pub fn full_reload_userland(&mut self, new_userland_instance: Rc<dyn InstanceNode>) {
356 let node = borrow!(self.runtime_context.userland_root_expanded_node)
357 .as_ref()
358 .map(Rc::clone)
359 .unwrap();
360 *borrow_mut!(self.runtime_context.userland_frame_instance_node) =
361 Rc::clone(&new_userland_instance);
362 node.fully_recreate_with_new_data(new_userland_instance.clone(), &self.runtime_context);
363 }
364
365 pub fn tick(&mut self) -> Vec<NativeMessage> {
378 self.root_expanded_node
383 .recurse_update(&mut self.runtime_context);
384
385 let ctx = &self.runtime_context;
386 occlusion::update_node_occlusion(&self.root_expanded_node, ctx);
387 let time = &ctx.globals().frames_elapsed;
388 time.set(time.get() + 1);
389
390 ctx.flush_custom_events().unwrap();
391 let native_messages = ctx.take_native_messages();
392 native_messages
393 }
394
395 pub fn render(&mut self, rcs: &mut dyn RenderContext) {
396 self.root_expanded_node
399 .recurse_render_queue(&mut self.runtime_context, rcs);
400 self.runtime_context.recurse_flush_queued_renders(rcs);
401 }
402
403 pub fn get_expanded_node(&self, id: ExpandedNodeIdentifier) -> Option<Rc<ExpandedNode>> {
404 let val = self.runtime_context.get_expanded_node_by_eid(id).clone();
405 val.map(|v| (v.clone()))
406 }
407
408 pub fn set_viewport_size(&mut self, new_viewport_size: (f64, f64)) {
410 self.runtime_context.edit_globals(|globals| {
411 globals
412 .viewport
413 .update(|t_and_b| t_and_b.bounds = new_viewport_size);
414 });
415 }
416
417 pub fn global_dispatch_focus(&self, args: Focus) -> bool {
418 let mut prevent_default = false;
419 self.root_expanded_node
420 .recurse_visit_postorder(&mut |expanded_node| {
421 prevent_default |= expanded_node.dispatch_focus(
422 Event::new(args.clone()),
423 &self.runtime_context.globals(),
424 &self.runtime_context,
425 );
426 });
427 prevent_default
428 }
429
430 pub fn global_dispatch_select_start(&self, args: SelectStart) -> bool {
431 let mut prevent_default = false;
432 self.root_expanded_node
433 .recurse_visit_postorder(&mut |expanded_node| {
434 prevent_default |= expanded_node.dispatch_select_start(
435 Event::new(args.clone()),
436 &self.runtime_context.globals(),
437 &self.runtime_context,
438 );
439 });
440 prevent_default
441 }
442
443 pub fn global_dispatch_key_down(&self, args: KeyDown) -> bool {
444 let mut prevent_default = false;
445 self.root_expanded_node
446 .recurse_visit_postorder(&mut |expanded_node| {
447 prevent_default |= expanded_node.dispatch_key_down(
448 Event::new(args.clone()),
449 &self.runtime_context.globals(),
450 &self.runtime_context,
451 );
452 });
453 prevent_default
454 }
455
456 pub fn global_dispatch_key_up(&self, args: KeyUp) -> bool {
457 let mut prevent_default = false;
458 self.root_expanded_node
459 .recurse_visit_postorder(&mut |expanded_node| {
460 prevent_default |= expanded_node.dispatch_key_up(
461 Event::new(args.clone()),
462 &self.runtime_context.globals(),
463 &self.runtime_context,
464 );
465 });
466 prevent_default
467 }
468
469 pub fn global_dispatch_key_press(&self, args: KeyPress) -> bool {
470 let mut prevent_default = false;
471 self.root_expanded_node
472 .recurse_visit_postorder(&mut |expanded_node| {
473 prevent_default |= expanded_node.dispatch_key_press(
474 Event::new(args.clone()),
475 &self.runtime_context.globals(),
476 &self.runtime_context,
477 );
478 });
479 prevent_default
480 }
481}