Emulate double click to play items
Change the behavior of `ListView` to treat a click on an item that is already selected as a double click. It allows the user to play songs (not episodes since currently it's not possible to determine if a `ListItem` is an episode) by "double clicking" them. It is a bit of a hack, but it works pretty well. A possible downside is that when people that don't like mouse integration in a TUI click to focus the terminal window, it could jump to the song they clicked if that happened to be the selected one.
This commit is contained in:
@@ -125,6 +125,11 @@ impl<I: ListItem + Clone> ListView<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether this [ListView] has visible scrollbars.
|
||||||
|
pub fn has_visible_scrollbars(&self) -> bool {
|
||||||
|
self.scroller.get_show_scrollbars()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_selected_index(&self) -> usize {
|
pub fn get_selected_index(&self) -> usize {
|
||||||
self.selected
|
self.selected
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,9 @@ impl<I: ListItem + Clone> ListView<I> {
|
|||||||
self.move_focus_to(max(new, 0) as usize);
|
self.move_focus_to(max(new, 0) as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Append the currently selected item and all the following ones to the queue after the
|
||||||
|
/// currently playing track and start playing them. Returns true if adding and playing the
|
||||||
|
/// tracks succeeded, false otherwhise.
|
||||||
fn attempt_play_all_tracks(&self) -> bool {
|
fn attempt_play_all_tracks(&self) -> bool {
|
||||||
let content = self.content.read().unwrap();
|
let content = self.content.read().unwrap();
|
||||||
let any = &(*content) as &dyn std::any::Any;
|
let any = &(*content) as &dyn std::any::Any;
|
||||||
@@ -172,6 +180,14 @@ impl<I: ListItem + Clone> ListView<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Appends the currently focused item after the currently playing item and starts playing it.
|
||||||
|
fn play_current_item(&mut self) {
|
||||||
|
let mut content = self.content.write().unwrap();
|
||||||
|
if let Some(listitem) = content.get_mut(self.selected) {
|
||||||
|
listitem.play(&self.queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove(&self, index: usize) {
|
pub fn remove(&self, index: usize) {
|
||||||
let mut c = self.content.write().unwrap();
|
let mut c = self.content.write().unwrap();
|
||||||
c.remove(index);
|
c.remove(index);
|
||||||
@@ -328,31 +344,60 @@ impl<I: ListItem + Clone> View for ListView<I> {
|
|||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
} => {
|
} => {
|
||||||
if self.scroller.get_show_scrollbars()
|
// This is safe as a mouse event is only propagated to a view when it is inside the
|
||||||
&& position
|
// view. Therefore underflow shouldn't occur.
|
||||||
.checked_sub(offset)
|
let view_coordinates_click_position = position - offset;
|
||||||
.map(|p| self.scroller.start_drag(p))
|
|
||||||
.unwrap_or(false)
|
let drag_started = if self.has_visible_scrollbars() {
|
||||||
{
|
self.scroller.start_drag(view_coordinates_click_position)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if drag_started {
|
||||||
log::debug!("grabbing scroller");
|
log::debug!("grabbing scroller");
|
||||||
} else {
|
} else {
|
||||||
let viewport = self.scroller.content_viewport().top_left();
|
let viewport = self.scroller.content_viewport().top_left();
|
||||||
let selected_row = position.checked_sub(offset).map(|p| p.y + viewport.y);
|
let selected_row = position.checked_sub(offset).map(|p| p.y + viewport.y);
|
||||||
if let Some(y) = selected_row.filter(|row| row < &self.content_len(false)) {
|
if let Some(clicked_row_index) =
|
||||||
self.move_focus_to(y);
|
selected_row.filter(|row| *row < self.content_len(false))
|
||||||
|
{
|
||||||
|
let currently_selected_listitem = self
|
||||||
|
.content
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(clicked_row_index)
|
||||||
|
.map(ListItem::as_listitem);
|
||||||
|
let currently_selected_is_individual = currently_selected_listitem
|
||||||
|
.filter(|item| item.track().is_some())
|
||||||
|
.is_some();
|
||||||
|
if self.selected == clicked_row_index && currently_selected_is_individual {
|
||||||
|
// The selected position was already focused. Play the item at the
|
||||||
|
// position as if Enter was pressed. This sort of emulates double
|
||||||
|
// clicking, which isn't supported by Cursive.
|
||||||
|
self.queue.clear();
|
||||||
|
|
||||||
let queue = self.queue.clone();
|
if !self.attempt_play_all_tracks() {
|
||||||
let library = self.library.clone();
|
self.play_current_item();
|
||||||
if let Some(target) = {
|
}
|
||||||
|
} else {
|
||||||
|
// The clicked position wasn't focused yet or the item is a collection
|
||||||
|
// that can be opened.
|
||||||
|
self.move_focus_to(clicked_row_index);
|
||||||
let content = self.content.read().unwrap();
|
let content = self.content.read().unwrap();
|
||||||
content.get(self.selected).map(|t| t.as_listitem())
|
let clicked_list_item =
|
||||||
} {
|
content.get(self.selected).map(ListItem::as_listitem);
|
||||||
if let Some(view) = target.open(queue, library) {
|
|
||||||
return EventResult::Consumed(Some(Callback::from_fn_once(
|
if let Some(target) = clicked_list_item {
|
||||||
move |s| {
|
if let Some(view) =
|
||||||
s.on_layout(|_, mut l| l.push_view(view));
|
target.open(self.queue.clone(), self.library.clone())
|
||||||
},
|
{
|
||||||
)));
|
return EventResult::Consumed(Some(Callback::from_fn_once(
|
||||||
|
move |s| {
|
||||||
|
s.on_layout(|_, mut l| l.push_view(view));
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +431,7 @@ impl<I: ListItem + Clone> View for ListView<I> {
|
|||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
} => {
|
} => {
|
||||||
if self.scroller.get_show_scrollbars() {
|
if self.has_visible_scrollbars() {
|
||||||
self.scroller.drag(position.saturating_sub(offset));
|
self.scroller.drag(position.saturating_sub(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,10 +470,7 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
|
|||||||
self.queue.clear();
|
self.queue.clear();
|
||||||
|
|
||||||
if !self.attempt_play_all_tracks() {
|
if !self.attempt_play_all_tracks() {
|
||||||
let mut content = self.content.write().unwrap();
|
self.play_current_item();
|
||||||
if let Some(item) = content.get_mut(self.selected) {
|
|
||||||
item.play(&self.queue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(CommandResult::Consumed(None));
|
return Ok(CommandResult::Consumed(None));
|
||||||
|
|||||||
Reference in New Issue
Block a user