use rlvgl_core::event::Event;
use rlvgl_core::renderer::Renderer;
use rlvgl_core::widget::{Rect, Widget};
use super::bargraph::LedBargraph;
use super::needle::NeedleVu;
use super::numeric::NumericPeak;
pub trait MeterWidget: Widget {
fn update(&mut self, dbfs: f32, dt: f32);
fn reset(&mut self);
}
impl MeterWidget for LedBargraph {
fn update(&mut self, dbfs: f32, dt: f32) {
LedBargraph::update(self, dbfs, dt);
}
fn reset(&mut self) {
LedBargraph::reset(self);
}
}
impl MeterWidget for NeedleVu {
fn update(&mut self, dbfs: f32, dt: f32) {
let _ = NeedleVu::update(self, dbfs, dt);
}
fn reset(&mut self) {
NeedleVu::reset(self);
}
}
impl MeterWidget for NumericPeak {
fn update(&mut self, dbfs: f32, dt: f32) {
let _ = NumericPeak::update(self, dbfs, dt);
}
fn reset(&mut self) {
NumericPeak::reset(self);
}
}
pub fn split_horizontal(outer: Rect, gap: i32) -> (Rect, Rect) {
let half = ((outer.width - gap) / 2).max(1);
let left = Rect {
x: outer.x,
y: outer.y,
width: half,
height: outer.height,
};
let right = Rect {
x: outer.x + half + gap,
y: outer.y,
width: outer.width - half - gap,
height: outer.height,
};
(left, right)
}
pub struct StereoPair<W: MeterWidget> {
bounds: Rect,
gap: i32,
pub left: W,
pub right: W,
}
impl<W: MeterWidget> StereoPair<W> {
pub fn new(bounds: Rect, gap: i32, left: W, right: W) -> Self {
Self {
bounds,
gap,
left,
right,
}
}
pub fn bounds(&self) -> Rect {
self.bounds
}
pub fn gap(&self) -> i32 {
self.gap
}
pub fn update_stereo(&mut self, left_dbfs: f32, right_dbfs: f32, dt: f32) {
self.left.update(left_dbfs, dt);
self.right.update(right_dbfs, dt);
}
pub fn reset(&mut self) {
self.left.reset();
self.right.reset();
}
}
impl<W: MeterWidget> Widget for StereoPair<W> {
fn bounds(&self) -> Rect {
self.bounds
}
fn draw(&self, renderer: &mut dyn Renderer) {
self.left.draw(renderer);
self.right.draw(renderer);
}
fn handle_event(&mut self, _event: &Event) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::meters::presets::BROADCAST_CLASSIC_BARGRAPH;
use rlvgl_core::widget::Color;
#[test]
fn split_horizontal_basic() {
let outer = Rect {
x: 0,
y: 0,
width: 100,
height: 50,
};
let (l, r) = split_horizontal(outer, 4);
assert_eq!(l.x, 0);
assert_eq!(l.width, 48);
assert_eq!(r.x, 52);
assert_eq!(r.width, 48);
}
#[test]
fn stereo_bargraph_forwards_updates_independently() {
let outer = Rect {
x: 0,
y: 0,
width: 80,
height: 320,
};
let (lb, rb) = split_horizontal(outer, 4);
let left = LedBargraph::new(lb, &BROADCAST_CLASSIC_BARGRAPH);
let right = LedBargraph::new(rb, &BROADCAST_CLASSIC_BARGRAPH);
let mut pair = StereoPair::new(outer, 4, left, right);
for _ in 0..120 {
pair.update_stereo(-30.0, -10.0, 1.0 / 60.0);
}
let l_reading = pair.left.reading_db();
let r_reading = pair.right.reading_db();
assert!(
r_reading > l_reading + 5.0,
"right ({}) should read meaningfully higher than left ({})",
r_reading,
l_reading
);
}
#[test]
fn stereo_draws_both_children() {
struct Counter {
count: usize,
}
impl Renderer for Counter {
fn fill_rect(&mut self, _r: Rect, _c: Color) {
self.count += 1;
}
fn draw_text(&mut self, _p: (i32, i32), _t: &str, _c: Color) {}
}
let outer = Rect {
x: 0,
y: 0,
width: 80,
height: 320,
};
let (lb, rb) = split_horizontal(outer, 4);
let left = LedBargraph::new(lb, &BROADCAST_CLASSIC_BARGRAPH);
let right = LedBargraph::new(rb, &BROADCAST_CLASSIC_BARGRAPH);
let pair = StereoPair::new(outer, 4, left, right);
let mut c = Counter { count: 0 };
pair.draw(&mut c);
let expected = 2 * (1 + BROADCAST_CLASSIC_BARGRAPH.layout.led_count as usize);
assert_eq!(c.count, expected);
}
}