use std::sync::{Arc, Mutex}; use crate::{ animation::{self, Animations}, assets::AssetProvider, event::{self, EventAlterables, EventListenerCollection, EventRefs}, transform_stack::{Transform, TransformStack}, widget::{self, EventParams, WidgetState, div::Div}, }; use glam::{Vec2, vec2}; use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; use taffy::{TaffyTree, TraversePartialTree}; new_key_type! { pub struct WidgetID; } pub type BoxWidget = Arc>; pub type WidgetMap = HopSlotMap; pub type WidgetNodeMap = SecondaryMap; pub struct Layout { pub tree: TaffyTree, pub assets: Box, pub widget_map: WidgetMap, pub widget_node_map: WidgetNodeMap, pub root_widget: WidgetID, pub root_node: taffy::NodeId, pub prev_size: Vec2, pub content_size: Vec2, pub needs_redraw: bool, pub haptics_triggered: bool, pub animations: Animations, } fn add_child_internal( tree: &mut taffy::TaffyTree, widget_map: &mut WidgetMap, widget_node_map: &mut WidgetNodeMap, parent_node: Option, widget: WidgetState, style: taffy::Style, ) -> anyhow::Result<(WidgetID, taffy::NodeId)> { #[allow(clippy::arc_with_non_send_sync)] let child_id = widget_map.insert(Arc::new(Mutex::new(widget))); let child_node = tree.new_leaf_with_context(style, child_id)?; if let Some(parent_node) = parent_node { tree.add_child(parent_node, child_node)?; } widget_node_map.insert(child_id, child_node); Ok((child_id, child_node)) } impl Layout { pub fn add_child( &mut self, parent_widget_id: WidgetID, widget: WidgetState, style: taffy::Style, ) -> anyhow::Result<(WidgetID, taffy::NodeId)> { let parent_node = *self.widget_node_map.get(parent_widget_id).unwrap(); self.needs_redraw = true; add_child_internal( &mut self.tree, &mut self.widget_map, &mut self.widget_node_map, Some(parent_node), widget, style, ) } fn push_event_children( &self, listeners: &EventListenerCollection, parent_node_id: taffy::NodeId, event: &event::Event, alterables: &mut EventAlterables, user_data: &mut (&mut U1, &mut U2), ) -> anyhow::Result<()> { for child_id in self.tree.child_ids(parent_node_id) { self.push_event_widget(listeners, child_id, event, alterables, user_data)?; } Ok(()) } fn push_event_widget( &self, listeners: &EventListenerCollection, node_id: taffy::NodeId, event: &event::Event, alterables: &mut EventAlterables, user_data: &mut (&mut U1, &mut U2), ) -> anyhow::Result<()> { let l = self.tree.layout(node_id)?; let Some(widget_id) = self.tree.get_node_context(node_id).cloned() else { anyhow::bail!("invalid widget ID"); }; let style = self.tree.style(node_id)?; let Some(widget) = self.widget_map.get(widget_id) else { debug_assert!(false); anyhow::bail!("invalid widget"); }; let mut widget = widget.lock().unwrap(); let transform = Transform { pos: Vec2::new(l.location.x, l.location.y), dim: Vec2::new(l.size.width, l.size.height), transform: glam::Mat4::IDENTITY, // TODO: event transformations? Not needed for now }; alterables.transform_stack.push(transform); let mut iter_children = true; let mut params = EventParams { refs: &EventRefs { tree: &self.tree, widget_map: &self.widget_map, widget_node_map: &self.widget_node_map, }, layout: l, alterables, node_id, style, }; if let Some(listeners) = listeners.get(widget_id) { match widget.process_event(widget_id, listeners, node_id, event, user_data, &mut params) { widget::EventResult::Pass => { // go on } widget::EventResult::Consumed => { iter_children = false; } widget::EventResult::Outside => { iter_children = false; } widget::EventResult::Unused => { iter_children = false; } } } drop(widget); // free mutex if iter_children { self.push_event_children(listeners, node_id, event, alterables, user_data)?; } alterables.transform_stack.pop(); Ok(()) } pub fn check_toggle_needs_redraw(&mut self) -> bool { if self.needs_redraw { self.needs_redraw = false; true } else { false } } pub fn check_toggle_haptics_triggered(&mut self) -> bool { if self.haptics_triggered { self.haptics_triggered = false; true } else { false } } pub fn push_event( &mut self, listeners: &mut EventListenerCollection, event: &event::Event, mut user_data: (&mut U1, &mut U2), ) -> anyhow::Result<()> { let mut alterables = EventAlterables::default(); self.push_event_widget( listeners, self.root_node, event, &mut alterables, &mut user_data, )?; self.process_alterables(alterables)?; listeners.gc(); Ok(()) } pub fn new(assets: Box) -> anyhow::Result { let mut tree = TaffyTree::new(); let mut widget_node_map = WidgetNodeMap::default(); let mut widget_map = HopSlotMap::with_key(); let (root_widget, root_node) = add_child_internal( &mut tree, &mut widget_map, &mut widget_node_map, None, // no parent Div::create()?, taffy::Style { size: taffy::Size::auto(), ..Default::default() }, )?; Ok(Self { tree, prev_size: Vec2::default(), content_size: Vec2::default(), root_node, root_widget, widget_node_map, widget_map, needs_redraw: true, haptics_triggered: false, animations: Animations::default(), assets, }) } pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> { let mut alterables = EventAlterables::default(); let refs = EventRefs { tree: &self.tree, widget_map: &self.widget_map, widget_node_map: &self.widget_node_map, }; self .animations .process(&refs, &mut alterables, timestep_alpha); self.process_alterables(alterables)?; if self.tree.dirty(self.root_node)? || self.prev_size != size { self.needs_redraw = true; log::debug!("re-computing layout, size {}x{}", size.x, size.y); self.prev_size = size; self.tree.compute_layout_with_measure( self.root_node, taffy::Size { width: taffy::AvailableSpace::Definite(size.x), height: taffy::AvailableSpace::Definite(size.y), }, |known_dimensions, available_space, _node_id, node_context, _style| { if let taffy::Size { width: Some(width), height: Some(height), } = known_dimensions { return taffy::Size { width, height }; } match node_context { None => taffy::Size::ZERO, Some(h) => { if let Some(w) = self.widget_map.get(*h) { w.lock() .unwrap() .obj .measure(known_dimensions, available_space) } else { taffy::Size::ZERO } } } }, )?; let root_size = self.tree.layout(self.root_node).unwrap().size; log::debug!( "content size {:.0}x{:.0} → {:.0}x{:.0}", self.content_size.x, self.content_size.y, root_size.width, root_size.height ); self.content_size = vec2(root_size.width, root_size.height); } Ok(()) } pub fn tick(&mut self) -> anyhow::Result<()> { let mut alterables = EventAlterables::default(); let refs = EventRefs { tree: &self.tree, widget_map: &self.widget_map, widget_node_map: &self.widget_node_map, }; self.animations.tick(&refs, &mut alterables); self.process_alterables(alterables)?; Ok(()) } fn process_alterables(&mut self, alterables: EventAlterables) -> anyhow::Result<()> { for node in alterables.dirty_nodes { self.tree.mark_dirty(node)?; } if alterables.needs_redraw { self.needs_redraw = true; } if alterables.trigger_haptics { self.haptics_triggered = true; } if !alterables.animations.is_empty() { self.needs_redraw = true; for anim in alterables.animations { self.animations.add(anim); } } for request in alterables.style_set_requests { if let Err(e) = self.tree.set_style(request.0, request.1) { log::error!( "failed to set style for taffy widget ID {:?}: {:?}", request.0, e ); } } Ok(()) } }