use iced::Length; use iced::Color; use symphonia::core::formats::FormatReader; use crate::player::PlayerError; use iced::canvas::event::Status; use iced::canvas::Event; use crate::styles::Theme; use iced::canvas::Frame; use iced::canvas::Geometry; use iced::canvas::Cursor; use iced::Rectangle; use crate::app::Message; use iced::canvas::Program; use iced::Canvas; use iced::mouse::{self, Button}; use crate::player::Player; #[derive(Debug, Clone, Copy)] pub enum ControlsEvent { SeekPosition(f32), TogglePlay, } enum ErrorState { Error(PlayerError), NoError { player: Player, has_device: bool } } use ErrorState::*; pub struct Controls { error_state: ErrorState, } impl Controls { pub fn new(song: Box) -> Self { match Player::new(song) { Ok(player) => { Controls { error_state: NoError { has_device: player.has_output_device(), player, } } }, Err(e) => { Controls { error_state: Error(e) } } } } pub fn view_progress(&mut self, theme: Theme) -> Canvas { Canvas::new((&*self, theme)) .width(Length::Units(50)) .height(Length::Fill) } pub fn handle_event(&mut self, event: ControlsEvent) { if let NoError { player, has_device } = &mut self.error_state { let result = match event { ControlsEvent::SeekPosition(pos) => player.seek_percentage(pos), ControlsEvent::TogglePlay => player.toggle_play(), }; match result { Ok(now_has_device) => { *has_device = now_has_device; }, Err(e) => { self.error_state = Error(e); }, }; } } pub fn is_playing(&self) -> bool { if let NoError { player, has_device: true } = &self.error_state { player.is_playing() } else { false } } } impl Program for (&Controls, Theme) { fn draw(&self, bounds: Rectangle, _cursor: Cursor) -> Vec { let mut frame = Frame::new(bounds.size()); match &self.0.error_state { NoError { player, has_device: true } => { let mut background = self.1.text_color; background.a = 0.2; frame.fill_rectangle(bounds.position(), bounds.size(), background); let mut played_size = bounds.size(); played_size.height *= player.position_percent(); frame.fill_rectangle(bounds.position(), played_size, self.1.text_color); }, NoError { player: _, has_device: false } => { let mut background = self.1.text_color; background.a = 0.1; frame.fill_rectangle(bounds.position(), bounds.size(), background); }, Error(e) => { let background = Color {r: 1., g: 0.1, b: 0.1, a: 1.}; frame.fill_rectangle(bounds.position(), bounds.size(), background); eprintln!("Error!!! {}", e.to_string()); } } vec![frame.into_geometry()] } fn update(&mut self, event: Event, bounds: Rectangle, cursor: Cursor) -> (Status, Option) { match (event, cursor) { (Event::Mouse(mouse::Event::ButtonReleased(Button::Left)), Cursor::Available(pos)) if bounds.contains(pos) => { let sought = (pos.y - bounds.position().y) / bounds.size().height; (Status::Captured, Some(Message::ControlsEvent(ControlsEvent::SeekPosition(sought)))) }, (Event::Mouse(mouse::Event::ButtonReleased(Button::Right)), Cursor::Available(pos)) if bounds.contains(pos) => { // TODO! This should be somewhere intuitive (Status::Captured, Some(Message::ControlsEvent(ControlsEvent::TogglePlay))) }, _ => (Status::Ignored, None), } } }