deLyrium/src/controls.rs

140 lines
3.4 KiB
Rust

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<dyn FormatReader>) -> 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<Message, (&Controls, Theme)> {
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<Message> for (&Controls, Theme) {
fn draw(&self, bounds: Rectangle<f32>, _cursor: Cursor) -> Vec<Geometry> {
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<f32>, cursor: Cursor) -> (Status, Option<Message>) {
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),
}
}
}