lemna 0.4.0

A Reactive UI framework for Rust
use std::hash::Hash;

use lyon::tessellation;
use lyon::tessellation::basic_shapes;
use lyon::tessellation::math as lyon_math;

use crate::base_types::*;
use crate::component::{Component, ComponentHasher, RenderContext};
use crate::render::{
    renderables::shape::{self, Shape},
    Renderable,
};

#[derive(Debug)]
pub struct RoundedRect {
    pub background_color: Color,
    pub border_color: Color,
    pub border_width: f32,
    pub radius: (f32, f32, f32, f32),
}

impl Default for RoundedRect {
    fn default() -> Self {
        Self {
            background_color: Color::WHITE,
            border_color: Color::BLACK,
            border_width: 0.0,
            radius: (3.0, 3.0, 3.0, 3.0),
        }
    }
}

impl RoundedRect {
    pub fn new<C: Into<Color>>(bg: C, radius: f32) -> Self {
        Self {
            background_color: bg.into(),
            border_color: Color::BLACK,
            border_width: 0.0,
            radius: (radius, radius, radius, radius),
        }
    }

    pub fn radius(mut self, r: f32) -> Self {
        self.radius = (r, r, r, r);
        self
    }
}

impl Component for RoundedRect {
    fn render_hash(&self, hasher: &mut ComponentHasher) {
        self.background_color.hash(hasher);
        self.border_color.hash(hasher);
        (self.border_width as u32).hash(hasher);
        (self.radius.0 as i32).hash(hasher);
        (self.radius.1 as i32).hash(hasher);
        (self.radius.2 as i32).hash(hasher);
        (self.radius.3 as i32).hash(hasher);
    }

    fn render(&mut self, context: RenderContext) -> Option<Vec<Renderable>> {
        let mut geometry = shape::ShapeGeometry::new();
        let rect = lyon_math::rect(0.0, 0.0, context.aabb.width(), context.aabb.height());
        let radii = basic_shapes::BorderRadii {
            top_left: self.radius.0,
            top_right: self.radius.1,
            bottom_right: self.radius.2,
            bottom_left: self.radius.3,
        };

        let fill_count = basic_shapes::fill_rounded_rectangle(
            &rect,
            &radii,
            &tessellation::FillOptions::tolerance(shape::TOLERANCE),
            &mut tessellation::BuffersBuilder::new(
                &mut geometry,
                shape::Vertex::basic_vertex_constructor,
            ),
        )
        .unwrap();

        if self.border_width > 0.0 {
            basic_shapes::stroke_rounded_rectangle(
                &rect,
                &radii,
                &tessellation::StrokeOptions::tolerance(shape::TOLERANCE).dont_apply_line_width(),
                &mut tessellation::BuffersBuilder::new(
                    &mut geometry,
                    shape::Vertex::stroke_vertex_constructor,
                ),
            )
            .unwrap();
        }

        Some(vec![Renderable::Shape(Shape::new(
            geometry,
            fill_count.indices,
            self.background_color,
            self.border_color,
            self.border_width * 0.5,
            0.0,
            &mut context.caches.shape_buffer_cache.write().unwrap(),
            context.prev_state.as_ref().and_then(|v| match v.get(0) {
                Some(Renderable::Shape(r)) => Some(r.buffer_id),
                _ => None,
            }),
        ))])
    }
}