diff --git a/src/app.rs b/src/app.rs index a7495bd..48f6195 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,19 +1,20 @@ use std::{path::PathBuf, time::Duration}; -use rmp::TrackChangeOptions; +use rmp::{QueuePlaylist, TrackChangeOptions}; use crate::playback::Playback; +pub struct AppOptions { + pub title: String, + pub enhanced_graphics: bool, +} + pub struct App { pub options: AppOptions, pub should_quit: bool, pub playback: Playback, pub track_change_options: TrackChangeOptions, -} - -pub struct AppOptions { - pub title: String, - pub enhanced_graphics: bool, + pub queue_playlist: QueuePlaylist, } impl App { @@ -24,6 +25,7 @@ impl App { playback: Playback::new(), // TODO: Maybe read from some configuration (args, file, etc...) track_change_options: Default::default(), + queue_playlist: QueuePlaylist::generate_mock(), } } @@ -62,12 +64,21 @@ impl App { pub fn on_right(&mut self) {} - pub fn on_up(&mut self) {} + pub fn on_up(&mut self) { + // TODO: Apply to selected playlist + self.queue_playlist.playlist.previous(); + } - pub fn on_down(&mut self) {} + pub fn on_down(&mut self) { + // TODO: Apply to selected playlist + self.queue_playlist.playlist.next(); + } pub fn on_enter(&mut self) { - self.play("".into()); // TODO: Remove hardcoding + // TODO: Apply to selected playlist + if let Some(current) = self.queue_playlist.playlist.current() { + self.play(current.into()); + } } pub fn on_tab(&mut self) {} diff --git a/src/lib.rs b/src/lib.rs index 889dba6..9f2341e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,102 @@ -use std::{path::PathBuf, str::FromStr}; +use std::{ops::Deref, path::PathBuf}; -#[derive(Debug, Default)] -pub struct ServerState { - pub track_change_options: TrackChangeOptions, - pub player: Option, +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +pub struct ListState { + offset: usize, + selected: Option, +} + +impl ListState { + pub fn offset(&self) -> usize { + self.offset + } + + pub fn offset_mut(&mut self) -> &mut usize { + &mut self.offset + } + + pub fn with_selected(mut self, selected: Option) -> Self { + self.selected = selected; + self + } + + pub fn with_offset(mut self, offset: usize) -> Self { + self.offset = offset; + self + } + + pub fn selected(&self) -> Option { + self.selected + } + + pub fn select(&mut self, index: Option) { + self.selected = index; + if index.is_none() { + self.offset = 0; + } + } } #[derive(Debug, Default)] -pub struct PlayerState { - pub track: PlaylistElement, - pub is_paused: bool, - pub duration: Option, - pub current_position: Option, +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +impl StatefulList +where + T: Deref + Eq, +{ + pub fn with_items(items: Vec) -> StatefulList { + StatefulList { + state: ListState::default(), + items, + } + } + + pub fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + pub fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + pub fn index_of(&self, item: T) -> Option { + self.items.iter().position(|element| *element == item) + } + + pub fn current(&self) -> Option<&T> { + self.state + .selected + .map_or(None, |index| self.items.get(index)) + } + + pub fn current_mut(&mut self) -> Option<&mut T> { + self.state + .selected + .map_or(None, |index| self.items.get_mut(index)) + } } pub type PlaylistElement = PathBuf; @@ -21,23 +106,32 @@ pub enum PlaylistType { Queue, } -#[derive(Debug, Default)] -pub struct Playlist { - pub items: Vec, - pub current: Option, -} - -impl Playlist {} - #[derive(Debug)] pub struct DirectoryPlaylist { pub directory: PathBuf, - pub playlist: Playlist, + pub playlist: StatefulList, } #[derive(Debug, Default)] pub struct QueuePlaylist { - pub playlist: Playlist, + pub playlist: StatefulList, +} + +impl QueuePlaylist { + pub fn generate_mock() -> Self { + Self { + playlist: StatefulList::with_items(vec![ + "/home/hheikkinen/Music/Mariya Takeuchi/Mariya Takeuchi (竹内 まりや) - September [Lyrics Kan⧸Rom⧸Eng] [3wkxIBBP7Vg].opus", + "/home/hheikkinen/Music/Compilerbau - Le Jardin/LEJARDIN.OGG", + "/home/hheikkinen/Music/Casiopea - Mint Jams (1982) FULL ALBUM/001 Take Me.opus", + "/home/hheikkinen/Music/Casiopea - Mint Jams (1982) FULL ALBUM/002 Asayake.opus", + "/home/hheikkinen/Music/A Groovy Thing/01 - Flamingosis - A Groovy Intro.mp3", + "/home/hheikkinen/Music/Brian Ellis - Smocaine 3/Brian Ellis - Smocaine 3- An MDE Film OST - 01 Smocaine Theme (TV Edit).wav", + "/home/hheikkinen/Music/Jun Fukamachi - Starview HCT-5808/T-02.m4a", + "/home/hheikkinen/Music/noby/Fluidy [189046035].flac", + ].iter().map(|val| val.into()).collect()), + } + } } #[derive(Debug)] @@ -57,26 +151,6 @@ impl Default for TrackChangeOptions { } } -#[derive(Clone, Copy, Debug, Default)] -pub enum LogLevel { - Quiet = 0, - Error = 1, - #[default] - All = 2, -} - -impl FromStr for LogLevel { - type Err = (); - fn from_str(s: &str) -> Result { - match s { - "quiet" => Ok(Self::Quiet), - "error" => Ok(Self::Error), - "all" => Ok(Self::All), - _ => Err(()), - } - } -} - pub mod server { use std::{fmt::Debug, path::PathBuf}; diff --git a/src/ui.rs b/src/ui.rs index baf07b1..bd37047 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -19,8 +19,44 @@ static SECONDARY_COLOR: Color = Color::Rgb(200, 200, 200); static PRIMARY_CONTRAST: Color = Color::Black; static CLEAR_CONTRAST: Color = Color::Rgb(100, 100, 100); -fn draw_playlist(f: &mut Frame, _app: &App, area: Rect) { - let playlist = List::new(vec![]) +fn draw_playlist(f: &mut Frame, app: &App, area: Rect) { + let tracks: Vec<_> = app + .queue_playlist + .playlist + .items + .iter() + .enumerate() + .map(|(index, path)| { + let selected = app + .queue_playlist + .playlist + .state + .selected() + .map_or(false, |selected| index == selected); + // let playing = app + // .player_state + // .currently_playing + // .clone() + // .map_or(false, |currently_playing| currently_playing == *path); + let playing = false; + let mut style = Style::default(); + match (selected, playing) { + (true, false) => { + style.fg = Some(Color::Black); + style.bg = Some(PRIMARY_COLOR); + } + (false, true) => style.fg = Some(PRIMARY_COLOR), + (true, true) => { + style.fg = None; + style.bg = Some(PRIMARY_COLOR); + } + (_, _) => (), + } + let content = Span::from(path.to_string_lossy().to_string()); + ListItem::new(content).set_style(style) + }) + .collect(); + let playlist = List::new(tracks) .block( Block::default() .borders(Borders::ALL)