make contextmenu aware of commands
e.g. to process vim-like keybindings for navigation fixes #108, fixes #157, fixes #178, fixes #199, fixes #250
This commit is contained in:
@@ -10,12 +10,12 @@ use crate::library::Library;
|
|||||||
use crate::queue::{Queue, RepeatSetting};
|
use crate::queue::{Queue, RepeatSetting};
|
||||||
use crate::spotify::{Spotify, VOLUME_PERCENT};
|
use crate::spotify::{Spotify, VOLUME_PERCENT};
|
||||||
use crate::traits::ViewExt;
|
use crate::traits::ViewExt;
|
||||||
|
use crate::ui::contextmenu::ContextMenu;
|
||||||
use crate::ui::help::HelpView;
|
use crate::ui::help::HelpView;
|
||||||
use crate::ui::layout::Layout;
|
use crate::ui::layout::Layout;
|
||||||
use crate::UserData;
|
use crate::UserData;
|
||||||
use cursive::event::{Event, Key};
|
use cursive::event::{Event, Key};
|
||||||
use cursive::traits::View;
|
use cursive::traits::View;
|
||||||
use cursive::views::ViewRef;
|
|
||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
@@ -188,8 +188,13 @@ impl CommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_callbacks(&self, s: &mut Cursive, cmd: &Command) -> Result<Option<String>, String> {
|
fn handle_callbacks(&self, s: &mut Cursive, cmd: &Command) -> Result<Option<String>, String> {
|
||||||
let local = {
|
let local = if let Some(mut contextmenu) = s.find_name::<ContextMenu>("contextmenu") {
|
||||||
let mut main: ViewRef<Layout> = s.find_name("main").unwrap();
|
contextmenu.on_command(s, cmd)?
|
||||||
|
} else {
|
||||||
|
debug!("no contextmenu");
|
||||||
|
let mut main = s
|
||||||
|
.find_name::<Layout>("main")
|
||||||
|
.expect("could not find layout");
|
||||||
main.on_command(s, cmd)?
|
main.on_command(s, cmd)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ use crate::commands::CommandManager;
|
|||||||
use crate::events::{Event, EventManager};
|
use crate::events::{Event, EventManager};
|
||||||
use crate::library::Library;
|
use crate::library::Library;
|
||||||
use crate::spotify::PlayerEvent;
|
use crate::spotify::PlayerEvent;
|
||||||
|
use crate::ui::contextmenu::ContextMenu;
|
||||||
|
|
||||||
fn setup_logging(filename: &str) -> Result<(), fern::InitError> {
|
fn setup_logging(filename: &str) -> Result<(), fern::InitError> {
|
||||||
fern::Dispatch::new()
|
fern::Dispatch::new()
|
||||||
@@ -267,9 +268,11 @@ fn main() {
|
|||||||
layout.set_view("library");
|
layout.set_view("library");
|
||||||
|
|
||||||
cursive.add_global_callback(':', move |s| {
|
cursive.add_global_callback(':', move |s| {
|
||||||
s.call_on_name("main", |v: &mut ui::layout::Layout| {
|
if s.find_name::<ContextMenu>("contextmenu").is_none() {
|
||||||
v.enable_cmdline();
|
s.call_on_name("main", |v: &mut ui::layout::Layout| {
|
||||||
});
|
v.enable_cmdline();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
layout.cmdline.set_on_edit(move |s, cmd, _| {
|
layout.cmdline.set_on_edit(move |s, cmd, _| {
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use cursive::view::{Margins, ViewWrapper};
|
use cursive::view::{Margins, ViewWrapper};
|
||||||
use cursive::views::{Dialog, ScrollView, SelectView};
|
use cursive::views::{Dialog, NamedView, ScrollView, SelectView};
|
||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
|
|
||||||
|
use crate::command::{Command, MoveAmount, MoveMode};
|
||||||
|
use crate::commands::CommandResult;
|
||||||
use crate::library::Library;
|
use crate::library::Library;
|
||||||
use crate::queue::Queue;
|
use crate::queue::Queue;
|
||||||
use crate::track::Track;
|
use crate::track::Track;
|
||||||
use crate::traits::ListItem;
|
use crate::traits::{ListItem, ViewExt};
|
||||||
use crate::ui::layout::Layout;
|
use crate::ui::layout::Layout;
|
||||||
use crate::ui::modal::Modal;
|
use crate::ui::modal::Modal;
|
||||||
#[cfg(feature = "share_clipboard")]
|
#[cfg(feature = "share_clipboard")]
|
||||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
use clipboard::{ClipboardContext, ClipboardProvider};
|
||||||
|
use cursive::traits::{Finder, Nameable};
|
||||||
|
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
dialog: Modal<Dialog>,
|
dialog: Modal<Dialog>,
|
||||||
@@ -44,7 +47,7 @@ impl ContextMenu {
|
|||||||
Modal::new(dialog)
|
Modal::new(dialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(item: &dyn ListItem, queue: Arc<Queue>, library: Arc<Library>) -> Self {
|
pub fn new(item: &dyn ListItem, queue: Arc<Queue>, library: Arc<Library>) -> NamedView<Self> {
|
||||||
let mut content: SelectView<ContextMenuAction> = SelectView::new().autojump();
|
let mut content: SelectView<ContextMenuAction> = SelectView::new().autojump();
|
||||||
if let Some(a) = item.artist() {
|
if let Some(a) = item.artist() {
|
||||||
content.add_item("Show artist", ContextMenuAction::ShowItem(Box::new(a)));
|
content.add_item("Show artist", ContextMenuAction::ShowItem(Box::new(a)));
|
||||||
@@ -92,9 +95,52 @@ impl ContextMenu {
|
|||||||
.title(item.display_left())
|
.title(item.display_left())
|
||||||
.dismiss_button("Cancel")
|
.dismiss_button("Cancel")
|
||||||
.padding(Margins::lrtb(1, 1, 1, 0))
|
.padding(Margins::lrtb(1, 1, 1, 0))
|
||||||
.content(content);
|
.content(content.with_name("contextmenu_select"));
|
||||||
Self {
|
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<CommandResult, String> {
|
||||||
|
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<ContextMenuAction>| {
|
||||||
|
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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ use cursive::view::ViewWrapper;
|
|||||||
use cursive::views::{ScrollView, TextView};
|
use cursive::views::{ScrollView, TextView};
|
||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
|
|
||||||
use crate::command::Command;
|
use crate::command::{Command, MoveAmount, MoveMode};
|
||||||
use crate::commands::CommandResult;
|
use crate::commands::CommandResult;
|
||||||
use crate::config::config_path;
|
use crate::config::config_path;
|
||||||
use crate::traits::ViewExt;
|
use crate::traits::ViewExt;
|
||||||
|
use cursive::view::scroll::Scroller;
|
||||||
|
|
||||||
pub struct HelpView {
|
pub struct HelpView {
|
||||||
view: ScrollView<TextView>,
|
view: ScrollView<TextView>,
|
||||||
@@ -50,10 +51,36 @@ impl ViewExt for HelpView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_command(&mut self, _s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
|
fn on_command(&mut self, _s: &mut Cursive, cmd: &Command) -> Result<CommandResult, String> {
|
||||||
if let Command::Help = cmd {
|
match cmd {
|
||||||
Ok(CommandResult::Consumed(None))
|
Command::Help => Ok(CommandResult::Consumed(None)),
|
||||||
} else {
|
Command::Move(mode, amount) => {
|
||||||
Ok(CommandResult::Ignored)
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,22 @@ use cursive::event::{Event, EventResult};
|
|||||||
use cursive::view::{View, ViewWrapper};
|
use cursive::view::{View, ViewWrapper};
|
||||||
|
|
||||||
pub struct Modal<T: View> {
|
pub struct Modal<T: View> {
|
||||||
|
block_events: bool,
|
||||||
inner: T,
|
inner: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: View> Modal<T> {
|
impl<T: View> Modal<T> {
|
||||||
pub fn new(inner: T) -> Self {
|
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<T: View> ViewWrapper for Modal<T> {
|
|||||||
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
|
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
|
||||||
match self.inner.on_event(ch) {
|
match self.inner.on_event(ch) {
|
||||||
EventResult::Consumed(cb) => EventResult::Consumed(cb),
|
EventResult::Consumed(cb) => EventResult::Consumed(cb),
|
||||||
_ => EventResult::Consumed(None),
|
_ => match self.block_events {
|
||||||
|
true => EventResult::Consumed(None),
|
||||||
|
false => EventResult::Ignored,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user