diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 54d6a85..d9e615b 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -41,6 +41,7 @@ type WeakNode = Weak>; type NodePtr = Option>; type NodeLayoutMap = PtrWeakKeyHashMap>, taffy::node::Node>; +/// The entry point of the UI. pub struct MainEntry { /// The root node of the UI pub root: SharedNode, @@ -48,9 +49,15 @@ pub struct MainEntry { /// This is checked every 'frame' based on the monitor refresh rate. /// If there are no messages and no user input, no frame is scheduled. /// Currently, you don't need to use this after an event callback - a frame is scheduled after any event. + /// The "render queue" is cleared on each frame so that sending multiple values to this channel will only schedule one frame. pub render: std::sync::mpsc::Receiver<()>, } +/// Starts the event loop. +/// +/// The event loop only returns when the window is closed, and all the resources regarding the window are freed. +/// Note that the DOM tree may not be destroyed if you hold a reference to it, and the DOM tree can be used again, although it's discouraged - +/// your app should exit at this point and only do cleanup. pub fn run_event_loop(entry: MainEntry) -> () { let event_loop = EventLoop::new().unwrap(); let (buffer_context, gl_display, window, surface) = create_window(&event_loop); @@ -261,6 +268,7 @@ pub fn run_event_loop(entry: MainEntry) -> () { // dbg!("recomputed"); } // dbg!(&root); + while let Ok(_) = entry.render.try_recv() {} render(&buffer_context, &surface, &window, &mut context, &root); } _ => {} @@ -272,7 +280,6 @@ pub fn run_event_loop(entry: MainEntry) -> () { // some leeway before vsync target.set_control_flow(ControlFlow::wait_duration(Duration::from_millis(1000 / refresh_rate as u64 - 100/refresh_rate as u64))); if let Ok(_) = entry.render.try_recv() { - while let Ok(_) = entry.render.try_recv() {} window.request_redraw(); } } diff --git a/ui/src/nodes/layout.rs b/ui/src/nodes/layout.rs index fb88b50..cd18d8a 100644 --- a/ui/src/nodes/layout.rs +++ b/ui/src/nodes/layout.rs @@ -41,6 +41,13 @@ impl Node for Layout { self.style.layout.size.height = Dimension::Points(height); } + fn add_child_at(&mut self, child: crate::SharedNode, index: usize) -> Result<(), super::ChildAddError> { + if let Some(_) = self.has_child(&child) { + return Err(super::ChildAddError::ChildAlreadyExists); + } + self.children.insert(index, child); Ok(()) + } + fn event_handlers(&self) -> Option { Some(self.events.handlers.clone()) } diff --git a/ui/src/nodes/mod.rs b/ui/src/nodes/mod.rs index e7ebe2e..9dd94a3 100644 --- a/ui/src/nodes/mod.rs +++ b/ui/src/nodes/mod.rs @@ -2,15 +2,16 @@ pub mod layout; pub mod primitives; use std::fmt::Debug; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; use femtovg::{Canvas, Color}; use taffy::layout::Layout; -pub use taffy::style::Style as TaffyStyle; use taffy::Taffy; use crate::events::Location; -use crate::events::handler::InnerEventHandlerDataset; +use crate::events::handler::{EventHandlerDatabase, InnerEventHandlerDataset}; use crate::{NodeLayoutMap, NodePtr, CurrentRenderer, SharedNode}; +pub use taffy::style::Style as TaffyStyle; + pub struct RenderContext { pub canvas: Canvas, pub node_layout: NodeLayoutMap, @@ -52,24 +53,92 @@ pub struct Style { type NodeChildren = Vec; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ChildAddError { + ChildrenNotSupported, + /// The child already exists in the node + ChildAlreadyExists, + /// The index is out of bounds (cannot be thrown from add_child as long as the [`Node`] implementation is correct) + OutOfBounds, + /// Generic error text (subject to change for better error handling) + GenericError(String) +} + +/// A node in the UI tree. This is the main trait for nodes. +/// +/// Implementation for style, children (readable list) and one of [`Node::render_pre_children`] or [`Node::render_post_children`] is required. +/// +/// # Root node +/// If you are implementing a root node, you will need to implement [`Node::resize`] to resize your node correctly based on window size. +/// Simply updating the style is sufficient +/// +/// # Children +/// +/// If you don't need to support children, you can return None from [`Node::children`]. +/// +/// If you support children in a read only matter, you can return a [`NodeChildren`] from [`Node::children`]. +/// +/// If you also need to be able to update children, implement [`Node::add_child_at`] in addition to [`Node::children`]. +/// Other child adding methods are implemented using [`Node::add_child_at`] and/or [`Node::children`]. pub trait Node: Debug { - /// Return style. Usually, you just want self.style. + /// Return style. + /// + /// If you're using [`Style`] in your struct directly, your implementation can be as simple as: + /// ```rust + /// fn style(&self) -> &Style { &self.style } + /// ``` fn style(&self) -> &Style; /// Returns the children of the node. If the node has no children, return None (empty Vec also works, None is mainly for nodes without children support). + /// + /// If you're using [`NodeChildren`] in your struct directly, your implementation can be as simple as: + /// ```rust + /// fn children(&self) -> Option<&NodeChildren> { Some(&self.children) } + /// ``` fn children(&self) -> Option<&NodeChildren>; + /// Add a child to the node. If the node does not support children, returns error ChildrenNotSupported. + /// Adding the same child multiple times or to multiple parents is not supported and will result in undefined behavior. + /// Arc> does **NOT** mean that it's safe to add the same node multiple times. + /// + /// Default implementation uses [`Node::add_child_at`] and adds the child at the end. + /// Assumes that if [`Node::children`] returns None, the node does not support children, and the if it returns Some, the node does support children. + /// Also assumes that [`Node::children`] returns correct values - the length matches the actual number of children and so on. + fn add_child(&mut self, _child: SharedNode) -> Result<(), ChildAddError> { + if let Some(children) = self.children() { + self.add_child_at(_child, children.len()) + } else { + Err(ChildAddError::ChildrenNotSupported) + } + } + + /// Add a child to the node at the given index. If the node does not support children, returns error ChildrenNotSupported. + /// Adding the same child multiple times or to multiple parents is not supported and will result in undefined behavior. + /// Arc> does **NOT** mean that it's safe to add the same node multiple times. + /// + /// Implementors can check [`Node::has_child`] to check if the child already exists. Default implementation thros [`ChildAddError::ChildrenNotSupported`]. + fn add_child_at(&mut self, _child: SharedNode, _index: usize) -> Result<(), ChildAddError> { Err(ChildAddError::ChildrenNotSupported) } + + /// Adds a child after the given child. If the node does not support children, returns error ChildrenNotSupported. + fn add_child_after(&mut self, child: SharedNode, after: &SharedNode) -> Result<(), ChildAddError> { + if let Some(_) = self.children() { + if let Some(index) = self.has_child(after) { + self.add_child_at(child, index + 1) + } else { + Err(ChildAddError::GenericError("Child not found".to_owned())) + } + } else { + Err(ChildAddError::ChildrenNotSupported) + } + } + /// Render the node, called before rendering it's children /// Canvas considers 0, 0 to be top left corner (for location after layouting happens) fn render_pre_children(&self, _context: &mut RenderContext, _layout: Layout) {} /// Render the node, called after rendering it's children /// Canvas considers 0, 0 to be top left corner (for location after layouting happens) fn render_post_children(&self, _context: &mut RenderContext, _layout: Layout) {} - /// Called when an event happens on the node. This is called after the children have been called. - /// Beware! Events include a path and target with [Arc>]s, but you already have a write lock for this node! - /// Remember to check if the node is the same as self, and if it is, use self instead of the node in the path to prevent deadlocks! - // fn on_event(&mut self, _event: &crate::events::NodeEvent) {} /// Returns the event handlers of the node. If the node has no event handlers, return None. - /// Use [EventHandlerDatabase] to manage this, and return it's handlers property. + /// Use [`EventHandlerDatabase`] to manage this, and return it's handlers property. /// Example code: /// ```rust /// fn event_handlers(&self) -> Option { @@ -80,6 +149,21 @@ pub trait Node: Debug { None } + /// Returns true if the node has the given child + /// Returns false if there are no children (or if the node does not support children) + fn has_child(&self, child: &SharedNode) -> Option { + let mut i = 0; + if let Some(children) = self.children() { + for c in children { + if Arc::ptr_eq(c, child) { + return Some(i); + } + i += 1; + } + } + None + } + /// Called when the size of window changes on the root node. Layouts do implement this. /// Is an optional function instead of another trait because of missing support for trait upcasting // TODO: When rust supports trait upcasting, make this a trait @@ -88,9 +172,9 @@ pub trait Node: Debug { /// Runs event handlers for the given path /// The target element should be the last one in path (event handlers are ran in reverse order) -pub fn run_event_handlers(path: Vec, event: crate::events::NodeEvent) { +pub(crate) fn run_event_handlers(path: Vec, event: crate::events::NodeEvent) { for node in path.iter().rev() { - let mut node = node.read().unwrap(); + let node = node.read().unwrap(); if let Some(handlers) = node.event_handlers() { drop(node); for handler in handlers.lock().unwrap().values_mut() { @@ -100,8 +184,8 @@ pub fn run_event_handlers(path: Vec, event: crate::events::NodeEvent } } -pub fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEvent) { - let mut node = node.read().unwrap(); +pub(crate) fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEvent) { + let node = node.read().unwrap(); if let Some(handlers) = node.event_handlers() { drop(node); for handler in handlers.lock().unwrap().values_mut() { @@ -111,7 +195,7 @@ pub fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEve } /// Attempts to get path to the element at the target location. Assumes elements are always inside their parents. -pub fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option> { +pub(crate) fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option> { let node_borrowed = node.read().unwrap(); let children = node_borrowed.children(); let taffy_node = context.node_layout.get(node); @@ -141,7 +225,7 @@ pub fn get_element_at(node: &SharedNode, context: &RenderContext, location: Loca } } -pub fn layout_recursively(node: &SharedNode, context: &mut RenderContext) -> taffy::node::Node { +pub(crate) fn layout_recursively(node: &SharedNode, context: &mut RenderContext) -> taffy::node::Node { let taffy_node = context.node_layout.get(node); let taffy_node = match taffy_node { Some(taffy_node) => taffy_node, @@ -168,7 +252,7 @@ pub fn layout_recursively(node: &SharedNode, context: &mut RenderContext) -> taf taffy_node } -pub fn render_recursively(node: &SharedNode, context: &mut RenderContext) { +pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext) { let read_node = node.read().unwrap(); let styles = read_node.style(); let taffy_node = context.node_layout.get(node).unwrap();