use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::iter;
use std::rc::Rc;
use pax_message::{NativeMessage, OcclusionPatch};
use crate::api::{
ArgsKeyDown, ArgsKeyPress, ArgsKeyUp, CommonProperties, Interpolatable, Layer, NodeContext,
OcclusionLayerGen, RenderContext, TransitionManager,
};
use piet::InterpolationMode;
use crate::declarative_macros::{handle_vtable_update, handle_vtable_update_optional};
use crate::{
ComponentInstance, ExpressionContext, InstanceNode, RuntimeContext,
RuntimePropertiesStackFrame, TransformAndBounds,
};
pub mod node_interface;
mod expanded_node;
pub use expanded_node::ExpandedNode;
#[cfg(feature = "designtime")]
use pax_designtime::DesigntimeManager;
#[cfg_attr(debug_assertions, derive(Debug))]
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 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 enum HandlerLocation {
Inline,
Component,
}
pub struct Handler {
pub function: fn(Rc<RefCell<dyn Any>>, &NodeContext, Option<Box<dyn Any>>),
pub location: HandlerLocation,
}
impl Handler {
pub fn new_inline_handler(
function: fn(Rc<RefCell<dyn Any>>, &NodeContext, Option<Box<dyn Any>>),
) -> Self {
Handler {
function,
location: HandlerLocation::Inline,
}
}
pub fn new_component_handler(
function: fn(Rc<RefCell<dyn Any>>, &NodeContext, Option<Box<dyn Any>>),
) -> Self {
Handler {
function,
location: HandlerLocation::Component,
}
}
}
pub struct HandlerRegistry {
pub handlers: HashMap<String, Vec<Handler>>,
}
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>>>,
}
#[cfg(debug_assertions)]
impl Debug for ExpressionTable {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
unimplemented!()
}
}
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,
viewport_size: (f64, f64),
) -> Self {
use crate::math::Transform2;
let globals = Globals {
frames_elapsed: 0,
viewport: TransformAndBounds {
transform: Transform2::identity(),
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,
}
}
#[cfg(feature = "designtime")]
pub fn new_with_designtime(
main_component_instance: Rc<ComponentInstance>,
expression_table: ExpressionTable,
viewport_size: (f64, f64),
designtime: Rc<RefCell<DesigntimeManager>>,
) -> Self {
use crate::math::Transform2;
let globals = Globals {
frames_elapsed: 0,
viewport: TransformAndBounds {
transform: Transform2::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,
}
}
pub fn replace_by_id(&mut self, id: &str, new_instance: Rc<dyn InstanceNode>) {
let found_nodes = self.runtime_context.get_expanded_nodes_by_id(id);
if found_nodes.len() > 0 {
let node = found_nodes.first().unwrap();
let parent = node.parent_expanded_node.borrow().upgrade();
if let Some(p) = parent {
let env = Rc::clone(&p.stack);
let new_templates = vec![new_instance.clone()]
.into_iter()
.zip(iter::repeat(env));
p.set_children(new_templates, &mut self.runtime_context);
self.root_node.recurse_update(&mut self.runtime_context);
return;
}
}
}
pub fn tick(&mut self) -> Vec<NativeMessage> {
self.root_node.recurse_update(&mut self.runtime_context);
{
self.runtime_context.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.runtime_context.z_index_node_cache,
);
}
let mut occlusion_ind = OcclusionLayerGen::new(None);
for node in self.runtime_context.z_index_node_cache.clone().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_expanded_node(&self, id: u32) -> Option<&Rc<ExpandedNode>> {
self.runtime_context.node_cache.get(&id)
}
pub fn set_viewport_size(&mut self, new_viewport_size: (f64, f64)) {
self.runtime_context.globals_mut().viewport.bounds = new_viewport_size;
}
pub fn global_dispatch_key_down(&self, args: ArgsKeyDown) {
self.root_node.recurse_visit_postorder(
&|expanded_node, _| {
expanded_node.dispatch_key_down(
args.clone(),
self.runtime_context.globals(),
&self.runtime_context,
)
},
&mut (),
);
}
pub fn global_dispatch_key_up(&self, args: ArgsKeyUp) {
self.root_node.recurse_visit_postorder(
&|expanded_node, _| {
expanded_node.dispatch_key_up(
args.clone(),
self.runtime_context.globals(),
&self.runtime_context,
)
},
&mut (),
);
}
pub fn global_dispatch_key_press(&self, args: ArgsKeyPress) {
self.root_node.recurse_visit_postorder(
&|expanded_node, _| {
expanded_node.dispatch_key_press(
args.clone(),
self.runtime_context.globals(),
&self.runtime_context,
)
},
&mut (),
);
}
}