From 08384bc4c12c3cbbc84de2a08e2c30682e3f4729 Mon Sep 17 00:00:00 2001 From: AnthonyMichaelTDM <68485672+AnthonyMichaelTDM@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:53:52 -0700 Subject: [PATCH] feat(tui): right click to return to previous view right click with the mouse over the ContentView to return to the previous ActiveView. there is currently no keybind to perform this action uses a stack (implemented as a vec) to track previous Views so they can be returned to. --- tui/src/state/action.rs | 10 ++- tui/src/state/mod.rs | 6 +- tui/src/state/view.rs | 70 +++++++++++++++++-- tui/src/ui/components/content_view/mod.rs | 28 +++++--- .../ui/components/content_view/views/album.rs | 31 ++++---- .../components/content_view/views/artist.rs | 28 ++++---- .../content_view/views/collection.rs | 16 ++--- .../components/content_view/views/generic.rs | 4 +- .../ui/components/content_view/views/mod.rs | 16 +++-- .../components/content_view/views/playlist.rs | 22 +++--- .../ui/components/content_view/views/radio.rs | 8 +-- .../components/content_view/views/search.rs | 16 ++--- .../ui/components/content_view/views/song.rs | 31 ++++---- tui/src/ui/components/sidebar.rs | 26 +++---- 14 files changed, 201 insertions(+), 111 deletions(-) diff --git a/tui/src/state/action.rs b/tui/src/state/action.rs index b5292c0f..d16b7aca 100644 --- a/tui/src/state/action.rs +++ b/tui/src/state/action.rs @@ -22,7 +22,7 @@ pub enum Action { /// Actions that effect the library state store. Library(LibraryAction), /// Actions that effect the current view. - SetCurrentView(ActiveView), + ActiveView(ViewAction), /// Actions regarding popups Popup(PopupAction), /// Actions that change the active component @@ -108,6 +108,14 @@ pub enum LibraryAction { CreatePlaylistAndAddThings(String, Vec), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ViewAction { + /// Set the active view + Set(ActiveView), + /// Return to a previous view + Back, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum PopupAction { /// Open a popup diff --git a/tui/src/state/mod.rs b/tui/src/state/mod.rs index 04c9cd0d..86e9e3d0 100644 --- a/tui/src/state/mod.rs +++ b/tui/src/state/mod.rs @@ -38,7 +38,7 @@ struct Senders { pub audio: UnboundedSender, pub search: UnboundedSender, pub library: UnboundedSender, - pub view: UnboundedSender, + pub view: UnboundedSender, pub popup: UnboundedSender, pub component: UnboundedSender, } @@ -172,8 +172,8 @@ impl Dispatcher { Action::Library(action) => { senders.library.send(action)?; } - Action::SetCurrentView(view) => { - senders.view.send(view)?; + Action::ActiveView(action) => { + senders.view.send(action)?; } Action::Popup(popup) => senders.popup.send(popup)?, Action::ActiveComponent(action) => { diff --git a/tui/src/state/view.rs b/tui/src/state/view.rs index 79d0daaa..721b2e69 100644 --- a/tui/src/state/view.rs +++ b/tui/src/state/view.rs @@ -7,6 +7,8 @@ use tokio::sync::{ use crate::{termination::Interrupted, ui::components::content_view::ActiveView}; +use super::action::ViewAction; + /// The `ViewStore` is responsible for managing the `CurrentView` to be displayed. #[allow(clippy::module_name_repetitions)] pub struct ViewState { @@ -28,21 +30,23 @@ impl ViewState { /// Fails if the state cannot be sent pub async fn main_loop( &self, - mut action_rx: UnboundedReceiver, + mut action_rx: UnboundedReceiver, mut interrupt_rx: broadcast::Receiver, ) -> anyhow::Result { let mut state = ActiveView::default(); + // a stack to keep track of previous views + let mut view_stack = Vec::new(); // the initial state once - self.state_tx.send(state)?; + self.state_tx.send(state.clone())?; let result = loop { tokio::select! { // Handle the actions coming from the UI // and process them to do async operations Some(action) = action_rx.recv() => { - state = action; - self.state_tx.send(state)?; + state = self.handle_action(&state, &mut view_stack, action); + self.state_tx.send(state.clone())?; }, // Catch and handle interrupt signal to gracefully shutdown Ok(interrupted) = interrupt_rx.recv() => { @@ -53,4 +57,62 @@ impl ViewState { Ok(result) } + + /// Handle the action, returning the new state + pub fn handle_action( + &self, + state: &ActiveView, + view_stack: &mut Vec, + action: ViewAction, + ) -> ActiveView { + match action { + ViewAction::Set(view) => { + view_stack.push(state.clone()); + view + } + ViewAction::Back => view_stack.pop().unwrap_or_default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_handle_action() { + let (view, _) = ViewState::new(); + + let mut view_stack = Vec::new(); + + let mut state = view.handle_action( + &ActiveView::default(), + &mut view_stack, + ViewAction::Set(ActiveView::Search), + ); + assert_eq!(state, ActiveView::Search); + + state = view.handle_action(&state, &mut view_stack, ViewAction::Set(ActiveView::Songs)); + assert_eq!(state, ActiveView::Songs); + + state = view.handle_action( + &state, + &mut view_stack, + ViewAction::Set(ActiveView::Artists), + ); + assert_eq!(state, ActiveView::Artists); + + state = view.handle_action(&state, &mut view_stack, ViewAction::Back); + assert_eq!(state, ActiveView::Songs); + + state = view.handle_action(&state, &mut view_stack, ViewAction::Back); + assert_eq!(state, ActiveView::Search); + + state = view.handle_action(&state, &mut view_stack, ViewAction::Back); + assert_eq!(state, ActiveView::default()); + + state = view.handle_action(&state, &mut view_stack, ViewAction::Back); + assert_eq!(state, ActiveView::default()); + } } diff --git a/tui/src/ui/components/content_view/mod.rs b/tui/src/ui/components/content_view/mod.rs index c9858e45..4c65aa37 100644 --- a/tui/src/ui/components/content_view/mod.rs +++ b/tui/src/ui/components/content_view/mod.rs @@ -19,7 +19,7 @@ use views::{ use crate::{ state::{ - action::{Action, ComponentAction}, + action::{Action, ComponentAction, ViewAction}, component::ActiveComponent, }, ui::AppState, @@ -204,14 +204,24 @@ impl Component for ContentView { mouse: crossterm::event::MouseEvent, area: ratatui::prelude::Rect, ) { - if mouse.kind == MouseEventKind::Down(MouseButton::Left) - && area.contains(Position::new(mouse.column, mouse.row)) - { - self.action_tx - .send(Action::ActiveComponent(ComponentAction::Set( - ActiveComponent::ContentView, - ))) - .unwrap(); + let mouse_position = Position::new(mouse.column, mouse.row); + match mouse.kind { + // this doesn't return because the active view may want to do something as well + MouseEventKind::Down(MouseButton::Left) if area.contains(mouse_position) => { + self.action_tx + .send(Action::ActiveComponent(ComponentAction::Set( + ActiveComponent::ContentView, + ))) + .unwrap(); + } + // this returns because the active view should handle the event (since it changes the active view) + MouseEventKind::Down(MouseButton::Right) if area.contains(mouse_position) => { + self.action_tx + .send(Action::ActiveView(ViewAction::Back)) + .unwrap(); + return; + } + _ => {} } // defer to active view diff --git a/tui/src/ui/components/content_view/views/album.rs b/tui/src/ui/components/content_view/views/album.rs index a2886251..b5c50dda 100644 --- a/tui/src/ui/components/content_view/views/album.rs +++ b/tui/src/ui/components/content_view/views/album.rs @@ -13,7 +13,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT}, components::{content_view::ActiveView, Component, ComponentRender, RenderProps}, @@ -124,7 +124,7 @@ impl Component for LibraryAlbumsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -143,9 +143,9 @@ impl Component for LibraryAlbumsView { let things = self.tree_state.lock().unwrap().get_checked_things(); if !things.is_empty() { self.action_tx - .send(Action::SetCurrentView(ActiveView::Radio( + .send(Action::ActiveView(ViewAction::Set(ActiveView::Radio( things, RADIO_SIZE, - ))) + )))) .unwrap(); } } @@ -520,10 +520,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("album", item_id()).into()], RADIO_SIZE - )) + ))) ); view.handle_key_event(KeyEvent::from(KeyCode::Char('p'))); assert_eq!( @@ -547,7 +547,7 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); // check the item @@ -568,10 +568,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -672,7 +672,7 @@ mod item_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Artist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Artist(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) @@ -875,7 +875,10 @@ mod library_view_tests { // open view.handle_key_event(KeyEvent::from(KeyCode::Enter)); let action = rx.blocking_recv().unwrap(); - assert_eq!(action, Action::SetCurrentView(ActiveView::Album(item_id()))); + assert_eq!( + action, + Action::ActiveView(ViewAction::Set(ActiveView::Album(item_id()))) + ); // there are checked items view.handle_key_event(KeyEvent::from(KeyCode::Char(' '))); @@ -897,10 +900,10 @@ mod library_view_tests { let action = rx.blocking_recv().unwrap(); assert_eq!( action, - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("album", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -1013,7 +1016,7 @@ mod library_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Album(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Album(item_id()))) ); } } diff --git a/tui/src/ui/components/content_view/views/artist.rs b/tui/src/ui/components/content_view/views/artist.rs index be61bc6d..fb666b0d 100644 --- a/tui/src/ui/components/content_view/views/artist.rs +++ b/tui/src/ui/components/content_view/views/artist.rs @@ -13,7 +13,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT}, components::{content_view::ActiveView, Component, ComponentRender, RenderProps}, @@ -123,7 +123,7 @@ impl Component for LibraryArtistsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -142,9 +142,9 @@ impl Component for LibraryArtistsView { let things = self.tree_state.lock().unwrap().get_checked_things(); if !things.is_empty() { self.action_tx - .send(Action::SetCurrentView(ActiveView::Radio( + .send(Action::ActiveView(ViewAction::Set(ActiveView::Radio( things, RADIO_SIZE, - ))) + )))) .unwrap(); } } @@ -498,10 +498,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("artist", item_id()).into()], RADIO_SIZE - )) + ))) ); view.handle_key_event(KeyEvent::from(KeyCode::Char('p'))); assert_eq!( @@ -525,7 +525,7 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); // check the item @@ -546,10 +546,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -650,7 +650,7 @@ mod item_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Album(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Album(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) @@ -847,7 +847,7 @@ mod library_view_tests { let action = rx.blocking_recv().unwrap(); assert_eq!( action, - Action::SetCurrentView(ActiveView::Artist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Artist(item_id()))) ); // there are checked items @@ -870,10 +870,10 @@ mod library_view_tests { let action = rx.blocking_recv().unwrap(); assert_eq!( action, - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("artist", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -986,7 +986,7 @@ mod library_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Artist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Artist(item_id()))) ); } } diff --git a/tui/src/ui/components/content_view/views/collection.rs b/tui/src/ui/components/content_view/views/collection.rs index 35f35a00..b2d5bef3 100644 --- a/tui/src/ui/components/content_view/views/collection.rs +++ b/tui/src/ui/components/content_view/views/collection.rs @@ -16,7 +16,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::Action, + state::action::{Action, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT, TEXT_NORMAL}, components::{content_view::ActiveView, Component, ComponentRender, RenderProps}, @@ -136,7 +136,7 @@ impl Component for CollectionView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -409,7 +409,7 @@ impl Component for LibraryCollectionsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -455,7 +455,7 @@ impl Component for LibraryCollectionsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -790,7 +790,7 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); // check the artist @@ -888,7 +888,7 @@ mod item_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); let expected = Buffer::with_lines([ "┌Collection View sorted by: Artist─────────────────────────┐", @@ -1049,7 +1049,7 @@ mod library_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Collection(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Collection(item_id()))) ); } @@ -1105,7 +1105,7 @@ mod library_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Collection(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Collection(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) diff --git a/tui/src/ui/components/content_view/views/generic.rs b/tui/src/ui/components/content_view/views/generic.rs index c73c0972..52bb4165 100644 --- a/tui/src/ui/components/content_view/views/generic.rs +++ b/tui/src/ui/components/content_view/views/generic.rs @@ -10,7 +10,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::Action, + state::action::{Action, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT, TEXT_NORMAL}, components::{Component, ComponentRender, RenderProps}, @@ -98,7 +98,7 @@ where if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } diff --git a/tui/src/ui/components/content_view/views/mod.rs b/tui/src/ui/components/content_view/views/mod.rs index 88c2987b..7658d0dd 100644 --- a/tui/src/ui/components/content_view/views/mod.rs +++ b/tui/src/ui/components/content_view/views/mod.rs @@ -320,7 +320,7 @@ pub mod checktree_utils { }; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ components::content_view::ActiveView, widgets::{ @@ -380,7 +380,7 @@ pub mod checktree_utils { (selected_things == self.get_selected_thing()) .then_some(selected_things) .flatten() - .map(|thing| Action::SetCurrentView(thing.into())) + .map(|thing| Action::ActiveView(ViewAction::Set(thing.into()))) } MouseEventKind::ScrollDown => { self.key_down(); @@ -481,13 +481,17 @@ pub mod checktree_utils { current_thing: Option<&Thing>, ) -> Option { if checked_things.is_empty() { - current_thing - .map(|id| Action::SetCurrentView(ActiveView::Radio(vec![id.clone()], RADIO_SIZE))) + current_thing.map(|id| { + Action::ActiveView(ViewAction::Set(ActiveView::Radio( + vec![id.clone()], + RADIO_SIZE, + ))) + }) } else { - Some(Action::SetCurrentView(ActiveView::Radio( + Some(Action::ActiveView(ViewAction::Set(ActiveView::Radio( checked_things, RADIO_SIZE, - ))) + )))) } } diff --git a/tui/src/ui/components/content_view/views/playlist.rs b/tui/src/ui/components/content_view/views/playlist.rs index 9a9e4907..aebd8bdd 100644 --- a/tui/src/ui/components/content_view/views/playlist.rs +++ b/tui/src/ui/components/content_view/views/playlist.rs @@ -14,7 +14,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::{Action, LibraryAction}, + state::action::{Action, LibraryAction, ViewAction}, ui::{ colors::{ BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT, TEXT_HIGHLIGHT_ALT, TEXT_NORMAL, @@ -138,7 +138,7 @@ impl Component for PlaylistView { let selected_things = self.tree_state.lock().unwrap().get_selected_thing(); if let Some(thing) = selected_things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -469,7 +469,7 @@ impl Component for LibraryPlaylistsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -887,10 +887,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("playlist", item_id()).into()], RADIO_SIZE - )) + ))) ); view.handle_key_event(KeyEvent::from(KeyCode::Char('p'))); assert_eq!( @@ -912,7 +912,7 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); // check the artist @@ -933,10 +933,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -1030,7 +1030,7 @@ mod item_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); let expected = Buffer::with_lines([ "┌Playlist View sorted by: Artist───────────────────────────┐", @@ -1224,7 +1224,7 @@ mod library_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Playlist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Playlist(item_id()))) ); // new playlist @@ -1301,7 +1301,7 @@ mod library_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Playlist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Playlist(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) diff --git a/tui/src/ui/components/content_view/views/radio.rs b/tui/src/ui/components/content_view/views/radio.rs index 897352a4..4176cf98 100644 --- a/tui/src/ui/components/content_view/views/radio.rs +++ b/tui/src/ui/components/content_view/views/radio.rs @@ -14,7 +14,7 @@ use tokio::sync::mpsc::UnboundedSender; use super::{checktree_utils::create_song_tree_leaf, RadioViewProps}; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT, TEXT_NORMAL}, components::{Component, ComponentRender, RenderProps}, @@ -108,7 +108,7 @@ impl Component for RadioView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -458,7 +458,7 @@ mod tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); // check the artist @@ -536,7 +536,7 @@ mod tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) diff --git a/tui/src/ui/components/content_view/views/search.rs b/tui/src/ui/components/content_view/views/search.rs index 6df04a42..e257b410 100644 --- a/tui/src/ui/components/content_view/views/search.rs +++ b/tui/src/ui/components/content_view/views/search.rs @@ -13,7 +13,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ colors::{ BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT, TEXT_HIGHLIGHT_ALT, TEXT_NORMAL, @@ -145,7 +145,7 @@ impl Component for SearchView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -164,9 +164,9 @@ impl Component for SearchView { let things = self.tree_state.lock().unwrap().get_checked_things(); if !things.is_empty() { self.action_tx - .send(Action::SetCurrentView(ActiveView::Radio( + .send(Action::ActiveView(ViewAction::Set(ActiveView::Radio( things, RADIO_SIZE, - ))) + )))) .unwrap(); } } @@ -229,7 +229,7 @@ impl Component for SearchView { if selected_things == self.tree_state.lock().unwrap().get_selected_thing() { if let Some(thing) = selected_things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -630,10 +630,10 @@ mod tests { let action = rx.blocking_recv().unwrap(); assert_eq!( action, - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); view.handle_key_event(KeyEvent::from(KeyCode::Char('p'))); @@ -857,7 +857,7 @@ mod tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); let expected = Buffer::with_lines([ "┌Search────────────────────────┐", diff --git a/tui/src/ui/components/content_view/views/song.rs b/tui/src/ui/components/content_view/views/song.rs index c603ac21..dcfddde6 100644 --- a/tui/src/ui/components/content_view/views/song.rs +++ b/tui/src/ui/components/content_view/views/song.rs @@ -13,7 +13,7 @@ use ratatui::{ use tokio::sync::mpsc::UnboundedSender; use crate::{ - state::action::{Action, AudioAction, PopupAction, QueueAction}, + state::action::{Action, AudioAction, PopupAction, QueueAction, ViewAction}, ui::{ colors::{BORDER_FOCUSED, BORDER_UNFOCUSED, TEXT_HIGHLIGHT}, components::{content_view::ActiveView, Component, ComponentRender, RenderProps}, @@ -123,7 +123,7 @@ impl Component for LibrarySongsView { if let Some(thing) = things { self.action_tx - .send(Action::SetCurrentView(thing.into())) + .send(Action::ActiveView(ViewAction::Set(thing.into()))) .unwrap(); } } @@ -142,9 +142,9 @@ impl Component for LibrarySongsView { let things = self.tree_state.lock().unwrap().get_checked_things(); if !things.is_empty() { self.action_tx - .send(Action::SetCurrentView(ActiveView::Radio( + .send(Action::ActiveView(ViewAction::Set(ActiveView::Radio( things, RADIO_SIZE, - ))) + )))) .unwrap(); } } @@ -671,10 +671,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); view.handle_key_event(KeyEvent::from(KeyCode::Char('p'))); assert_eq!( @@ -696,7 +696,7 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Album(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Album(item_id()))) ); // check the item @@ -717,10 +717,10 @@ mod item_view_tests { view.handle_key_event(KeyEvent::from(KeyCode::Char('r'))); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("album", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -821,7 +821,7 @@ mod item_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Artist(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Artist(item_id()))) ); let buffer = terminal .draw(|frame| view.render(frame, props)) @@ -1032,7 +1032,10 @@ mod library_view_tests { // open view.handle_key_event(KeyEvent::from(KeyCode::Enter)); let action = rx.blocking_recv().unwrap(); - assert_eq!(action, Action::SetCurrentView(ActiveView::Song(item_id()))); + assert_eq!( + action, + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) + ); // there are checked items view.handle_key_event(KeyEvent::from(KeyCode::Char(' '))); @@ -1054,10 +1057,10 @@ mod library_view_tests { let action = rx.blocking_recv().unwrap(); assert_eq!( action, - Action::SetCurrentView(ActiveView::Radio( + Action::ActiveView(ViewAction::Set(ActiveView::Radio( vec![("song", item_id()).into()], RADIO_SIZE - )) + ))) ); // add to playlist @@ -1170,7 +1173,7 @@ mod library_view_tests { ); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Song(item_id())) + Action::ActiveView(ViewAction::Set(ActiveView::Song(item_id()))) ); } } diff --git a/tui/src/ui/components/sidebar.rs b/tui/src/ui/components/sidebar.rs index 09c75f21..74b2ae55 100644 --- a/tui/src/ui/components/sidebar.rs +++ b/tui/src/ui/components/sidebar.rs @@ -16,7 +16,7 @@ use tokio::sync::mpsc::UnboundedSender; use crate::{ state::{ - action::{Action, ComponentAction, LibraryAction, PopupAction}, + action::{Action, ComponentAction, LibraryAction, PopupAction, ViewAction}, component::ActiveComponent, }, ui::{ @@ -56,15 +56,15 @@ impl SidebarItem { #[must_use] pub const fn to_action(&self) -> Option { match self { - Self::Search => Some(Action::SetCurrentView(ActiveView::Search)), + Self::Search => Some(Action::ActiveView(ViewAction::Set(ActiveView::Search))), Self::LibraryRescan => Some(Action::Library(LibraryAction::Rescan)), Self::LibraryAnalyze => Some(Action::Library(LibraryAction::Analyze)), Self::LibraryRecluster => Some(Action::Library(LibraryAction::Recluster)), - Self::Songs => Some(Action::SetCurrentView(ActiveView::Songs)), - Self::Artists => Some(Action::SetCurrentView(ActiveView::Artists)), - Self::Albums => Some(Action::SetCurrentView(ActiveView::Albums)), - Self::Playlists => Some(Action::SetCurrentView(ActiveView::Playlists)), - Self::Collections => Some(Action::SetCurrentView(ActiveView::Collections)), + Self::Songs => Some(Action::ActiveView(ViewAction::Set(ActiveView::Songs))), + Self::Artists => Some(Action::ActiveView(ViewAction::Set(ActiveView::Artists))), + Self::Albums => Some(Action::ActiveView(ViewAction::Set(ActiveView::Albums))), + Self::Playlists => Some(Action::ActiveView(ViewAction::Set(ActiveView::Playlists))), + Self::Collections => Some(Action::ActiveView(ViewAction::Set(ActiveView::Collections))), Self::Space => None, } } @@ -356,7 +356,7 @@ mod tests { sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Search) + Action::ActiveView(ViewAction::Set(ActiveView::Search)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down)); @@ -364,35 +364,35 @@ mod tests { sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Songs) + Action::ActiveView(ViewAction::Set(ActiveView::Songs)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down)); sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Artists) + Action::ActiveView(ViewAction::Set(ActiveView::Artists)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down)); sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Albums) + Action::ActiveView(ViewAction::Set(ActiveView::Albums)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down)); sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Playlists) + Action::ActiveView(ViewAction::Set(ActiveView::Playlists)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down)); sidebar.handle_key_event(KeyEvent::from(KeyCode::Enter)); assert_eq!( rx.blocking_recv().unwrap(), - Action::SetCurrentView(ActiveView::Collections) + Action::ActiveView(ViewAction::Set(ActiveView::Collections)) ); sidebar.handle_key_event(KeyEvent::from(KeyCode::Down));