[go: up one dir, main page]

Documentation
use wgpu_canvas::Area as CanvasArea;
use wgpu_canvas::Item as CanvasItem;

use std::fmt::Debug;
use std::any::Any;

use super::{Context, resources};
use super::events::*;
use super::layout::{SizeRequest, Area};
use crate::events::OnEvent;

pub use wgpu_canvas::{Text, Font, Span, Align, Cursor, Color};
pub use wgpu_canvas::Shape as ShapeType;

#[derive(Default, Debug, Clone)]
pub struct RequestBranch(pub SizeRequest, Vec<RequestBranch>);

#[derive(Default, Debug, Clone)]
pub struct SizedBranch(pub Size, Vec<(Offset, SizedBranch)>);

type Offset = (f32, f32);
type Rect = (f32, f32, f32, f32);
type Size = (f32, f32);

/// A renderable element in the UI.
///
/// The `Drawable` trait is implemented by all visual elements
/// such as shapes, text, and images.
#[allow(private_bounds)]
pub trait Drawable: _Drawable + Debug + Any {
    fn request_size(&self, ctx: &mut Context) -> SizeRequest;
    fn name(&self) -> String;

    fn into_any(self: Box<Self>) -> Box<dyn Any>;
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

impl _Drawable for Box<dyn Drawable> {
    fn request_size(&self, ctx: &mut Context) -> RequestBranch {_Drawable::request_size(&**self, ctx)}
    fn build(&mut self, ctx: &mut Context, size: Size, request: RequestBranch) -> SizedBranch {
        _Drawable::build(&mut **self, ctx, size, request)
    }
    fn draw(&mut self, sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        _Drawable::draw(&mut **self, sized, offset, bound)
    }

    fn name(&self) -> String {_Drawable::name(&**self)}

    fn event(&mut self, ctx: &mut Context, sized: SizedBranch, event: Box<dyn Event>) {
        _Drawable::event(&mut **self, ctx, sized, event)
    }
}

impl<D: _Drawable + Debug + Any> Drawable for D {
    fn request_size(&self, ctx: &mut Context) -> SizeRequest {_Drawable::request_size(self, ctx).0}
    fn name(&self) -> String {_Drawable::name(self)}

    fn into_any(self: Box<Self>) -> Box<dyn Any> { self }
    fn as_any(&self) -> &dyn Any { self }
    fn as_any_mut(&mut self) -> &mut dyn Any { self }
}

pub(crate) trait _Drawable: Debug {
    fn request_size(&self, ctx: &mut Context) -> RequestBranch;
    fn build(&mut self, _ctx: &mut Context, size: Size, request: RequestBranch) -> SizedBranch {
        SizedBranch(request.0.get(size), vec![])
    }
    fn draw(&mut self, sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)>;

    fn name(&self) -> String {std::any::type_name_of_val(self).to_string()}

    fn event(&mut self, _ctx: &mut Context, _sized: SizedBranch, _event: Box<dyn Event>) {}
}

impl _Drawable for Text {
    fn request_size(&self, ctx: &mut Context) -> RequestBranch {
        RequestBranch(SizeRequest::fixed(self.size(ctx)), vec![])
    }

    fn draw(&mut self, _sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        vec![(CanvasArea(offset, Some(bound)), CanvasItem::Text(self.clone()))]
    }
}

impl<D: _Drawable + Debug + Any> _Drawable for Option<D> {
    fn request_size(&self, ctx: &mut Context) -> RequestBranch {
        match self {
            Some(d) => d.request_size(ctx),
            None => RequestBranch(SizeRequest::fixed((0.0, 0.0)), vec![]),
        }
    }

    fn build(&mut self, ctx: &mut Context, size: Size, request: RequestBranch) -> SizedBranch {
        match self {
            Some(d) => d.build(ctx, size, request),
            None => SizedBranch(size, vec![]),
        }
    }

    fn draw(&mut self, sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        match self {
            Some(d) => d.draw(sized, offset, bound),
            None => vec![],
        }
    }

    fn name(&self) -> String {
        match self {
            Some(d) => d.name(),
            None => "None".to_string(),
        }
    }

    fn event(&mut self, ctx: &mut Context, sized: SizedBranch, event: Box<dyn Event>) {
        if let Some(d) = self {
            d.event(ctx, sized, event);
        }
    }
}


/// A basic drawable shape with a fill color.
#[derive(Clone, Copy, Debug)]
pub struct Shape {
    /// The geometric form of the shape (e.g., rectangle, circle).
    pub shape: ShapeType,
    /// The fill or stroke color of the shape.
    pub color: Color,
}

impl _Drawable for Shape {
    fn request_size(&self, _ctx: &mut Context) -> RequestBranch {
        RequestBranch(SizeRequest::fixed(self.shape.size()), vec![])
    }

    fn draw(&mut self, _sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        //TODO: use sized.0 as the size of the shape?
        vec![(CanvasArea(offset, Some(bound)), CanvasItem::Shape(self.shape, self.color))]
    }
}

/// A drawable image.
#[derive(Clone, Debug)]
pub struct Image {
    /// The geometric bounds or clipping shape for the image.
    pub shape: ShapeType,
    /// The image resource to be rendered.
    pub image: resources::Image,
    /// An optional color that will replace all opaque parts of the image.
    /// This is usefull when recoloring svgs.
    pub color: Option<Color>,
}

impl _Drawable for Image {
    fn request_size(&self, _ctx: &mut Context) -> RequestBranch {
        RequestBranch(SizeRequest::fixed(self.shape.size()), vec![])
    }

    fn draw(&mut self, _sized: SizedBranch, offset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        vec![(CanvasArea(offset, Some(bound)), CanvasItem::Image(self.shape, self.image.clone(), self.color))]
    }
}

/// A composable UI element with children.
///
/// `Component` represents higher-level UI building blocks. 
/// Unlike simple `Drawable`s, components can contain other 
/// drawables and define their own layout, rendering, and event handling.
pub trait Component: Debug {
    /// Returns mutable reference to child drawables.
    fn children_mut(&mut self) -> Vec<&mut dyn Drawable>;
    /// Returns reference to child drawables.
    fn children(&self) -> Vec<&dyn Drawable>;

    /// Compute layout needs based on children.
    fn request_size(&self, ctx: &mut Context, children: Vec<SizeRequest>) -> SizeRequest;
    /// Position children and return their areas.
    fn build(&mut self, ctx: &mut Context, size: Size, children: Vec<SizeRequest>) -> Vec<Area>;
}

impl<C: Component + ?Sized + 'static + OnEvent> _Drawable for C {
    fn request_size(&self, ctx: &mut Context) -> RequestBranch {
        let requests = self.children().into_iter().map(|i| _Drawable::request_size(i, ctx)).collect::<Vec<_>>();
        let info = requests.iter().map(|i| i.0).collect::<Vec<_>>();
        let r = Component::request_size(self, ctx, info);
        RequestBranch(r, requests)
    }

    fn build(&mut self, ctx: &mut Context, size: Size, request: RequestBranch) -> SizedBranch {
        let size = request.0.get(size);
        let children = request.1.iter().map(|b| b.0).collect::<Vec<_>>();
        SizedBranch(
            size,
            Component::build(self, ctx, size, children).into_iter()
            .zip(self.children_mut()).zip(request.1)
            .map(|((Area{offset, size}, child), branch)| {
                (offset, child.build(ctx, size, branch))
            }).collect()
        )
    }

    fn draw(&mut self, sized: SizedBranch, poffset: Offset, bound: Rect) -> Vec<(CanvasArea, CanvasItem)> {
        sized.1.into_iter().zip(self.children_mut()).flat_map(|((offset, branch), child)| {
            let size = branch.0;
            let poffset = (poffset.0+offset.0, poffset.1+offset.1);

            let bound = (
                bound.0.max(poffset.0), bound.1.max(poffset.1),//New bound offset
                bound.2.min((offset.0 + size.0).max(0.0)), bound.3.min((offset.1 + size.1).max(0.0))//New bound size
            );

            if bound.2 != 0.0 && bound.3 != 0.0 {
                child.draw(branch, poffset, bound)
            } else {vec![]}
        }).collect()
    }

    fn event(&mut self, ctx: &mut Context, sized: SizedBranch, event: Box<dyn Event>) {
        let children = sized.1.iter().map(|(o, branch)| (*o, branch.0)).collect::<Vec<_>>();
        for event in OnEvent::on_event(self, ctx, event) {
            event.pass(ctx, &children).into_iter().zip(self.children_mut()).zip(sized.1.iter()).for_each(
                |((e, child), branch)| if let Some(e) = e {child.event(ctx, branch.1.clone(), e);}
            );
        }
    }
}

#[macro_export]
macro_rules! drawables {
    ( $( $x:expr ),* $(,)? ) => {
        {
            vec![
                $(Box::new($x) as Box<dyn roost::drawable::Drawable>),*
            ]
        }
    };
}