fix: Only re-draw the screen if data/layout has changed

This commit is contained in:
Jack Wills
2025-02-21 16:40:34 +00:00
parent 53625e67cb
commit bfc295c50e
9 changed files with 184 additions and 49 deletions
+43 -20
View File
@@ -23,6 +23,8 @@ use tracing::error;
mod color_match;
mod draw_blocks;
mod gui_state;
mod redraw;
pub use redraw::Redraw;
pub use self::color_match::*;
pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status};
@@ -37,16 +39,19 @@ use crate::{
input_handler::InputMessages,
};
const POLL_RATE: Duration = std::time::Duration::from_millis(100);
const POLL_RATE: Duration = std::time::Duration::from_millis(50);
// could have a render struct, which takes in poll rate, and docker
pub struct Ui {
app_data: Arc<Mutex<AppData>>,
cursor_position: Position,
gui_state: Arc<Mutex<GuiState>>,
input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>,
now: Instant,
redraw: Arc<Redraw>,
terminal: Terminal<CrosstermBackend<Stdout>>,
cursor_position: Position,
}
impl Ui {
@@ -68,6 +73,7 @@ impl Ui {
gui_state: Arc<Mutex<GuiState>>,
input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>,
redraw: Arc<Redraw>,
) {
if let Ok(mut terminal) = Self::setup_terminal() {
let cursor_position = terminal.get_cursor_position().unwrap_or_default();
@@ -78,6 +84,7 @@ impl Ui {
input_tx,
is_running,
now: Instant::now(),
redraw,
terminal,
};
if let Err(e) = ui.draw_ui().await {
@@ -126,18 +133,18 @@ impl Ui {
let mut seconds = 5;
let colors = self.app_data.lock().config.app_colors;
let keymap = self.app_data.lock().config.keymap.clone();
let mut render = true;
let mut redraw = true;
loop {
if self.now.elapsed() >= std::time::Duration::from_secs(1) {
seconds -= 1;
self.now = Instant::now();
render = true;
redraw = true;
if seconds < 1 {
break;
}
}
if render
if redraw
&& self
.terminal
.draw(|f| {
@@ -153,7 +160,7 @@ impl Ui {
{
return Err(AppError::Terminal);
}
render = false;
redraw = false;
std::thread::sleep(POLL_RATE);
}
Ok(())
@@ -178,25 +185,41 @@ impl Ui {
self.gui_state.lock().status_del(Status::Exec);
}
/// Use the previously redrawn time, the current time, the docker_interval, and the redraw struct, to calculate
/// if the screen should be redrawn or not
fn should_redraw(&self, previous: &mut Instant, docker_interval_ms: u128) -> bool {
let result = self.redraw.swap() || previous.elapsed().as_millis() >= docker_interval_ms;
if result {
*previous = std::time::Instant::now();
}
result
}
/// The loop for drawing the main UI to the terminal
async fn gui_loop(&mut self) -> Result<(), AppError> {
let colors = self.app_data.lock().config.app_colors;
let keymap = self.app_data.lock().config.keymap.clone();
while self.is_running.load(Ordering::SeqCst) {
let fd = FrameData::from(&*self);
let exec = fd.status.contains(&Status::Exec);
if exec {
self.exec().await;
}
let docker_interval_ms = u128::from(self.app_data.lock().config.docker_interval_ms);
let mut drawn_at = std::time::Instant::now();
if self
.terminal
.draw(|frame| {
draw_frame(&self.app_data, colors, &keymap, frame, &fd, &self.gui_state);
})
.is_err()
{
return Err(AppError::Terminal);
while self.is_running.load(Ordering::SeqCst) {
if self.should_redraw(&mut drawn_at, docker_interval_ms) {
let fd = FrameData::from(&*self);
let exec = fd.status.contains(&Status::Exec);
if exec {
self.exec().await;
}
if self
.terminal
.draw(|frame| {
draw_frame(&self.app_data, colors, &keymap, frame, &fd, &self.gui_state);
})
.is_err()
{
return Err(AppError::Terminal);
}
}
if crossterm::event::poll(POLL_RATE).unwrap_or(false) {