mirror of
https://github.com/talwat/lowfi
synced 2025-02-05 15:21:28 +00:00
feat: make ui much more asthetically pleasing
This commit is contained in:
parent
e9857e5c11
commit
1b2466e1f8
117
src/player/ui.rs
117
src/player/ui.rs
@ -1,9 +1,11 @@
|
||||
use std::{io::stderr, sync::Arc, time::Duration};
|
||||
|
||||
use crate::tracks::TrackInfo;
|
||||
|
||||
use super::Player;
|
||||
use crossterm::{
|
||||
cursor::{MoveToColumn, MoveUp, Show},
|
||||
style::Print,
|
||||
cursor::{Hide, MoveToColumn, MoveUp, Show},
|
||||
style::{Print, Stylize},
|
||||
terminal::{Clear, ClearType},
|
||||
};
|
||||
use tokio::{
|
||||
@ -14,53 +16,109 @@ use tokio::{
|
||||
|
||||
use super::Messages;
|
||||
|
||||
async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
|
||||
const WIDTH: usize = 25;
|
||||
const PROGRESS_WIDTH: usize = WIDTH - 4;
|
||||
fn format_duration(duration: &Duration) -> String {
|
||||
let seconds = duration.as_secs() % 60;
|
||||
let minutes = duration.as_secs() / 60;
|
||||
|
||||
loop {
|
||||
// We can get away with only redrawing every 0.25 seconds
|
||||
// since it's just an audio player.
|
||||
sleep(Duration::from_secs_f32(0.25)).await;
|
||||
crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown))?;
|
||||
format!("{:02}:{:02}", minutes, seconds)
|
||||
}
|
||||
|
||||
let mut main = match queue.current.load().as_ref() {
|
||||
Some(x) => {
|
||||
if queue.sink.is_paused() {
|
||||
format!("paused {}", x.format_name())
|
||||
} else {
|
||||
format!("playing {}", x.format_name())
|
||||
}
|
||||
}
|
||||
None => "loading...".to_owned(),
|
||||
enum Action {
|
||||
Paused(TrackInfo),
|
||||
Playing(TrackInfo),
|
||||
Loading,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
fn format(&self) -> (String, usize) {
|
||||
let (word, subject) = match self {
|
||||
Action::Playing(x) => ("playing", Some(x.format_name())),
|
||||
Action::Paused(x) => ("paused", Some(x.format_name())),
|
||||
Action::Loading => ("loading", None),
|
||||
};
|
||||
|
||||
main.push_str("\r\n");
|
||||
if let Some(subject) = subject {
|
||||
(
|
||||
format!("{} {}", word, subject.bold()),
|
||||
word.len() + 1 + subject.len(),
|
||||
)
|
||||
} else {
|
||||
(format!("{}", word), word.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn interface(queue: Arc<Player>) -> eyre::Result<()> {
|
||||
const WIDTH: usize = 27;
|
||||
const PROGRESS_WIDTH: usize = WIDTH - 16;
|
||||
|
||||
loop {
|
||||
let (mut main, len) = match queue.current.load().as_ref() {
|
||||
Some(x) => {
|
||||
if queue.sink.is_paused() {
|
||||
Action::Paused(*x.clone())
|
||||
} else {
|
||||
Action::Playing(*x.clone())
|
||||
}
|
||||
}
|
||||
None => Action::Loading,
|
||||
}
|
||||
.format();
|
||||
|
||||
if len > WIDTH {
|
||||
main = format!("{}...", &main[..=WIDTH]);
|
||||
} else {
|
||||
main = format!("{}{}", main, " ".repeat(WIDTH - len));
|
||||
}
|
||||
|
||||
let mut duration = Duration::new(0, 0);
|
||||
let elapsed = queue.sink.get_pos();
|
||||
|
||||
let mut filled = 0;
|
||||
if let Some(current) = queue.current.load().as_ref() {
|
||||
if let Some(duration) = current.duration {
|
||||
let elapsed = queue.sink.get_pos().as_secs() as f32 / duration.as_secs() as f32;
|
||||
if let Some(x) = current.duration {
|
||||
duration = x;
|
||||
|
||||
let elapsed = elapsed.as_secs() as f32 / duration.as_secs() as f32;
|
||||
filled = (elapsed * PROGRESS_WIDTH as f32).round() as usize;
|
||||
}
|
||||
};
|
||||
|
||||
let progress = format!(
|
||||
" [{}{}] ",
|
||||
" [{}{}] {}/{} ",
|
||||
"/".repeat(filled as usize),
|
||||
" ".repeat(PROGRESS_WIDTH - filled)
|
||||
" ".repeat(PROGRESS_WIDTH - filled),
|
||||
format_duration(&elapsed),
|
||||
format_duration(&duration),
|
||||
);
|
||||
let bar = ["[s]kip", "[p]ause", "[q]uit"];
|
||||
let bar = [
|
||||
format!("{}kip", "[s]".bold()),
|
||||
format!("{}ause", "[p]".bold()),
|
||||
format!("{}uit", "[q]".bold()),
|
||||
];
|
||||
|
||||
crossterm::execute!(stderr(), MoveToColumn(0), Print(main))?;
|
||||
crossterm::execute!(stderr(), Print(progress), Print("\r\n"))?;
|
||||
crossterm::execute!(stderr(), Print(bar.join(" ")), Print("\r\n"))?;
|
||||
crossterm::execute!(stderr(), MoveToColumn(0), MoveUp(3))?;
|
||||
// Formats the menu properly
|
||||
let menu =
|
||||
[main, progress, bar.join(" ")].map(|x| format!("│ {x} │\r\n").reset().to_string());
|
||||
|
||||
crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown))?;
|
||||
crossterm::execute!(
|
||||
stderr(),
|
||||
MoveToColumn(0),
|
||||
Print(format!("┌{}┐\r\n", "─".repeat(WIDTH + 2))),
|
||||
Print(menu.join("")),
|
||||
Print(format!("└{}┘", "─".repeat(WIDTH + 2))),
|
||||
MoveToColumn(0),
|
||||
MoveUp(4)
|
||||
)?;
|
||||
|
||||
sleep(Duration::from_secs_f32(0.25)).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(queue: Arc<Player>, sender: Sender<Messages>) -> eyre::Result<()> {
|
||||
crossterm::terminal::enable_raw_mode()?;
|
||||
crossterm::execute!(stderr(), Hide)?;
|
||||
//crossterm::execute!(stderr(), EnterAlternateScreen, MoveTo(0, 0))?;
|
||||
|
||||
task::spawn(interface(queue.clone()));
|
||||
@ -90,6 +148,7 @@ pub async fn start(queue: Arc<Player>, sender: Sender<Messages>) -> eyre::Result
|
||||
}
|
||||
}
|
||||
|
||||
//crossterm::execute!(stderr(), LeaveAlternateScreen)?;
|
||||
crossterm::execute!(stderr(), Clear(ClearType::FromCursorDown), Show)?;
|
||||
crossterm::terminal::disable_raw_mode()?;
|
||||
|
||||
|
@ -34,7 +34,12 @@ pub struct TrackInfo {
|
||||
|
||||
impl TrackInfo {
|
||||
pub fn format_name(&self) -> &'static str {
|
||||
self.name.split("/").nth(2).unwrap()
|
||||
self.name
|
||||
.split("/")
|
||||
.nth(2)
|
||||
.unwrap()
|
||||
.strip_suffix(".mp3")
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user