From a20134bdcda1af1a19fb9f467188f8075f5733e0 Mon Sep 17 00:00:00 2001 From: EdJoPaTo Date: Mon, 6 May 2024 17:20:42 +0200 Subject: [PATCH] feat(example): debounce, top right corner performance info and update on resize --- examples/example.rs | 90 +++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 2ec7343..fbcc3e0 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,6 +1,10 @@ +use std::time::{Duration, Instant}; + use crossterm::event::{Event, KeyCode, MouseEventKind}; use ratatui::backend::{Backend, CrosstermBackend}; +use ratatui::layout::Rect; use ratatui::style::{Color, Modifier, Style}; +use ratatui::text::Span; use ratatui::widgets::{Block, Scrollbar, ScrollbarOrientation}; use ratatui::{Frame, Terminal}; use tui_tree_widget::{Tree, TreeItem, TreeState}; @@ -133,32 +137,72 @@ fn main() -> std::io::Result<()> { } fn run_app(terminal: &mut Terminal, mut app: App) -> std::io::Result<()> { + const DEBOUNCE: Duration = Duration::from_millis(20); // 50 FPS + + let before = Instant::now(); terminal.draw(|frame| app.draw(frame))?; + let mut last_render_took = before.elapsed(); + + let mut debounce: Option = None; + loop { - let update = match crossterm::event::read()? { - Event::Key(key) => match key.code { - KeyCode::Char('q') => return Ok(()), - KeyCode::Char('\n' | ' ') => app.state.toggle_selected(), - KeyCode::Left => app.state.key_left(), - KeyCode::Right => app.state.key_right(), - KeyCode::Down => app.state.key_down(), - KeyCode::Up => app.state.key_up(), - KeyCode::Esc => app.state.select(Vec::new()), - KeyCode::Home => app.state.select_first(), - KeyCode::End => app.state.select_last(), - KeyCode::PageDown => app.state.scroll_down(3), - KeyCode::PageUp => app.state.scroll_up(3), + let timeout = debounce.map_or(DEBOUNCE, |start| DEBOUNCE.saturating_sub(start.elapsed())); + if crossterm::event::poll(timeout)? { + let update = match crossterm::event::read()? { + Event::Key(key) => match key.code { + KeyCode::Char('q') => return Ok(()), + KeyCode::Char('\n' | ' ') => app.state.toggle_selected(), + KeyCode::Left => app.state.key_left(), + KeyCode::Right => app.state.key_right(), + KeyCode::Down => app.state.key_down(), + KeyCode::Up => app.state.key_up(), + KeyCode::Esc => app.state.select(Vec::new()), + KeyCode::Home => app.state.select_first(), + KeyCode::End => app.state.select_last(), + KeyCode::PageDown => app.state.scroll_down(3), + KeyCode::PageUp => app.state.scroll_up(3), + _ => false, + }, + Event::Mouse(mouse) => match mouse.kind { + MouseEventKind::ScrollDown => app.state.scroll_down(1), + MouseEventKind::ScrollUp => app.state.scroll_up(1), + _ => false, + }, + Event::Resize(_, _) => true, _ => false, - }, - Event::Mouse(mouse) => match mouse.kind { - MouseEventKind::ScrollDown => app.state.scroll_down(1), - MouseEventKind::ScrollUp => app.state.scroll_up(1), - _ => false, - }, - _ => false, - }; - if update { - terminal.draw(|frame| app.draw(frame))?; + }; + if update { + debounce.get_or_insert_with(Instant::now); + } + } + if debounce.is_some_and(|debounce| debounce.elapsed() > DEBOUNCE) { + let before = Instant::now(); + terminal.draw(|frame| { + app.draw(frame); + + // Performance info in top right corner + { + let text = format!( + " {} {last_render_took:?} {:.1} FPS", + frame.count(), + 1.0 / last_render_took.as_secs_f64() + ); + #[allow(clippy::cast_possible_truncation)] + let area = Rect { + y: 0, + height: 1, + x: frame.size().width.saturating_sub(text.len() as u16), + width: text.len() as u16, + }; + frame.render_widget( + Span::styled(text, Style::new().fg(Color::Black).bg(Color::Gray)), + area, + ); + } + })?; + last_render_took = before.elapsed(); + + debounce = None; } } }