use std::collections::{BTreeMap, HashMap};
use std::iter;
use std::rc::Rc;
use_RefCell!();
use crate::api::{math::Point2, CommonProperties, RenderContext, SceneAmbientLight, SceneLight};
use pax_manifest::cartridge_generation::ComponentTransitionConfig;
use pax_manifest::{
SettingsBlockElement, TemplateNodeSelectorInfo, TypeId, UniqueTemplateNodeIdentifier,
};
use pax_message::NativeInterrupt;
use pax_runtime_api::pax_value::PaxAny;
use pax_runtime_api::{borrow, use_RefCell, Variable};
use piet::{Color, StrokeStyle};
use crate::api::{Layer, Scroll, Window};
use crate::{
create_new_common_properties, create_new_common_properties_from_columns,
property_columns_from_defined_properties, update_existing_common_properties,
update_existing_common_properties_from_columns, ErasedComponentDescriptor, ExpandedNode,
HandlerRegistry, ReceivedChildrenSource, RuntimeContext, RuntimePropertiesStackFrame,
TemplatePropertyPlan,
};
use pax_manifest::ValueDefinition;
pub type InstanceNodePtr = Rc<dyn InstanceNode>;
pub type InstanceNodePtrList = RefCell<Vec<InstanceNodePtr>>;
pub type CommonPropertiesFactory = Box<
dyn Fn(
Rc<RuntimePropertiesStackFrame>,
Option<Rc<ExpandedNode>>,
) -> Option<Rc<RefCell<CommonProperties>>>,
>;
pub type PropertiesFactory = Box<
dyn Fn(
Rc<RuntimePropertiesStackFrame>,
Option<Rc<ExpandedNode>>,
) -> Option<Rc<RefCell<PaxAny>>>,
>;
pub type PropertiesScopeFactory = Box<dyn Fn(Rc<RefCell<PaxAny>>) -> HashMap<String, Variable>>;
pub enum CommonPropertiesInit {
Default,
Inline {
defined_properties: BTreeMap<String, ValueDefinition>,
},
Template(Rc<TemplatePropertyPlan>),
Factory(CommonPropertiesFactory),
}
impl CommonPropertiesInit {
fn requires_pre_node_binding(&self) -> bool {
match self {
Self::Factory(_) => true,
Self::Inline { defined_properties } => defined_properties
.values()
.any(crate::value_uses_local_property_scope),
Self::Template(plan) => plan.requires_pre_node_binding,
Self::Default => false,
}
}
pub(crate) fn allocate(
&self,
stack: Rc<RuntimePropertiesStackFrame>,
) -> Rc<RefCell<CommonProperties>> {
match self {
Self::Factory(factory) => {
factory(stack, None).expect("common properties factory must allocate properties")
}
_ => Rc::new(RefCell::new(CommonProperties::default())),
}
}
pub fn materialize(
&self,
stack_frame: Rc<RuntimePropertiesStackFrame>,
expanded_node: Option<Rc<ExpandedNode>>,
) -> Option<Rc<RefCell<CommonProperties>>> {
match self {
CommonPropertiesInit::Default => expanded_node
.is_none()
.then(|| Rc::new(RefCell::new(CommonProperties::default()))),
CommonPropertiesInit::Inline { defined_properties } => {
if let Some(expanded_node) = expanded_node {
update_existing_common_properties(
&expanded_node,
defined_properties,
&stack_frame,
);
None
} else {
Some(create_new_common_properties(
defined_properties,
&stack_frame,
))
}
}
CommonPropertiesInit::Template(plan) => {
let columns = plan.resolve(expanded_node.as_ref());
if let Some(node) = expanded_node {
update_existing_common_properties_from_columns(&node, &columns, &stack_frame);
None
} else {
Some(create_new_common_properties_from_columns(
&columns,
&stack_frame,
))
}
}
CommonPropertiesInit::Factory(factory) => factory(stack_frame, expanded_node),
}
}
}
pub enum PropertiesInit {
DescriptorDefault(&'static ErasedComponentDescriptor),
DescriptorInline {
descriptor: &'static ErasedComponentDescriptor,
defined_properties: BTreeMap<String, ValueDefinition>,
},
Template {
descriptor: &'static ErasedComponentDescriptor,
plan: Rc<TemplatePropertyPlan>,
},
Factory(PropertiesFactory),
}
impl PropertiesInit {
fn requires_pre_node_binding(&self) -> bool {
match self {
Self::Factory(_) => true,
Self::DescriptorInline {
defined_properties, ..
} => defined_properties
.values()
.any(crate::value_uses_local_property_scope),
Self::Template { plan, .. } => plan.requires_pre_node_binding,
Self::DescriptorDefault(_) => false,
}
}
pub(crate) fn allocate(&self, stack: Rc<RuntimePropertiesStackFrame>) -> Rc<RefCell<PaxAny>> {
match self {
Self::DescriptorDefault(descriptor)
| Self::DescriptorInline { descriptor, .. }
| Self::Template { descriptor, .. } => {
Rc::new(RefCell::new((descriptor.create_properties)()))
}
Self::Factory(factory) => {
factory(stack, None).expect("properties factory must allocate properties")
}
}
}
pub fn materialize(
&self,
stack_frame: Rc<RuntimePropertiesStackFrame>,
expanded_node: Option<Rc<ExpandedNode>>,
) -> Option<Rc<RefCell<PaxAny>>> {
match self {
PropertiesInit::DescriptorDefault(descriptor) => expanded_node
.is_none()
.then(|| Rc::new(RefCell::new((descriptor.create_properties)()))),
PropertiesInit::DescriptorInline {
descriptor,
defined_properties,
} => {
if let Some(expanded_node) = expanded_node {
let outer_ref = expanded_node.properties.borrow();
let rc = Rc::clone(&outer_ref);
let mut inner_ref = (*rc).borrow_mut();
(descriptor.apply_defined_properties)(
descriptor.typed_descriptor,
&mut inner_ref,
&property_columns_from_defined_properties(defined_properties),
&stack_frame,
);
None
} else {
let mut properties = (descriptor.create_properties)();
(descriptor.apply_defined_properties)(
descriptor.typed_descriptor,
&mut properties,
&property_columns_from_defined_properties(defined_properties),
&stack_frame,
);
Some(Rc::new(RefCell::new(properties)))
}
}
PropertiesInit::Template { descriptor, plan } => {
let columns = plan.resolve(expanded_node.as_ref());
if let Some(node) = expanded_node {
let properties = Rc::clone(&node.properties.borrow());
(descriptor.apply_defined_properties)(
descriptor.typed_descriptor,
&mut properties.borrow_mut(),
&columns,
&stack_frame,
);
None
} else {
let mut properties = (descriptor.create_properties)();
(descriptor.apply_defined_properties)(
descriptor.typed_descriptor,
&mut properties,
&columns,
&stack_frame,
);
Some(Rc::new(RefCell::new(properties)))
}
}
PropertiesInit::Factory(factory) => factory(stack_frame, expanded_node),
}
}
}
pub enum PropertiesScopeInit {
None,
Descriptor(&'static ErasedComponentDescriptor),
Factory(PropertiesScopeFactory),
}
impl PropertiesScopeInit {
pub fn build(&self, properties: Rc<RefCell<PaxAny>>) -> HashMap<String, Variable> {
match self {
PropertiesScopeInit::None => HashMap::new(),
PropertiesScopeInit::Descriptor(descriptor) => {
let props_ref = borrow!(properties.as_ref());
(descriptor.build_property_scope)(descriptor.typed_descriptor, &props_ref)
}
PropertiesScopeInit::Factory(factory) => factory(properties),
}
}
}
pub struct InstantiationArgs {
pub prototypical_common_properties: CommonPropertiesInit,
pub prototypical_properties: PropertiesInit,
pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
pub children: Option<InstanceNodePtrList>,
pub component_template: Option<InstanceNodePtrList>,
pub component_settings: Option<Vec<SettingsBlockElement>>,
pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
pub template_node_type_id: Option<TypeId>,
pub template_node_selector_info: Option<TemplateNodeSelectorInfo>,
pub transition_config: ComponentTransitionConfig,
pub properties_scope: PropertiesScopeInit,
}
pub struct ReusableInstanceNodeArgs {
pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
pub children: InstanceNodePtrList,
pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
pub template_node_type_id: Option<TypeId>,
pub template_node_selector_info: Option<TemplateNodeSelectorInfo>,
}
impl ReusableInstanceNodeArgs {
pub fn new(base: &BaseInstance) -> Self {
ReusableInstanceNodeArgs {
handler_registry: base.handler_registry.clone(),
children: base.instance_children.clone(),
template_node_identifier: base.template_node_identifier.clone(),
template_node_type_id: base.template_node_type_id.clone(),
template_node_selector_info: base.template_node_selector_info.clone(),
}
}
}
#[derive(Clone)]
pub enum NodeType {
Component,
Primitive,
}
impl std::fmt::Debug for dyn InstanceNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.resolve_debug(f, None)
}
}
pub trait InstanceNode {
fn base(&self) -> &BaseInstance;
fn instantiate(args: InstantiationArgs) -> Rc<Self>
where
Self: Sized;
fn resolve_debug(
&self,
f: &mut std::fmt::Formatter,
expanded_node: Option<&ExpandedNode>,
) -> std::fmt::Result;
fn requires_non_reactive_update(&self, _expanded_node: &ExpandedNode) -> bool {
false
}
fn resolve_scene_light(
&self,
_expanded_node: &ExpandedNode,
_context: &RuntimeContext,
) -> Option<SceneLight> {
None
}
fn resolve_scene_ambient_light(
&self,
_expanded_node: &ExpandedNode,
_context: &RuntimeContext,
) -> Option<SceneAmbientLight> {
None
}
fn establishes_light_frame(&self) -> bool {
false
}
fn update(self: Rc<Self>, _expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {}
#[allow(unused_variables)]
fn handle_pre_render(
&self,
expanded_node: &ExpandedNode,
context: &Rc<RuntimeContext>,
rcs: &mut dyn RenderContext,
) {
}
#[allow(unused_variables)]
fn render(
&self,
expanded_node: &ExpandedNode,
context: &Rc<RuntimeContext>,
rcs: &mut dyn RenderContext,
) {
}
#[allow(unused_variables)]
fn handle_post_render(
&self,
expanded_node: &ExpandedNode,
context: &Rc<RuntimeContext>,
rcs: &mut dyn RenderContext,
) {
}
#[allow(unused_variables)]
fn handle_mount(
self: Rc<Self>,
expanded_node: &Rc<ExpandedNode>,
context: &Rc<RuntimeContext>,
) {
let env = Rc::clone(&expanded_node.stack);
let children = borrow!(self.base().get_instance_children());
let children_with_envs = children.iter().cloned().zip(iter::repeat(env));
let new_children = expanded_node.generate_children(
children_with_envs,
context,
&expanded_node.parent_frame,
true,
);
expanded_node.children.set(new_children);
}
#[allow(unused_variables)]
fn handle_unmount(&self, expanded_node: &Rc<ExpandedNode>, context: &Rc<RuntimeContext>) {
}
#[allow(unused_variables)]
fn handle_scroll(&self, args_scroll: Scroll) {
}
fn get_template(&self) -> Option<&InstanceNodePtrList> {
None
}
fn clips_content(&self, _expanded_node: &ExpandedNode) -> bool {
false
}
fn ray_cast_test(&self, expanded_node: &ExpandedNode, ray: Point2<Window>) -> bool {
let t_and_b = expanded_node.transform_and_bounds.get();
let inverted_transform = t_and_b.transform.inverse();
let transformed_ray = inverted_transform * ray;
let (width, height) = t_and_b.bounds;
transformed_ray.x > 0.0
&& transformed_ray.y > 0.0
&& transformed_ray.x < width
&& transformed_ray.y < height
}
fn scrolls_content(&self, _expanded_node: &ExpandedNode) -> bool {
false
}
fn property_requires_occlusion_recompute(&self, _property_name: &str) -> bool {
true
}
fn materializes_native_surface(&self, _expanded_node: &ExpandedNode) -> bool {
false
}
fn materializes_native_surface_before_children(&self, _expanded_node: &ExpandedNode) -> bool {
false
}
fn resolve_scroll_offset(&self, _expanded_node: &ExpandedNode) -> Option<(f64, f64)> {
None
}
fn received_children_source(&self) -> ReceivedChildrenSource {
if self.base().flags().is_component {
ReceivedChildrenSource::Projected
} else {
ReceivedChildrenSource::Owned
}
}
fn handle_native_interrupt(
&self,
_expanded_node: &Rc<ExpandedNode>,
_interrupt: &NativeInterrupt,
) {
}
fn handle_control_flow_node_expansion(
self: Rc<Self>,
_expanded_node: &Rc<ExpandedNode>,
_context: &Rc<RuntimeContext>,
) {
}
fn handle_setup_projected_children(
self: Rc<Self>,
_expanded_node: &Rc<ExpandedNode>,
_context: &Rc<RuntimeContext>,
) {
}
fn resolve_coverage_path(&self, _expanded_node: &ExpandedNode) -> Option<kurbo::BezPath> {
None
}
fn resolve_alpha_mask_paints(
&self,
_expanded_node: &ExpandedNode,
) -> Vec<pax_runtime_api::AlphaMaskPaint> {
Vec::new()
}
fn resolve_occlusion_path(&self, expanded_node: &ExpandedNode) -> Option<kurbo::BezPath> {
self.resolve_coverage_path(expanded_node)
}
fn resolve_coverage_opacity(&self, expanded_node: &ExpandedNode) -> f64 {
expanded_node.computed_opacity.get()
}
fn resolve_effect_clip_path(&self, _expanded_node: &ExpandedNode) -> Option<kurbo::BezPath> {
None
}
}
pub struct BaseInstance {
pub handler_registry: Option<Rc<RefCell<HandlerRegistry>>>,
pub instance_prototypical_properties: PropertiesInit,
pub instance_prototypical_common_properties: CommonPropertiesInit,
pub component_settings: Option<Vec<SettingsBlockElement>>,
pub template_node_identifier: Option<UniqueTemplateNodeIdentifier>,
pub template_node_type_id: Option<TypeId>,
pub template_node_selector_info: Option<TemplateNodeSelectorInfo>,
pub transition_config: ComponentTransitionConfig,
pub properties_scope: PropertiesScopeInit,
instance_children: InstanceNodePtrList,
flags: InstanceFlags,
}
#[derive(Clone)]
pub struct InstanceFlags {
pub invisible_to_slot: bool,
pub invisible_to_raycasting: bool,
pub layer: Layer,
pub is_component: bool,
pub is_slot: bool,
}
impl BaseInstance {
pub(crate) fn requires_pre_node_binding(&self) -> bool {
self.instance_prototypical_common_properties
.requires_pre_node_binding()
|| self
.instance_prototypical_properties
.requires_pre_node_binding()
|| matches!(self.properties_scope, PropertiesScopeInit::Factory(_))
}
pub(crate) fn bind_properties(&self, node: &Rc<ExpandedNode>) {
if let (
CommonPropertiesInit::Template(common_plan),
PropertiesInit::Template { descriptor, plan },
) = (
&self.instance_prototypical_common_properties,
&self.instance_prototypical_properties,
) {
if Rc::ptr_eq(common_plan, plan) {
let columns = plan.resolve(Some(node));
update_existing_common_properties_from_columns(node, &columns, &node.stack);
let properties = Rc::clone(&node.properties.borrow());
(descriptor.apply_defined_properties)(
descriptor.typed_descriptor,
&mut properties.borrow_mut(),
&columns,
&node.stack,
);
return;
}
}
self.instance_prototypical_common_properties
.materialize(Rc::clone(&node.stack), Some(Rc::clone(node)));
self.instance_prototypical_properties
.materialize(Rc::clone(&node.stack), Some(Rc::clone(node)));
}
pub fn new(args: InstantiationArgs, flags: InstanceFlags) -> Self {
BaseInstance {
handler_registry: args.handler_registry,
instance_prototypical_common_properties: args.prototypical_common_properties,
instance_prototypical_properties: args.prototypical_properties,
component_settings: args.component_settings,
instance_children: args.children.unwrap_or_default(),
flags,
template_node_identifier: args.template_node_identifier,
template_node_type_id: args.template_node_type_id,
template_node_selector_info: args.template_node_selector_info,
transition_config: args.transition_config,
properties_scope: args.properties_scope,
}
}
pub fn get_handler_registry(&self) -> Option<Rc<RefCell<HandlerRegistry>>> {
match &self.handler_registry {
Some(registry) => Some(Rc::clone(registry)),
_ => None,
}
}
pub fn get_instance_children(&self) -> &InstanceNodePtrList {
&self.instance_children
}
pub fn flags(&self) -> &InstanceFlags {
&self.flags
}
pub fn transition_config(&self) -> &ComponentTransitionConfig {
&self.transition_config
}
}
pub struct StrokeInstance {
pub color: Color,
pub width: f64,
pub style: StrokeStyle,
}