mirror of
https://github.com/figsoda/mmtc
synced 2024-11-22 07:03:05 +00:00
ability the search and filter the queue
This commit is contained in:
parent
7029aaf030
commit
14faf62864
6 changed files with 389 additions and 118 deletions
|
@ -46,29 +46,35 @@ Config(
|
|||
),
|
||||
])),
|
||||
Fixed(1, Columns([
|
||||
Min(0, Textbox(If(Not(Stopped), Styled([Bold], Parts([
|
||||
Styled([Fg(Indexed(113))], Parts([
|
||||
If(Playing, Text("[playing: "), Text("[paused: ")),
|
||||
CurrentElapsed,
|
||||
Text("/"),
|
||||
CurrentDuration,
|
||||
Text("] "),
|
||||
])),
|
||||
If(TitleExist,
|
||||
Parts([
|
||||
Styled([Fg(Indexed(149))], CurrentTitle),
|
||||
If(ArtistExist, Parts([
|
||||
Styled([Fg(Indexed(216))], Text(" ◆ ")),
|
||||
Styled([Fg(Indexed(185))], CurrentArtist),
|
||||
If(AlbumExist, Parts([
|
||||
Min(0, Textbox(Styled([Bold], If(Searching,
|
||||
Parts([
|
||||
Styled([Fg(Indexed(113))], Text("Searching: ")),
|
||||
Styled([Fg(Indexed(185))], Query),
|
||||
]),
|
||||
If(Not(Stopped), Parts([
|
||||
Styled([Fg(Indexed(113))], Parts([
|
||||
If(Playing, Text("[playing: "), Text("[paused: ")),
|
||||
CurrentElapsed,
|
||||
Text("/"),
|
||||
CurrentDuration,
|
||||
Text("] "),
|
||||
])),
|
||||
If(TitleExist,
|
||||
Parts([
|
||||
Styled([Fg(Indexed(149))], CurrentTitle),
|
||||
If(ArtistExist, Parts([
|
||||
Styled([Fg(Indexed(216))], Text(" ◆ ")),
|
||||
Styled([Fg(Indexed(221))], CurrentAlbum),
|
||||
Styled([Fg(Indexed(185))], CurrentArtist),
|
||||
If(AlbumExist, Parts([
|
||||
Styled([Fg(Indexed(216))], Text(" ◆ ")),
|
||||
Styled([Fg(Indexed(221))], CurrentAlbum),
|
||||
])),
|
||||
])),
|
||||
])),
|
||||
]),
|
||||
Styled([Fg(Indexed(185))], CurrentFile),
|
||||
),
|
||||
]))))),
|
||||
]),
|
||||
Styled([Fg(Indexed(185))], CurrentFile),
|
||||
),
|
||||
])),
|
||||
)))),
|
||||
Fixed(12, TextboxR(Styled([Fg(Indexed(81))], Parts([
|
||||
Text("["),
|
||||
If(Repeat, Text("@")),
|
||||
|
|
|
@ -59,6 +59,7 @@ pub enum Texts {
|
|||
QueueTitle,
|
||||
QueueArtist,
|
||||
QueueAlbum,
|
||||
Query,
|
||||
Styled(Vec<AddStyle>, Box<Texts>),
|
||||
Parts(Vec<Texts>),
|
||||
If(Condition, Box<Texts>, Option<Box<Texts>>),
|
||||
|
@ -103,6 +104,7 @@ pub enum Condition {
|
|||
AlbumExist,
|
||||
QueueCurrent,
|
||||
Selected,
|
||||
Searching,
|
||||
Not(Box<Condition>),
|
||||
And(Box<Condition>, Box<Condition>),
|
||||
Or(Box<Condition>, Box<Condition>),
|
||||
|
@ -147,6 +149,7 @@ impl<'de> Deserialize<'de> for Texts {
|
|||
QueueTitle,
|
||||
QueueArtist,
|
||||
QueueAlbum,
|
||||
Query,
|
||||
Styled,
|
||||
Parts,
|
||||
If,
|
||||
|
@ -219,6 +222,7 @@ impl<'de> Deserialize<'de> for Texts {
|
|||
Variant::QueueTitle => unit_variant!(QueueTitle),
|
||||
Variant::QueueArtist => unit_variant!(QueueArtist),
|
||||
Variant::QueueAlbum => unit_variant!(QueueAlbum),
|
||||
Variant::Query => unit_variant!(Query),
|
||||
Variant::Styled => va.tuple_variant(2, StyledVisitor),
|
||||
Variant::Parts => Ok(Texts::Parts(va.newtype_variant()?)),
|
||||
Variant::If => va.tuple_variant(3, IfVisitor),
|
||||
|
@ -241,6 +245,7 @@ impl<'de> Deserialize<'de> for Texts {
|
|||
"QueueTitle",
|
||||
"QueueArtist",
|
||||
"QueueAlbum",
|
||||
"Query",
|
||||
"Styled",
|
||||
"Parts",
|
||||
"If",
|
||||
|
|
135
src/defaults.rs
135
src/defaults.rs
|
@ -152,71 +152,90 @@ pub fn layout() -> Widget {
|
|||
Widget::Columns(vec![
|
||||
Constrained::Min(
|
||||
0,
|
||||
Widget::Textbox(Texts::If(
|
||||
Condition::Not(Box::new(Condition::Stopped)),
|
||||
Box::new(Texts::Styled(
|
||||
vec![AddStyle::Bold],
|
||||
Widget::Textbox(Texts::Styled(
|
||||
vec![AddStyle::Bold],
|
||||
Box::new(Texts::If(
|
||||
Condition::Searching,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(113))],
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::If(
|
||||
Condition::Playing,
|
||||
Box::new(Texts::Text(String::from("[playing: "))),
|
||||
Some(Box::new(Texts::Text(String::from("[paused: ")))),
|
||||
),
|
||||
Texts::CurrentElapsed,
|
||||
Texts::Text(String::from("/")),
|
||||
Texts::CurrentDuration,
|
||||
Texts::Text(String::from("] ")),
|
||||
])),
|
||||
Box::new(Texts::Text(String::from("Searching: "))),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::TitleExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(149))],
|
||||
Box::new(Texts::CurrentTitle),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::ArtistExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(216))],
|
||||
Box::new(Texts::Text(String::from(" ◆ "))),
|
||||
),
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(185))],
|
||||
Box::new(Texts::CurrentArtist),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::AlbumExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(216))],
|
||||
Box::new(Texts::Text(String::from(
|
||||
" ◆ ",
|
||||
))),
|
||||
),
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(221))],
|
||||
Box::new(Texts::CurrentAlbum),
|
||||
),
|
||||
])),
|
||||
None,
|
||||
),
|
||||
])),
|
||||
None,
|
||||
),
|
||||
])),
|
||||
Some(Box::new(Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(185))],
|
||||
Box::new(Texts::CurrentFile),
|
||||
))),
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(185))],
|
||||
Box::new(Texts::Query),
|
||||
),
|
||||
])),
|
||||
Some(Box::new(Texts::If(
|
||||
Condition::Not(Box::new(Condition::Stopped)),
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(113))],
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::If(
|
||||
Condition::Playing,
|
||||
Box::new(Texts::Text(String::from("[playing: "))),
|
||||
Some(Box::new(Texts::Text(String::from(
|
||||
"[paused: ",
|
||||
)))),
|
||||
),
|
||||
Texts::CurrentElapsed,
|
||||
Texts::Text(String::from("/")),
|
||||
Texts::CurrentDuration,
|
||||
Texts::Text(String::from("] ")),
|
||||
])),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::TitleExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(149))],
|
||||
Box::new(Texts::CurrentTitle),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::ArtistExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(216))],
|
||||
Box::new(Texts::Text(String::from(" ◆ "))),
|
||||
),
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(185))],
|
||||
Box::new(Texts::CurrentArtist),
|
||||
),
|
||||
Texts::If(
|
||||
Condition::AlbumExist,
|
||||
Box::new(Texts::Parts(vec![
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(
|
||||
216,
|
||||
))],
|
||||
Box::new(Texts::Text(
|
||||
String::from(" ◆ "),
|
||||
)),
|
||||
),
|
||||
Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(
|
||||
221,
|
||||
))],
|
||||
Box::new(Texts::CurrentAlbum),
|
||||
),
|
||||
])),
|
||||
None,
|
||||
),
|
||||
])),
|
||||
None,
|
||||
),
|
||||
])),
|
||||
Some(Box::new(Texts::Styled(
|
||||
vec![AddStyle::Fg(Color::Indexed(185))],
|
||||
Box::new(Texts::CurrentFile),
|
||||
))),
|
||||
),
|
||||
])),
|
||||
None,
|
||||
))),
|
||||
)),
|
||||
None,
|
||||
)),
|
||||
),
|
||||
Constrained::Fixed(
|
||||
|
|
167
src/layout.rs
167
src/layout.rs
|
@ -17,6 +17,9 @@ pub fn render(
|
|||
size: Rect,
|
||||
widget: &Widget,
|
||||
queue: &[Track],
|
||||
searching: bool,
|
||||
query: &str,
|
||||
filtered: &Option<Vec<usize>>,
|
||||
status: &Status,
|
||||
liststate: &mut ListState,
|
||||
) {
|
||||
|
@ -53,7 +56,9 @@ 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, liststate);
|
||||
render(
|
||||
frame, chunk, w, queue, searching, query, filtered, status, liststate,
|
||||
);
|
||||
}
|
||||
}
|
||||
Widget::Columns(xs) => {
|
||||
|
@ -88,7 +93,9 @@ 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, liststate);
|
||||
render(
|
||||
frame, chunk, w, queue, searching, query, filtered, status, liststate,
|
||||
);
|
||||
}
|
||||
}
|
||||
Widget::Textbox(xs) => {
|
||||
|
@ -105,6 +112,8 @@ pub fn render(
|
|||
None,
|
||||
false,
|
||||
false,
|
||||
searching,
|
||||
query,
|
||||
Style::default(),
|
||||
);
|
||||
frame.render_widget(Paragraph::new(Spans::from(spans)), size);
|
||||
|
@ -123,6 +132,8 @@ pub fn render(
|
|||
None,
|
||||
false,
|
||||
false,
|
||||
searching,
|
||||
query,
|
||||
Style::default(),
|
||||
);
|
||||
frame.render_widget(
|
||||
|
@ -144,6 +155,8 @@ pub fn render(
|
|||
None,
|
||||
false,
|
||||
false,
|
||||
searching,
|
||||
query,
|
||||
Style::default(),
|
||||
);
|
||||
frame.render_widget(
|
||||
|
@ -184,19 +197,40 @@ pub fn render(
|
|||
};
|
||||
|
||||
let mut items = Vec::with_capacity(len);
|
||||
for (i, track) in queue.iter().enumerate() {
|
||||
let mut spans = Vec::new();
|
||||
flatten(
|
||||
&mut spans,
|
||||
txts,
|
||||
status,
|
||||
current_track,
|
||||
Some(track),
|
||||
pos == Some(i),
|
||||
liststate.selected() == Some(i),
|
||||
Style::default(),
|
||||
);
|
||||
items.push(ListItem::new(Spans::from(spans)));
|
||||
if let Some(filtered) = filtered {
|
||||
for &i in filtered {
|
||||
let mut spans = Vec::new();
|
||||
flatten(
|
||||
&mut spans,
|
||||
txts,
|
||||
status,
|
||||
current_track,
|
||||
Some(&queue[i]),
|
||||
pos == Some(i),
|
||||
liststate.selected() == Some(i),
|
||||
searching,
|
||||
query,
|
||||
Style::default(),
|
||||
);
|
||||
items.push(ListItem::new(Spans::from(spans)));
|
||||
}
|
||||
} else {
|
||||
for (i, track) in queue.iter().enumerate() {
|
||||
let mut spans = Vec::new();
|
||||
flatten(
|
||||
&mut spans,
|
||||
txts,
|
||||
status,
|
||||
current_track,
|
||||
Some(track),
|
||||
pos == Some(i),
|
||||
liststate.selected() == Some(i),
|
||||
searching,
|
||||
query,
|
||||
Style::default(),
|
||||
);
|
||||
items.push(ListItem::new(Spans::from(spans)));
|
||||
}
|
||||
}
|
||||
ws.push(
|
||||
List::new(items)
|
||||
|
@ -231,6 +265,8 @@ fn flatten(
|
|||
queue_track: Option<&Track>,
|
||||
queue_current: bool,
|
||||
selected: bool,
|
||||
searching: bool,
|
||||
query: &str,
|
||||
style: Style,
|
||||
) {
|
||||
match xs {
|
||||
|
@ -319,6 +355,9 @@ fn flatten(
|
|||
spans.push(Span::styled(album.clone(), style));
|
||||
}
|
||||
}
|
||||
Texts::Query => {
|
||||
spans.push(Span::styled(String::from(query), style));
|
||||
}
|
||||
Texts::Styled(styles, box xs) => {
|
||||
flatten(
|
||||
spans,
|
||||
|
@ -328,6 +367,8 @@ fn flatten(
|
|||
queue_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
patch_style(style, styles),
|
||||
);
|
||||
}
|
||||
|
@ -341,6 +382,8 @@ fn flatten(
|
|||
queue_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
style,
|
||||
);
|
||||
}
|
||||
|
@ -348,7 +391,15 @@ fn flatten(
|
|||
Texts::If(cond, box yes, Some(box no)) => {
|
||||
flatten(
|
||||
spans,
|
||||
if eval_cond(cond, status, current_track, queue_current, selected) {
|
||||
if eval_cond(
|
||||
cond,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
) {
|
||||
yes
|
||||
} else {
|
||||
no
|
||||
|
@ -358,11 +409,21 @@ fn flatten(
|
|||
queue_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
style,
|
||||
);
|
||||
}
|
||||
Texts::If(cond, box xs, None) => {
|
||||
if eval_cond(cond, status, current_track, queue_current, selected) {
|
||||
if eval_cond(
|
||||
cond,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
) {
|
||||
flatten(
|
||||
spans,
|
||||
xs,
|
||||
|
@ -371,6 +432,8 @@ fn flatten(
|
|||
queue_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
style,
|
||||
);
|
||||
}
|
||||
|
@ -453,6 +516,8 @@ fn eval_cond(
|
|||
current_track: Option<&Track>,
|
||||
queue_current: bool,
|
||||
selected: bool,
|
||||
searching: bool,
|
||||
query: &str,
|
||||
) -> bool {
|
||||
match cond {
|
||||
Condition::Repeat => status.repeat,
|
||||
|
@ -473,18 +538,72 @@ fn eval_cond(
|
|||
Condition::AlbumExist => matches!(current_track, Some(Track { album: Some(_), .. })),
|
||||
Condition::QueueCurrent => queue_current,
|
||||
Condition::Selected => selected,
|
||||
Condition::Not(box x) => !eval_cond(x, status, current_track, queue_current, selected),
|
||||
Condition::Searching => searching,
|
||||
Condition::Not(box x) => !eval_cond(
|
||||
x,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
),
|
||||
Condition::And(box x, box y) => {
|
||||
eval_cond(x, status, current_track, queue_current, selected)
|
||||
&& eval_cond(y, status, current_track, queue_current, selected)
|
||||
eval_cond(
|
||||
x,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
) && eval_cond(
|
||||
y,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
)
|
||||
}
|
||||
Condition::Or(box x, box y) => {
|
||||
eval_cond(x, status, current_track, queue_current, selected)
|
||||
|| eval_cond(y, status, current_track, queue_current, selected)
|
||||
eval_cond(
|
||||
x,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
) || eval_cond(
|
||||
y,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
)
|
||||
}
|
||||
Condition::Xor(box x, box y) => {
|
||||
eval_cond(x, status, current_track, queue_current, selected)
|
||||
^ eval_cond(y, status, current_track, queue_current, selected)
|
||||
eval_cond(
|
||||
x,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
) ^ eval_cond(
|
||||
y,
|
||||
status,
|
||||
current_track,
|
||||
queue_current,
|
||||
selected,
|
||||
searching,
|
||||
query,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
111
src/main.rs
111
src/main.rs
|
@ -27,6 +27,10 @@ use std::{
|
|||
fs,
|
||||
io::{stdout, Write},
|
||||
process::exit,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
|
@ -91,6 +95,10 @@ enum Command {
|
|||
Up,
|
||||
JumpDown,
|
||||
JumpUp,
|
||||
InputSearch(char),
|
||||
BackspaceSearch,
|
||||
UpdateSearch,
|
||||
QuitSearch,
|
||||
}
|
||||
|
||||
fn cleanup() -> Result<()> {
|
||||
|
@ -148,11 +156,15 @@ async fn run() -> Result<()> {
|
|||
let mut idle_cl = mpd::init(addr).await?;
|
||||
let mut cl = mpd::init(addr).await?;
|
||||
|
||||
let mut queue = mpd::queue(&mut idle_cl).await?;
|
||||
let (mut queue, mut queue_strings) = mpd::queue(&mut idle_cl).await?;
|
||||
let mut status = mpd::status(&mut cl).await?;
|
||||
let mut selected = status.song.map_or(0, |song| song.pos);
|
||||
let mut liststate = ListState::default();
|
||||
liststate.select(Some(selected));
|
||||
let searching = Arc::new(AtomicBool::new(false));
|
||||
let searching1 = Arc::clone(&searching);
|
||||
let mut query = String::with_capacity(32);
|
||||
let mut filtered = None;
|
||||
|
||||
let seek_backwards = format!("seekcur -{}\n", seek_secs);
|
||||
let seek_backwards = seek_backwards.as_bytes();
|
||||
|
@ -200,9 +212,29 @@ async fn run() -> Result<()> {
|
|||
Terminal::new(CrosstermBackend::new(stdout)).context("Failed to initialize terminal")?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
let searching = searching1;
|
||||
let tx = tx3;
|
||||
while let Ok(ev) = event::read() {
|
||||
if let Some(cmd) = match ev {
|
||||
if let Event::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) = ev
|
||||
{
|
||||
tx.send(Command::QuitSearch).await.unwrap_or_else(die);
|
||||
} else if searching.load(Ordering::Acquire) {
|
||||
if let Event::Key(KeyEvent { code, .. }) = ev {
|
||||
if let Some(cmd) = match code {
|
||||
KeyCode::Char(c) => Some(Command::InputSearch(c)),
|
||||
KeyCode::Backspace => Some(Command::BackspaceSearch),
|
||||
KeyCode::Enter => {
|
||||
searching.store(false, Ordering::Release);
|
||||
Some(Command::UpdateFrame)
|
||||
}
|
||||
_ => None,
|
||||
} {
|
||||
tx.send(cmd).await.unwrap_or_else(die);
|
||||
}
|
||||
}
|
||||
} else if let Some(cmd) = match ev {
|
||||
Event::Key(KeyEvent { code, .. }) => match code {
|
||||
KeyCode::Char('q') => Some(Command::Quit),
|
||||
KeyCode::Char('r') => Some(Command::ToggleRepeat),
|
||||
|
@ -222,6 +254,10 @@ async fn run() -> Result<()> {
|
|||
KeyCode::Char('k') | KeyCode::Up => Some(Command::Up),
|
||||
KeyCode::Char('J') | KeyCode::PageDown => Some(Command::JumpDown),
|
||||
KeyCode::Char('K') | KeyCode::PageUp => Some(Command::JumpUp),
|
||||
KeyCode::Char('/') => {
|
||||
searching.store(true, Ordering::Release);
|
||||
Some(Command::UpdateFrame)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Event::Mouse(MouseEvent::ScrollDown(..)) => Some(Command::Down),
|
||||
|
@ -244,16 +280,24 @@ async fn run() -> Result<()> {
|
|||
frame.size(),
|
||||
&cfg.layout,
|
||||
&queue,
|
||||
searching.load(Ordering::Acquire),
|
||||
&query,
|
||||
&filtered,
|
||||
&status,
|
||||
&mut liststate,
|
||||
);
|
||||
})
|
||||
.context("Failed to draw to terminal")?,
|
||||
Command::UpdateQueue => {
|
||||
queue = mpd::queue(&mut cl).await.context("Failed to query queue")?;
|
||||
let res = mpd::queue(&mut cl).await.context("Failed to query queue")?;
|
||||
queue = res.0;
|
||||
queue_strings = res.1;
|
||||
selected = status.song.map_or(0, |song| song.pos);
|
||||
liststate = ListState::default();
|
||||
liststate.select(Some(selected));
|
||||
if searching.load(Ordering::Acquire) {
|
||||
tx.send(Command::UpdateSearch).await?;
|
||||
}
|
||||
}
|
||||
Command::UpdateStatus => {
|
||||
status = mpd::status(&mut cl)
|
||||
|
@ -373,11 +417,17 @@ async fn run() -> Result<()> {
|
|||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::Play => {
|
||||
if selected < queue.len() {
|
||||
if let Some(filtered) = &filtered {
|
||||
if selected < filtered.len() {
|
||||
mpd::play(&mut cl, filtered[selected])
|
||||
.await
|
||||
.context("Failed to play the selected song")?;
|
||||
}
|
||||
} else if selected < queue.len() {
|
||||
mpd::play(&mut cl, selected)
|
||||
.await
|
||||
.context("Failed to play the selected song")?;
|
||||
}
|
||||
};
|
||||
tx.send(Command::UpdateStatus).await?;
|
||||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
|
@ -387,7 +437,11 @@ async fn run() -> Result<()> {
|
|||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::Down => {
|
||||
let len = queue.len();
|
||||
let len = if let Some(filtered) = &filtered {
|
||||
filtered.len()
|
||||
} else {
|
||||
query.len()
|
||||
};
|
||||
if selected >= len {
|
||||
selected = status.song.map_or(0, |song| song.pos);
|
||||
} else if selected == len - 1 {
|
||||
|
@ -401,7 +455,11 @@ async fn run() -> Result<()> {
|
|||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::Up => {
|
||||
let len = queue.len();
|
||||
let len = if let Some(filtered) = &filtered {
|
||||
filtered.len()
|
||||
} else {
|
||||
query.len()
|
||||
};
|
||||
if selected >= len {
|
||||
selected = status.song.map_or(0, |song| song.pos);
|
||||
} else if selected == 0 {
|
||||
|
@ -415,7 +473,11 @@ async fn run() -> Result<()> {
|
|||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::JumpDown => {
|
||||
let len = queue.len();
|
||||
let len = if let Some(filtered) = &filtered {
|
||||
filtered.len()
|
||||
} else {
|
||||
query.len()
|
||||
};
|
||||
if selected >= len {
|
||||
selected = status.song.map_or(0, |song| song.pos);
|
||||
} else {
|
||||
|
@ -429,7 +491,11 @@ async fn run() -> Result<()> {
|
|||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::JumpUp => {
|
||||
let len = queue.len();
|
||||
let len = if let Some(filtered) = &filtered {
|
||||
filtered.len()
|
||||
} else {
|
||||
query.len()
|
||||
};
|
||||
if selected >= len {
|
||||
selected = status.song.map_or(0, |song| song.pos);
|
||||
} else {
|
||||
|
@ -442,6 +508,33 @@ async fn run() -> Result<()> {
|
|||
liststate.select(Some(selected));
|
||||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::InputSearch(c) => {
|
||||
query.push(c);
|
||||
tx.send(Command::UpdateSearch).await?;
|
||||
}
|
||||
Command::BackspaceSearch => {
|
||||
query.pop();
|
||||
tx.send(Command::UpdateSearch).await?;
|
||||
}
|
||||
Command::UpdateSearch => {
|
||||
let query = query.to_lowercase();
|
||||
let mut xs = Vec::new();
|
||||
for (i, track) in queue_strings.iter().enumerate() {
|
||||
if track.contains(&query) {
|
||||
xs.push(i);
|
||||
}
|
||||
}
|
||||
filtered = Some(xs);
|
||||
selected = 0;
|
||||
liststate.select(Some(selected));
|
||||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
Command::QuitSearch => {
|
||||
searching.store(false, Ordering::Release);
|
||||
filtered = None;
|
||||
query.clear();
|
||||
tx.send(Command::UpdateFrame).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
41
src/mpd.rs
41
src/mpd.rs
|
@ -73,14 +73,15 @@ pub async fn idle(cl: &mut Client) -> Result<(bool, bool)> {
|
|||
Ok((queue, status))
|
||||
}
|
||||
|
||||
pub async fn queue(cl: &mut Client) -> Result<Vec<Track>> {
|
||||
pub async fn queue(cl: &mut Client) -> Result<(Vec<Track>, Vec<String>)> {
|
||||
let mut first = true;
|
||||
let mut tracks = Vec::new();
|
||||
let mut track_strings = Vec::new();
|
||||
|
||||
let mut file = None;
|
||||
let mut artist = None;
|
||||
let mut album = None;
|
||||
let mut title = None;
|
||||
let mut file: Option<String> = None;
|
||||
let mut artist: Option<String> = None;
|
||||
let mut album: Option<String> = None;
|
||||
let mut title: Option<String> = None;
|
||||
let mut time = None;
|
||||
|
||||
cl.write_all(b"playlistinfo\n").await?;
|
||||
|
@ -93,6 +94,20 @@ pub async fn queue(cl: &mut Client) -> Result<Vec<Track>> {
|
|||
if first {
|
||||
first = false;
|
||||
} else if let (Some(file), Some(time)) = (file, time) {
|
||||
let mut track_string = String::from(file.to_lowercase());
|
||||
if let Some(artist) = &artist {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&artist.to_lowercase());
|
||||
}
|
||||
if let Some(album) = &album {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&album.to_lowercase());
|
||||
}
|
||||
if let Some(title) = &title {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&title.to_lowercase());
|
||||
}
|
||||
track_strings.push(track_string);
|
||||
tracks.push(Track {
|
||||
file,
|
||||
artist,
|
||||
|
@ -127,6 +142,20 @@ pub async fn queue(cl: &mut Client) -> Result<Vec<Track>> {
|
|||
}
|
||||
|
||||
if let (Some(file), Some(time)) = (file, time) {
|
||||
let mut track_string = String::from(file.to_lowercase());
|
||||
if let Some(artist) = &artist {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&artist.to_lowercase());
|
||||
}
|
||||
if let Some(album) = &album {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&album.to_lowercase());
|
||||
}
|
||||
if let Some(title) = &title {
|
||||
track_string.push('\n');
|
||||
track_string.push_str(&title.to_lowercase());
|
||||
}
|
||||
track_strings.push(track_string);
|
||||
tracks.push(Track {
|
||||
file,
|
||||
artist,
|
||||
|
@ -136,7 +165,7 @@ pub async fn queue(cl: &mut Client) -> Result<Vec<Track>> {
|
|||
});
|
||||
}
|
||||
|
||||
Ok(tracks)
|
||||
Ok((tracks, track_strings))
|
||||
}
|
||||
|
||||
pub async fn status(cl: &mut Client) -> Result<Status> {
|
||||
|
|
Loading…
Reference in a new issue