rosin 0.3.0

Rosin is a GUI library that aims to be extremely easy to use.
use crate::{
    kurbo::{Affine, BezPath, Rect, Stroke},
    peniko::{Color, Fill},
    prelude::*,
    widgets::widget_styles,
};

pub fn checkbox<S, H>(ui: &mut Ui<S, H>, id: NodeId, value: WeakVar<bool>, label: impl Into<UIString>) -> &mut Ui<S, H> {
    ui.node()
        .id(id)
        .classes("checkbox")
        .style_sheet(widget_styles())
        .event(On::PointerDown, move |_, ctx| {
            let Some(mut v) = value.write() else { return };
            *v = !*v;
            ctx.emit_change();
        })
        .event(On::Keyboard, move |_, ctx| {
            let Some(ev) = ctx.keyboard() else { return };

            if ev.state != KeyState::Down {
                return;
            }

            let activate = match &ev.key {
                Key::Named(NamedKey::Enter) => true,
                Key::Character(s) => s == " ",
                _ => false,
            };

            if activate {
                let Some(mut v) = value.write() else { return };
                *v = !*v;
                ctx.emit_change();
            }
        })
        .event(On::Focus, |_, _| {})
        .children(|ui| {
            ui.node()
                .id(id!(id))
                .classes("box")
                .on_style(move |_, style| {
                    if let Some(true) = value.get() {
                        if let Unit::Px(px) = &mut style.width {
                            *px += style.border_right_width.resolve(style.font_size) + style.border_left_width.resolve(style.font_size);
                        }
                        if let Unit::Px(px) = &mut style.height {
                            *px += style.border_top_width.resolve(style.font_size) + style.border_bottom_width.resolve(style.font_size);
                        }

                        style.border_top_width = Length::ZERO;
                        style.border_right_width = Length::ZERO;
                        style.border_bottom_width = Length::ZERO;
                        style.border_left_width = Length::ZERO;
                    }
                })
                .on_canvas(move |_, ctx| {
                    if let Some(true) = value.get() {
                        let shape = Rect::from_origin_size((0.0, 0.0), ctx.rect.rect().size());
                        ctx.scene.fill(Fill::NonZero, Affine::IDENTITY, ctx.style.color, None, &shape);

                        let p0 = (15.0 * ctx.rect.width() / 48.0, ctx.rect.height() / 2.0);
                        let p1 = (6.0 * ctx.rect.width() / 12.0, 7.0 * ctx.rect.height() / 10.0);
                        let p2 = (18.0 * ctx.rect.width() / 24.0, ctx.rect.height() / 3.0);

                        let mut check = BezPath::new();
                        check.move_to(p0);
                        check.line_to(p1);
                        check.line_to(p2);

                        ctx.scene.stroke(&Stroke::new(3.0), Affine::IDENTITY, Color::WHITE, None, &check);
                    }
                });
            ui.node().id(id!(id)).text(label).classes("checkbox_label");
        })
}