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::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<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() {
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<GNode> = Arc::new(nodes::RedBoxDemo::new());
let root_style = <dyn Node<OpenGl>>::style(root.deref());
let root_node = taffy.new_leaf(root_style.layout.to_owned()).unwrap();
let mut root = Layout::<OpenGl>::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<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 {
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<RwLock<GNode>> = groot.clone();
layout_recursively(&root, &mut context);
let src_nodes = context.taffy_map.values().map(|v| v.to_owned()).collect::<Vec<_>>();
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<WindowSurface>,
window: &Window,
context: &mut RenderContext<OpenGl>,
root_node: &Arc<GNode>
root_node: &Arc<RwLock<GNode>>
) {
// Make sure the canvas has the right size:
let size = window.inner_size();

View file

@ -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<T> {
style: Style,
children: NodeChildren<T>
pub style: Style,
pub children: NodeChildren<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> {
fn style(&self) -> &Style {
&self.style
@ -26,7 +38,8 @@ impl<T: Renderer> Node<T> for Layout<T> {
fn children(&self) -> Option<&NodeChildren<T>> {
Some(&self.children)
}
fn render(&self, context: &mut RenderContext<T>, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext<T>)) {
render_children(context);
}
// fn render_(&self, context: &mut RenderContext<T>, _layout: taffy::layout::Layout, render_children: &dyn Fn(&mut RenderContext<T>)) {
// render_children(context);
// }
}

View file

@ -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<T> = Arc<RwLock<dyn Node<T>>>;
pub struct RenderContext<T: Renderer> {
pub canvas: Canvas<T>,
@ -16,7 +18,7 @@ pub struct RenderContext<T: Renderer> {
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<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.
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<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)
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 = 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<GNode>, context: &mut RenderContext<OpenGl>
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<GNode>, context: &mut RenderContext<OpenGl>
taffy_node
}
pub fn render_recursively(node: &Arc<GNode>, context: &mut RenderContext<OpenGl>) {
let styles = node.style();
pub fn render_recursively(node: &SharedGNode, context: &mut RenderContext<OpenGl>) {
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<GNode>, context: &mut RenderContext<OpenGl>
);
}
}
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<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
// );
// }
// }
}