diff --git a/src/ui/listview.rs b/src/ui/listview.rs index 304790d..593cbab 100644 --- a/src/ui/listview.rs +++ b/src/ui/listview.rs @@ -125,6 +125,11 @@ impl ListView { } } + /// 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 ListView { 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 ListView { } } + /// 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 View for ListView { 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 View for ListView { 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 ViewExt for ListView { 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));