use std::cell::Ref;
use std::collections::HashMap;
use std::iter;
use std::rc::Rc;
use pax_runtime_api::{
borrow, borrow_mut, use_RefCell, Interpolatable, PaxValue, Property, ToPaxValue, Variable,
};
use_RefCell!();
use crate::api::{Layer, Timeline};
use crate::{
BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstanceNodePtrList,
InstantiationArgs, RuntimeContext,
};
pub struct ComponentInstance {
pub template: InstanceNodePtrList,
pub timeline: Option<Rc<RefCell<Timeline>>>,
base: BaseInstance,
}
impl InstanceNode for ComponentInstance {
fn instantiate(mut args: InstantiationArgs) -> Rc<Self> {
let component_template = args.component_template.take();
let template = component_template.unwrap_or_default();
let base = BaseInstance::new(
args,
InstanceFlags {
invisible_to_slot: false,
invisible_to_raycasting: true,
layer: Layer::DontCare,
is_component: true,
is_slot: false,
},
);
Rc::new(ComponentInstance {
base,
template,
timeline: None,
})
}
fn handle_setup_slot_children(
self: Rc<Self>,
expanded_node: &Rc<ExpandedNode>,
context: &Rc<RuntimeContext>,
) {
if let Some(containing_component) = expanded_node.containing_component.upgrade() {
let env = if let Some(stack_frame) =
ScrollPosition::create_builtin_if_exists(borrow!(expanded_node.properties_scope))
{
expanded_node.stack.push(stack_frame)
} else {
Rc::clone(&expanded_node.stack)
};
let children = borrow!(self.base().get_instance_children());
let children_with_env = children.iter().cloned().zip(iter::repeat(env));
let new_slot_children = containing_component.create_children_detached(
children_with_env,
context,
&Rc::downgrade(expanded_node),
);
*borrow_mut!(expanded_node.expanded_slot_children) = Some(new_slot_children);
}
}
fn handle_mount(
self: Rc<Self>,
expanded_node: &Rc<ExpandedNode>,
context: &Rc<RuntimeContext>,
) {
let mut properties_scope = borrow_mut!(expanded_node.properties_scope);
properties_scope.insert(
"$suspended".to_string(),
Variable::new_from_typed_property(expanded_node.suspended.clone()),
);
let new_env = expanded_node.stack.push(properties_scope.clone());
let children = borrow!(self.template);
let children_with_envs = children.iter().cloned().zip(iter::repeat(new_env));
expanded_node.children.replace_with(Property::new_with_name(
expanded_node.generate_children(
children_with_envs,
context,
&expanded_node.parent_frame,
true,
),
&format!("component (node id: {})", expanded_node.id.0),
));
}
fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, context: &Rc<RuntimeContext>) {
if let Some(slot_children) = borrow_mut!(expanded_node.expanded_slot_children).take() {
for slot_child in slot_children {
slot_child.recurse_unmount(context);
}
}
}
fn resolve_debug(
&self,
f: &mut std::fmt::Formatter,
_expanded_node: Option<&ExpandedNode>,
) -> std::fmt::Result {
f.debug_struct("Component").finish()
}
fn base(&self) -> &BaseInstance {
&self.base
}
fn get_template(&self) -> Option<&InstanceNodePtrList> {
Some(&self.template)
}
}
#[derive(Debug, Clone, Default)]
pub struct ScrollPosition {
pub x: f64,
pub y: f64,
}
impl Interpolatable for ScrollPosition {
fn interpolate(&self, other: &Self, t: f64) -> Self {
ScrollPosition {
x: self.x + (other.x - self.x) * t,
y: self.y + (other.y - self.y) * t,
}
}
}
impl ToPaxValue for ScrollPosition {
fn to_pax_value(self) -> PaxValue {
PaxValue::Object(
vec![
("x".to_string(), self.x.to_pax_value()),
("y".to_string(), self.y.to_pax_value()),
]
.into_iter()
.collect(),
)
}
}
impl ScrollPosition {
pub fn create_builtin_if_exists(
property_scope: Ref<HashMap<String, Variable>>,
) -> Option<HashMap<String, Variable>> {
let scroll_pos_x: Property<f64> = Property::new_from_untyped(
property_scope
.get("scroll_pos_x")?
.get_untyped_property()
.clone(),
);
let scroll_pos_y: Property<f64> = Property::new_from_untyped(
property_scope
.get("scroll_pos_y")?
.get_untyped_property()
.clone(),
);
let deps = [scroll_pos_x.untyped(), scroll_pos_y.untyped()];
let scroll_position = Property::computed(
move || ScrollPosition {
x: scroll_pos_x.get(),
y: scroll_pos_y.get(),
},
&deps,
);
let scroll_position_var = Variable::new_from_typed_property(scroll_position);
let stack_frame = vec![("$scroll_position".to_string(), scroll_position_var)]
.into_iter()
.collect();
Some(stack_frame)
}
}