From aa6af3683660c8532bc60494c3a1ddb3da4cca2d Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Thu, 19 Oct 2023 21:32:04 +0200 Subject: [PATCH] semi working renderer + layout --- src/main.rs | 71 ++++++++++++++++++++++++---- src/nodes/layout.rs | 23 +++++++-- src/nodes/mod.rs | 100 +++++++++++++++------------------------- src/nodes/primitives.rs | 79 +++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 75 deletions(-) create mode 100644 src/nodes/primitives.rs diff --git a/src/main.rs b/src/main.rs index cf80076..0aeecb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::num::NonZeroU32; use std::ops::Deref; -use std::sync::{Arc, Weak}; +use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, Weak}; use femtovg::renderer::OpenGl; use femtovg::{Canvas, Color}; @@ -20,14 +20,19 @@ use glutin::{ prelude::*, surface::{SurfaceAttributesBuilder, WindowSurface}, }; +use taffy::geometry::Size; +use taffy::style::Dimension; +use taffy::style_helpers::TaffyMaxContent; use taffy::Taffy; use weak_table::PtrWeakKeyHashMap; -use crate::nodes::{layout_recursively, Node, render_recursively, RenderContext}; +use crate::nodes::{layout_recursively, Node, render_recursively, RenderContext, Style, TaffyStyle}; +use crate::nodes::layout::Layout; mod nodes; type GNode = dyn Node; -type TaffyMap = PtrWeakKeyHashMap, taffy::node::Node>; +type SharedGNode = Arc>; +type TaffyMap = PtrWeakKeyHashMap>, taffy::node::Node>; fn main() { let event_loop = EventLoop::new(); @@ -41,11 +46,47 @@ fn main() { let mut taffy = Taffy::new(); let mut taffy_map = TaffyMap::new(); - let root: Arc = Arc::new(nodes::RedBoxDemo::new()); - let root_style = >::style(root.deref()); - let root_node = taffy.new_leaf(root_style.layout.to_owned()).unwrap(); + let mut root = Layout::::new(); + root.style.layout.display = taffy::style::Display::Flex; + root.style.layout.flex_direction = taffy::style::FlexDirection::Row; + root.children.push(Arc::new(RwLock::new(nodes::primitives::Rectangle { + style: Style { + overflow: nodes::Overflow::Visible, + layout: TaffyStyle { + min_size: Size { + width: Dimension::Points(100.), + height: Dimension::Points(100.) + }, + ..Default::default() + } + }, + color: Color::rgb(255, 0, 0), + radius: 0. + }))); + root.children.push(Arc::new(RwLock::new(nodes::primitives::Rectangle { + style: Style { + overflow: nodes::Overflow::Visible, + layout: TaffyStyle { + min_size: Size { + width: Dimension::Points(50.), + height: Dimension::Points(100.) + }, + ..Default::default() + } + }, + color: Color::rgb(0, 255, 0), + radius: 10. + }))); + let groot: Arc>> = Arc::new(RwLock::new(root)); + { + let clonned = groot.clone(); + let root = clonned.read().unwrap(); + let root_style = GNode::style(root.deref()); + let root_layout = root_style.layout.to_owned(); + let root_node = taffy.new_leaf(root_layout).unwrap(); - taffy_map.insert(root.clone(), root_node); + taffy_map.insert(groot.clone(), root_node); + } let mut context = RenderContext { canvas, @@ -53,6 +94,9 @@ fn main() { taffy }; + // let mut width: u32 = 0; + // let mut height: u32 = 0; + event_loop.run(move |event, _target, control_flow| match event { Event::WindowEvent { event, .. } => match event { // WindowEvent::CursorMoved { position, .. } => { @@ -60,14 +104,22 @@ fn main() { // } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(size) => { + // width = size.width; + // height = size.height; let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap(); let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap(); surface.resize(&buffer_context, width, height); + let mut groot = groot.write().unwrap(); + let scale_factor = window.scale_factor(); + groot.style.layout.size.width = Dimension::Points(size.width as f32 * scale_factor as f32); + groot.style.layout.size.height = Dimension::Points(size.height as f32 * scale_factor as f32); + drop(groot); window.request_redraw(); }, _ => {} }, Event::RedrawRequested(_) => { + let root: Arc> = groot.clone(); layout_recursively(&root, &mut context); let src_nodes = context.taffy_map.values().map(|v| v.to_owned()).collect::>(); context.taffy_map.remove_expired(); @@ -75,8 +127,11 @@ fn main() { for src_node in src_nodes { if !dst_nodes.contains(&src_node) { context.taffy.remove(src_node).unwrap(); + dbg!("Removed node", src_node); } } + context.taffy.compute_layout(*context.taffy_map.get(&root).unwrap(), Size::MAX_CONTENT).unwrap(); + // dbg!(&root); render(&buffer_context, &surface, &window, &mut context, &root); }, _ => {} @@ -126,7 +181,7 @@ fn render( surface: &Surface, window: &Window, context: &mut RenderContext, - root_node: &Arc + root_node: &Arc> ) { // Make sure the canvas has the right size: let size = window.inner_size(); diff --git a/src/nodes/layout.rs b/src/nodes/layout.rs index 71013e3..755b926 100644 --- a/src/nodes/layout.rs +++ b/src/nodes/layout.rs @@ -1,10 +1,13 @@ +use std::fmt::{Debug, Formatter}; use femtovg::Renderer; use crate::nodes::{Node, NodeChildren, Overflow, RenderContext, Style}; use taffy::style::{Style as TaffyStyle}; +#[derive(Clone, Default)] + pub struct Layout { - style: Style, - children: NodeChildren + pub style: Style, + pub children: NodeChildren } impl Layout { @@ -19,6 +22,15 @@ impl Layout { } } +impl Debug for Layout { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Layout") + .field("style", &self.style) + .field("children", &self.children) + .finish() + } +} + impl Node for Layout { fn style(&self) -> &Style { &self.style @@ -26,7 +38,8 @@ impl Node for Layout { fn children(&self) -> Option<&NodeChildren> { Some(&self.children) } - fn render(&self, context: &mut RenderContext, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext)) { - render_children(context); - } + // fn render_(&self, context: &mut RenderContext, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext)) { + // render_children(context); + // } + } \ No newline at end of file diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index e184f47..82e98c9 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -1,14 +1,16 @@ pub mod layout; +pub mod primitives; -use std::sync::Arc; -use femtovg::{Canvas, Color, Renderer}; +use std::fmt::Debug; +use std::sync::{Arc, RwLock}; +use femtovg::{Canvas, Renderer}; use femtovg::renderer::OpenGl; -use taffy::geometry::Size; use taffy::layout::Layout; -use taffy::prelude::Dimension; -use taffy::style::Style as TaffyStyle; +pub use taffy::style::Style as TaffyStyle; use taffy::Taffy; -use crate::{GNode, TaffyMap}; +use crate::{GNode, TaffyMap, SharedGNode}; + +type SharedTNode = Arc>>; pub struct RenderContext { pub canvas: Canvas, @@ -16,7 +18,7 @@ pub struct RenderContext { pub taffy: Taffy } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, Debug)] #[non_exhaustive] pub enum Overflow { #[default] @@ -28,30 +30,33 @@ pub enum Overflow { // Scroll, // Auto } - +#[derive(Clone, Default, Debug)] pub struct Style { pub(crate) layout: TaffyStyle, pub overflow: Overflow } -type NodeChildren = Vec>>; +type NodeChildren = Vec>; -pub trait Node { +pub trait Node: Debug { /// Return style. Usually, you just want 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). fn children(&self) -> Option<&NodeChildren>; - /// Render the node and its children. render_children gets ['children'] and calls this function there as well. When drawing, the canvas is translated to the node's location. + /// 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(&self, context: &mut RenderContext, layout: Layout, render_children: &dyn Fn(&mut RenderContext)); + 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) {} } -pub fn layout_recursively(node: &Arc, context: &mut RenderContext) -> taffy::node::Node { +pub fn layout_recursively(node: &SharedTNode, context: &mut RenderContext) -> taffy::node::Node { let taffy_node = context.taffy_map.get(node); let taffy_node = match taffy_node { Some(taffy_node) => taffy_node, None => { - let taffy_node = context.taffy.new_leaf(node.style().layout.to_owned()).unwrap(); + let taffy_node = context.taffy.new_leaf(node.read().unwrap().style().layout.to_owned()).unwrap(); context.taffy_map.insert(node.clone(), taffy_node); context.taffy_map.get(node).unwrap() } @@ -59,7 +64,7 @@ pub fn layout_recursively(node: &Arc, context: &mut RenderContext let taffy_node = taffy_node.to_owned(); - match node.children() { + match node.read().unwrap().children() { None => {}, Some(children) => { let mut t_children = Vec::with_capacity(children.len()); @@ -73,13 +78,15 @@ pub fn layout_recursively(node: &Arc, context: &mut RenderContext taffy_node } -pub fn render_recursively(node: &Arc, context: &mut RenderContext) { - let styles = node.style(); +pub fn render_recursively(node: &SharedGNode, context: &mut RenderContext) { + let read_node = node.read().unwrap(); + let styles = read_node.style(); let taffy_node = context.taffy_map.get(node).unwrap(); let layout = *context.taffy.layout(*taffy_node).unwrap(); let sself = node.clone(); context.canvas.save(); context.canvas.translate(layout.location.x, layout.location.y); + dbg!(node, layout); match styles.overflow { Overflow::Visible => {}, Overflow::Hidden => { @@ -91,51 +98,20 @@ pub fn render_recursively(node: &Arc, context: &mut RenderContext ); } } - sself.render(context, layout, & (|context| { - if let Some(children) = sself.children() { - for child in children { - render_recursively(child, context); - } + drop(read_node); + // sself.render(context, layout, & (|context| { + // if let Some(children) = sself.children() { + // for child in children { + // render_recursively(child, context); + // } + // } + // })); + sself.read().unwrap().render_pre_children(context, layout); + if let Some(children) = sself.read().unwrap().children() { + for child in children { + render_recursively(child, context); } - })); + } + sself.read().unwrap().render_post_children(context, layout); context.canvas.restore(); -} - -pub struct RedBoxDemo { - style: Style -} - -impl RedBoxDemo { - pub(crate) fn new() -> RedBoxDemo { - RedBoxDemo { - style: Style { - layout: TaffyStyle { - size: Size { - width: Dimension::Points(30.), - height: Dimension::Points(30.) - }, - ..TaffyStyle::default() - }, - overflow: Overflow::Visible - } - } - } -} - -impl Node for RedBoxDemo { - fn style(&self) -> &Style { - &self.style - } - fn children(&self) -> Option<&NodeChildren> { - None - } - fn render(&self, context: &mut RenderContext, _layout: Layout, _render_children: &dyn Fn(&mut RenderContext)) { - context.canvas.clear_rect( - 0, - 0, - 30, - 30, - Color::rgbf(1., 0., 0.), - ); - } } \ No newline at end of file diff --git a/src/nodes/primitives.rs b/src/nodes/primitives.rs new file mode 100644 index 0000000..2806588 --- /dev/null +++ b/src/nodes/primitives.rs @@ -0,0 +1,79 @@ +use femtovg::{Color, Paint, Path, Renderer}; +use taffy::geometry::Size; +use taffy::layout::Layout; +use taffy::style::Dimension; +use crate::nodes::{Node, NodeChildren, Overflow, RenderContext, Style, TaffyStyle}; + +#[derive(Clone, Default, Debug)] +pub struct Rectangle { + pub style: Style, + pub color: Color, + pub radius: f32 +} + +impl Rectangle { + pub(crate) fn new() -> Rectangle { + Rectangle { + style: Style::default(), + color: Color::rgb(0, 0, 0), + radius: 0. + } + } +} + +impl Node for Rectangle { + fn style(&self) -> &Style { + &self.style + } + fn children(&self) -> Option<&NodeChildren> { + None + } + fn render_pre_children(&self, context: &mut RenderContext, layout: Layout) { + if self.radius > 0. { + let mut path = Path::new(); + path.rounded_rect( + 0., + 0., + layout.size.width, + layout.size.height, + self.radius + ); + context.canvas.fill_path( + &path, + &Paint::color(self.color) + ); + } else { + context.canvas.clear_rect( + 0, + 0, + layout.size.width as u32, + layout.size.height as u32, + self.color + ); + } + } + // fn render(&self, context: &mut RenderContext, layout: Layout, _render_children: &dyn Fn(&mut RenderContext)) { + // if self.radius > 0. { + // let mut path = Path::new(); + // path.rounded_rect( + // 0., + // 0., + // layout.size.width, + // layout.size.height, + // self.radius + // ); + // context.canvas.fill_path( + // &path, + // &Paint::color(self.color) + // ); + // } else { + // context.canvas.clear_rect( + // 0, + // 0, + // layout.size.width as u32, + // layout.size.height as u32, + // self.color + // ); + // } + // } +} \ No newline at end of file