documentation and adding children support

This commit is contained in:
Daniel Bulant 2023-10-26 15:39:59 +02:00
parent 8f6e22798e
commit 119eb9cfb8
3 changed files with 115 additions and 17 deletions

View file

@ -41,6 +41,7 @@ type WeakNode = Weak<RwLock<dyn Node>>;
type NodePtr = Option<Vec<WeakNode>>;
type NodeLayoutMap = PtrWeakKeyHashMap<Weak<RwLock<dyn Node>>, 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();
}
}

View file

@ -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<crate::events::handler::InnerEventHandlerDataset> {
Some(self.events.handlers.clone())
}

View file

@ -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<CurrentRenderer>,
pub node_layout: NodeLayoutMap,
@ -52,24 +53,92 @@ pub struct Style {
type NodeChildren = Vec<SharedNode>;
#[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<RwLock<Node>> 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<RwLock<Node>> 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<RwLock<Node>>]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<InnerEventHandlerDataset> {
@ -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<usize> {
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<SharedNode>, event: crate::events::NodeEvent) {
pub(crate) fn run_event_handlers(path: Vec<SharedNode>, 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<SharedNode>, 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<Vec<SharedNode>> {
pub(crate) fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option<Vec<SharedNode>> {
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();