yuno 0.2.1

Multimedia UI layout and rendering framework powered by Skia.
pub mod audio_spectrum_chart;

use crate::controls::audio_visualization::audio_spectrum_chart::AudioSpectrumChart;
use crate::drawing::{Drawable, Plan, RoundedRectangle};
use crate::interacting::{Event, Interactive};
use crate::layouts::{EdgeLayout, EdgeLayoutElement, Edges, HasPlanner, Layout};
use skia_safe::{Canvas, Color4f, Paint, Rect};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

pub struct AudioVisualization {
    w: u32,
    h: u32,

    planner: RefCell<Option<Rc<dyn Layout>>>,
    internal_layout: Rc<EdgeLayout>,
    spectrum_chart: Rc<AudioSpectrumChart>,

    enabled: RefCell<bool>,
}

impl HasPlanner for AudioVisualization {
    fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
        &self.planner
    }
    fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
        *self.planner.borrow_mut() = p;
    }
}

impl Layout for AudioVisualization {
    fn measure_child(&self, _c: &dyn Drawable, canvas: &Canvas) -> Rect {
        // Transparently pass the planned area to the internal layout
        self.get_planned_drawing_area(canvas)
    }

    fn as_layout(&self) -> &dyn Layout {
        self
    }

    fn foreach_child(&self, f: &mut dyn FnMut(&dyn Drawable)) {
        f(self.internal_layout.as_ref());
        f(self.spectrum_chart.as_ref());
    }
}

impl Interactive for AudioVisualization {
    fn handle_and_route(&self, e: &Event, u: &dyn Any) {
        self.foreach_child(&mut |x| {
            // Check if the child is enabled before attempting to route events
            if x.is_enabled()
                && let Some(i) = x.try_to_interactive()
            {
                i.handle_and_route(e, u);
            }
        });
    }
}

impl Drawable for AudioVisualization {
    fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
        self.internal_layout.draw(canvas)?;
        Ok(())
    }

    fn get_horizontal_plan(&self) -> Plan {
        Plan::Fit(self.w as f32)
    }

    fn get_vertical_plan(&self) -> Plan {
        Plan::Fit(self.h as f32)
    }

    fn replan(&self) {
        self.foreach_child(&mut |x| x.replan());
    }

    fn try_to_interactive(&self) -> Option<&dyn Interactive> {
        Some(self)
    }

    fn is_enabled(&self) -> bool {
        *self.enabled.borrow()
    }

    fn set_enabled(&self, e: bool) {
        *self.enabled.borrow_mut() = e;
    }

    fn as_drawable(&self) -> &dyn Drawable {
        self
    }
}

impl AudioVisualization {
    pub fn new(v: &HashMap<u64, [f32; 128]>, w: u32, h: u32) -> Rc<Self> {
        // 1. Setup Paints
        let mut bg_paint = Paint::default();
        bg_paint
            .set_anti_alias(true)
            .set_color4f(Color4f::new(0.08, 0.08, 0.1, 0.75), None);

        let mut border_paint = Paint::default();
        border_paint
            .set_anti_alias(true)
            .set_style(skia_safe::paint::Style::Stroke)
            .set_stroke_width(1.5)
            .set_color4f(Color4f::new(1.0, 1.0, 1.0, 0.12), None);

        // 2. Initialize Components
        let bg =
            RoundedRectangle::new(Plan::FillPositive, Plan::FillPositive, 12.0, 12.0, bg_paint);
        let border = RoundedRectangle::new(
            Plan::FillPositive,
            Plan::FillPositive,
            12.0,
            12.0,
            border_paint,
        );
        let spectrum_chart = AudioSpectrumChart::new(v.clone());

        // 3. Assemble Layout using Fluent API
        let internal_layout = EdgeLayout::new(true)
            .add_child(
                "bg",
                EdgeLayoutElement {
                    drawable: bg,
                    l_edge: Edges::Left,
                    l_margin: 0.0,
                    t_edge: Edges::Top,
                    t_margin: 0.0,
                    r_edge: Edges::Right,
                    r_margin: 0.0,
                    b_edge: Edges::Bottom,
                    b_margin: 0.0,
                },
            )
            .add_child(
                "border",
                EdgeLayoutElement {
                    drawable: border,
                    l_edge: Edges::Left,
                    l_margin: 0.0,
                    t_edge: Edges::Top,
                    t_margin: 0.0,
                    r_edge: Edges::Right,
                    r_margin: 0.0,
                    b_edge: Edges::Bottom,
                    b_margin: 0.0,
                },
            )
            .add_child(
                "spectrum",
                EdgeLayoutElement {
                    drawable: spectrum_chart.clone(),
                    l_edge: Edges::Left,
                    l_margin: 16.0,
                    t_edge: Edges::Top,
                    t_margin: 20.0,
                    r_edge: Edges::Right,
                    r_margin: 16.0,
                    b_edge: Edges::Bottom,
                    b_margin: 20.0,
                },
            );

        // 4. Final Construction
        let instance = Rc::new(Self {
            w,
            h,
            planner: RefCell::new(None),
            internal_layout: internal_layout.clone(),
            spectrum_chart,
            enabled: RefCell::new(true),
        });

        internal_layout.set_planner(Some(instance.clone() as Rc<dyn Layout>));

        instance
    }

    pub fn set_data(&self, v: &HashMap<u64, [f32; 128]>) {
        *self.spectrum_chart.v.borrow_mut() = v.clone();
    }

    pub fn set_current_time(&self, t: i64) {
        *self.spectrum_chart.current_time.borrow_mut() = t;
    }
}