mirror of
https://github.com/danbulant/mangui
synced 2026-06-15 12:21:18 +00:00
documentation and adding children support
This commit is contained in:
parent
8f6e22798e
commit
119eb9cfb8
3 changed files with 115 additions and 17 deletions
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue