From 7aa75c176da8c675a4a978cc7576bcafe0c414d6 Mon Sep 17 00:00:00 2001 From: LeoRiether Date: Thu, 8 Feb 2024 17:16:07 -0300 Subject: [PATCH] I gave up trying to organize these commits sorry --- tori/src/default_config.yaml | 1 + tori/src/events/action.rs | 3 +- tori/src/events/command.rs | 1 + tori/src/input.rs | 49 +++++++++++++++++---------- tori/src/state/mod.rs | 4 ++- tori/src/ui/eventful_widget.rs | 7 ++-- tori/src/ui/mod.rs | 10 ++++-- tori/src/update/mod.rs | 60 ++++++++++++++++++---------------- 8 files changed, 81 insertions(+), 54 deletions(-) diff --git a/tori/src/default_config.yaml b/tori/src/default_config.yaml index 9d5b99b..6f7d8f0 100644 --- a/tori/src/default_config.yaml +++ b/tori/src/default_config.yaml @@ -3,6 +3,7 @@ visualizer_gradient: - "#2e1442" - "#101e47" keybindings: + esc: Esc '?': OpenHelpModal C-c: Quit C-d: Quit diff --git a/tori/src/events/action.rs b/tori/src/events/action.rs index bcf7b09..60133e1 100644 --- a/tori/src/events/action.rs +++ b/tori/src/events/action.rs @@ -1,16 +1,15 @@ use super::Command; -use crossterm::event::KeyEvent; #[derive(Debug, Clone)] pub enum Action { Rerender, Tick, - Input(KeyEvent), ScrollDown, ScrollUp, Command(Command), SongAdded { playlist: String, song: String }, RefreshSongs, + RefreshPlaylists, SelectSong(usize), SelectPlaylist(usize), } diff --git a/tori/src/events/command.rs b/tori/src/events/command.rs index ceb9740..f86e9cb 100644 --- a/tori/src/events/command.rs +++ b/tori/src/events/command.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; pub enum Command { #[default] Nop, + Esc, Quit, NextSong, PrevSong, diff --git a/tori/src/input.rs b/tori/src/input.rs index 8a03c5b..6477ed2 100644 --- a/tori/src/input.rs +++ b/tori/src/input.rs @@ -4,6 +4,13 @@ use tui::{ widgets::{Block, Paragraph, Widget}, }; +#[derive(Debug, Default, Clone, Copy)] +pub enum InputResponse { + #[default] + NotHandled, + Handled, +} + #[derive(Debug, Default, Clone)] pub struct Input { pub value: String, @@ -11,7 +18,7 @@ pub struct Input { } impl Input { - pub fn handle_event(&mut self, key: KeyEvent) { + pub fn handle_event(&mut self, key: KeyEvent) -> InputResponse { use KeyCode::*; match key.code { Char(c) => { @@ -60,8 +67,9 @@ impl Input { End => { self.cursor = self.value.len(); } - _ => {} + _ => return InputResponse::NotHandled, } + InputResponse::Handled } fn move_cursor(&mut self, x: isize) { @@ -72,6 +80,27 @@ impl Input { self.cursor = inc(self.cursor); } } + + pub fn split_at_cursor(&self) -> (&str, &str, &str) { + let (left, right) = self.value.split_at(self.cursor); + + let (cursor, right) = right + .char_indices() + .next() + .map(|(i, _)| right.split_at(i)) + .unwrap_or(("", right)); + + (left, cursor, right) + } + + pub fn styled(&self, style: Style) -> Line { + let (left, cursor, right) = self.split_at_cursor(); + Line::from(vec![ + Span::styled(left, style), + Span::styled(cursor, style.reversed().underlined()), + Span::styled(right, style), + ]) + } } pub struct InputWidget<'a> { @@ -102,21 +131,7 @@ impl<'a> InputWidget<'a> { impl<'a> Widget for InputWidget<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let input = self.input; - let (left, right) = input.value.split_at(input.cursor); - - let (cursor, right) = right - .char_indices() - .next() - .map(|(i, _)| right.split_at(i)) - .unwrap_or(("", right)); - - let mut paragraph = Paragraph::new(Line::from(vec![ - Span::styled(left, self.style), - Span::styled(cursor, Style::default().add_modifier(Modifier::REVERSED)), - right.into(), - ])); - + let mut paragraph = Paragraph::new(self.input.styled(self.style)); if let Some(block) = self.block { paragraph = paragraph.block(block); } diff --git a/tori/src/state/mod.rs b/tori/src/state/mod.rs index 519c799..d758e51 100644 --- a/tori/src/state/mod.rs +++ b/tori/src/state/mod.rs @@ -8,7 +8,7 @@ use crate::{ app::modal::Modal, component::{Notification, NowPlaying, Visualizer}, error::Result, - player::{DefaultPlayer, Player}, events::channel::Tx, + player::{DefaultPlayer, Player}, events::{channel::Tx, Action}, ui::Listener, }; use self::browse_screen::BrowseScreen; @@ -16,6 +16,7 @@ use self::browse_screen::BrowseScreen; /// Holds most of the state of the application, like a kind of database pub struct State<'n> { pub quit: bool, + pub listeners: Vec>, pub player: DefaultPlayer, pub screen: Screen, pub now_playing: NowPlaying, @@ -32,6 +33,7 @@ impl<'n> State<'n> { pub fn new(tx: Tx, width: usize) -> Result { Ok(Self { quit: false, + listeners: Vec::new(), player: DefaultPlayer::new()?, screen: Screen::BrowseScreen(BrowseScreen::new()?), now_playing: NowPlaying::default(), diff --git a/tori/src/ui/eventful_widget.rs b/tori/src/ui/eventful_widget.rs index 425e580..f7ef3a5 100644 --- a/tori/src/ui/eventful_widget.rs +++ b/tori/src/ui/eventful_widget.rs @@ -4,10 +4,13 @@ use tui::prelude::*; /// Listener signals that we should emit a message of type `M` when `event` occurs. pub struct Listener { pub event: UIEvent, - pub emitter: Box M>, + pub emitter: Box M + Send + Sync>, } -pub fn on(event: UIEvent, emitter: impl Fn(TermEvent) -> M + 'static) -> Listener { +pub fn on(event: UIEvent, emitter: F) -> Listener +where + F: Fn(TermEvent) -> M + Send + Sync + 'static, +{ Listener { event, emitter: Box::new(emitter), diff --git a/tori/src/ui/mod.rs b/tori/src/ui/mod.rs index 66796cd..02d0a5e 100644 --- a/tori/src/ui/mod.rs +++ b/tori/src/ui/mod.rs @@ -18,8 +18,9 @@ use crate::{ }; use tui::{prelude::*, widgets::Borders}; -pub fn ui(state: &mut State, area: Rect, buf: &mut Buffer) -> Vec> { - let mut l = Vec::new(); +pub fn ui(state: &mut State, area: Rect, buf: &mut Buffer) { + let mut l = std::mem::take(&mut state.listeners); + l.clear(); state.visualizer.render(area, buf); @@ -37,9 +38,10 @@ pub fn ui(state: &mut State, area: Rect, buf: &mut Buffer) -> Vec, ev: TermEvent) -> Result> { Ok(match ev { - TermEvent::Key(key) if key.kind != KeyEventKind::Release => match &state.screen { - Screen::BrowseScreen(screen) => match &screen.focus { - Focus::Playlists | Focus::Songs => Some(transform_normal_mode_key(key)), - Focus::PlaylistsFilter(_) | Focus::SongsFilter(_) => Some(Action::Input(key)), + TermEvent::Key(key) if key.kind != KeyEventKind::Release => match &mut state.screen { + Screen::BrowseScreen(screen) => match &mut screen.focus { + Focus::Playlists | Focus::Songs => transform_normal_mode_key(key), + Focus::PlaylistsFilter(filter) | Focus::SongsFilter(filter) => { + let event_handled = filter.handle_event(key); + match event_handled { + InputResponse::Handled => Some(Action::RefreshPlaylists), + InputResponse::NotHandled => transform_normal_mode_key(key), + } + } }, }, TermEvent::Mouse(mouse) => match mouse.kind { @@ -35,10 +41,10 @@ pub fn handle_event(state: &mut State<'_>, ev: TermEvent) -> Result Action { +fn transform_normal_mode_key(key_event: KeyEvent) -> Option { match Config::global().keybindings.get_from_event(key_event) { - Some(cmd) if cmd != Command::Nop => Action::Command(cmd), - _ => Action::Input(key_event), + Some(cmd) if cmd != Command::Nop => Some(Action::Command(cmd)), + _ => None, } } @@ -49,26 +55,6 @@ pub fn update(state: &mut State<'_>, tx: Tx, act: Action) -> Result match &mut state.screen { - Screen::BrowseScreen(screen) => match &mut screen.focus { - Focus::PlaylistsFilter(filter) | Focus::SongsFilter(filter) => { - match key.code { - KeyCode::Esc => { - let focus = mem::take(&mut screen.focus); - screen.focus = match focus { - Focus::PlaylistsFilter(_) => Focus::Playlists, - Focus::SongsFilter(_) => Focus::Songs, - _ => focus, - }; - } - _ => filter.handle_event(key), - } - screen.refresh_playlists()?; - } - _ => {} - }, - }, - ScrollDown => match &mut state.screen { Screen::BrowseScreen(screen) => match &screen.focus { Focus::Playlists => screen.shown_playlists.select_next(), @@ -113,6 +99,10 @@ pub fn update(state: &mut State<'_>, tx: Tx, act: Action) -> Result { + let Screen::BrowseScreen(screen) = &mut state.screen; + screen.refresh_playlists()?; + } SelectSong(i) => { let Screen::BrowseScreen(screen) = &mut state.screen; screen.shown_songs.select(Some(i)); @@ -137,6 +127,18 @@ fn handle_command(state: &mut State<'_>, _tx: Tx, cmd: Command) -> Result