use crate::constants::{
BUTTON_CLICK_HANDLERS, CHECKBOX_CHANGE_HANDLERS, CLAP_HANDLERS, CLICK_HANDLERS,
CONTEXT_MENU_HANDLERS, DOUBLE_CLICK_HANDLERS, KEY_DOWN_HANDLERS, KEY_PRESS_HANDLERS,
KEY_UP_HANDLERS, MOUSE_DOWN_HANDLERS, MOUSE_MOVE_HANDLERS, MOUSE_OUT_HANDLERS,
MOUSE_OVER_HANDLERS, MOUSE_UP_HANDLERS, SCROLL_HANDLERS, TEXTBOX_CHANGE_HANDLERS,
TEXTBOX_INPUT_HANDLERS, TOUCH_END_HANDLERS, TOUCH_MOVE_HANDLERS, TOUCH_START_HANDLERS,
WHEEL_HANDLERS,
};
use crate::math::Point2;
use crate::Globals;
#[cfg(debug_assertions)]
use core::fmt;
use std::any::Any;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use crate::api::{
ArgsButtonClick, ArgsCheckboxChange, ArgsClap, ArgsClick, ArgsContextMenu, ArgsDoubleClick,
ArgsKeyDown, ArgsKeyPress, ArgsKeyUp, ArgsMouseDown, ArgsMouseMove, ArgsMouseOut,
ArgsMouseOver, ArgsMouseUp, ArgsScroll, ArgsTextboxChange, ArgsTextboxInput, ArgsTouchEnd,
ArgsTouchMove, ArgsTouchStart, ArgsWheel, Axis, CommonProperties, NodeContext, RenderContext,
Size, Window,
};
use crate::{
compute_tab, ComponentInstance, HandlerLocation, InstanceNode, InstanceNodePtr,
PropertiesComputable, RuntimeContext, RuntimePropertiesStackFrame, TransformAndBounds,
};
pub struct ExpandedNode {
#[allow(dead_code)]
pub id_chain: Vec<u32>,
pub instance_node: InstanceNodePtr,
pub parent_expanded_node: RefCell<Weak<ExpandedNode>>,
pub containing_component: Weak<ExpandedNode>,
pub stack: Rc<RuntimePropertiesStackFrame>,
pub children: RefCell<Vec<Rc<ExpandedNode>>>,
pub properties: Rc<RefCell<dyn Any>>,
common_properties: Rc<RefCell<CommonProperties>>,
pub layout_properties: RefCell<Option<LayoutProperties>>,
pub expanded_slot_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
pub expanded_and_flattened_slot_children: RefCell<Option<Vec<Rc<ExpandedNode>>>>,
pub attached: RefCell<u32>,
pub occlusion_id: RefCell<u32>,
}
macro_rules! dispatch_event_handler {
($fn_name:ident, $arg_type:ty, $handler_key:ident, $recurse:expr) => {
pub fn $fn_name(&self, args: $arg_type, globals: &Globals, ctx: &RuntimeContext) {
if let Some(registry) = self.instance_node.base().get_handler_registry() {
let component_properties = if let Some(cc) = self.containing_component.upgrade() {
Rc::clone(&cc.properties)
} else {
Rc::clone(&self.properties)
};
let comp_props = self.layout_properties.borrow();
let bounds_self = comp_props.as_ref().unwrap().computed_tab.bounds;
let bounds_parent = self
.parent_expanded_node
.borrow()
.upgrade()
.map(|parent| {
let comp_props = parent.layout_properties.borrow();
let bounds_parent = comp_props.as_ref().unwrap().computed_tab.bounds;
bounds_parent
})
.unwrap_or(globals.viewport.bounds);
let context = NodeContext {
bounds_self,
bounds_parent,
frames_elapsed: globals.frames_elapsed,
runtime_context: ctx,
#[cfg(feature = "designtime")]
designtime: globals.designtime.clone(),
};
let borrowed_registry = &(*registry).borrow();
if let Some(handlers) = borrowed_registry.handlers.get($handler_key) {
handlers.iter().for_each(|handler| {
let properties = if let HandlerLocation::Component = &handler.location {
Rc::clone(&self.properties)
} else {
Rc::clone(&component_properties)
};
(handler.function)(
Rc::clone(&properties),
&context,
Some(Box::new(args.clone()) as Box<dyn Any>),
);
});
};
}
if $recurse {
if let Some(parent) = &self.parent_expanded_node.borrow().upgrade() {
parent.$fn_name(args, globals, ctx);
}
}
}
};
}
impl ExpandedNode {
pub fn root(template: Rc<ComponentInstance>, context: &mut RuntimeContext) -> Rc<Self> {
let root_env =
RuntimePropertiesStackFrame::new(Rc::new(RefCell::new(())) as Rc<RefCell<dyn Any>>);
let root_node = Self::new(template, root_env, context, Weak::new());
Rc::clone(&root_node).recurse_mount(context);
root_node
}
fn new(
template: Rc<dyn InstanceNode>,
env: Rc<RuntimePropertiesStackFrame>,
context: &mut RuntimeContext,
containing_component: Weak<ExpandedNode>,
) -> Rc<Self> {
let properties = (&template.base().instance_prototypical_properties_factory)();
let common_properties = (&template
.base()
.instance_prototypical_common_properties_factory)();
Rc::new(ExpandedNode {
id_chain: vec![context.gen_uid().0],
instance_node: Rc::clone(&template),
attached: RefCell::new(0),
properties,
common_properties,
stack: env,
parent_expanded_node: Default::default(),
containing_component,
children: RefCell::new(Vec::new()),
layout_properties: RefCell::new(None),
expanded_slot_children: Default::default(),
expanded_and_flattened_slot_children: Default::default(),
occlusion_id: RefCell::new(0),
})
}
pub fn is_descendant_of(&self, other_expanded_node_id: &Vec<u32>) -> bool {
if let Some(parent) = self.parent_expanded_node.borrow().upgrade() {
if parent.id_chain.eq(other_expanded_node_id) {
true
} else {
parent.is_descendant_of(other_expanded_node_id)
}
} else {
false
}
}
pub fn create_children_detached(
self: &Rc<Self>,
templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
context: &mut RuntimeContext,
) -> Vec<Rc<ExpandedNode>> {
let containing_component = if self.instance_node.base().flags().is_component {
Rc::downgrade(&self)
} else {
Weak::clone(&self.containing_component)
};
let mut children = Vec::new();
for (template, env) in templates {
children.push(Self::new(
template,
env,
context,
Weak::clone(&containing_component),
));
}
children
}
pub fn attach_children(
self: &Rc<Self>,
new_children: Vec<Rc<ExpandedNode>>,
context: &mut RuntimeContext,
) {
let mut curr_children = self.children.borrow_mut();
if *self.attached.borrow() > 0 {
for child in curr_children.iter() {
Rc::clone(child).recurse_unmount(context);
}
for child in new_children.iter() {
Rc::clone(child).recurse_mount(context);
}
}
for child in new_children.iter() {
*child.parent_expanded_node.borrow_mut() = Rc::downgrade(self);
}
*curr_children = new_children;
}
pub fn set_children(
self: &Rc<Self>,
templates: impl IntoIterator<Item = (Rc<dyn InstanceNode>, Rc<RuntimePropertiesStackFrame>)>,
context: &mut RuntimeContext,
) {
let new_children = self.create_children_detached(templates, context);
self.attach_children(new_children, context);
}
pub fn recurse_update(self: &Rc<Self>, context: &mut RuntimeContext) {
self.get_common_properties()
.borrow_mut()
.compute_properties(&self.stack, context.expression_table());
let viewport = self
.parent_expanded_node
.borrow()
.upgrade()
.and_then(|p| {
let props = p.layout_properties.borrow();
props.as_ref().map(|c| c.computed_tab.clone())
})
.unwrap_or(context.globals().viewport.clone());
*self.layout_properties.borrow_mut() = Some(LayoutProperties {
computed_tab: compute_tab(self, &viewport),
});
if let Some(ref registry) = self.instance_node.base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("tick")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties),
&self.get_node_context(context),
None,
)
}
}
Rc::clone(&self.instance_node).update(&self, context);
if *self.attached.borrow() > 0 {
self.instance_node.handle_native_patches(self, context);
}
if let Some(ref registry) = self.instance_node.base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("pre_render")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties),
&self.get_node_context(context),
None,
)
}
}
for child in self.children.borrow().iter() {
child.recurse_update(context);
}
}
fn recurse_mount(self: Rc<Self>, context: &mut RuntimeContext) {
if *self.attached.borrow() == 0 {
*self.attached.borrow_mut() += 1;
context
.node_cache
.insert(self.id_chain[0], Rc::clone(&self));
self.instance_node.handle_mount(&self, context);
if let Some(ref registry) = self.instance_node.base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("mount")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties),
&self.get_node_context(context),
None,
)
}
}
}
for child in self.children.borrow().iter() {
Rc::clone(child).recurse_mount(context);
}
}
pub fn recurse_unmount(self: Rc<Self>, context: &mut RuntimeContext) {
for child in self.children.borrow().iter() {
Rc::clone(child).recurse_unmount(context);
}
if *self.attached.borrow() == 1 {
*self.attached.borrow_mut() -= 1;
context.node_cache.remove(&self.id_chain[0]);
self.instance_node.handle_unmount(&self, context);
}
}
pub fn recurse_render(&self, context: &mut RuntimeContext, rcs: &mut dyn RenderContext) {
for child in self.children.borrow().iter().rev() {
child.recurse_render(context, rcs);
}
self.instance_node.render(&self, context, rcs);
}
pub fn with_properties_unwrapped<T: 'static, R>(
&self,
callback: impl FnOnce(&mut T) -> R,
) -> R {
let mut borrowed = self.properties.borrow_mut();
let mut unwrapped_value = if let Some(val) = borrowed.downcast_mut::<T>() {
val
} else {
panic!() };
callback(&mut unwrapped_value)
}
pub fn recurse_visit_postorder<T>(
self: &Rc<Self>,
func: &impl Fn(&Rc<Self>, &mut T),
val: &mut T,
) {
for child in self.children.borrow().iter().rev() {
child.recurse_visit_postorder(func, val);
}
func(self, val);
}
pub fn get_node_context<'a>(&'a self, context: &'a RuntimeContext) -> NodeContext {
let globals = context.globals();
let computed_props = self.layout_properties.borrow();
let bounds_self = computed_props
.as_ref()
.map(|v| v.computed_tab.bounds)
.unwrap_or(globals.viewport.bounds);
let parent = self.parent_expanded_node.borrow().upgrade();
let bounds_parent = parent
.as_ref()
.and_then(|p| {
let props = p.layout_properties.borrow();
props.as_ref().map(|v| v.computed_tab.bounds)
})
.unwrap_or(globals.viewport.bounds);
NodeContext {
frames_elapsed: globals.frames_elapsed,
bounds_self,
bounds_parent,
runtime_context: context,
#[cfg(feature = "designtime")]
designtime: globals.designtime.clone(),
}
}
pub fn get_common_properties(&self) -> Rc<RefCell<CommonProperties>> {
Rc::clone(&self.common_properties)
}
pub fn ray_cast_test(&self, ray: Point2<Window>) -> bool {
if self.instance_node.base().flags().invisible_to_raycasting {
return false;
}
let props = self.layout_properties.borrow();
let computed_tab = &props.as_ref().unwrap().computed_tab;
let inverted_transform = computed_tab.transform.inverse();
let transformed_ray = inverted_transform * ray;
let relevant_bounds = computed_tab.bounds;
let res = transformed_ray.x > 0.0
&& transformed_ray.y > 0.0
&& transformed_ray.x < relevant_bounds.0
&& transformed_ray.y < relevant_bounds.1;
res
}
pub fn get_size(&self) -> (Size, Size) {
self.instance_node.get_size(self)
}
pub fn get_size_computed(&self, bounds: (f64, f64)) -> (f64, f64) {
let size = self.get_size();
(
size.0.evaluate(bounds, Axis::X),
size.1.evaluate(bounds, Axis::Y),
)
}
pub fn get_clipping_size(&self) -> Option<(Size, Size)> {
None
}
pub fn get_clipping_size_computed(&self, bounds: (f64, f64)) -> (f64, f64) {
match self.get_clipping_size() {
None => bounds,
Some(size_raw) => (
size_raw.0.evaluate(bounds, Axis::X),
size_raw.1.evaluate(bounds, Axis::Y),
),
}
}
pub fn get_scroll_offset(&mut self) -> (f64, f64) {
todo!("patch into an ExpandedNode-friendly way to track this state");
}
pub fn compute_flattened_slot_children(&self) {
if let Some(slot_children) = self.expanded_slot_children.borrow().as_ref() {
*self.expanded_and_flattened_slot_children.borrow_mut() =
Some(flatten_expanded_nodes_for_slot(&slot_children));
}
}
dispatch_event_handler!(dispatch_scroll, ArgsScroll, SCROLL_HANDLERS, true);
dispatch_event_handler!(dispatch_clap, ArgsClap, CLAP_HANDLERS, true);
dispatch_event_handler!(
dispatch_touch_start,
ArgsTouchStart,
TOUCH_START_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_touch_move,
ArgsTouchMove,
TOUCH_MOVE_HANDLERS,
true
);
dispatch_event_handler!(dispatch_touch_end, ArgsTouchEnd, TOUCH_END_HANDLERS, true);
dispatch_event_handler!(dispatch_key_down, ArgsKeyDown, KEY_DOWN_HANDLERS, false);
dispatch_event_handler!(dispatch_key_up, ArgsKeyUp, KEY_UP_HANDLERS, false);
dispatch_event_handler!(dispatch_key_press, ArgsKeyPress, KEY_PRESS_HANDLERS, false);
dispatch_event_handler!(
dispatch_checkbox_change,
ArgsCheckboxChange,
CHECKBOX_CHANGE_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_textbox_change,
ArgsTextboxChange,
TEXTBOX_CHANGE_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_textbox_input,
ArgsTextboxInput,
TEXTBOX_INPUT_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_button_click,
ArgsButtonClick,
BUTTON_CLICK_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_mouse_down,
ArgsMouseDown,
MOUSE_DOWN_HANDLERS,
true
);
dispatch_event_handler!(dispatch_mouse_up, ArgsMouseUp, MOUSE_UP_HANDLERS, true);
dispatch_event_handler!(
dispatch_mouse_move,
ArgsMouseMove,
MOUSE_MOVE_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_mouse_over,
ArgsMouseOver,
MOUSE_OVER_HANDLERS,
true
);
dispatch_event_handler!(dispatch_mouse_out, ArgsMouseOut, MOUSE_OUT_HANDLERS, true);
dispatch_event_handler!(
dispatch_double_click,
ArgsDoubleClick,
DOUBLE_CLICK_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_context_menu,
ArgsContextMenu,
CONTEXT_MENU_HANDLERS,
true
);
dispatch_event_handler!(dispatch_click, ArgsClick, CLICK_HANDLERS, true);
dispatch_event_handler!(dispatch_wheel, ArgsWheel, WHEEL_HANDLERS, true);
}
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct LayoutProperties {
pub computed_tab: TransformAndBounds,
}
fn flatten_expanded_nodes_for_slot(nodes: &[Rc<ExpandedNode>]) -> Vec<Rc<ExpandedNode>> {
let mut result = vec![];
for node in nodes {
if node.instance_node.base().flags().invisible_to_slot {
result.extend(flatten_expanded_nodes_for_slot(
node.children
.borrow()
.clone()
.into_iter()
.collect::<Vec<_>>()
.as_slice(),
));
} else {
result.push(Rc::clone(&node))
}
}
result
}
#[cfg(debug_assertions)]
impl std::fmt::Debug for ExpandedNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
pub struct Fmt<F>(pub F)
where
F: Fn(&mut fmt::Formatter) -> fmt::Result;
impl<F> fmt::Debug for Fmt<F>
where
F: Fn(&mut fmt::Formatter) -> fmt::Result,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(self.0)(f)
}
}
f.debug_struct("ExpandedNode")
.field(
"instance_node",
&Fmt(|f| self.instance_node.resolve_debug(f, Some(self))),
)
.field("id_chain", &self.id_chain)
.field(
"children",
&self.children.try_borrow().iter().collect::<Vec<_>>(),
)
.field(
"parent",
&self
.parent_expanded_node
.borrow()
.upgrade()
.map(|v| v.id_chain.clone()),
)
.field("occlusion_id", &self.occlusion_id.borrow())
.field(
"containing_component",
&self
.containing_component
.upgrade()
.map(|v| v.id_chain.clone()),
)
.finish()
}
}