semi working renderer + layout

This commit is contained in:
Daniel Bulant 2023-10-19 21:32:04 +02:00
parent 8ca035e6ff
commit aa6af36836
4 changed files with 198 additions and 75 deletions

View file

@ -1,6 +1,6 @@
use std::num::NonZeroU32; use std::num::NonZeroU32;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, Weak};
use femtovg::renderer::OpenGl; use femtovg::renderer::OpenGl;
use femtovg::{Canvas, Color}; use femtovg::{Canvas, Color};
@ -20,14 +20,19 @@ use glutin::{
prelude::*, prelude::*,
surface::{SurfaceAttributesBuilder, WindowSurface}, surface::{SurfaceAttributesBuilder, WindowSurface},
}; };
use taffy::geometry::Size;
use taffy::style::Dimension;
use taffy::style_helpers::TaffyMaxContent;
use taffy::Taffy; use taffy::Taffy;
use weak_table::PtrWeakKeyHashMap; 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; mod nodes;
type GNode = dyn Node<OpenGl>; type GNode = dyn Node<OpenGl>;
type TaffyMap = PtrWeakKeyHashMap<Weak<GNode>, taffy::node::Node>; type SharedGNode = Arc<RwLock<GNode>>;
type TaffyMap = PtrWeakKeyHashMap<Weak<RwLock<GNode>>, taffy::node::Node>;
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -41,11 +46,47 @@ fn main() {
let mut taffy = Taffy::new(); let mut taffy = Taffy::new();
let mut taffy_map = TaffyMap::new(); let mut taffy_map = TaffyMap::new();
let root: Arc<GNode> = Arc::new(nodes::RedBoxDemo::new()); let mut root = Layout::<OpenGl>::new();
let root_style = <dyn Node<OpenGl>>::style(root.deref()); root.style.layout.display = taffy::style::Display::Flex;
let root_node = taffy.new_leaf(root_style.layout.to_owned()).unwrap(); 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<RwLock<Layout<OpenGl>>> = 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 { let mut context = RenderContext {
canvas, canvas,
@ -53,6 +94,9 @@ fn main() {
taffy taffy
}; };
// let mut width: u32 = 0;
// let mut height: u32 = 0;
event_loop.run(move |event, _target, control_flow| match event { event_loop.run(move |event, _target, control_flow| match event {
Event::WindowEvent { event, .. } => match event { Event::WindowEvent { event, .. } => match event {
// WindowEvent::CursorMoved { position, .. } => { // WindowEvent::CursorMoved { position, .. } => {
@ -60,14 +104,22 @@ fn main() {
// } // }
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
// width = size.width;
// height = size.height;
let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap(); let width: NonZeroU32 = NonZeroU32::new(size.width).unwrap();
let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap(); let height: NonZeroU32 = NonZeroU32::new(size.height).unwrap();
surface.resize(&buffer_context, width, height); 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(); window.request_redraw();
}, },
_ => {} _ => {}
}, },
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
let root: Arc<RwLock<GNode>> = groot.clone();
layout_recursively(&root, &mut context); layout_recursively(&root, &mut context);
let src_nodes = context.taffy_map.values().map(|v| v.to_owned()).collect::<Vec<_>>(); let src_nodes = context.taffy_map.values().map(|v| v.to_owned()).collect::<Vec<_>>();
context.taffy_map.remove_expired(); context.taffy_map.remove_expired();
@ -75,8 +127,11 @@ fn main() {
for src_node in src_nodes { for src_node in src_nodes {
if !dst_nodes.contains(&src_node) { if !dst_nodes.contains(&src_node) {
context.taffy.remove(src_node).unwrap(); 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); render(&buffer_context, &surface, &window, &mut context, &root);
}, },
_ => {} _ => {}
@ -126,7 +181,7 @@ fn render(
surface: &Surface<WindowSurface>, surface: &Surface<WindowSurface>,
window: &Window, window: &Window,
context: &mut RenderContext<OpenGl>, context: &mut RenderContext<OpenGl>,
root_node: &Arc<GNode> root_node: &Arc<RwLock<GNode>>
) { ) {
// Make sure the canvas has the right size: // Make sure the canvas has the right size:
let size = window.inner_size(); let size = window.inner_size();

View file

@ -1,10 +1,13 @@
use std::fmt::{Debug, Formatter};
use femtovg::Renderer; use femtovg::Renderer;
use crate::nodes::{Node, NodeChildren, Overflow, RenderContext, Style}; use crate::nodes::{Node, NodeChildren, Overflow, RenderContext, Style};
use taffy::style::{Style as TaffyStyle}; use taffy::style::{Style as TaffyStyle};
#[derive(Clone, Default)]
pub struct Layout<T> { pub struct Layout<T> {
style: Style, pub style: Style,
children: NodeChildren<T> pub children: NodeChildren<T>
} }
impl<T: Renderer> Layout<T> { impl<T: Renderer> Layout<T> {
@ -19,6 +22,15 @@ impl<T: Renderer> Layout<T> {
} }
} }
impl<T: Renderer> Debug for Layout<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Layout")
.field("style", &self.style)
.field("children", &self.children)
.finish()
}
}
impl<T: Renderer> Node<T> for Layout<T> { impl<T: Renderer> Node<T> for Layout<T> {
fn style(&self) -> &Style { fn style(&self) -> &Style {
&self.style &self.style
@ -26,7 +38,8 @@ impl<T: Renderer> Node<T> for Layout<T> {
fn children(&self) -> Option<&NodeChildren<T>> { fn children(&self) -> Option<&NodeChildren<T>> {
Some(&self.children) Some(&self.children)
} }
fn render(&self, context: &mut RenderContext<T>, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext<T>)) { // fn render_(&self, context: &mut RenderContext<T>, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext<T>)) {
render_children(context); // render_children(context);
} // }
} }

View file

@ -1,14 +1,16 @@
pub mod layout; pub mod layout;
pub mod primitives;
use std::sync::Arc; use std::fmt::Debug;
use femtovg::{Canvas, Color, Renderer}; use std::sync::{Arc, RwLock};
use femtovg::{Canvas, Renderer};
use femtovg::renderer::OpenGl; use femtovg::renderer::OpenGl;
use taffy::geometry::Size;
use taffy::layout::Layout; use taffy::layout::Layout;
use taffy::prelude::Dimension; pub use taffy::style::Style as TaffyStyle;
use taffy::style::Style as TaffyStyle;
use taffy::Taffy; use taffy::Taffy;
use crate::{GNode, TaffyMap}; use crate::{GNode, TaffyMap, SharedGNode};
type SharedTNode<T> = Arc<RwLock<dyn Node<T>>>;
pub struct RenderContext<T: Renderer> { pub struct RenderContext<T: Renderer> {
pub canvas: Canvas<T>, pub canvas: Canvas<T>,
@ -16,7 +18,7 @@ pub struct RenderContext<T: Renderer> {
pub taffy: Taffy pub taffy: Taffy
} }
#[derive(Copy, Clone, Default)] #[derive(Copy, Clone, Default, Debug)]
#[non_exhaustive] #[non_exhaustive]
pub enum Overflow { pub enum Overflow {
#[default] #[default]
@ -28,30 +30,33 @@ pub enum Overflow {
// Scroll, // Scroll,
// Auto // Auto
} }
#[derive(Clone, Default, Debug)]
pub struct Style { pub struct Style {
pub(crate) layout: TaffyStyle, pub(crate) layout: TaffyStyle,
pub overflow: Overflow pub overflow: Overflow
} }
type NodeChildren<T> = Vec<Arc<dyn Node<T>>>; type NodeChildren<T> = Vec<SharedTNode<T>>;
pub trait Node<T: Renderer> { pub trait Node<T: Renderer>: Debug {
/// Return style. Usually, you just want self.style. /// Return style. Usually, you just want self.style.
fn 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). /// 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<T>>; fn children(&self) -> Option<&NodeChildren<T>>;
/// 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) /// Canvas considers 0, 0 to be top left corner (for location after layouting happens)
fn render(&self, context: &mut RenderContext<T>, layout: Layout, render_children: &dyn Fn(&mut RenderContext<T>)); fn render_pre_children(&self, _context: &mut RenderContext<T>, _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<T>, _layout: Layout) {}
} }
pub fn layout_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>) -> taffy::node::Node { pub fn layout_recursively(node: &SharedTNode<OpenGl>, context: &mut RenderContext<OpenGl>) -> taffy::node::Node {
let taffy_node = context.taffy_map.get(node); let taffy_node = context.taffy_map.get(node);
let taffy_node = match taffy_node { let taffy_node = match taffy_node {
Some(taffy_node) => taffy_node, Some(taffy_node) => taffy_node,
None => { 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.insert(node.clone(), taffy_node);
context.taffy_map.get(node).unwrap() context.taffy_map.get(node).unwrap()
} }
@ -59,7 +64,7 @@ pub fn layout_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>
let taffy_node = taffy_node.to_owned(); let taffy_node = taffy_node.to_owned();
match node.children() { match node.read().unwrap().children() {
None => {}, None => {},
Some(children) => { Some(children) => {
let mut t_children = Vec::with_capacity(children.len()); let mut t_children = Vec::with_capacity(children.len());
@ -73,13 +78,15 @@ pub fn layout_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>
taffy_node taffy_node
} }
pub fn render_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>) { pub fn render_recursively(node: &SharedGNode, context: &mut RenderContext<OpenGl>) {
let styles = node.style(); let read_node = node.read().unwrap();
let styles = read_node.style();
let taffy_node = context.taffy_map.get(node).unwrap(); let taffy_node = context.taffy_map.get(node).unwrap();
let layout = *context.taffy.layout(*taffy_node).unwrap(); let layout = *context.taffy.layout(*taffy_node).unwrap();
let sself = node.clone(); let sself = node.clone();
context.canvas.save(); context.canvas.save();
context.canvas.translate(layout.location.x, layout.location.y); context.canvas.translate(layout.location.x, layout.location.y);
dbg!(node, layout);
match styles.overflow { match styles.overflow {
Overflow::Visible => {}, Overflow::Visible => {},
Overflow::Hidden => { Overflow::Hidden => {
@ -91,51 +98,20 @@ pub fn render_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>
); );
} }
} }
sself.render(context, layout, & (|context| { drop(read_node);
if let Some(children) = sself.children() { // sself.render(context, layout, & (|context| {
for child in children { // if let Some(children) = sself.children() {
render_recursively(child, context); // 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(); 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<T: Renderer> Node<T> for RedBoxDemo {
fn style(&self) -> &Style {
&self.style
}
fn children(&self) -> Option<&NodeChildren<T>> {
None
}
fn render(&self, context: &mut RenderContext<T>, _layout: Layout, _render_children: &dyn Fn(&mut RenderContext<T>)) {
context.canvas.clear_rect(
0,
0,
30,
30,
Color::rgbf(1., 0., 0.),
);
}
} }

79
src/nodes/primitives.rs Normal file
View file

@ -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<T: Renderer> Node<T> for Rectangle {
fn style(&self) -> &Style {
&self.style
}
fn children(&self) -> Option<&NodeChildren<T>> {
None
}
fn render_pre_children(&self, context: &mut RenderContext<T>, 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<T>, layout: Layout, _render_children: &dyn Fn(&mut RenderContext<T>)) {
// 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
// );
// }
// }
}