Skip to content

Commit bfc295c

Browse files
committed
fix: Only re-draw the screen if data/layout has changed
1 parent 53625e6 commit bfc295c

File tree

9 files changed

+183
-48
lines changed

9 files changed

+183
-48
lines changed

src/app_data/mod.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod container_state;
1313
use crate::{
1414
app_error::AppError,
1515
config::Config,
16-
ui::{log_sanitizer, GuiState, Status},
16+
ui::{log_sanitizer, GuiState, Redraw, Status},
1717
ENTRY_POINT,
1818
};
1919
pub use container_state::*;
@@ -122,7 +122,9 @@ pub struct AppData {
122122
error: Option<AppError>,
123123
filter: Filter,
124124
hidden_containers: Vec<ContainerItem>,
125+
redraw: Arc<Redraw>,
125126
sorted_by: Option<(Header, SortedOrder)>,
127+
current_sorted_id: Vec<ContainerId>,
126128
pub config: Config,
127129
}
128130

@@ -134,18 +136,22 @@ pub struct AppData {
134136
pub error: Option<AppError>,
135137
pub filter: Filter,
136138
pub hidden_containers: Vec<ContainerItem>,
139+
pub current_sorted_id: Vec<ContainerId>,
140+
pub redraw: Arc<Redraw>,
137141
pub sorted_by: Option<(Header, SortedOrder)>,
138142
}
139143

140144
impl AppData {
141145
/// Generate a default app_state
142-
pub fn default(config: Config) -> Self {
146+
pub fn new(config: Config, redraw: &Arc<Redraw>) -> Self {
143147
Self {
144148
config,
145149
containers: StatefulList::new(vec![]),
150+
current_sorted_id: vec![],
146151
error: None,
147152
filter: Filter::new(),
148153
hidden_containers: vec![],
154+
redraw: Arc::clone(redraw),
149155
sorted_by: None,
150156
}
151157
}
@@ -186,6 +192,7 @@ impl AppData {
186192
/// sets the state to start if any filtering has occurred
187193
/// Also search in the "hidden" vec for items and insert back into the main containers vec
188194
fn filter_containers(&mut self) {
195+
self.redraw.set_true();
189196
let pre_len = self.get_container_len();
190197

191198
if !self.hidden_containers.is_empty() {
@@ -289,6 +296,7 @@ impl AppData {
289296
/// Remove the sorted header & order, and sort by default - created datetime
290297
pub fn reset_sorted(&mut self) {
291298
self.set_sorted(None);
299+
self.redraw.set_true();
292300
}
293301

294302
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting
@@ -309,10 +317,19 @@ impl AppData {
309317
self.sorted_by
310318
}
311319

320+
/// Get a vec of the containers ID's
321+
fn get_current_ids(&self) -> Vec<ContainerId> {
322+
self.containers
323+
.items
324+
.iter()
325+
.map(|i| i.id.clone())
326+
.collect::<Vec<_>>()
327+
}
312328
/// Sort the containers vec, based on a heading (and if clash, then by name), either ascending or descending,
313329
/// If not sort set, then sort by created time
314330
pub fn sort_containers(&mut self) {
315331
if let Some((head, ord)) = self.sorted_by {
332+
let pre_order = self.get_current_ids();
316333
let sort_closure = |a: &ContainerItem, b: &ContainerItem| -> std::cmp::Ordering {
317334
let item_ord = match ord {
318335
SortedOrder::Asc => (a, b),
@@ -372,13 +389,19 @@ impl AppData {
372389
.then_with(|| item_ord.0.id.cmp(&item_ord.1.id)),
373390
}
374391
};
392+
375393
self.containers.items.sort_by(sort_closure);
376-
} else {
394+
if pre_order != self.get_current_ids() {
395+
self.redraw.set_true();
396+
}
397+
} else if self.current_sorted_id != self.get_current_ids() {
377398
self.containers.items.sort_by(|a, b| {
378399
a.created
379400
.cmp(&b.created)
380401
.then_with(|| a.name.get().cmp(b.name.get()))
381402
});
403+
self.redraw.set_true();
404+
self.current_sorted_id = self.get_current_ids();
382405
}
383406
}
384407

@@ -414,21 +437,25 @@ impl AppData {
414437
/// Select the first container
415438
pub fn containers_start(&mut self) {
416439
self.containers.start();
440+
self.redraw.set_true();
417441
}
418442

419443
/// select the last container
420444
pub fn containers_end(&mut self) {
421445
self.containers.end();
446+
self.redraw.set_true();
422447
}
423448

424449
/// Select the next container
425450
pub fn containers_next(&mut self) {
426451
self.containers.next();
452+
self.redraw.set_true();
427453
}
428454

429455
/// select the previous container
430456
pub fn containers_previous(&mut self) {
431457
self.containers.previous();
458+
self.redraw.set_true();
432459
}
433460

434461
/// Get ListState of containers
@@ -521,6 +548,11 @@ impl AppData {
521548
self.get_selected_container().map(|i| i.id.clone())
522549
}
523550

551+
/// Check if a given ID matches the currently selected container
552+
pub fn is_selected_container(&self, id: &ContainerId) -> bool {
553+
self.get_selected_container().is_some_and(|i| &i.id == id)
554+
}
555+
524556
/// Get the Id and State for the currently selected container - used by the exec check method
525557
pub fn get_selected_container_id_state_name(&self) -> Option<(ContainerId, State, String)> {
526558
self.get_selected_container()
@@ -545,27 +577,31 @@ impl AppData {
545577
pub fn docker_controls_next(&mut self) {
546578
if let Some(i) = self.get_mut_selected_container() {
547579
i.docker_controls.next();
580+
self.redraw.set_true();
548581
}
549582
}
550583

551584
/// Change selected choice of docker commands of selected container
552585
pub fn docker_controls_previous(&mut self) {
553586
if let Some(i) = self.get_mut_selected_container() {
554587
i.docker_controls.previous();
588+
self.redraw.set_true();
555589
}
556590
}
557591

558592
/// Change selected choice of docker commands of selected container
559593
pub fn docker_controls_start(&mut self) {
560594
if let Some(i) = self.get_mut_selected_container() {
561595
i.docker_controls.start();
596+
self.redraw.set_true();
562597
}
563598
}
564599

565600
/// Change selected choice of docker commands of selected container
566601
pub fn docker_controls_end(&mut self) {
567602
if let Some(i) = self.get_mut_selected_container() {
568603
i.docker_controls.end();
604+
self.redraw.set_true();
569605
}
570606
}
571607

@@ -603,27 +639,31 @@ impl AppData {
603639
pub fn log_next(&mut self) {
604640
if let Some(i) = self.get_mut_selected_container() {
605641
i.logs.next();
642+
self.redraw.set_true();
606643
}
607644
}
608645

609646
/// select previous selected log line
610647
pub fn log_previous(&mut self) {
611648
if let Some(i) = self.get_mut_selected_container() {
612649
i.logs.previous();
650+
self.redraw.set_true();
613651
}
614652
}
615653

616654
/// select last selected log line
617655
pub fn log_end(&mut self) {
618656
if let Some(i) = self.get_mut_selected_container() {
619657
i.logs.end();
658+
self.redraw.set_true();
620659
}
621660
}
622661

623662
/// select first selected log line
624663
pub fn log_start(&mut self) {
625664
if let Some(i) = self.get_mut_selected_container() {
626665
i.logs.start();
666+
self.redraw.set_true();
627667
}
628668
}
629669

@@ -664,12 +704,14 @@ impl AppData {
664704
/// Remove single app_state error
665705
pub fn remove_error(&mut self) {
666706
self.error = None;
707+
self.redraw.set_true();
667708
}
668709

669710
/// Insert single app_state error
670711
pub fn set_error(&mut self, error: AppError, gui_state: &Arc<Mutex<GuiState>>, status: Status) {
671712
gui_state.lock().status_push(status);
672713
self.error = Some(error);
714+
self.redraw.set_true();
673715
}
674716

675717
/// Check if the selected container is a dockerised version of oxker
@@ -758,6 +800,9 @@ impl AppData {
758800
container.tx.update(tx);
759801
container.mem_limit.update(mem_limit);
760802
}
803+
if self.is_selected_container(id) {
804+
self.redraw.set_true();
805+
}
761806
self.sort_containers();
762807
}
763808

@@ -793,6 +838,9 @@ impl AppData {
793838
// Check is some, else can cause out of bounds error, if containers get removed before a docker update
794839
if self.containers.items.get(index).is_some() {
795840
self.containers.items.remove(index);
841+
if self.is_selected_container(id) {
842+
self.redraw.set_true();
843+
}
796844
}
797845
}
798846
}
@@ -872,6 +920,7 @@ impl AppData {
872920
}
873921
}
874922
}
923+
// self.redraw.set_true("update_containers");
875924
}
876925
}
877926

@@ -919,6 +968,9 @@ impl AppData {
919968
container.logs.end();
920969
}
921970
}
971+
if self.is_selected_container(id) {
972+
self.redraw.set_true();
973+
}
922974
}
923975
}
924976
}

src/config/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod parse_config_file;
1818
pub struct Config {
1919
pub app_colors: AppColors,
2020
pub color_logs: bool,
21-
pub docker_interval: u32,
21+
pub docker_interval_ms: u32,
2222
pub gui: bool,
2323
pub host: Option<String>,
2424
pub in_container: bool,
@@ -38,7 +38,7 @@ impl From<&Args> for Config {
3838
Self {
3939
app_colors: AppColors::new(),
4040
color_logs: args.color,
41-
docker_interval: args.docker_interval,
41+
docker_interval_ms: args.docker_interval,
4242
gui: !args.gui,
4343
host: args.host.clone(),
4444
in_container: Self::check_if_in_container(),
@@ -60,7 +60,7 @@ impl From<ConfigFile> for Config {
6060
Self {
6161
app_colors: AppColors::from(config_file.colors),
6262
color_logs: config_file.color_logs.unwrap_or(false),
63-
docker_interval: config_file.docker_interval.unwrap_or(1000),
63+
docker_interval_ms: config_file.docker_interval.unwrap_or(1000),
6464
gui: config_file.gui.unwrap_or(true),
6565
host: config_file.host,
6666
in_container: Self::check_if_in_container(),
@@ -129,16 +129,16 @@ impl Config {
129129
/// make sure color_logs and raw_logs can't clash
130130
fn merge_args(mut self, config_from_cli: Self) -> Self {
131131
self.color_logs = config_from_cli.color_logs;
132-
self.docker_interval = config_from_cli.docker_interval;
132+
self.docker_interval_ms = config_from_cli.docker_interval_ms;
133133
self.gui = config_from_cli.gui;
134134
self.raw_logs = config_from_cli.raw_logs;
135135
self.show_self = config_from_cli.show_self;
136136
self.show_std_err = config_from_cli.show_std_err;
137137
self.show_timestamp = config_from_cli.show_timestamp;
138138
self.use_cli = config_from_cli.use_cli;
139139

140-
if config_from_cli.docker_interval < 1000 {
141-
self.docker_interval = 1000;
140+
if config_from_cli.docker_interval_ms < 1000 {
141+
self.docker_interval_ms = 1000;
142142
}
143143

144144
if let Some(host) = config_from_cli.host {

src/docker_data/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ impl DockerData {
400400

401401
/// Send an update message every x ms, where x is the args.docker_interval
402402
fn heartbeat(config: &Config, docker_tx: Sender<DockerMessage>) {
403-
let update_duration = std::time::Duration::from_millis(u64::from(config.docker_interval));
403+
let update_duration =
404+
std::time::Duration::from_millis(u64::from(config.docker_interval_ms));
404405
let mut now = std::time::Instant::now();
405406
tokio::spawn(async move {
406407
loop {

src/input_handler/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ impl InputHandler {
625625
self.gui_state.lock().status_push(Status::Help);
626626
}
627627

628-
self.gui_state.lock().get_intersect_panel(mouse_point);
628+
self.gui_state.lock().check_panel_intersect(mouse_point);
629629
}
630630
_ => (),
631631
}

src/main.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ mod exec;
2323
mod input_handler;
2424
mod ui;
2525

26-
use ui::{GuiState, Status, Ui};
26+
use ui::{GuiState, Redraw, Status, Ui};
2727

2828
use crate::docker_data::DockerMessage;
2929

@@ -98,9 +98,10 @@ fn handler_init(
9898
async fn main() {
9999
setup_tracing();
100100
let config = config::Config::new();
101+
let redraw = Arc::new(Redraw::new());
101102

102-
let app_data = Arc::new(Mutex::new(AppData::default(config.clone())));
103-
let gui_state = Arc::new(Mutex::new(GuiState::default()));
103+
let app_data = Arc::new(Mutex::new(AppData::new(config.clone(), &redraw)));
104+
let gui_state = Arc::new(Mutex::new(GuiState::new(&redraw)));
104105
let is_running = Arc::new(AtomicBool::new(true));
105106
let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32);
106107

@@ -109,7 +110,7 @@ async fn main() {
109110
if config.gui {
110111
let (input_tx, input_rx) = tokio::sync::mpsc::channel(32);
111112
handler_init(&app_data, &docker_tx, &gui_state, input_rx, &is_running);
112-
Ui::start(app_data, gui_state, input_tx, is_running).await;
113+
Ui::start(app_data, gui_state, input_tx, is_running, redraw).await;
113114
} else {
114115
info!("in debug mode\n");
115116
let mut now = std::time::Instant::now();
@@ -120,7 +121,7 @@ async fn main() {
120121
error!("{}", err);
121122
process::exit(1);
122123
}
123-
if let Some(Ok(to_sleep)) = u128::from(config.docker_interval)
124+
if let Some(Ok(to_sleep)) = u128::from(config.docker_interval_ms)
124125
.checked_sub(now.elapsed().as_millis())
125126
.map(u64::try_from)
126127
{
@@ -148,6 +149,8 @@ async fn main() {
148149
#[allow(clippy::unwrap_used)]
149150
mod tests {
150151

152+
use std::sync::Arc;
153+
151154
use bollard::service::{ContainerSummary, Port};
152155

153156
use crate::{
@@ -156,13 +159,14 @@ mod tests {
156159
RunningState, State, StatefulList,
157160
},
158161
config::{AppColors, Config, Keymap},
162+
ui::Redraw,
159163
};
160164

161165
/// Default test config, has timestamps turned off
162166
pub fn gen_config() -> Config {
163167
Config {
164168
color_logs: false,
165-
docker_interval: 1000,
169+
docker_interval_ms: 1000,
166170
gui: true,
167171
host: None,
168172
show_std_err: false,
@@ -200,8 +204,10 @@ mod tests {
200204
AppData {
201205
containers: StatefulList::new(containers.to_vec()),
202206
hidden_containers: vec![],
207+
current_sorted_id: vec![],
203208
error: None,
204209
sorted_by: None,
210+
redraw: Arc::new(Redraw::new()),
205211
filter: Filter::new(),
206212
config: gen_config(),
207213
}

0 commit comments

Comments
 (0)