maolan-widgets 0.0.2

Widgets used for Maolan DAW
Documentation
use crate::{
    midi::{PITCH_MAX, PianoNote},
    piano::{note_color, note_two_edge_gradient, row_height},
};
use iced::{
    Element, Length, Point,
    widget::{container, pin},
};

pub struct PianoRoll<'a, Message> {
    notes: Vec<PianoNote>,
    clip_length_samples: usize,
    zoom_y: f32,
    pixels_per_sample: f32,
    zoom_x: f32,
    interaction: Element<'a, Message>,
}

impl<'a, Message: 'a> PianoRoll<'a, Message> {
    pub fn new(
        notes: Vec<PianoNote>,
        clip_length_samples: usize,
        zoom_y: f32,
        pixels_per_sample: f32,
        zoom_x: f32,
        interaction: Element<'a, Message>,
    ) -> Self {
        Self {
            notes,
            clip_length_samples,
            zoom_y,
            pixels_per_sample,
            zoom_x,
            interaction,
        }
    }

    pub fn into_element(self) -> Element<'a, Message> {
        let row_h = row_height(self.zoom_y);
        let pps = (self.pixels_per_sample * self.zoom_x).max(0.0001);
        let notes_w = (self.clip_length_samples as f32 * pps).max(1.0);
        let notes_h = crate::midi::MIDI_NOTE_COUNT as f32 * row_h;

        let mut layers: Vec<Element<'a, Message>> = vec![];
        for note in &self.notes {
            if note.pitch > PITCH_MAX {
                continue;
            }
            let y_idx = usize::from(PITCH_MAX.saturating_sub(note.pitch));
            let y = y_idx as f32 * row_h + 1.0;
            let x = note.start_sample as f32 * pps;
            let w = (note.length_samples as f32 * pps).max(2.0);
            let color = note_color(note.velocity, note.channel);
            layers.push(
                pin(container("")
                    .width(Length::Fixed(w))
                    .height(Length::Fixed((row_h - 2.0).max(2.0)))
                    .style(move |_theme| container::Style {
                        background: Some(note_two_edge_gradient(color)),
                        ..container::Style::default()
                    }))
                .position(Point::new(x, y))
                .into(),
            );
        }

        layers.push(pin(self.interaction).position(Point::new(0.0, 0.0)).into());

        iced::widget::Stack::from_vec(layers)
            .width(Length::Fixed(notes_w))
            .height(Length::Fixed(notes_h))
            .into()
    }
}