use crate::api::math::Point2;
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::{properties, Globals};
#[cfg(debug_assertions)]
use core::fmt;
use std::any::Any;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use crate::api::{
Axis, ButtonClick, CheckboxChange, Clap, Click, CommonProperties, ContextMenu, DoubleClick,
Event, KeyDown, KeyPress, KeyUp, MouseDown, MouseMove, MouseOut, MouseOver, MouseUp,
NodeContext, RenderContext, Scroll, Size, TextboxChange, TextboxInput, TouchEnd, TouchMove,
TouchStart, Wheel, 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: RefCell<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: RefCell<Rc<RefCell<dyn Any>>>,
common_properties: RefCell<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) -> bool {
let event = Event::new(args.clone());
if let Some(registry) = self.instance_node.borrow().base().get_handler_registry() {
let component_properties = if let Some(cc) = self.containing_component.upgrade() {
Rc::clone(&cc.properties.borrow())
} else {
Rc::clone(&self.properties.borrow())
};
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.borrow())
} else {
Rc::clone(&component_properties)
};
(handler.function)(
Rc::clone(&properties),
&context,
Some(Box::new(event.clone()) as Box<dyn Any>),
);
});
};
}
if $recurse {
if let Some(parent) = &self.parent_expanded_node.borrow().upgrade() {
let parent_prevent_default = parent.$fn_name(args, globals, ctx);
return event.cancelled() || parent_prevent_default;
}
}
event.cancelled()
}
};
}
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: RefCell::new(Rc::clone(&template)),
attached: RefCell::new(0),
properties: RefCell::new(properties),
common_properties: RefCell::new(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 recreate_with_new_data(self: &Rc<Self>, template: Rc<dyn InstanceNode>) {
*self.instance_node.borrow_mut() = Rc::clone(&template);
*self.properties.borrow_mut() =
(&template.base().instance_prototypical_properties_factory)();
*self.common_properties.borrow_mut() = (template
.base()
.instance_prototypical_common_properties_factory)(
);
}
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.borrow().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(), context.globals());
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.borrow().base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("tick")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties.borrow()),
&self.get_node_context(context),
None,
)
}
}
Rc::clone(&self.instance_node.borrow()).update(&self, context);
if *self.attached.borrow() > 0 {
self.instance_node
.borrow()
.handle_native_patches(self, context);
}
if let Some(ref registry) = self.instance_node.borrow().base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("pre_render")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties.borrow()),
&self.get_node_context(context),
None,
)
}
}
for child in self.children.borrow().iter() {
child.recurse_update(context);
}
}
pub 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));
let uni = self
.instance_node
.borrow()
.base()
.template_node_identifier
.clone();
if let Some(uni) = uni {
if let Some(nodes) = context.uni_to_eid.get_mut(&uni) {
nodes.push(self.id_chain[0]);
} else {
context.uni_to_eid.insert(uni, vec![self.id_chain[0]]);
}
}
self.instance_node.borrow().handle_mount(&self, context);
Rc::clone(&self.instance_node.borrow()).update(&self, context);
if let Some(ref registry) = self.instance_node.borrow().base().handler_registry {
for handler in registry
.borrow()
.handlers
.get("mount")
.unwrap_or(&Vec::new())
{
(handler.function)(
Rc::clone(&self.properties.borrow()),
&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]);
let uni = self
.instance_node
.borrow()
.base()
.template_node_identifier
.clone();
if let Some(uni) = uni {
if let Some(nodes) = context.uni_to_eid.get_mut(&uni) {
nodes.retain(|id| id != &self.id_chain[0]);
}
}
self.instance_node.borrow().handle_unmount(&self, context);
}
}
pub fn recurse_render(&self, ctx: &mut RuntimeContext, rcs: &mut dyn RenderContext) {
self.instance_node
.borrow()
.handle_pre_render(&self, ctx, rcs);
for child in self.children.borrow().iter().rev() {
child.recurse_render(ctx, rcs);
}
self.instance_node.borrow().render(&self, ctx, rcs);
self.instance_node
.borrow()
.handle_post_render(&self, ctx, rcs);
}
pub fn with_properties_unwrapped<T: 'static, R>(
&self,
callback: impl FnOnce(&mut T) -> R,
) -> R {
let properties = self.properties.borrow();
let mut borrowed = 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.borrow())
}
pub fn ray_cast_test(&self, ray: Point2<Window>) -> bool {
if self
.instance_node
.borrow()
.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.borrow().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, Scroll, SCROLL_HANDLERS, true);
dispatch_event_handler!(dispatch_clap, Clap, CLAP_HANDLERS, true);
dispatch_event_handler!(dispatch_touch_start, TouchStart, TOUCH_START_HANDLERS, true);
dispatch_event_handler!(dispatch_touch_move, TouchMove, TOUCH_MOVE_HANDLERS, true);
dispatch_event_handler!(dispatch_touch_end, TouchEnd, TOUCH_END_HANDLERS, true);
dispatch_event_handler!(dispatch_key_down, KeyDown, KEY_DOWN_HANDLERS, false);
dispatch_event_handler!(dispatch_key_up, KeyUp, KEY_UP_HANDLERS, false);
dispatch_event_handler!(dispatch_key_press, KeyPress, KEY_PRESS_HANDLERS, false);
dispatch_event_handler!(
dispatch_checkbox_change,
CheckboxChange,
CHECKBOX_CHANGE_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_textbox_change,
TextboxChange,
TEXTBOX_CHANGE_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_textbox_input,
TextboxInput,
TEXTBOX_INPUT_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_button_click,
ButtonClick,
BUTTON_CLICK_HANDLERS,
true
);
dispatch_event_handler!(dispatch_mouse_down, MouseDown, MOUSE_DOWN_HANDLERS, true);
dispatch_event_handler!(dispatch_mouse_up, MouseUp, MOUSE_UP_HANDLERS, true);
dispatch_event_handler!(dispatch_mouse_move, MouseMove, MOUSE_MOVE_HANDLERS, true);
dispatch_event_handler!(dispatch_mouse_over, MouseOver, MOUSE_OVER_HANDLERS, true);
dispatch_event_handler!(dispatch_mouse_out, MouseOut, MOUSE_OUT_HANDLERS, true);
dispatch_event_handler!(
dispatch_double_click,
DoubleClick,
DOUBLE_CLICK_HANDLERS,
true
);
dispatch_event_handler!(
dispatch_context_menu,
ContextMenu,
CONTEXT_MENU_HANDLERS,
true
);
dispatch_event_handler!(dispatch_click, Click, CLICK_HANDLERS, true);
dispatch_event_handler!(dispatch_wheel, Wheel, 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.borrow().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.borrow().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()
}
}