#![warn(
clippy::all,
clippy::pedantic,
clippy::nursery,
clippy::cargo_common_metadata
)]
#![allow(clippy::non_ascii_literal)]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss,
clippy::cast_lossless
)]
#![allow(let_underscore_drop)]
use std::{
io::stdin,
process::exit,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
time::Duration,
};
use crossbeam_channel::{bounded, Sender};
use diskit::StdDiskit;
use eframe::egui;
use egui::Slider;
use legacylisten::{
api::ApiRequester, audio::RodioAudioHandler, commands::Command, conffile::Conffile,
l10n::messages::Message, runner, songs::Repeat,
};
use legacygui::writer::MessageList;
struct MyApp
{
all_messages: MessageList,
is_quit: Arc<AtomicBool>,
api: ApiRequester,
old_likelihood: f64,
quit_requester: Sender<()>,
strings: Vec<String>,
info: Vec<String>,
}
impl MyApp
{
fn new() -> Self
{
let is_quit = Arc::new(AtomicBool::new(false));
let is_quit_clone = is_quit.clone();
let (all_messages, writer_handler) = MessageList::new();
let writer_handler_clone = writer_handler;
let (api, requester) = ApiRequester::new();
let (quit_requester, quit_request) = bounded(1);
thread::spawn(move || {
let writer_handler = writer_handler_clone;
if let Err(err) = runner::run::<_, RodioAudioHandler, _, _>(
Conffile::new,
writer_handler,
|| stdin().lock(),
requester,
quit_request,
StdDiskit::default(),
)
{
eprintln!("Error with legacylisten: {err:?}");
}
is_quit_clone.store(true, Ordering::SeqCst);
});
let strings = vec![
api.localize(Message::Description(Command::Quit)).unwrap(),
api.localize(Message::Description(Command::SkipToPrevious))
.unwrap(),
api.localize(Message::Description(Command::Pause)).unwrap(),
api.localize(Message::Description(Command::Resume)).unwrap(),
api.localize(Message::Description(Command::Skip)).unwrap(),
api.localize(Message::Description(Command::IncreaseLikelihood))
.unwrap(),
api.localize(Message::Description(Command::DecreaseLikelihood))
.unwrap(),
api.localize(Message::Volume).unwrap(),
api.localize(Message::Description(Command::DisableRepeat))
.unwrap(),
api.localize(Message::Description(Command::RepeatOnce))
.unwrap(),
api.localize(Message::Description(Command::RepeatForever))
.unwrap(),
api.localize(Message::Description(Command::QuitAfterSong))
.unwrap(),
api.localize(Message::Description(Command::PauseAfterSong))
.unwrap(),
api.localize(Message::Description(Command::ShowInfo))
.unwrap(),
api.localize(Message::HideInfo).unwrap(),
];
Self {
all_messages,
is_quit,
api,
old_likelihood: 10.0,
quit_requester,
strings,
info: vec![],
}
}
}
impl eframe::App for MyApp
{
fn on_close_event(&mut self) -> bool
{
let _ = self.quit_requester.send(());
if self.api.quit().is_none()
{
exit(0);
}
thread::sleep(Duration::from_millis(1000));
true
}
#[allow(clippy::too_many_lines)]
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame)
{
egui::CentralPanel::default().show(ctx, |ui| {
if self.is_quit.load(Ordering::SeqCst)
{
frame.close();
}
let is_playing = self.api.is_playing().unwrap();
if ui.button(&self.strings[0]).clicked()
{
frame.close();
}
ui.horizontal(|ui| {
if ui.button(&self.strings[1]).clicked()
{
self.api.previous(2).unwrap();
self.api.skip(1).unwrap();
}
if is_playing
{
if ui.button(&self.strings[2]).clicked()
{
self.api.pause().unwrap();
}
}
else if ui.button(&self.strings[3]).clicked()
{
self.api.resume().unwrap();
}
if ui.button(&self.strings[4]).clicked()
{
self.api.skip(1).unwrap();
}
});
ui.horizontal(|ui| {
let mut likelihood = self.api.get_likelihood().unwrap();
let orig_likelihood = likelihood;
if self.old_likelihood > likelihood as f64
{
self.old_likelihood = likelihood as _;
}
else
{
self.old_likelihood =
15.0f64.mul_add(self.old_likelihood, likelihood as f64) / 16.0;
}
if ui.button(&self.strings[5]).clicked()
{
likelihood += 1;
}
if ui.button(&self.strings[6]).clicked()
{
likelihood -= 1;
}
ui.add(Slider::new(
&mut likelihood,
0..=((2.0 * self.old_likelihood) as u32 + 10),
));
if likelihood != orig_likelihood
{
self.api.set_likelihood(likelihood).unwrap();
}
});
ui.horizontal(|ui| {
let mut loud = self.api.get_volume().unwrap() * 100.0;
let old_loud = loud;
ui.add(Slider::new(&mut loud, 0.0..=100.0).text(&self.strings[7]));
if (loud - old_loud).abs() > 0.01
{
println!("{loud}");
self.api.set_volume(loud / 100.0).unwrap();
}
});
let (cur, len) = self.api.get_duration().unwrap();
#[allow(clippy::option_if_let_else)]
let mes = if let Some(len) = len
{
Message::DurationKnown((cur * 100.0).round() / 100.0, (len * 100.0).round() / 100.0)
}
else
{
Message::DurationUnknown((cur * 100.0).round() / 100.0)
};
ui.label(self.api.localize(mes).unwrap());
ui.horizontal(|ui| {
if ui.button(&self.strings[8]).clicked()
{
self.api.repeat(Repeat::Not).unwrap();
}
if ui.button(&self.strings[9]).clicked()
{
self.api.repeat(Repeat::Once).unwrap();
}
if ui.button(&self.strings[10]).clicked()
{
self.api.repeat(Repeat::Always).unwrap();
}
});
ui.horizontal(|ui| {
if ui.button(&self.strings[11]).clicked()
{
self.api.quit_after_song().unwrap();
}
if ui.button(&self.strings[12]).clicked()
{
self.api.pause_after_song().unwrap();
}
});
if self.info.is_empty()
{
if ui.button(&self.strings[13]).clicked()
{
self.info = self
.api
.get_info()
.unwrap()
.into_iter()
.map(|mes| self.api.localize(mes).unwrap())
.collect();
}
}
else
{
ui.separator();
if ui.button(&self.strings[14]).clicked()
{
self.info.clear();
}
else
{
ui.vertical(|ui| {
self.info.iter().for_each(|mes| {
ui.label(mes);
});
});
}
}
ui.separator();
ui.vertical(|ui| {
for mes in self.all_messages.get_list().into_iter().rev()
{
ui.label(mes);
}
});
});
ctx.request_repaint_after(Duration::from_millis(50));
}
}
fn main()
{
let options = eframe::NativeOptions::default();
eframe::run_native("legacygui", options, Box::new(|_cc| Box::new(MyApp::new())));
}