Skip to content

Commit d5ed111

Browse files
committed
Reduce redraws and fix kitty previews
1 parent ddc0375 commit d5ed111

4 files changed

Lines changed: 377 additions & 134 deletions

File tree

src/presentation/event_loop.rs

Lines changed: 177 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,33 @@ pub fn run(
6161
}
6262
Event::Resize(cols, rows) => {
6363
renderer.update_size(cols, rows);
64-
overlays::draw(renderer, app_state);
64+
redraw_current_view(
65+
renderer,
66+
app_state,
67+
delete_paths,
68+
find_filter,
69+
ripgrep_filter,
70+
*favorites_active,
71+
favorites_items,
72+
*favorites_selected,
73+
);
6574
}
6675
_ => {}
6776
}
6877
}
6978
handle_async_messages(receiver, app_state, renderer);
70-
app_state
71-
.active_panel_mut()
72-
.clear_notification_if_expired(3000);
79+
if clear_expired_notification(app_state, 3000) {
80+
redraw_current_view(
81+
renderer,
82+
app_state,
83+
delete_paths,
84+
find_filter,
85+
ripgrep_filter,
86+
*favorites_active,
87+
favorites_items,
88+
*favorites_selected,
89+
);
90+
}
7391
}
7492
}
7593

@@ -91,13 +109,24 @@ fn route_input(
91109
favorites_selected: &mut usize,
92110
two_pane: bool,
93111
) {
94-
if handle_help(event, app_state, renderer) {
112+
if let Some(needs_redraw) = handle_help(event, app_state) {
113+
if needs_redraw {
114+
redraw_current_view(
115+
renderer,
116+
app_state,
117+
delete_paths,
118+
find_filter,
119+
ripgrep_filter,
120+
*favorites_active,
121+
favorites_items,
122+
*favorites_selected,
123+
);
124+
}
95125
return;
96126
}
97-
if handle_modal_overlays(
127+
if let Some(needs_redraw) = handle_modal_overlays(
98128
event,
99129
app_state,
100-
renderer,
101130
fs_adapter,
102131
sender,
103132
delete_paths,
@@ -107,17 +136,37 @@ fn route_input(
107136
favorites_items,
108137
favorites_selected,
109138
) {
139+
if needs_redraw {
140+
redraw_current_view(
141+
renderer,
142+
app_state,
143+
delete_paths,
144+
find_filter,
145+
ripgrep_filter,
146+
*favorites_active,
147+
favorites_items,
148+
*favorites_selected,
149+
);
150+
}
110151
return;
111152
}
112153
if two_pane && event.code == KeyCode::Tab {
113154
app_state.switch_pane();
114-
overlays::draw(renderer, app_state);
155+
redraw_current_view(
156+
renderer,
157+
app_state,
158+
delete_paths,
159+
find_filter,
160+
ripgrep_filter,
161+
*favorites_active,
162+
favorites_items,
163+
*favorites_selected,
164+
);
115165
return;
116166
}
117-
if shortcuts::handle(
167+
if let Some(needs_redraw) = shortcuts::handle(
118168
event,
119169
app_state,
120-
renderer,
121170
vscode_adapter,
122171
delete_paths,
123172
find_filter,
@@ -126,41 +175,61 @@ fn route_input(
126175
favorites_items,
127176
favorites_selected,
128177
) {
178+
if needs_redraw {
179+
redraw_current_view(
180+
renderer,
181+
app_state,
182+
delete_paths,
183+
find_filter,
184+
ripgrep_filter,
185+
*favorites_active,
186+
favorites_items,
187+
*favorites_selected,
188+
);
189+
}
129190
return;
130191
}
131-
handle_mode_input(
192+
if handle_mode_input(
132193
event,
133194
app_state,
134195
renderer,
135196
fs_adapter,
136197
open_adapter,
137198
clipboard_adapter,
138199
sender,
139-
);
140-
overlays::draw(renderer, app_state);
200+
) {
201+
redraw_current_view(
202+
renderer,
203+
app_state,
204+
delete_paths,
205+
find_filter,
206+
ripgrep_filter,
207+
*favorites_active,
208+
favorites_items,
209+
*favorites_selected,
210+
);
211+
}
141212
}
142213

143-
fn handle_help(event: KeyEvent, app_state: &mut AppState, renderer: &mut TerminalRenderer) -> bool {
214+
fn handle_help(event: KeyEvent, app_state: &mut AppState) -> Option<bool> {
144215
if app_state.show_help {
145216
if matches!(event.code, KeyCode::F(1) | KeyCode::Esc) {
146217
app_state.show_help = false;
218+
return Some(true);
147219
}
148-
overlays::draw(renderer, app_state);
149-
return true;
220+
return Some(false);
150221
}
151222
if event.code == KeyCode::F(1) {
152223
app_state.show_help = !app_state.show_help;
153-
overlays::draw(renderer, app_state);
154-
return true;
224+
return Some(true);
155225
}
156-
false
226+
None
157227
}
158228

159229
#[allow(clippy::too_many_arguments)]
160230
fn handle_modal_overlays(
161231
event: KeyEvent,
162232
app_state: &mut AppState,
163-
renderer: &mut TerminalRenderer,
164233
fs_adapter: &StdFileSystem,
165234
sender: &Sender<Message>,
166235
delete_paths: &mut Option<Vec<PathBuf>>,
@@ -169,41 +238,43 @@ fn handle_modal_overlays(
169238
favorites_active: &mut bool,
170239
favorites_items: &[String],
171240
favorites_selected: &mut usize,
172-
) -> bool {
241+
) -> Option<bool> {
173242
if *favorites_active {
174-
handle_favorites_input(
243+
return Some(handle_favorites_input(
175244
event,
176245
favorites_active,
177246
favorites_items,
178247
favorites_selected,
179248
fs_adapter,
180249
app_state,
181-
);
182-
overlays::draw_with_favorites(
183-
renderer,
184-
app_state,
185-
*favorites_active,
186-
favorites_items,
187-
*favorites_selected,
188-
);
189-
return true;
250+
));
190251
}
191252
if delete_paths.is_some() {
192-
handle_delete_confirmation(event, delete_paths, fs_adapter, app_state);
193-
overlays::draw_with_delete(renderer, app_state, delete_paths.as_ref());
194-
return true;
253+
return Some(handle_delete_confirmation(
254+
event,
255+
delete_paths,
256+
fs_adapter,
257+
app_state,
258+
));
195259
}
196260
if find_filter.is_some() {
197-
handle_find_input(event, find_filter, sender, fs_adapter, app_state);
198-
overlays::draw_with_find(renderer, app_state, find_filter.as_ref());
199-
return true;
261+
return Some(handle_find_input(
262+
event,
263+
find_filter,
264+
sender,
265+
fs_adapter,
266+
app_state,
267+
));
200268
}
201269
if ripgrep_filter.is_some() {
202-
handle_ripgrep_input(event, ripgrep_filter, fs_adapter, app_state);
203-
overlays::draw_with_ripgrep(renderer, app_state, ripgrep_filter.as_ref());
204-
return true;
270+
return Some(handle_ripgrep_input(
271+
event,
272+
ripgrep_filter,
273+
fs_adapter,
274+
app_state,
275+
));
205276
}
206-
false
277+
None
207278
}
208279

209280
fn handle_mode_input(
@@ -214,7 +285,7 @@ fn handle_mode_input(
214285
open_adapter: &SystemOpenAdapter,
215286
clipboard_adapter: &mut SystemClipboard,
216287
sender: &Sender<Message>,
217-
) {
288+
) -> bool {
218289
let panel = app_state.active_panel_mut();
219290
match &panel.mode {
220291
PanelMode::Normal => handle_normal_mode(
@@ -239,6 +310,37 @@ fn handle_mode_input(
239310
}
240311
}
241312

313+
#[allow(clippy::too_many_arguments)]
314+
fn redraw_current_view(
315+
renderer: &mut TerminalRenderer,
316+
app_state: &AppState,
317+
delete_paths: &Option<Vec<PathBuf>>,
318+
find_filter: &Option<String>,
319+
ripgrep_filter: &Option<String>,
320+
favorites_active: bool,
321+
favorites_items: &[String],
322+
favorites_selected: usize,
323+
) {
324+
if favorites_active {
325+
overlays::draw_with_favorites(renderer, app_state, true, favorites_items, favorites_selected);
326+
} else if delete_paths.is_some() {
327+
overlays::draw_with_delete(renderer, app_state, delete_paths.as_ref());
328+
} else if find_filter.is_some() {
329+
overlays::draw_with_find(renderer, app_state, find_filter.as_ref());
330+
} else if ripgrep_filter.is_some() {
331+
overlays::draw_with_ripgrep(renderer, app_state, ripgrep_filter.as_ref());
332+
} else {
333+
overlays::draw(renderer, app_state);
334+
}
335+
}
336+
337+
fn clear_expired_notification(app_state: &mut AppState, timeout_ms: u64) -> bool {
338+
let panel = app_state.active_panel_mut();
339+
let had_notification = panel.notification.is_some();
340+
panel.clear_notification_if_expired(timeout_ms);
341+
had_notification && panel.notification.is_none()
342+
}
343+
242344
fn handle_async_messages(
243345
receiver: &Receiver<Message>,
244346
app_state: &mut AppState,
@@ -270,3 +372,36 @@ fn handle_async_messages(
270372
}
271373
}
272374
}
375+
376+
#[cfg(test)]
377+
mod tests {
378+
use super::*;
379+
use crossterm::event::KeyEvent;
380+
381+
#[test]
382+
fn help_overlay_ignores_unmapped_keys_without_redraw() {
383+
let mut state = AppState::new(false);
384+
state.show_help = true;
385+
386+
assert_eq!(
387+
handle_help(KeyEvent::from(KeyCode::Char('x')), &mut state),
388+
Some(false)
389+
);
390+
}
391+
392+
#[test]
393+
fn help_toggle_requests_redraw() {
394+
let mut state = AppState::new(false);
395+
396+
assert_eq!(handle_help(KeyEvent::from(KeyCode::F(1)), &mut state), Some(true));
397+
}
398+
399+
#[test]
400+
fn clearing_expired_notification_marks_dirty() {
401+
let mut state = AppState::new(false);
402+
state.active_panel_mut().set_notification("Test".to_string());
403+
std::thread::sleep(std::time::Duration::from_millis(10));
404+
405+
assert!(clear_expired_notification(&mut state, 1));
406+
}
407+
}

0 commit comments

Comments
 (0)