diff --git a/src/main.rs b/src/main.rs index 6a0d1c2..6c3699d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,13 @@ use eframe::egui; use std::boxed::Box; use std::collections::VecDeque; use std::marker::PhantomData; +use symphonia::core::io::{MediaSource, MediaSourceStream}; struct PipeDash { msg_queue: VecDeque, selected_level: Option, level_list: Vec, + loaded_song: Option, editor: Editor, } @@ -29,13 +31,22 @@ enum Message { struct Editor { scroll_pos: f32, pts_per_second: f32, - beats_per_bar: f32, subdivisions: f32, - beat_rate: music::BeatRate, - time_signatures: music::TimeSignature, + data: GdlData, +} + +struct GdlData { green_lines: music::Lines, orange_lines: music::Lines, yellow_lines: music::Lines, + beat_rate: music::BeatRate, + time_signatures: music::TimeSignature, +} + +struct Song { + name: String, + id: u32, + stream: MediaSourceStream, } struct Orange; @@ -45,12 +56,31 @@ struct Green; struct BeatRateWidget<'a>(&'a mut Editor); struct TimeSignatureWidget<'a>(&'a mut Editor); struct LinesWidget<'a, C: WithColor>(&'a mut Editor, PhantomData); +struct WaveformWidget<'a>(&'a mut Editor); -trait WithColor {} +trait WithColor { + const COLOR: Color; +} -impl WithColor for Orange {} -impl WithColor for Yellow {} -impl WithColor for Green {} +impl From for eframe::epaint::Color32 { + fn from(rhs: Color) -> Self { + match rhs { + Color::Green => Self::from_rgb(0, 255, 0), + Color::Orange => Self::from_rgb(255, 127, 0), + Color::Yellow => Self::from_rgb(255, 255, 0), + } + } +} + +impl WithColor for Orange { + const COLOR: Color = Color::Orange; +} +impl WithColor for Yellow { + const COLOR: Color = Color::Yellow; +} +impl WithColor for Green { + const COLOR: Color = Color::Green; +} impl Editor { pub fn beat_rate_widget(&mut self) -> BeatRateWidget { @@ -64,6 +94,10 @@ impl Editor { pub fn lines_widget(&mut self) -> LinesWidget { LinesWidget(self, Default::default()) } + + pub fn waveform_widget(&mut self) -> WaveformWidget { + WaveformWidget(self) + } } impl<'a> egui::Widget for BeatRateWidget<'a> { @@ -100,22 +134,58 @@ impl<'a> egui::Widget for TimeSignatureWidget<'a> { } } +impl<'a, C: WithColor> egui::Widget for LinesWidget<'a, C> { + fn ui(self, ui: &mut egui::Ui) -> egui::Response { + // 1. choose size + let max_rect = ui.max_rect(); + let preferred_size = egui::Vec2::new(max_rect.size().x, 60.0); + // 2. allocate space + let (rect, res) = ui.allocate_exact_size(preferred_size, egui::Sense::click_and_drag()); + // 3. handle interactions + // 4. draw widget + if ui.is_rect_visible(rect) { + ui.painter() + .rect_filled(rect, 0f32, eframe::epaint::Color32::from_gray(0)); + } + res + } +} + +impl<'a> egui::Widget for WaveformWidget<'a> { + fn ui(self, ui: &mut egui::Ui) -> egui::Response { + // 1. choose size + let max_rect = ui.max_rect(); + let preferred_size = egui::Vec2::new(max_rect.size().x, 60.0); + // 2. allocate space + let (rect, res) = ui.allocate_exact_size(preferred_size, egui::Sense::click_and_drag()); + // 3. handle interactions + // 4. draw widget + if ui.is_rect_visible(rect) { + ui.painter() + .rect_filled(rect, 0f32, eframe::epaint::Color32::from_gray(0)); + } + res + } +} + impl PipeDash { fn new(_cc: &eframe::CreationContext) -> Self { Self { selected_level: None, msg_queue: VecDeque::new(), level_list: gd::OuterLevel::load_all(), + loaded_song: None, editor: Editor { scroll_pos: 0f32, pts_per_second: 5f32, - beats_per_bar: 4.0, subdivisions: 4.0, - beat_rate: music::StaticBeatRate::from_bpm(120f32).into(), - time_signatures: music::StaticTimeSignature::new(4, 4).into(), - green_lines: music::Lines::new(), - orange_lines: music::Lines::new(), - yellow_lines: music::Lines::new(), + data: GdlData { + beat_rate: music::StaticBeatRate::from_bpm(120f32).into(), + time_signatures: music::StaticTimeSignature::new(4, 4).into(), + green_lines: music::Lines::new(), + orange_lines: music::Lines::new(), + yellow_lines: music::Lines::new(), + }, }, } } @@ -124,20 +194,25 @@ impl PipeDash { egui::SidePanel::left("level_picker") .default_width(100f32) .show(ctx, |ui| { - egui::ScrollArea::vertical().show(ui, |ui| { - ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui| { - for (idx, level) in self.level_list.iter().enumerate() { - if ui - .selectable_label( - self.selected_level == Some(idx), - level.display_name(), - ) - .clicked() - { - self.msg_queue.push_back(Message::LevelSelected(idx)); - } - } - }) + ui.with_layout(egui::Layout::top_down(egui::Align::LEFT).with_main_justify(false), |ui| { + ui.with_layout(egui::Layout::top_down(egui::Align::LEFT).with_main_justify(true), |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + ui.with_layout(egui::Layout::top_down_justified(egui::Align::Min), |ui| { + for (idx, level) in self.level_list.iter().enumerate() { + if ui + .selectable_label( + self.selected_level == Some(idx), + level.display_name(), + ) + .clicked() + { + self.msg_queue.push_back(Message::LevelSelected(idx)); + } + } + }) + }); + }); + ui.button("Load Level"); }); }); } @@ -145,11 +220,27 @@ impl PipeDash { fn center_panel(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { egui::CentralPanel::default().show(ctx, |ui| { ui.vertical_centered_justified(|ui| { - ui.vertical(|ui| { - ui.label("Song name"); - ui.label("Song id"); - }); + ui.label( + egui::RichText::new(match &self.loaded_song { + Some(song) => &song.name, + None => "No song loaded...", + }) + .size(32.0), + ); + ui.label( + egui::RichText::new(match &self.loaded_song { + Some(song) => song.id.to_string(), + None => "No song loaded...".into(), + }) + .size(20.0), + ); + + ui.add(self.editor.time_signature_widget()); ui.add(self.editor.beat_rate_widget()); + ui.add(self.editor.lines_widget::()); + ui.add(self.editor.lines_widget::()); + ui.add(self.editor.lines_widget::()); + ui.add(self.editor.waveform_widget()); }); }); }