naviagte through queue via j/k/down/up

This commit is contained in:
figsoda 2020-10-31 15:19:45 -04:00
parent cab3baad34
commit 06db49324e
5 changed files with 93 additions and 20 deletions

View file

@ -7,7 +7,10 @@ Config(
])),
Min(0, Queue(
columns: [
Ratio(5, QueueTitle),
Ratio(5, Parts([
If(Selected, Text("> ")),
QueueTitle,
])),
Ratio(4, QueueArtist),
Ratio(4, QueueAlbum),
],

View file

@ -11,6 +11,8 @@ use std::{
#[derive(Deserialize)]
pub struct Config {
#[serde(default)]
pub cycle: bool,
#[serde(default = "ups_default")]
pub ups: f64,
pub layout: Widget,
@ -65,6 +67,7 @@ pub enum Condition {
TitleExist,
ArtistExist,
AlbumExist,
Selected,
Not(Box<Condition>),
And(Box<Condition>, Box<Condition>),
Or(Box<Condition>, Box<Condition>),

View file

@ -2,7 +2,7 @@ use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
text::{Span, Spans},
widgets::{List, ListItem, Paragraph},
widgets::{List, ListItem, ListState, Paragraph},
Frame,
};
@ -17,6 +17,8 @@ pub fn render(
widget: &Widget,
queue: &Vec<Track>,
status: &Status,
selected: usize,
liststate: &ListState,
) {
match widget {
Widget::Rows(xs) => {
@ -51,7 +53,7 @@ pub fn render(
let mut ws = ws.into_iter();
while let (Some(chunk), Some(w)) = (chunks.next(), ws.next()) {
render(frame, chunk, w, queue, status);
render(frame, chunk, w, queue, status, selected, liststate);
}
}
Widget::Columns(xs) => {
@ -86,7 +88,7 @@ pub fn render(
let mut ws = ws.into_iter();
while let (Some(chunk), Some(w)) = (chunks.next(), ws.next()) {
render(frame, chunk, w, queue, status);
render(frame, chunk, w, queue, status, selected, liststate);
}
}
Widget::Textbox(xss) => {
@ -96,7 +98,7 @@ pub fn render(
} else {
None
};
flatten(&mut spans, &xss, status, current_track, None);
flatten(&mut spans, &xss, status, current_track, None, false);
frame.render_widget(Paragraph::new(Spans::from(spans)), size);
}
Widget::Queue { columns } => {
@ -125,10 +127,18 @@ pub fn render(
Constrained::Min(n, w) => (w, Constraint::Min(*n)),
Constrained::Ratio(n, xs) => (xs, Constraint::Ratio(*n, denom)),
};
let mut items = Vec::with_capacity(len);
for x in queue {
for i in 0 .. queue.len() - 1 {
let mut spans = Vec::new();
flatten(&mut spans, xs, status, current_track, Some(x));
flatten(
&mut spans,
xs,
status,
current_track,
Some(&queue[i]),
i == selected,
);
items.push(ListItem::new(Spans::from(spans)));
}
ws.push(List::new(items));
@ -143,7 +153,7 @@ pub fn render(
let mut ws = ws.into_iter();
while let (Some(chunk), Some(w)) = (chunks.next(), ws.next()) {
frame.render_widget(w, chunk);
frame.render_stateful_widget(w, chunk, &mut liststate.clone());
}
}
}
@ -155,6 +165,7 @@ fn flatten(
status: &Status,
current_track: Option<&Track>,
queue_track: Option<&Track>,
selected: bool,
) {
match xs {
Texts::Empty => (),
@ -240,21 +251,26 @@ fn flatten(
}
Texts::Parts(xss) => {
for xs in xss {
flatten(spans, xs, status, current_track, queue_track);
flatten(spans, xs, status, current_track, queue_track, selected);
}
}
Texts::If(cond, box yes, box no) => {
let xs = if eval_cond(cond, status, current_track) {
let xs = if eval_cond(cond, status, current_track, selected) {
yes
} else {
no
};
flatten(spans, xs, status, current_track, queue_track);
flatten(spans, xs, status, current_track, queue_track, selected);
}
}
}
fn eval_cond(cond: &Condition, status: &Status, current_track: Option<&Track>) -> bool {
fn eval_cond(
cond: &Condition,
status: &Status,
current_track: Option<&Track>,
selected: bool,
) -> bool {
match cond {
Condition::Playing => current_track.is_some(),
Condition::Repeat => status.repeat,
@ -270,15 +286,19 @@ fn eval_cond(cond: &Condition, status: &Status, current_track: Option<&Track>) -
}),
),
Condition::AlbumExist => matches!(current_track, Some(Track { album: Some(_), .. })),
Condition::Not(box x) => !eval_cond(x, status, current_track),
Condition::Selected => selected,
Condition::Not(box x) => !eval_cond(x, status, current_track, selected),
Condition::And(box x, box y) => {
eval_cond(x, status, current_track) && eval_cond(y, status, current_track)
eval_cond(x, status, current_track, selected)
&& eval_cond(y, status, current_track, selected)
}
Condition::Or(box x, box y) => {
eval_cond(x, status, current_track) || eval_cond(y, status, current_track)
eval_cond(x, status, current_track, selected)
|| eval_cond(y, status, current_track, selected)
}
Condition::Xor(box x, box y) => {
eval_cond(x, status, current_track) ^ eval_cond(y, status, current_track)
eval_cond(x, status, current_track, selected)
^ eval_cond(y, status, current_track, selected)
}
}
}

View file

@ -16,7 +16,7 @@ use tokio::{
sync::mpsc,
time::{sleep_until, Duration, Instant},
};
use tui::{backend::CrosstermBackend, Terminal};
use tui::{backend::CrosstermBackend, widgets::ListState, Terminal};
use std::{
io::{stdout, Write},
@ -47,6 +47,8 @@ enum Command {
UpdateFrame,
UpdateQueue(Vec<Track>),
UpdateStatus(Status),
Down,
Up,
}
#[tokio::main]
@ -68,6 +70,9 @@ async fn run() -> Result<()> {
let mut queue = mpd::queue(&mut idle_cl).await?;
let mut status = mpd::status(&mut status_cl).await?;
let mut selected = status.song.map_or(0, |song| song.pos);
let mut liststate = ListState::default();
liststate.select(Some(selected));
let update_interval = Duration::from_secs_f64(1.0 / cfg.ups);
@ -123,7 +128,13 @@ async fn run() -> Result<()> {
match ev {
Event::Key(KeyEvent { code, .. }) => match code {
KeyCode::Char('q') | KeyCode::Esc => {
tx.send(Command::Quit).await.unwrap_or_else(die)
tx.send(Command::Quit).await.unwrap_or_else(die);
}
KeyCode::Char('j') | KeyCode::Down => {
tx.send(Command::Down).await.unwrap_or_else(die);
}
KeyCode::Char('k') | KeyCode::Up => {
tx.send(Command::Up).await.unwrap_or_else(die);
}
_ => (),
},
@ -138,7 +149,15 @@ async fn run() -> Result<()> {
Command::Quit => break,
Command::UpdateFrame => term
.draw(|frame| {
layout::render(frame, frame.size(), &cfg.layout, &queue, &status);
layout::render(
frame,
frame.size(),
&cfg.layout,
&queue,
&status,
selected,
&liststate,
);
})
.context("Failed to draw to terminal")?,
Command::UpdateQueue(new_queue) => {
@ -149,6 +168,34 @@ async fn run() -> Result<()> {
status = new_status;
tx.send(Command::UpdateFrame).await?;
}
Command::Down => {
let len = queue.len();
if selected >= len {
selected = status.song.map_or(0, |song| song.pos);
} else if selected == len - 1 {
if cfg.cycle {
selected = 0
}
} else {
selected += 1
}
liststate.select(Some(selected));
tx.send(Command::UpdateFrame).await?;
}
Command::Up => {
let len = queue.len();
if selected >= len {
selected = status.song.map_or(0, |song| song.pos);
} else if selected == 0 {
if cfg.cycle {
selected = len - 1
}
} else {
selected -= 1
}
liststate.select(Some(selected));
tx.send(Command::UpdateFrame).await?;
}
}
}

View file

@ -20,7 +20,7 @@ pub struct Status {
pub song: Option<Song>,
}
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub struct Song {
pub pos: usize,
pub elapsed: u16,