diff --git a/src/commands.rs b/src/commands.rs index 4ffb5bb..4cea443 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -10,12 +10,12 @@ use crate::library::Library; use crate::queue::{Queue, RepeatSetting}; use crate::spotify::{Spotify, VOLUME_PERCENT}; use crate::traits::ViewExt; +use crate::ui::contextmenu::ContextMenu; use crate::ui::help::HelpView; use crate::ui::layout::Layout; use crate::UserData; use cursive::event::{Event, Key}; use cursive::traits::View; -use cursive::views::ViewRef; use cursive::Cursive; use std::cell::RefCell; @@ -188,8 +188,13 @@ impl CommandManager { } fn handle_callbacks(&self, s: &mut Cursive, cmd: &Command) -> Result, String> { - let local = { - let mut main: ViewRef = s.find_name("main").unwrap(); + let local = if let Some(mut contextmenu) = s.find_name::("contextmenu") { + contextmenu.on_command(s, cmd)? + } else { + debug!("no contextmenu"); + let mut main = s + .find_name::("main") + .expect("could not find layout"); main.on_command(s, cmd)? }; diff --git a/src/main.rs b/src/main.rs index 402edde..999c5da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,6 +77,7 @@ use crate::commands::CommandManager; use crate::events::{Event, EventManager}; use crate::library::Library; use crate::spotify::PlayerEvent; +use crate::ui::contextmenu::ContextMenu; fn setup_logging(filename: &str) -> Result<(), fern::InitError> { fern::Dispatch::new() @@ -267,9 +268,11 @@ fn main() { layout.set_view("library"); cursive.add_global_callback(':', move |s| { - s.call_on_name("main", |v: &mut ui::layout::Layout| { - v.enable_cmdline(); - }); + if s.find_name::("contextmenu").is_none() { + s.call_on_name("main", |v: &mut ui::layout::Layout| { + v.enable_cmdline(); + }); + } }); layout.cmdline.set_on_edit(move |s, cmd, _| { diff --git a/src/ui/contextmenu.rs b/src/ui/contextmenu.rs index 77a04c9..abdbb7b 100644 --- a/src/ui/contextmenu.rs +++ b/src/ui/contextmenu.rs @@ -1,17 +1,20 @@ use std::sync::Arc; use cursive::view::{Margins, ViewWrapper}; -use cursive::views::{Dialog, ScrollView, SelectView}; +use cursive::views::{Dialog, NamedView, ScrollView, SelectView}; use cursive::Cursive; +use crate::command::{Command, MoveAmount, MoveMode}; +use crate::commands::CommandResult; use crate::library::Library; use crate::queue::Queue; use crate::track::Track; -use crate::traits::ListItem; +use crate::traits::{ListItem, ViewExt}; use crate::ui::layout::Layout; use crate::ui::modal::Modal; #[cfg(feature = "share_clipboard")] use clipboard::{ClipboardContext, ClipboardProvider}; +use cursive::traits::{Finder, Nameable}; pub struct ContextMenu { dialog: Modal, @@ -44,7 +47,7 @@ impl ContextMenu { Modal::new(dialog) } - pub fn new(item: &dyn ListItem, queue: Arc, library: Arc) -> Self { + pub fn new(item: &dyn ListItem, queue: Arc, library: Arc) -> NamedView { let mut content: SelectView = SelectView::new().autojump(); if let Some(a) = item.artist() { content.add_item("Show artist", ContextMenuAction::ShowItem(Box::new(a))); @@ -92,9 +95,52 @@ impl ContextMenu { .title(item.display_left()) .dismiss_button("Cancel") .padding(Margins::lrtb(1, 1, 1, 0)) - .content(content); + .content(content.with_name("contextmenu_select")); Self { - dialog: Modal::new(dialog), + dialog: Modal::new_ext(dialog), + } + .with_name("contextmenu") + } +} + +impl ViewExt for ContextMenu { + fn on_command(&mut self, s: &mut Cursive, cmd: &Command) -> Result { + match cmd { + Command::Back => { + s.pop_layer(); + Ok(CommandResult::Consumed(None)) + } + Command::Move(mode, amount) => self + .dialog + .call_on_name( + "contextmenu_select", + |select: &mut SelectView| { + let items = select.len(); + match mode { + MoveMode::Up => { + match amount { + MoveAmount::Extreme => select.set_selection(0), + MoveAmount::Integer(amount) => { + select.select_up(*amount as usize) + } + }; + Ok(CommandResult::Consumed(None)) + } + MoveMode::Down => { + match amount { + MoveAmount::Extreme => select.set_selection(items), + MoveAmount::Integer(amount) => { + select.select_down(*amount as usize) + } + }; + Ok(CommandResult::Consumed(None)) + } + _ => Ok(CommandResult::Consumed(None)), + } + }, + ) + .unwrap_or(Ok(CommandResult::Consumed(None))), + _ => Ok(CommandResult::Consumed(None)), } } } diff --git a/src/ui/help.rs b/src/ui/help.rs index 620fbd0..9266651 100644 --- a/src/ui/help.rs +++ b/src/ui/help.rs @@ -6,10 +6,11 @@ use cursive::view::ViewWrapper; use cursive::views::{ScrollView, TextView}; use cursive::Cursive; -use crate::command::Command; +use crate::command::{Command, MoveAmount, MoveMode}; use crate::commands::CommandResult; use crate::config::config_path; use crate::traits::ViewExt; +use cursive::view::scroll::Scroller; pub struct HelpView { view: ScrollView, @@ -50,10 +51,36 @@ impl ViewExt for HelpView { } fn on_command(&mut self, _s: &mut Cursive, cmd: &Command) -> Result { - if let Command::Help = cmd { - Ok(CommandResult::Consumed(None)) - } else { - Ok(CommandResult::Ignored) + match cmd { + Command::Help => Ok(CommandResult::Consumed(None)), + Command::Move(mode, amount) => { + let scroller = self.view.get_scroller_mut(); + let viewport = scroller.content_viewport(); + match mode { + MoveMode::Up => { + match amount { + MoveAmount::Extreme => { + self.view.scroll_to_top(); + } + MoveAmount::Integer(amount) => scroller + .scroll_to_y(viewport.top().saturating_sub(*amount as usize)), + }; + Ok(CommandResult::Consumed(None)) + } + MoveMode::Down => { + match amount { + MoveAmount::Extreme => { + self.view.scroll_to_bottom(); + } + MoveAmount::Integer(amount) => scroller + .scroll_to_y(viewport.bottom().saturating_add(*amount as usize)), + }; + Ok(CommandResult::Consumed(None)) + } + _ => Ok(CommandResult::Consumed(None)), + } + } + _ => Ok(CommandResult::Ignored), } } } diff --git a/src/ui/modal.rs b/src/ui/modal.rs index 177ead5..feec6c7 100644 --- a/src/ui/modal.rs +++ b/src/ui/modal.rs @@ -2,12 +2,22 @@ use cursive::event::{Event, EventResult}; use cursive::view::{View, ViewWrapper}; pub struct Modal { + block_events: bool, inner: T, } impl Modal { pub fn new(inner: T) -> Self { - Modal { inner } + Modal { + block_events: true, + inner, + } + } + pub fn new_ext(inner: T) -> Self { + Modal { + block_events: false, + inner, + } } } @@ -16,7 +26,10 @@ impl ViewWrapper for Modal { fn wrap_on_event(&mut self, ch: Event) -> EventResult { match self.inner.on_event(ch) { EventResult::Consumed(cb) => EventResult::Consumed(cb), - _ => EventResult::Consumed(None), + _ => match self.block_events { + true => EventResult::Consumed(None), + false => EventResult::Ignored, + }, } } }