use std::{
io::Write,
sync::{Arc, Mutex},
thread,
time::{Duration, Instant},
};
use crossterm::{
cursor::MoveTo,
event::{Event, KeyCode, KeyModifiers},
queue,
terminal::{disable_raw_mode, enable_raw_mode},
};
use crate::{
term::{self, get_terminal_size, is_raw_mode},
Canvas, Paint,
};
pub struct Animation {
canvas: Arc<Mutex<Canvas>>,
objs: Arc<Mutex<Vec<Box<dyn Update + Send>>>>,
fps: u32,
hide_cursor: bool,
size: Option<(i32, i32)>,
end: Arc<Mutex<bool>>,
}
impl Animation {
pub fn new() -> Self {
Self {
canvas: Arc::new(Mutex::new(Canvas::new())),
objs: Arc::new(Mutex::new(Vec::new())),
fps: 30,
hide_cursor: true,
size: None,
end: Arc::new(Mutex::new(false)),
}
}
pub fn push<T, F, N>(&mut self, obj: T, f: F, xy: (N, N))
where
T: Paint,
F: FnMut(&mut T) -> bool + Send + 'static,
N: Into<f64> + Copy,
{
let mut objs = self.objs.lock().unwrap();
objs.push(Box::new(UserObj {
obj,
f,
xy: (xy.0.into(), xy.1.into()),
is_end: false,
}));
}
pub fn run(&mut self) {
let duration = Duration::from_secs(1) / self.fps;
let objs = Arc::clone(&self.objs);
let canvas = Arc::clone(&self.canvas);
let mut stdout = std::io::stdout();
term::clear();
if self.hide_cursor {
term::hide_cursor();
}
enable_raw_mode().expect("can't enbale raw mode");
let end = Arc::clone(&self.end);
let mainloop = thread::spawn(move || loop {
let start_time = Instant::now();
{
let mut objs = objs.lock().unwrap();
let mut end = end.lock().unwrap();
if *end {
break;
}
if objs.iter().all(|obj| obj.is_end()) {
*end = true;
break;
}
let mut canvas = canvas.lock().unwrap();
canvas.clear();
queue!(stdout, MoveTo(0, 0)).unwrap();
for obj in &mut *objs {
obj.update(); obj.paint(&mut canvas);
}
canvas.print();
stdout.flush().unwrap();
}
let elapsed = start_time.elapsed();
if elapsed < duration {
thread::sleep(duration - elapsed);
}
});
let objs = Arc::clone(&self.objs);
let end = Arc::clone(&self.end);
let _keyloop = thread::spawn(move || loop {
if *end.lock().unwrap() {
break;
}
if crossterm::event::poll(Duration::from_millis(300)).unwrap() {
let event = crossterm::event::read().expect("can't read key");
let end_fn = || {
let mut objs = objs.lock().unwrap();
let mut end = end.lock().unwrap();
for obj in &mut *objs {
obj.end();
}
*end = true;
};
if let Event::Resize(_, _) = event {
term::clear();
}
if let Event::Key(key) = event {
if key.code == KeyCode::Esc {
end_fn();
break;
}
if key.code == KeyCode::Char('c') && key.modifiers == KeyModifiers::CONTROL {
end_fn();
break;
}
}
}
});
mainloop.join().unwrap();
term::show_cursor();
disable_raw_mode().expect("can't disable raw mode");
}
pub fn set_fps(&mut self, fps: u32) {
self.fps = fps;
}
pub fn hide_cursor(&mut self, hide_cursor: bool) {
self.hide_cursor = hide_cursor;
}
pub fn set_size<T>(&mut self, width: T, height: T)
where
T: Into<f64>,
{
self.canvas.lock().unwrap().set_size(width, height);
}
pub fn set_minx<T>(&mut self, minx: T)
where
T: Into<f64>,
{
self.canvas.lock().unwrap().set_minx(minx);
}
pub fn set_maxy<T>(&mut self, maxy: T)
where
T: Into<f64> + Copy,
{
self.canvas.lock().unwrap().set_maxy(maxy);
}
#[allow(unused)]
fn check_size(&self) {
let size = if let Some(size) = self.size {
size
} else {
return;
};
if !is_raw_mode() {
return;
}
let (rows, cols) = get_terminal_size();
if (rows as i32) < size.0 || (cols as i32) < size.1 {
println!(
"this anime need at least {}x{} terminal size, but only {}x{}",
size.1, size.0, cols, rows
);
}
}
}
struct UserObj<T, F> {
obj: T,
f: F,
xy: (f64, f64),
is_end: bool,
}
trait Update {
fn update(&mut self);
fn is_end(&self) -> bool;
fn end(&mut self);
fn paint(&self, canvas: &mut Canvas);
}
impl<T, F> Update for UserObj<T, F>
where
T: Paint,
F: FnMut(&mut T) -> bool,
{
fn update(&mut self) {
if self.is_end {
return;
}
self.is_end = (self.f)(&mut self.obj);
}
fn is_end(&self) -> bool {
self.is_end
}
fn end(&mut self) {
self.is_end = true;
}
fn paint(&self, canvas: &mut Canvas) {
let (x, y) = self.xy;
canvas.paint(&self.obj, x, y).unwrap();
}
}