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:
Thomas Frans
2023-03-25 23:53:19 +01:00
committed by GitHub
parent ecaec1a494
commit 8fe30e34d0

View File

@@ -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 {
self.selected
}
@@ -154,6 +159,9 @@ impl<I: ListItem + Clone> ListView<I> {
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 {
let content = self.content.read().unwrap();
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) {
let mut c = self.content.write().unwrap();
c.remove(index);
@@ -328,31 +344,60 @@ impl<I: ListItem + Clone> View for ListView<I> {
position,
offset,
} => {
if self.scroller.get_show_scrollbars()
&& position
.checked_sub(offset)
.map(|p| self.scroller.start_drag(p))
.unwrap_or(false)
{
// This is safe as a mouse event is only propagated to a view when it is inside the
// view. Therefore underflow shouldn't occur.
let view_coordinates_click_position = position - offset;
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");
} else {
let viewport = self.scroller.content_viewport().top_left();
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)) {
self.move_focus_to(y);
if let Some(clicked_row_index) =
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();
let library = self.library.clone();
if let Some(target) = {
if !self.attempt_play_all_tracks() {
self.play_current_item();
}
} 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();
content.get(self.selected).map(|t| t.as_listitem())
} {
if let Some(view) = target.open(queue, library) {
return EventResult::Consumed(Some(Callback::from_fn_once(
move |s| {
s.on_layout(|_, mut l| l.push_view(view));
},
)));
let clicked_list_item =
content.get(self.selected).map(ListItem::as_listitem);
if let Some(target) = clicked_list_item {
if let Some(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,
offset,
} => {
if self.scroller.get_show_scrollbars() {
if self.has_visible_scrollbars() {
self.scroller.drag(position.saturating_sub(offset));
}
}
@@ -425,10 +470,7 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
self.queue.clear();
if !self.attempt_play_all_tracks() {
let mut content = self.content.write().unwrap();
if let Some(item) = content.get_mut(self.selected) {
item.play(&self.queue);
}
self.play_current_item();
}
return Ok(CommandResult::Consumed(None));