use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use pax_message::{NativeMessage, OcclusionPatch};
use crate::api::{
CommonProperties, Interpolatable, Layer, NodeContext, OcclusionLayerGen, RenderContext,
TransitionManager,
};
use piet::InterpolationMode;
use crate::declarative_macros::{handle_vtable_update, handle_vtable_update_optional};
use crate::{
Affine, ComponentInstance, ExpressionContext, RuntimeContext, RuntimePropertiesStackFrame,
TransformAndBounds,
};
mod expanded_node;
pub use expanded_node::ExpandedNode;
#[cfg(feature = "designtime")]
use pax_designtime::DesigntimeManager;
pub struct Globals {
pub frames_elapsed: usize,
pub viewport: TransformAndBounds,
#[cfg(feature = "designtime")]
pub designtime: Rc<RefCell<DesigntimeManager>>,
}
pub struct PaxEngine {
pub runtime_context: RuntimeContext,
pub root_node: Rc<ExpandedNode>,
pub z_index_node_cache: Vec<Rc<ExpandedNode>>,
}
pub trait PropertiesComputable {
fn compute_properties(
&mut self,
stack: &Rc<RuntimePropertiesStackFrame>,
table: &ExpressionTable,
);
}
impl PropertiesComputable for CommonProperties {
fn compute_properties(
&mut self,
stack: &Rc<RuntimePropertiesStackFrame>,
table: &ExpressionTable,
) {
handle_vtable_update(table, stack, &mut self.width);
handle_vtable_update(table, stack, &mut self.height);
handle_vtable_update(table, stack, &mut self.transform);
handle_vtable_update_optional(table, stack, self.rotate.as_mut());
handle_vtable_update_optional(table, stack, self.scale_x.as_mut());
handle_vtable_update_optional(table, stack, self.scale_y.as_mut());
handle_vtable_update_optional(table, stack, self.skew_x.as_mut());
handle_vtable_update_optional(table, stack, self.skew_y.as_mut());
handle_vtable_update_optional(table, stack, self.anchor_x.as_mut());
handle_vtable_update_optional(table, stack, self.anchor_y.as_mut());
handle_vtable_update_optional(table, stack, self.x.as_mut());
handle_vtable_update_optional(table, stack, self.y.as_mut());
}
}
pub struct HandlerRegistry {
pub handlers:
HashMap<String, Vec<fn(Rc<RefCell<dyn Any>>, &NodeContext, Option<Box<dyn Any>>)>>,
}
impl Default for HandlerRegistry {
fn default() -> Self {
HandlerRegistry {
handlers: HashMap::new(),
}
}
}
pub struct Renderer<R: piet::RenderContext> {
pub backends: HashMap<String, R>,
pub image_map: HashMap<String, R::Image>,
}
impl<R: piet::RenderContext> Renderer<R> {
pub fn new() -> Self {
Self {
backends: HashMap::new(),
image_map: HashMap::new(),
}
}
pub fn add_context(&mut self, id: &str, context: R) {
self.backends.insert(id.to_owned(), context);
}
pub fn remove_context(&mut self, id: &str) {
self.backends.remove(id);
}
pub fn image_loaded(&self, path: &str) -> bool {
self.image_map.contains_key(path)
}
}
impl<R: piet::RenderContext> crate::api::RenderContext for Renderer<R> {
fn fill(&mut self, layer: &str, path: kurbo::BezPath, brush: &piet_common::PaintBrush) {
self.backends.get_mut(layer).unwrap().fill(path, brush);
}
fn stroke(
&mut self,
layer: &str,
path: kurbo::BezPath,
brush: &piet_common::PaintBrush,
width: f64,
) {
self.backends
.get_mut(layer)
.unwrap()
.stroke(path, brush, width);
}
fn save(&mut self, layer: &str) {
self.backends
.get_mut(layer)
.unwrap()
.save()
.expect("failed to save piet state");
}
fn clip(&mut self, layer: &str, path: kurbo::BezPath) {
self.backends.get_mut(layer).unwrap().clip(path);
}
fn restore(&mut self, layer: &str) {
self.backends
.get_mut(layer)
.unwrap()
.restore()
.expect("failed to restore piet state");
}
fn load_image(&mut self, path: &str, buf: &[u8], width: usize, height: usize) {
let render_context = self.backends.values_mut().next().unwrap();
let img = render_context
.make_image(width, height, buf, piet::ImageFormat::RgbaSeparate)
.expect("image creation successful");
self.image_map.insert(path.to_owned(), img);
}
fn draw_image(&mut self, layer: &str, image_path: &str, rect: kurbo::Rect) {
let Some(img) = self.image_map.get(image_path) else {
return;
};
self.backends
.get_mut(layer)
.unwrap()
.draw_image(img, rect, InterpolationMode::Bilinear);
}
}
pub struct ExpressionTable {
pub table: HashMap<usize, Box<dyn Fn(ExpressionContext) -> Box<dyn Any>>>,
}
impl ExpressionTable {
pub fn compute_vtable_value(
&self,
stack: &Rc<RuntimePropertiesStackFrame>,
vtable_id: usize,
) -> Box<dyn Any> {
if let Some(evaluator) = self.table.get(&vtable_id) {
let stack_frame = Rc::clone(stack);
let ec = ExpressionContext { stack_frame };
(**evaluator)(ec)
} else {
panic!() }
}
pub fn compute_eased_value<T: Clone + Interpolatable>(
&self,
transition_manager: Option<&mut TransitionManager<T>>,
globals: &Globals,
) -> Option<T> {
if let Some(tm) = transition_manager {
if tm.queue.len() > 0 {
let current_transition = tm.queue.get_mut(0).unwrap();
if let None = current_transition.global_frame_started {
current_transition.global_frame_started = Some(globals.frames_elapsed);
}
let progress = (1.0 + globals.frames_elapsed as f64
- current_transition.global_frame_started.unwrap() as f64)
/ (current_transition.duration_frames as f64);
return if progress >= 1.0 {
let new_value = current_transition.curve.interpolate(
¤t_transition.starting_value,
¤t_transition.ending_value,
progress,
);
tm.value = Some(new_value.clone());
tm.queue.pop_front();
self.compute_eased_value(Some(tm), globals)
} else {
let new_value = current_transition.curve.interpolate(
¤t_transition.starting_value,
¤t_transition.ending_value,
progress,
);
tm.value = Some(new_value.clone());
tm.value.clone()
};
} else {
return tm.value.clone();
}
}
None
}
}
impl PaxEngine {
#[cfg(not(feature = "designtime"))]
pub fn new(
main_component_instance: Rc<ComponentInstance>,
expression_table: ExpressionTable,
logger: crate::api::PlatformSpecificLogger,
viewport_size: (f64, f64),
) -> Self {
crate::api::register_logger(logger);
let globals = Globals {
frames_elapsed: 0,
viewport: TransformAndBounds {
transform: Affine::default(),
bounds: viewport_size,
},
};
let mut runtime_context = RuntimeContext::new(expression_table, globals);
let root_node = ExpandedNode::root(main_component_instance, &mut runtime_context);
PaxEngine {
runtime_context,
root_node,
z_index_node_cache: Vec::new(),
}
}
#[cfg(feature = "designtime")]
pub fn new_with_designtime(
main_component_instance: Rc<ComponentInstance>,
expression_table: ExpressionTable,
logger: crate::api::PlatformSpecificLogger,
viewport_size: (f64, f64),
designtime: Rc<RefCell<DesigntimeManager>>,
) -> Self {
crate::api::register_logger(logger);
let globals = Globals {
frames_elapsed: 0,
viewport: TransformAndBounds {
transform: Affine::default(),
bounds: viewport_size,
},
designtime: designtime.clone(),
};
let mut runtime_context = RuntimeContext::new(expression_table, globals);
let root_node = ExpandedNode::root(main_component_instance, &mut runtime_context);
PaxEngine {
runtime_context,
root_node,
z_index_node_cache: Vec::new(),
}
}
#[cfg(feature = "designtime")]
pub fn update_root_node(&mut self, main_component_instance: Rc<ComponentInstance>) {
self.root_node
.clone()
.recurse_unmount(&mut self.runtime_context);
self.root_node = ExpandedNode::root(main_component_instance, &mut self.runtime_context);
}
pub fn tick(&mut self) -> Vec<NativeMessage> {
self.root_node.recurse_update(&mut self.runtime_context);
{
self.z_index_node_cache.clear();
fn assign_z_indicies(n: &Rc<ExpandedNode>, state: &mut Vec<Rc<ExpandedNode>>) {
state.push(Rc::clone(&n));
}
self.root_node
.recurse_visit_postorder(&assign_z_indicies, &mut self.z_index_node_cache);
}
let mut occlusion_ind = OcclusionLayerGen::new(None);
for node in self.z_index_node_cache.iter() {
let layer = node.instance_node.base().flags().layer;
occlusion_ind.update_z_index(layer);
let new_occlusion_ind = occlusion_ind.get_level();
let mut curr_occlusion_ind = node.occlusion_id.borrow_mut();
if layer == Layer::Native && *curr_occlusion_ind != new_occlusion_ind {
self.runtime_context.enqueue_native_message(
pax_message::NativeMessage::OcclusionUpdate(OcclusionPatch {
id_chain: node.id_chain.clone(),
z_index: new_occlusion_ind,
}),
);
}
*curr_occlusion_ind = new_occlusion_ind;
}
self.runtime_context.take_native_messages()
}
pub fn render(&mut self, rcs: &mut dyn RenderContext) {
self.root_node
.recurse_render(&mut self.runtime_context, rcs);
}
pub fn get_topmost_element_beneath_ray(&self, ray: (f64, f64)) -> Option<Rc<ExpandedNode>> {
let mut ret: Option<Rc<ExpandedNode>> = None;
for node in self.z_index_node_cache.iter().rev().skip(1) {
if node.ray_cast_test(&ray) {
let mut ancestral_clipping_bounds_are_satisfied = true;
let mut parent: Option<Rc<ExpandedNode>> =
node.parent_expanded_node.borrow().upgrade();
loop {
if let Some(unwrapped_parent) = parent {
if let Some(_) = unwrapped_parent.get_clipping_size() {
ancestral_clipping_bounds_are_satisfied =
(*unwrapped_parent).ray_cast_test(&ray);
break;
}
parent = unwrapped_parent.parent_expanded_node.borrow().upgrade();
} else {
break;
}
}
if ancestral_clipping_bounds_are_satisfied {
ret = Some(Rc::clone(&node));
break;
}
}
}
ret
}
pub fn get_expanded_node(&self, id: u32) -> Option<&Rc<ExpandedNode>> {
self.runtime_context.lookup.get(&id)
}
pub fn get_focused_element(&self) -> Option<Rc<ExpandedNode>> {
let (x, y) = self.runtime_context.globals().viewport.bounds;
self.get_topmost_element_beneath_ray((x / 2.0, y / 2.0))
}
pub fn set_viewport_size(&mut self, new_viewport_size: (f64, f64)) {
self.runtime_context.globals_mut().viewport.bounds = new_viewport_size;
}
}