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 crate::story_board::{StoryBoard, easing};
use skia_safe::{Canvas, Color4f, Paint, Point, Rect};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
pub struct AudioVisualization {
sh_state: RefCell<StoryBoard>,
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 {
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| {
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<()> {
let slide_state = f64::from(&*self.sh_state.borrow()) as f32;
let slide_percent = slide_state / 100.0;
let panel_w = self.w as f32;
let max_slide_offset = -(panel_w + 90.0);
let anim_x = slide_percent * max_slide_offset;
if anim_x <= max_slide_offset + 5.0 {
return Ok(()); }
canvas.save();
canvas.translate(Point::new(anim_x, 0.0));
self.internal_layout.draw(canvas)?;
canvas.restore();
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> {
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);
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());
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,
},
);
let instance = Rc::new(Self {
sh_state: RefCell::new(StoryBoard::init(
100f64,
0f64,
500f64,
false,
easing::cubic_in_out,
)),
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 to_layout_element(self: &Rc<Self>) -> EdgeLayoutElement {
EdgeLayoutElement {
drawable: self.clone(),
l_edge: Edges::Left,
l_margin: 40.0,
t_edge: Edges::Top,
t_margin: 91.0,
r_edge: Edges::Left,
r_margin: 40.0 + self.w as f32,
b_edge: Edges::Top,
b_margin: 91.0 + self.h as f32,
}
}
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;
}
pub fn toggle_sh(&self) {
let mut sh = self.sh_state.borrow_mut();
sh.reverse();
sh.restart();
}
}