use std::rc::Rc;
use blinc_core::DrawContext;
use taffy::prelude::*;
use crate::div::{ElementBuilder, ElementTypeId};
use crate::element::{RenderLayer, RenderProps};
use crate::tree::{LayoutNodeId, LayoutTree};
#[derive(Clone, Copy, Debug)]
pub struct CanvasBounds {
pub width: f32,
pub height: f32,
}
pub type CanvasRenderFn = Rc<dyn Fn(&mut dyn DrawContext, CanvasBounds)>;
pub struct Canvas {
style: Style,
render_fn: Option<CanvasRenderFn>,
opacity: f32,
layer: RenderLayer,
}
impl Canvas {
pub fn new() -> Self {
Self {
style: Style::default(),
render_fn: None,
opacity: 1.0,
layer: RenderLayer::default(),
}
}
pub fn with_render<F>(render_fn: F) -> Self
where
F: Fn(&mut dyn DrawContext, CanvasBounds) + 'static,
{
Self {
style: Style::default(),
render_fn: Some(Rc::new(render_fn)),
opacity: 1.0,
layer: RenderLayer::default(),
}
}
pub fn render<F>(mut self, render_fn: F) -> Self
where
F: Fn(&mut dyn DrawContext, CanvasBounds) + 'static,
{
self.render_fn = Some(Rc::new(render_fn));
self
}
pub fn w(mut self, width: f32) -> Self {
self.style.size.width = Dimension::Length(width);
self
}
pub fn h(mut self, height: f32) -> Self {
self.style.size.height = Dimension::Length(height);
self
}
pub fn size(mut self, width: f32, height: f32) -> Self {
self.style.size.width = Dimension::Length(width);
self.style.size.height = Dimension::Length(height);
self
}
pub fn w_full(mut self) -> Self {
self.style.size.width = Dimension::Percent(1.0);
self
}
pub fn h_full(mut self) -> Self {
self.style.size.height = Dimension::Percent(1.0);
self
}
pub fn opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn flex_grow(mut self) -> Self {
self.style.flex_grow = 1.0;
self
}
pub fn absolute(mut self) -> Self {
self.style.position = Position::Absolute;
self
}
pub fn left(mut self, value: f32) -> Self {
self.style.inset.left = LengthPercentageAuto::Length(value);
self
}
pub fn top(mut self, value: f32) -> Self {
self.style.inset.top = LengthPercentageAuto::Length(value);
self
}
pub fn layer(mut self, layer: RenderLayer) -> Self {
self.layer = layer;
self
}
pub fn foreground(mut self) -> Self {
self.layer = RenderLayer::Foreground;
self
}
pub fn background(mut self) -> Self {
self.layer = RenderLayer::Background;
self
}
pub fn render_fn(&self) -> Option<&CanvasRenderFn> {
self.render_fn.as_ref()
}
}
impl Default for Canvas {
fn default() -> Self {
Self::new()
}
}
impl ElementBuilder for Canvas {
fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId {
tree.create_node(self.style.clone())
}
fn render_props(&self) -> RenderProps {
RenderProps {
opacity: self.opacity,
layer: self.layer,
..Default::default()
}
}
fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
&[]
}
fn element_type_id(&self) -> ElementTypeId {
ElementTypeId::Canvas
}
fn canvas_render_info(&self) -> Option<CanvasRenderFn> {
self.render_fn.clone()
}
fn layout_style(&self) -> Option<&taffy::Style> {
Some(&self.style)
}
}
#[derive(Clone)]
pub struct CanvasData {
pub render_fn: Option<CanvasRenderFn>,
}
impl std::fmt::Debug for CanvasData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CanvasData")
.field("has_render_fn", &self.render_fn.is_some())
.finish()
}
}
pub fn canvas<F>(render_fn: F) -> Canvas
where
F: Fn(&mut dyn DrawContext, CanvasBounds) + 'static,
{
Canvas::with_render(render_fn)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canvas_creation() {
let c = canvas(|_ctx, _bounds| {
})
.w(100.0)
.h(50.0);
assert!(matches!(c.style.size.width, Dimension::Length(w) if (w - 100.0).abs() < 0.001));
assert!(matches!(c.style.size.height, Dimension::Length(h) if (h - 50.0).abs() < 0.001));
assert!(c.render_fn.is_some());
}
#[test]
fn test_canvas_opacity() {
let c = Canvas::new().opacity(0.5);
assert_eq!(c.opacity, 0.5);
}
#[test]
fn test_canvas_absolute_positioning() {
let c = canvas(|_ctx, _bounds| {}).absolute().left(10.0).top(20.0);
assert!(matches!(c.style.position, Position::Absolute));
}
}