Skip to content

Commit

Permalink
I gave up trying to organize these commits sorry
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoRiether committed Feb 8, 2024
1 parent 1946dbd commit 7aa75c1
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 54 deletions.
1 change: 1 addition & 0 deletions tori/src/default_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ visualizer_gradient:
- "#2e1442"
- "#101e47"
keybindings:
esc: Esc
'?': OpenHelpModal
C-c: Quit
C-d: Quit
Expand Down
3 changes: 1 addition & 2 deletions tori/src/events/action.rs
Original file line number Diff line number Diff line change
@@ -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),
}
1 change: 1 addition & 0 deletions tori/src/events/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
pub enum Command {
#[default]
Nop,
Esc,
Quit,
NextSong,
PrevSong,
Expand Down
49 changes: 32 additions & 17 deletions tori/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@ 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,
pub cursor: usize,
}

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) => {
Expand Down Expand Up @@ -60,8 +67,9 @@ impl Input {
End => {
self.cursor = self.value.len();
}
_ => {}
_ => return InputResponse::NotHandled,
}
InputResponse::Handled
}

fn move_cursor(&mut self, x: isize) {
Expand All @@ -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> {
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 3 additions & 1 deletion tori/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ 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;

/// Holds most of the state of the application, like a kind of database
pub struct State<'n> {
pub quit: bool,
pub listeners: Vec<Listener<Action>>,
pub player: DefaultPlayer,
pub screen: Screen,
pub now_playing: NowPlaying,
Expand All @@ -32,6 +33,7 @@ impl<'n> State<'n> {
pub fn new(tx: Tx, width: usize) -> Result<Self> {
Ok(Self {
quit: false,
listeners: Vec::new(),
player: DefaultPlayer::new()?,
screen: Screen::BrowseScreen(BrowseScreen::new()?),
now_playing: NowPlaying::default(),
Expand Down
7 changes: 5 additions & 2 deletions tori/src/ui/eventful_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ use tui::prelude::*;
/// Listener signals that we should emit a message of type `M` when `event` occurs.
pub struct Listener<M> {
pub event: UIEvent,
pub emitter: Box<dyn Fn(TermEvent) -> M>,
pub emitter: Box<dyn Fn(TermEvent) -> M + Send + Sync>,
}

pub fn on<M>(event: UIEvent, emitter: impl Fn(TermEvent) -> M + 'static) -> Listener<M> {
pub fn on<M, F>(event: UIEvent, emitter: F) -> Listener<M>
where
F: Fn(TermEvent) -> M + Send + Sync + 'static,
{
Listener {
event,
emitter: Box::new(emitter),
Expand Down
10 changes: 7 additions & 3 deletions tori/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use crate::{
};
use tui::{prelude::*, widgets::Borders};

pub fn ui(state: &mut State, area: Rect, buf: &mut Buffer) -> Vec<Listener<Action>> {
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);

Expand All @@ -37,9 +38,10 @@ pub fn ui(state: &mut State, area: Rect, buf: &mut Buffer) -> Vec<Listener<Actio
m.render(area, buf)
}

l
state.listeners = l;
}

/// Draws the screen that allows the user to browse their playlists and songs.
fn browse_screen(
screen: &mut BrowseScreen,
area: Rect,
Expand All @@ -51,6 +53,7 @@ fn browse_screen(
songs_pane(screen, right, buf, l);
}

/// Draws the pane that shows the user's playlists contained in the browse screen.
fn playlists_pane(
screen: &mut BrowseScreen,
area: Rect,
Expand Down Expand Up @@ -100,6 +103,7 @@ fn playlists_pane(
screen.shown_playlists.state = list.get_state();
}

/// Draws the pane that shows the songs of a playlist inside the browse screen
fn songs_pane(
screen: &mut BrowseScreen,
area: Rect,
Expand Down
60 changes: 31 additions & 29 deletions tori/src/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@ use crate::{
config::Config,
error::Result,
events::{channel::Tx, Action, Command},
input::Input,
input::{Input, InputResponse},
player::Player,
state::{browse_screen::Focus, Screen, State},
util::copy_to_clipboard,
};
use crossterm::event::{Event as TermEvent, KeyCode, KeyEvent, KeyEventKind, MouseEventKind};
use crossterm::event::{Event as TermEvent, KeyEvent, KeyEventKind, MouseEventKind};

pub fn handle_event(state: &mut State<'_>, ev: TermEvent) -> Result<Option<Action>> {
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 {
Expand All @@ -35,10 +41,10 @@ pub fn handle_event(state: &mut State<'_>, ev: TermEvent) -> Result<Option<Actio

/// Transforms a key event into the corresponding action, if there is one.
/// Assumes state is in normal mode
fn transform_normal_mode_key(key_event: KeyEvent) -> Action {
fn transform_normal_mode_key(key_event: KeyEvent) -> Option<Action> {
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,
}
}

Expand All @@ -49,26 +55,6 @@ pub fn update(state: &mut State<'_>, tx: Tx, act: Action) -> Result<Option<Actio
// just triggered a rerender
}

Input(key) => 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(),
Expand Down Expand Up @@ -113,6 +99,10 @@ pub fn update(state: &mut State<'_>, tx: Tx, act: Action) -> Result<Option<Actio
let Screen::BrowseScreen(screen) = &mut state.screen;
screen.refresh_songs()?;
}
RefreshPlaylists => {
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));
Expand All @@ -137,6 +127,18 @@ fn handle_command(state: &mut State<'_>, _tx: Tx, cmd: Command) -> Result<Option
state.quit();
}

Esc => match &mut state.screen {
Screen::BrowseScreen(screen) => {
let focus = mem::take(&mut screen.focus);
screen.focus = match focus {
Focus::PlaylistsFilter(_) => Focus::Playlists,
Focus::SongsFilter(_) => Focus::Songs,
_ => focus,
};
return Ok(Some(Action::RefreshPlaylists));
}
},

SeekForward => {
state.player.seek(10.)?;
return Ok(Some(Action::Tick));
Expand Down

0 comments on commit 7aa75c1

Please sign in to comment.