mirror of
https://github.com/danbulant/mangui
synced 2026-06-19 06:11:21 +00:00
127 lines
No EOL
4.7 KiB
Rust
127 lines
No EOL
4.7 KiB
Rust
pub mod layout;
|
|
pub mod primitives;
|
|
|
|
use std::fmt::Debug;
|
|
use std::sync::{Arc, RwLock};
|
|
use femtovg::{Canvas, Color};
|
|
use taffy::layout::Layout;
|
|
pub use taffy::style::Style as TaffyStyle;
|
|
use taffy::Taffy;
|
|
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer};
|
|
|
|
type SharedTNode = Arc<RwLock<dyn Node>>;
|
|
|
|
pub struct RenderContext {
|
|
pub canvas: Canvas<CurrentRenderer>,
|
|
pub node_layout: NodeLayoutMap,
|
|
pub taffy: Taffy,
|
|
pub mouse: NodePtr,
|
|
pub keyboard_focus: NodePtr
|
|
}
|
|
|
|
impl RenderContext {
|
|
/// Fills a rectangle area with the specified color, using the current transform of the canvas.
|
|
/// Rotation WILL break this, this is mostly for simple scaling and translation.
|
|
pub fn fill_rect(&mut self, x: u32, y: u32, width: u32, height: u32, color: Color) {
|
|
let transform = self.canvas.transform();
|
|
let x = transform[0] * x as f32 + transform[2] * y as f32 + transform[4];
|
|
let y = transform[1] * x as f32 + transform[3] * y as f32 + transform[5];
|
|
let width = transform[0] * width as f32 + transform[2] * height as f32;
|
|
let height = transform[1] * width as f32 + transform[3] * height as f32;
|
|
self.canvas.clear_rect(x as u32, y as u32, width as u32, height as u32, color);
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Default, Debug)]
|
|
#[non_exhaustive]
|
|
pub enum Overflow {
|
|
#[default]
|
|
/// Content is not clipped and may be rendered outside the element's box
|
|
Visible,
|
|
/// Clips the content at the border of the element
|
|
Hidden,
|
|
// tbd :)
|
|
// Scroll,
|
|
// Auto
|
|
}
|
|
#[derive(Clone, Default, Debug)]
|
|
pub struct Style {
|
|
pub layout: TaffyStyle,
|
|
pub overflow: Overflow
|
|
}
|
|
|
|
type NodeChildren = Vec<SharedTNode>;
|
|
|
|
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, 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 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
|
|
fn resize(&mut self, _width: f32, _height: f32) {}
|
|
}
|
|
|
|
pub fn layout_recursively(node: &SharedTNode, 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,
|
|
None => {
|
|
let taffy_node = context.taffy.new_leaf(node.read().unwrap().style().layout.to_owned()).unwrap();
|
|
context.node_layout.insert(node.clone(), taffy_node);
|
|
context.node_layout.get(node).unwrap()
|
|
}
|
|
};
|
|
|
|
let taffy_node = taffy_node.to_owned();
|
|
|
|
match node.read().unwrap().children() {
|
|
None => {},
|
|
Some(children) => {
|
|
let mut t_children = Vec::with_capacity(children.len());
|
|
for child in children {
|
|
t_children.push(layout_recursively(child, context).to_owned());
|
|
}
|
|
context.taffy.set_children(taffy_node, t_children.as_slice()).unwrap();
|
|
}
|
|
}
|
|
|
|
taffy_node
|
|
}
|
|
|
|
pub fn render_recursively(node: &SharedTNode, context: &mut RenderContext) {
|
|
let read_node = node.read().unwrap();
|
|
let styles = read_node.style();
|
|
let taffy_node = context.node_layout.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);
|
|
match styles.overflow {
|
|
Overflow::Visible => {},
|
|
Overflow::Hidden => {
|
|
context.canvas.scissor(
|
|
0.,
|
|
0.,
|
|
layout.size.width,
|
|
layout.size.height,
|
|
);
|
|
}
|
|
}
|
|
drop(read_node);
|
|
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();
|
|
} |