1use std::io::{self, Write};
4use std::time::Instant;
5
6use crate::console::{ConsoleOptions, DynRenderable, Renderable};
7
8pub struct Live {
10 renderable: Option<DynRenderable>,
11 screen: bool,
12 auto_refresh: bool,
13 refresh_per_second: f64,
14 transient: bool,
15 started: Option<Instant>,
16 previous_line_count: usize,
17}
18
19impl std::fmt::Debug for Live {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 f.debug_struct("Live")
22 .field("screen", &self.screen)
23 .field("started", &self.started)
24 .finish()
25 }
26}
27
28impl Live {
29 pub fn new(renderable: impl Renderable + Send + Sync + 'static) -> Self {
30 Self {
31 renderable: Some(DynRenderable::new(renderable)),
32 screen: false,
33 auto_refresh: true,
34 refresh_per_second: 4.0,
35 transient: false,
36 started: None,
37 previous_line_count: 0,
38 }
39 }
40
41 pub fn screen(mut self) -> Self { self.screen = true; self }
42 pub fn no_auto_refresh(mut self) -> Self { self.auto_refresh = false; self }
43 pub fn refresh_per_second(mut self, rate: f64) -> Self { self.refresh_per_second = rate; self }
44 pub fn transient(mut self) -> Self { self.transient = true; self }
45
46 pub fn start(&mut self) -> io::Result<()> {
47 self.started = Some(Instant::now());
48 if self.screen {
49 write!(io::stdout(), "\x1b[?1049h")?;
50 }
51 write!(io::stdout(), "\x1b[?25l")?;
52 self.refresh()
53 }
54
55 pub fn stop(&mut self) -> io::Result<()> {
56 if self.transient {
57 for _ in 0..self.previous_line_count {
58 write!(io::stdout(), "\x1b[1A\x1b[2K")?;
59 }
60 }
61 if self.screen {
62 write!(io::stdout(), "\x1b[?1049l")?;
63 }
64 write!(io::stdout(), "\x1b[?25h")?;
65 io::stdout().flush()?;
66 self.started = None;
67 Ok(())
68 }
69
70 pub fn update(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> io::Result<()> {
71 self.renderable = Some(DynRenderable::new(renderable));
72 self.refresh()
73 }
74
75 pub fn refresh(&mut self) -> io::Result<()> {
76 if let Some(ref renderable) = self.renderable {
77 let opts = ConsoleOptions::default();
78 let result = renderable.render(&opts);
79
80 if self.previous_line_count > 0 {
81 write!(io::stdout(), "\x1b[{}F", self.previous_line_count)?;
82 }
83
84 let ansi = result.to_ansi();
85 let line_count = ansi.lines().count();
86
87 write!(io::stdout(), "{ansi}")?;
88 if line_count < self.previous_line_count {
89 for _ in line_count..self.previous_line_count {
90 write!(io::stdout(), "\x1b[2K\n")?;
91 }
92 }
93
94 self.previous_line_count = line_count;
95 io::stdout().flush()?;
96 }
97 Ok(())
98 }
99}
100
101impl Drop for Live {
102 fn drop(&mut self) {
103 let _ = self.stop();
104 }
105}