use cursive::traits::{Boxable, Identifiable}; use cursive::view::{Margins, ViewWrapper}; use cursive::views::{Dialog, EditView, ScrollView, SelectView}; use cursive::Cursive; use std::cmp::min; use std::sync::Arc; use crate::command::{Command, MoveMode, ShiftMode}; use crate::commands::CommandResult; use crate::library::Library; use crate::playable::Playable; use crate::queue::Queue; use crate::traits::ViewExt; use crate::ui::listview::ListView; use crate::ui::modal::Modal; pub struct QueueView { list: ListView, library: Arc, queue: Arc, } impl QueueView { pub fn new(queue: Arc, library: Arc) -> QueueView { let list = ListView::new(queue.queue.clone(), queue.clone(), library.clone()); QueueView { list, library, queue, } } fn save_dialog_cb( s: &mut Cursive, queue: Arc, library: Arc, id: Option, ) { let tracks = queue.queue.read().unwrap().clone(); match id { Some(id) => { library.overwrite_playlist(&id, &tracks); s.pop_layer(); } None => { s.pop_layer(); let edit = EditView::new() .on_submit(move |s: &mut Cursive, name| { library.save_playlist(name, &tracks); s.pop_layer(); }) .with_name("name") .fixed_width(20); let dialog = Dialog::new() .title("Enter name") .dismiss_button("Cancel") .padding(Margins::lrtb(1, 1, 1, 0)) .content(edit); s.add_layer(Modal::new(dialog)); } } } fn save_dialog(queue: Arc, library: Arc) -> Modal { let mut list_select: SelectView> = SelectView::new().autojump(); list_select.add_item("[Create new]", None); for list in library.playlists().iter() { list_select.add_item(list.name.clone(), Some(list.id.clone())); } list_select.set_on_submit(move |s, selected| { Self::save_dialog_cb(s, queue.clone(), library.clone(), selected.clone()) }); let dialog = Dialog::new() .title("Create new or overwrite existing playlist?") .dismiss_button("Cancel") .padding(Margins::lrtb(1, 1, 1, 0)) .content(ScrollView::new(list_select)); Modal::new(dialog) } } impl ViewWrapper for QueueView { wrap_impl!(self.list: ListView); } impl ViewExt for QueueView { fn title(&self) -> String { "Queue".to_string() } fn title_sub(&self) -> String { let track_count = self.queue.len(); let duration_secs: u64 = self .queue .queue .read() .unwrap() .iter() .map(|p| p.duration() as u64 / 1000) .sum(); if duration_secs > 0 { let duration = std::time::Duration::from_secs(duration_secs); format!( "{} tracks, {}", track_count, crate::utils::format_duration(&duration) ) } else { "".to_string() } } fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result { match cmd { Command::Play => { self.queue.play(self.list.get_selected_index(), true, false); return Ok(CommandResult::Consumed(None)); } Command::PlayNext => { return Ok(CommandResult::Ignored); } Command::Queue => { return Ok(CommandResult::Ignored); } Command::Delete => { let selected = self.list.get_selected_index(); let len = self.queue.len(); self.queue.remove(selected); if selected == len.saturating_sub(1) { self.list.move_focus(-1); } return Ok(CommandResult::Consumed(None)); } Command::Shift(mode, amount) => { let amount = match amount { Some(amount) => *amount, _ => 1, }; let selected = self.list.get_selected_index(); let len = self.queue.len(); match mode { ShiftMode::Up if selected > 0 => { self.queue .shift(selected, (selected as i32).saturating_sub(amount) as usize); self.list.move_focus(-(amount as i32)); return Ok(CommandResult::Consumed(None)); } ShiftMode::Down if selected < len.saturating_sub(1) => { self.queue .shift(selected, min(selected + amount as usize, len - 1)); self.list.move_focus(amount as i32); return Ok(CommandResult::Consumed(None)); } _ => {} } } Command::SaveQueue => { let dialog = Self::save_dialog(self.queue.clone(), self.library.clone()); s.add_layer(dialog); return Ok(CommandResult::Consumed(None)); } Command::Move(MoveMode::Playing, _) => { if let Some(playing) = self.queue.get_current_index() { self.list.move_focus_to(playing); } } _ => {} } self.with_view_mut(move |v| v.on_command(s, cmd)).unwrap() } }