diff --git a/examples/apps/advanced-widget-impl/src/main.rs b/examples/apps/advanced-widget-impl/src/main.rs index 102659eb..5187a145 100644 --- a/examples/apps/advanced-widget-impl/src/main.rs +++ b/examples/apps/advanced-widget-impl/src/main.rs @@ -12,7 +12,7 @@ use std::time::{Duration, Instant}; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Layout, Position, Rect, Size}; use ratatui::style::{Color, Style}; @@ -55,11 +55,8 @@ impl App { if !event::poll(timeout)? { return Ok(()); } - if let Event::Key(key) = event::read()? { - match key.code { - KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true, - _ => {} - } + if event::read()?.is_key_press() { + self.should_quit = true; } Ok(()) } diff --git a/examples/apps/async-github/src/main.rs b/examples/apps/async-github/src/main.rs index c9b3d5e7..f1d7445e 100644 --- a/examples/apps/async-github/src/main.rs +++ b/examples/apps/async-github/src/main.rs @@ -31,7 +31,7 @@ use std::sync::{Arc, RwLock}; use std::time::Duration; use color_eyre::Result; -use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind}; +use crossterm::event::{Event, EventStream, KeyCode}; use octocrab::params::pulls::Sort; use octocrab::params::Direction; use octocrab::Page; @@ -70,14 +70,14 @@ impl App { while !self.should_quit { tokio::select! { - _ = interval.tick() => { terminal.draw(|frame| self.draw(frame))?; }, + _ = interval.tick() => { terminal.draw(|frame| self.render(frame))?; }, Some(Ok(event)) = events.next() => self.handle_event(&event), } } Ok(()) } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]); let [title_area, body_area] = vertical.areas(frame.area()); let title = Line::from("Ratatui async example").centered().bold(); @@ -86,14 +86,12 @@ impl App { } fn handle_event(&mut self, event: &Event) { - if let Event::Key(key) = event { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true, - KeyCode::Char('j') | KeyCode::Down => self.pull_requests.scroll_down(), - KeyCode::Char('k') | KeyCode::Up => self.pull_requests.scroll_up(), - _ => {} - } + if let Some(key) = event.as_key_press_event() { + match key.code { + KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true, + KeyCode::Char('j') | KeyCode::Down => self.pull_requests.scroll_down(), + KeyCode::Char('k') | KeyCode::Up => self.pull_requests.scroll_up(), + _ => {} } } } diff --git a/examples/apps/calendar-explorer/src/main.rs b/examples/apps/calendar-explorer/src/main.rs index c37e171d..6837197f 100644 --- a/examples/apps/calendar-explorer/src/main.rs +++ b/examples/apps/calendar-explorer/src/main.rs @@ -11,7 +11,7 @@ use std::fmt; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Layout, Margin, Rect}; use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::text::{Line, Text}; @@ -34,21 +34,17 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { let mut calendar_style = StyledCalendar::Default; loop { terminal.draw(|frame| render(frame, calendar_style, selected_date))?; - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char('q') => break Ok(()), - KeyCode::Char('s') => calendar_style = calendar_style.next(), - KeyCode::Char('n') | KeyCode::Tab => selected_date = next_month(selected_date), - KeyCode::Char('p') | KeyCode::BackTab => { - selected_date = previous_month(selected_date); - } - KeyCode::Char('h') | KeyCode::Left => selected_date -= 1.days(), - KeyCode::Char('j') | KeyCode::Down => selected_date += 1.weeks(), - KeyCode::Char('k') | KeyCode::Up => selected_date -= 1.weeks(), - KeyCode::Char('l') | KeyCode::Right => selected_date += 1.days(), - _ => {} - } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), + KeyCode::Char('s') => calendar_style = calendar_style.next(), + KeyCode::Char('n') | KeyCode::Tab => selected_date = next_month(selected_date), + KeyCode::Char('p') | KeyCode::BackTab => selected_date = prev_month(selected_date), + KeyCode::Char('h') | KeyCode::Left => selected_date -= 1.days(), + KeyCode::Char('j') | KeyCode::Down => selected_date += 1.weeks(), + KeyCode::Char('k') | KeyCode::Up => selected_date -= 1.weeks(), + KeyCode::Char('l') | KeyCode::Right => selected_date += 1.days(), + _ => {} } } } @@ -65,7 +61,7 @@ fn next_month(date: Date) -> Date { } } -fn previous_month(date: Date) -> Date { +fn prev_month(date: Date) -> Date { if date.month() == Month::January { date.replace_month(Month::December) .unwrap() @@ -76,7 +72,7 @@ fn previous_month(date: Date) -> Date { } } -/// Draw the UI with a calendar. +/// Render the UI with a calendar. fn render(frame: &mut Frame, calendar_style: StyledCalendar, selected_date: Date) { let header = Text::from_iter([ Line::from("Calendar Example".bold()), diff --git a/examples/apps/canvas/src/main.rs b/examples/apps/canvas/src/main.rs index 4a25a6a4..fb4405f7 100644 --- a/examples/apps/canvas/src/main.rs +++ b/examples/apps/canvas/src/main.rs @@ -15,7 +15,7 @@ use std::{ use color_eyre::Result; use crossterm::event::{ - self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind, + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEventKind, }; use crossterm::ExecutableCommand; use itertools::Itertools; @@ -75,43 +75,33 @@ impl App { let tick_rate = Duration::from_millis(16); let mut last_tick = Instant::now(); while !self.exit { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; let timeout = tick_rate.saturating_sub(last_tick.elapsed()); - if event::poll(timeout)? { - match event::read()? { - Event::Key(key) => self.handle_key_press(key), - Event::Mouse(event) => self.handle_mouse_event(event), - _ => (), - } - } - - if last_tick.elapsed() >= tick_rate { + if !event::poll(timeout)? { self.on_tick(); last_tick = Instant::now(); + continue; + } + match event::read()? { + Event::Key(key) => self.handle_key_event(key), + Event::Mouse(event) => self.handle_mouse_event(event), + _ => (), } } Ok(()) } - fn handle_key_press(&mut self, key: event::KeyEvent) { - if key.kind != KeyEventKind::Press { + fn handle_key_event(&mut self, key: KeyEvent) { + if !key.is_press() { return; } match key.code { - KeyCode::Char('q') => self.exit = true, - KeyCode::Down | KeyCode::Char('j') => self.y += 1.0, - KeyCode::Up | KeyCode::Char('k') => self.y -= 1.0, - KeyCode::Right | KeyCode::Char('l') => self.x += 1.0, - KeyCode::Left | KeyCode::Char('h') => self.x -= 1.0, - KeyCode::Enter => { - self.marker = match self.marker { - Marker::Dot => Marker::Braille, - Marker::Braille => Marker::Block, - Marker::Block => Marker::HalfBlock, - Marker::HalfBlock => Marker::Bar, - Marker::Bar => Marker::Dot, - }; - } + KeyCode::Char('q') | KeyCode::Esc => self.exit = true, + KeyCode::Char('j') | KeyCode::Down => self.y += 1.0, + KeyCode::Char('k') | KeyCode::Up => self.y -= 1.0, + KeyCode::Char('l') | KeyCode::Right => self.x += 1.0, + KeyCode::Char('h') | KeyCode::Left => self.x -= 1.0, + KeyCode::Enter => self.cycle_marker(), _ => {} } } @@ -127,6 +117,16 @@ impl App { } } + fn cycle_marker(&mut self) { + self.marker = match self.marker { + Marker::Dot => Marker::Braille, + Marker::Braille => Marker::Block, + Marker::Block => Marker::HalfBlock, + Marker::HalfBlock => Marker::Bar, + Marker::Bar => Marker::Dot, + }; + } + fn on_tick(&mut self) { // bounce the ball by flipping the velocity vector let ball = &self.ball; @@ -145,7 +145,7 @@ impl App { self.ball.y += self.vy; } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let header = Text::from_iter([ "Canvas Example".bold(), " Quit | Change Marker | Move".into(), diff --git a/examples/apps/chart/src/main.rs b/examples/apps/chart/src/main.rs index 3f134efc..e99ce205 100644 --- a/examples/apps/chart/src/main.rs +++ b/examples/apps/chart/src/main.rs @@ -11,7 +11,7 @@ use std::time::{Duration, Instant}; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::symbols::{self, Marker}; @@ -82,19 +82,19 @@ impl App { let tick_rate = Duration::from_millis(250); let mut last_tick = Instant::now(); loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; let timeout = tick_rate.saturating_sub(last_tick.elapsed()); - if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - if key.code == KeyCode::Char('q') { - return Ok(()); - } - } - } - if last_tick.elapsed() >= tick_rate { + if !event::poll(timeout)? { self.on_tick(); last_tick = Instant::now(); + continue; + } + if event::read()? + .as_key_press_event() + .is_some_and(|key| key.code == KeyCode::Char('q')) + { + return Ok(()); } } } @@ -110,7 +110,7 @@ impl App { self.window[1] += 1.0; } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let [top, bottom] = Layout::vertical([Constraint::Fill(1); 2]).areas(frame.area()); let [animated_chart, bar_chart] = Layout::horizontal([Constraint::Fill(1), Constraint::Length(29)]).areas(top); diff --git a/examples/apps/color-explorer/src/main.rs b/examples/apps/color-explorer/src/main.rs index 5d20ed52..7198fbb9 100644 --- a/examples/apps/color-explorer/src/main.rs +++ b/examples/apps/color-explorer/src/main.rs @@ -9,7 +9,7 @@ //! [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event; use itertools::Itertools; use ratatui::layout::{Alignment, Constraint, Layout, Rect}; use ratatui::style::{Color, Style, Stylize}; @@ -27,16 +27,14 @@ fn main() -> Result<()> { fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - return Ok(()); - } + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -fn draw(frame: &mut Frame) { +fn render(frame: &mut Frame) { let layout = Layout::vertical([ Constraint::Length(30), Constraint::Length(17), diff --git a/examples/apps/colors-rgb/src/main.rs b/examples/apps/colors-rgb/src/main.rs index 694e20a5..99d540ce 100644 --- a/examples/apps/colors-rgb/src/main.rs +++ b/examples/apps/colors-rgb/src/main.rs @@ -19,7 +19,7 @@ use std::time::{Duration, Instant}; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event; use palette::convert::FromColorUnclamped; use palette::{Okhsv, Srgb}; use ratatui::buffer::Buffer; @@ -104,19 +104,16 @@ impl App { } /// Handle any events that have occurred since the last time the app was rendered. - /// - /// Currently, this only handles the q key to quit the app. fn handle_events(&mut self) -> Result<()> { // Ensure that the app only blocks for a period that allows the app to render at // approximately 60 FPS (this doesn't account for the time to render the frame, and will // also update the app immediately any time an event occurs) let timeout = Duration::from_secs_f32(1.0 / 60.0); - if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - self.state = AppState::Quit; - } - } + if !event::poll(timeout)? { + return Ok(()); + } + if event::read()?.is_key_press() { + self.state = AppState::Quit; } Ok(()) } diff --git a/examples/apps/constraint-explorer/src/main.rs b/examples/apps/constraint-explorer/src/main.rs index 9cd200d7..95ea4000 100644 --- a/examples/apps/constraint-explorer/src/main.rs +++ b/examples/apps/constraint-explorer/src/main.rs @@ -9,7 +9,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use itertools::Itertools; use ratatui::buffer::Buffer; use ratatui::layout::Constraint::{self, Fill, Length, Max, Min, Percentage, Ratio}; @@ -107,8 +107,8 @@ impl App { } fn handle_events(&mut self) -> Result<()> { - match event::read()? { - Event::Key(key) if key.kind == KeyEventKind::Press => match key.code { + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { KeyCode::Char('q') | KeyCode::Esc => self.exit(), KeyCode::Char('1') => self.swap_constraint(ConstraintName::Min), KeyCode::Char('2') => self.swap_constraint(ConstraintName::Max), @@ -125,8 +125,7 @@ impl App { KeyCode::Char('h') | KeyCode::Left => self.prev_block(), KeyCode::Char('l') | KeyCode::Right => self.next_block(), _ => {} - }, - _ => {} + } } Ok(()) } diff --git a/examples/apps/constraints/src/main.rs b/examples/apps/constraints/src/main.rs index df1bad0b..0893e1c1 100644 --- a/examples/apps/constraints/src/main.rs +++ b/examples/apps/constraints/src/main.rs @@ -6,7 +6,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::buffer::Buffer; use ratatui::layout::Constraint::{self, Fill, Length, Max, Min, Percentage, Ratio}; use ratatui::layout::{Layout, Rect}; @@ -90,10 +90,7 @@ impl App { } fn handle_events(&mut self) -> Result<()> { - if let Event::Key(key) = event::read()? { - if key.kind != KeyEventKind::Press { - return Ok(()); - } + if let Some(key) = event::read()?.as_key_press_event() { match key.code { KeyCode::Char('q') | KeyCode::Esc => self.quit(), KeyCode::Char('l') | KeyCode::Right => self.next(), diff --git a/examples/apps/custom-widget/src/main.rs b/examples/apps/custom-widget/src/main.rs index 7f90e84f..20a947ca 100644 --- a/examples/apps/custom-widget/src/main.rs +++ b/examples/apps/custom-widget/src/main.rs @@ -10,8 +10,8 @@ use std::{io::stdout, ops::ControlFlow, time::Duration}; use color_eyre::Result; use crossterm::event::{ - self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, MouseButton, MouseEvent, - MouseEventKind, + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseButton, + MouseEvent, MouseEventKind, }; use crossterm::execute; use ratatui::buffer::Buffer; @@ -147,15 +147,12 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { let mut selected_button: usize = 0; let mut button_states = [State::Selected, State::Normal, State::Normal]; loop { - terminal.draw(|frame| draw(frame, button_states))?; + terminal.draw(|frame| render(frame, button_states))?; if !event::poll(Duration::from_millis(100))? { continue; } match event::read()? { Event::Key(key) => { - if key.kind != event::KeyEventKind::Press { - continue; - } if handle_key_event(key, &mut button_states, &mut selected_button).is_break() { break; } @@ -169,7 +166,7 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { Ok(()) } -fn draw(frame: &mut Frame, states: [State; 3]) { +fn render(frame: &mut Frame, states: [State; 3]) { let vertical = Layout::vertical([ Constraint::Length(1), Constraint::Max(3), @@ -201,10 +198,13 @@ fn render_buttons(frame: &mut Frame<'_>, area: Rect, states: [State; 3]) { } fn handle_key_event( - key: event::KeyEvent, + key: KeyEvent, button_states: &mut [State; 3], selected_button: &mut usize, ) -> ControlFlow<()> { + if !key.is_press() { + return ControlFlow::Continue(()); + } match key.code { KeyCode::Char('q') => return ControlFlow::Break(()), KeyCode::Left | KeyCode::Char('h') => { diff --git a/examples/apps/demo/src/crossterm.rs b/examples/apps/demo/src/crossterm.rs index aeaa10dd..2bd77879 100644 --- a/examples/apps/demo/src/crossterm.rs +++ b/examples/apps/demo/src/crossterm.rs @@ -2,9 +2,7 @@ use std::error::Error; use std::io; use std::time::{Duration, Instant}; -use crossterm::event::{ - self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, -}; +use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, KeyCode}; use crossterm::execute; use crossterm::terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, @@ -56,23 +54,20 @@ where terminal.draw(|frame| ui::draw(frame, &mut app))?; let timeout = tick_rate.saturating_sub(last_tick.elapsed()); - if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Left | KeyCode::Char('h') => app.on_left(), - KeyCode::Up | KeyCode::Char('k') => app.on_up(), - KeyCode::Right | KeyCode::Char('l') => app.on_right(), - KeyCode::Down | KeyCode::Char('j') => app.on_down(), - KeyCode::Char(c) => app.on_key(c), - _ => {} - } - } - } - } - if last_tick.elapsed() >= tick_rate { + if !event::poll(timeout)? { app.on_tick(); last_tick = Instant::now(); + continue; + } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char('h') | KeyCode::Left => app.on_left(), + KeyCode::Char('j') | KeyCode::Down => app.on_down(), + KeyCode::Char('k') | KeyCode::Up => app.on_up(), + KeyCode::Char('l') | KeyCode::Right => app.on_right(), + KeyCode::Char(c) => app.on_key(c), + _ => {} + } } if app.should_quit { return Ok(()); diff --git a/examples/apps/demo2/src/app.rs b/examples/apps/demo2/src/app.rs index 0ce17ef0..9840369e 100644 --- a/examples/apps/demo2/src/app.rs +++ b/examples/apps/demo2/src/app.rs @@ -2,8 +2,7 @@ use std::time::Duration; use color_eyre::eyre::Context; use color_eyre::Result; -use crossterm::event; -use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use itertools::Itertools; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Layout, Rect}; @@ -50,7 +49,7 @@ impl App { pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { while self.is_running() { terminal - .draw(|frame| self.draw(frame)) + .draw(|frame| self.render(frame)) .wrap_err("terminal.draw")?; self.handle_events()?; } @@ -61,8 +60,8 @@ impl App { self.mode != Mode::Quit } - /// Draw a single frame of the app. - fn draw(&self, frame: &mut Frame) { + /// Render a single frame of the app. + fn render(&self, frame: &mut Frame) { frame.render_widget(self, frame.area()); if self.mode == Mode::Destroy { destroy::destroy(frame); @@ -78,25 +77,20 @@ impl App { if !event::poll(timeout)? { return Ok(()); } - match event::read()? { - Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key), - _ => {} + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit, + KeyCode::Char('h') | KeyCode::Left => self.prev_tab(), + KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => self.next_tab(), + KeyCode::Char('k') | KeyCode::Up => self.prev(), + KeyCode::Char('j') | KeyCode::Down => self.next(), + KeyCode::Char('d') | KeyCode::Delete => self.destroy(), + _ => {} + }; } Ok(()) } - fn handle_key_press(&mut self, key: KeyEvent) { - match key.code { - KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit, - KeyCode::Char('h') | KeyCode::Left => self.prev_tab(), - KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => self.next_tab(), - KeyCode::Char('k') | KeyCode::Up => self.prev(), - KeyCode::Char('j') | KeyCode::Down => self.next(), - KeyCode::Char('d') | KeyCode::Delete => self.destroy(), - _ => {} - }; - } - fn prev(&mut self) { match self.tab { Tab::About => self.about_tab.prev_row(), diff --git a/examples/apps/flex/src/main.rs b/examples/apps/flex/src/main.rs index a95b3b5c..8e6e61d4 100644 --- a/examples/apps/flex/src/main.rs +++ b/examples/apps/flex/src/main.rs @@ -11,7 +11,7 @@ use std::num::NonZeroUsize; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::buffer::Buffer; use ratatui::layout::Constraint::{self, Fill, Length, Max, Min, Percentage, Ratio}; use ratatui::layout::{Alignment, Flex, Layout, Rect}; @@ -168,8 +168,8 @@ impl App { } fn handle_events(&mut self) -> Result<()> { - match event::read()? { - Event::Key(key) if key.kind == KeyEventKind::Press => match key.code { + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { KeyCode::Char('q') | KeyCode::Esc => self.quit(), KeyCode::Char('l') | KeyCode::Right => self.next(), KeyCode::Char('h') | KeyCode::Left => self.previous(), @@ -180,8 +180,7 @@ impl App { KeyCode::Char('+') => self.increment_spacing(), KeyCode::Char('-') => self.decrement_spacing(), _ => (), - }, - _ => {} + } } Ok(()) } diff --git a/examples/apps/gauge/src/main.rs b/examples/apps/gauge/src/main.rs index 23403fc4..9d3d4407 100644 --- a/examples/apps/gauge/src/main.rs +++ b/examples/apps/gauge/src/main.rs @@ -8,7 +8,7 @@ use std::time::Duration; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::buffer::Buffer; use ratatui::layout::{Alignment, Constraint, Layout, Rect}; use ratatui::style::palette::tailwind; @@ -79,15 +79,14 @@ impl App { fn handle_events(&mut self) -> Result<()> { let timeout = Duration::from_secs_f32(1.0 / 20.0); - if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char(' ') | KeyCode::Enter => self.start(), - KeyCode::Char('q') | KeyCode::Esc => self.quit(), - _ => {} - } - } + if !event::poll(timeout)? { + return Ok(()); + } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char(' ') | KeyCode::Enter => self.start(), + KeyCode::Char('q') | KeyCode::Esc => self.quit(), + _ => {} } } Ok(()) diff --git a/examples/apps/hello-world/src/main.rs b/examples/apps/hello-world/src/main.rs index 52105060..e75dd40e 100644 --- a/examples/apps/hello-world/src/main.rs +++ b/examples/apps/hello-world/src/main.rs @@ -9,7 +9,7 @@ use std::time::Duration; use color_eyre::eyre::Context; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::widgets::Paragraph; use ratatui::{DefaultTerminal, Frame}; @@ -33,7 +33,7 @@ fn main() -> Result<()> { /// on events, or you could have a single application state and update it based on events. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; + terminal.draw(render)?; if should_quit()? { break; } @@ -43,7 +43,7 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { /// Render the application. This is where you would draw the application UI. This example draws a /// greeting. -fn draw(frame: &mut Frame) { +fn render(frame: &mut Frame) { let greeting = Paragraph::new("Hello World! (press 'q' to quit)"); frame.render_widget(greeting, frame.area()); } @@ -55,9 +55,11 @@ fn draw(frame: &mut Frame) { /// updating the application state, without blocking the event loop for too long. fn should_quit() -> Result { if event::poll(Duration::from_millis(250)).context("event poll failed")? { - if let Event::Key(key) = event::read().context("event read failed")? { - return Ok(KeyCode::Char('q') == key.code); - } + let q_pressed = event::read() + .context("event read failed")? + .as_key_press_event() + .is_some_and(|key| key.code == KeyCode::Char('q')); + return Ok(q_pressed); } Ok(false) } diff --git a/examples/apps/hyperlink/src/main.rs b/examples/apps/hyperlink/src/main.rs index ecfba93f..2f43f972 100644 --- a/examples/apps/hyperlink/src/main.rs +++ b/examples/apps/hyperlink/src/main.rs @@ -7,7 +7,7 @@ /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest /// [OSC 8]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use itertools::Itertools; use ratatui::buffer::Buffer; use ratatui::layout::Rect; @@ -38,10 +38,11 @@ impl App { fn run(self, mut terminal: DefaultTerminal) -> Result<()> { loop { terminal.draw(|frame| frame.render_widget(&self.hyperlink, frame.area()))?; - if let Event::Key(key) = event::read()? { - if matches!(key.code, KeyCode::Char('q') | KeyCode::Esc) { - break; - } + if event::read()? + .as_key_press_event() + .is_some_and(|key| matches!(key.code, KeyCode::Char('q') | KeyCode::Esc)) + { + break; } } Ok(()) diff --git a/examples/apps/inline/src/main.rs b/examples/apps/inline/src/main.rs index d5d309f2..dc2a29fe 100644 --- a/examples/apps/inline/src/main.rs +++ b/examples/apps/inline/src/main.rs @@ -170,7 +170,7 @@ where let mut redraw = true; loop { if redraw { - terminal.draw(|frame| draw(frame, &downloads))?; + terminal.draw(|frame| render(frame, &downloads))?; } redraw = true; @@ -222,7 +222,7 @@ where Ok(()) } -fn draw(frame: &mut Frame, downloads: &Downloads) { +fn render(frame: &mut Frame, downloads: &Downloads) { let area = frame.area(); let block = Block::new().title(Line::from("Progress").centered()); diff --git a/examples/apps/input-form/src/main.rs b/examples/apps/input-form/src/main.rs index b84036dd..515ea786 100644 --- a/examples/apps/input-form/src/main.rs +++ b/examples/apps/input-form/src/main.rs @@ -15,7 +15,7 @@ //! [`tui-textarea`]: https://crates.io/crates/tui-textarea use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; +use crossterm::event::{self, KeyCode, KeyEvent}; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Layout, Offset, Rect}; use ratatui::style::Stylize; @@ -71,13 +71,12 @@ impl App { } fn handle_events(&mut self) -> Result<()> { - match event::read()? { - Event::Key(event) if event.kind == KeyEventKind::Press => match event.code { + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { KeyCode::Esc => self.state = AppState::Cancelled, KeyCode::Enter => self.state = AppState::Submitted, - _ => self.form.on_key_press(event), - }, - _ => {} + _ => self.form.on_key_press(key), + } } Ok(()) } diff --git a/examples/apps/minimal/src/main.rs b/examples/apps/minimal/src/main.rs index 8cb1dba3..2f307986 100644 --- a/examples/apps/minimal/src/main.rs +++ b/examples/apps/minimal/src/main.rs @@ -11,22 +11,18 @@ /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest /// [examples]: https://github.com/ratatui/ratatui/blob/main/examples /// [hello-world]: https://github.com/ratatui/ratatui/blob/main/examples/apps/hello-world -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::text::Text; -use ratatui::Frame; fn main() { let mut terminal = ratatui::init(); loop { - terminal.draw(draw).expect("failed to draw frame"); - if matches!(event::read().expect("failed to read event"), Event::Key(_)) { + terminal + .draw(|frame| frame.render_widget(Text::raw("Hello World!"), frame.area())) + .expect("failed to draw frame"); + if event::read().expect("failed to read event").is_key_press() { break; } } ratatui::restore(); } - -fn draw(frame: &mut Frame) { - let text = Text::raw("Hello World!"); - frame.render_widget(text, frame.area()); -} diff --git a/examples/apps/modifiers/src/main.rs b/examples/apps/modifiers/src/main.rs index d6d24cee..a4bf7021 100644 --- a/examples/apps/modifiers/src/main.rs +++ b/examples/apps/modifiers/src/main.rs @@ -10,7 +10,7 @@ /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use std::{error::Error, iter::once, result}; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event; use itertools::Itertools; use ratatui::layout::{Constraint, Layout}; use ratatui::style::{Color, Modifier, Style, Stylize}; @@ -30,16 +30,14 @@ fn main() -> Result<()> { fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - return Ok(()); - } + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -fn draw(frame: &mut Frame) { +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]); let [text_area, main_area] = vertical.areas(frame.area()); frame.render_widget( diff --git a/examples/apps/mouse-drawing/src/main.rs b/examples/apps/mouse-drawing/src/main.rs index 18dfbff4..65d99728 100644 --- a/examples/apps/mouse-drawing/src/main.rs +++ b/examples/apps/mouse-drawing/src/main.rs @@ -60,8 +60,11 @@ impl MouseDrawingApp { } /// Quit the app if the user presses 'q' or 'Esc' - fn on_key_event(&mut self, event: KeyEvent) { - match event.code { + fn on_key_event(&mut self, key: KeyEvent) { + if !key.is_press() { + return; + } + match key.code { KeyCode::Char(' ') => { self.current_color = Color::Rgb(rand::random(), rand::random(), rand::random()); } diff --git a/examples/apps/panic/src/main.rs b/examples/apps/panic/src/main.rs index 573595b0..a80a755a 100644 --- a/examples/apps/panic/src/main.rs +++ b/examples/apps/panic/src/main.rs @@ -30,7 +30,7 @@ /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest /// [Color Eyre recipe]: https://ratatui.rs/recipes/apps/color-eyre use color_eyre::{eyre::bail, Result}; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::text::Line; use ratatui::widgets::{Block, Paragraph}; use ratatui::{DefaultTerminal, Frame}; @@ -53,9 +53,9 @@ impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; - if let Event::Key(key) = event::read()? { + if let Some(key) = event::read()?.as_key_press_event() { match key.code { KeyCode::Char('p') => panic!("intentional demo panic"), KeyCode::Char('e') => bail!("intentional demo error"), @@ -70,7 +70,7 @@ impl App { } } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let text = vec![ if self.hook_enabled { Line::from("HOOK IS CURRENTLY **ENABLED**") diff --git a/examples/apps/popup/src/main.rs b/examples/apps/popup/src/main.rs index 1e2a486b..244df311 100644 --- a/examples/apps/popup/src/main.rs +++ b/examples/apps/popup/src/main.rs @@ -8,7 +8,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Flex, Layout, Rect}; use ratatui::style::Stylize; use ratatui::widgets::{Block, Clear, Paragraph, Wrap}; @@ -30,21 +30,19 @@ struct App { impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char('q') => return Ok(()), - KeyCode::Char('p') => self.show_popup = !self.show_popup, - _ => {} - } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char('q') => return Ok(()), + KeyCode::Char('p') => self.show_popup = !self.show_popup, + _ => {} } } } } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let area = frame.area(); let vertical = Layout::vertical([Constraint::Percentage(20), Constraint::Percentage(80)]); diff --git a/examples/apps/scrollbar/src/main.rs b/examples/apps/scrollbar/src/main.rs index 1cd2dea4..8afd64c6 100644 --- a/examples/apps/scrollbar/src/main.rs +++ b/examples/apps/scrollbar/src/main.rs @@ -11,7 +11,7 @@ use std::time::{Duration, Instant}; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Alignment, Constraint, Layout, Margin}; use ratatui::style::{Color, Style, Stylize}; use ratatui::symbols::scrollbar; @@ -40,47 +40,52 @@ impl App { let tick_rate = Duration::from_millis(250); let mut last_tick = Instant::now(); loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; let timeout = tick_rate.saturating_sub(last_tick.elapsed()); - if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - match key.code { - KeyCode::Char('q') => return Ok(()), - KeyCode::Char('j') | KeyCode::Down => { - self.vertical_scroll = self.vertical_scroll.saturating_add(1); - self.vertical_scroll_state = - self.vertical_scroll_state.position(self.vertical_scroll); - } - KeyCode::Char('k') | KeyCode::Up => { - self.vertical_scroll = self.vertical_scroll.saturating_sub(1); - self.vertical_scroll_state = - self.vertical_scroll_state.position(self.vertical_scroll); - } - KeyCode::Char('h') | KeyCode::Left => { - self.horizontal_scroll = self.horizontal_scroll.saturating_sub(1); - self.horizontal_scroll_state = self - .horizontal_scroll_state - .position(self.horizontal_scroll); - } - KeyCode::Char('l') | KeyCode::Right => { - self.horizontal_scroll = self.horizontal_scroll.saturating_add(1); - self.horizontal_scroll_state = self - .horizontal_scroll_state - .position(self.horizontal_scroll); - } - _ => {} - } - } - } - if last_tick.elapsed() >= tick_rate { + if !event::poll(timeout)? { last_tick = Instant::now(); + continue; + } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char('q') => return Ok(()), + KeyCode::Char('j') | KeyCode::Down => self.scroll_down(), + KeyCode::Char('k') | KeyCode::Up => self.scroll_up(), + KeyCode::Char('h') | KeyCode::Left => self.scroll_left(), + KeyCode::Char('l') | KeyCode::Right => self.scroll_right(), + _ => {} + } } } } + fn scroll_down(&mut self) { + self.vertical_scroll = self.vertical_scroll.saturating_add(1); + self.vertical_scroll_state = self.vertical_scroll_state.position(self.vertical_scroll); + } + + fn scroll_up(&mut self) { + self.vertical_scroll = self.vertical_scroll.saturating_sub(1); + self.vertical_scroll_state = self.vertical_scroll_state.position(self.vertical_scroll); + } + + fn scroll_left(&mut self) { + self.horizontal_scroll = self.horizontal_scroll.saturating_sub(1); + self.horizontal_scroll_state = self + .horizontal_scroll_state + .position(self.horizontal_scroll); + } + + fn scroll_right(&mut self) { + self.horizontal_scroll = self.horizontal_scroll.saturating_add(1); + self.horizontal_scroll_state = self + .horizontal_scroll_state + .position(self.horizontal_scroll); + } + #[expect(clippy::too_many_lines, clippy::cast_possible_truncation)] - fn draw(&mut self, frame: &mut Frame) { + fn render(&mut self, frame: &mut Frame) { let area = frame.area(); // Words made "loooong" to demonstrate line breaking. diff --git a/examples/apps/table/src/main.rs b/examples/apps/table/src/main.rs index 3ad149f6..40b1ab75 100644 --- a/examples/apps/table/src/main.rs +++ b/examples/apps/table/src/main.rs @@ -6,7 +6,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers}; +use crossterm::event::{self, KeyCode, KeyModifiers}; use itertools::Itertools; use ratatui::layout::{Constraint, Layout, Margin, Rect}; use ratatui::style::{self, Color, Modifier, Style, Stylize}; @@ -173,29 +173,27 @@ impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - let shift_pressed = key.modifiers.contains(KeyModifiers::SHIFT); - match key.code { - KeyCode::Char('q') | KeyCode::Esc => return Ok(()), - KeyCode::Char('j') | KeyCode::Down => self.next_row(), - KeyCode::Char('k') | KeyCode::Up => self.previous_row(), - KeyCode::Char('l') | KeyCode::Right if shift_pressed => self.next_color(), - KeyCode::Char('h') | KeyCode::Left if shift_pressed => { - self.previous_color(); - } - KeyCode::Char('l') | KeyCode::Right => self.next_column(), - KeyCode::Char('h') | KeyCode::Left => self.previous_column(), - _ => {} + if let Some(key) = event::read()?.as_key_press_event() { + let shift_pressed = key.modifiers.contains(KeyModifiers::SHIFT); + match key.code { + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), + KeyCode::Char('j') | KeyCode::Down => self.next_row(), + KeyCode::Char('k') | KeyCode::Up => self.previous_row(), + KeyCode::Char('l') | KeyCode::Right if shift_pressed => self.next_color(), + KeyCode::Char('h') | KeyCode::Left if shift_pressed => { + self.previous_color(); } + KeyCode::Char('l') | KeyCode::Right => self.next_column(), + KeyCode::Char('h') | KeyCode::Left => self.previous_column(), + _ => {} } } } } - fn draw(&mut self, frame: &mut Frame) { + fn render(&mut self, frame: &mut Frame) { let vertical = &Layout::vertical([Constraint::Min(5), Constraint::Length(4)]); let rects = vertical.split(frame.area()); diff --git a/examples/apps/todo-list/src/main.rs b/examples/apps/todo-list/src/main.rs index dfc87218..acf83067 100644 --- a/examples/apps/todo-list/src/main.rs +++ b/examples/apps/todo-list/src/main.rs @@ -6,7 +6,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; +use crossterm::event::{self, KeyCode, KeyEvent}; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::palette::tailwind::{BLUE, GREEN, SLATE}; @@ -103,7 +103,7 @@ impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { while !self.should_exit { terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?; - if let Event::Key(key) = event::read()? { + if let Some(key) = event::read()?.as_key_press_event() { self.handle_key(key); } } @@ -111,9 +111,6 @@ impl App { } fn handle_key(&mut self, key: KeyEvent) { - if key.kind != KeyEventKind::Press { - return; - } match key.code { KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true, KeyCode::Char('h') | KeyCode::Left => self.select_none(), diff --git a/examples/apps/tracing/src/main.rs b/examples/apps/tracing/src/main.rs index eb9727c8..fb6fb746 100644 --- a/examples/apps/tracing/src/main.rs +++ b/examples/apps/tracing/src/main.rs @@ -40,7 +40,7 @@ fn main() -> Result<()> { let mut events = vec![]; // a buffer to store the recent events to display in the UI while !should_exit(&events) { handle_events(&mut events)?; - terminal.draw(|frame| draw(frame, &events))?; + terminal.draw(|frame| render(frame, &events))?; } ratatui::restore(); @@ -69,7 +69,7 @@ fn handle_events(events: &mut Vec) -> Result<()> { } #[instrument(skip_all)] -fn draw(frame: &mut Frame, events: &[Event]) { +fn render(frame: &mut Frame, events: &[Event]) { // To view this event, run the example with `RUST_LOG=tracing=debug cargo run --example tracing` trace!(frame_count = frame.count(), event_count = events.len()); let events = events.iter().map(|e| format!("{e:?}")).collect::>(); diff --git a/examples/apps/user-input/src/main.rs b/examples/apps/user-input/src/main.rs index 841833c4..4b6a925d 100644 --- a/examples/apps/user-input/src/main.rs +++ b/examples/apps/user-input/src/main.rs @@ -21,7 +21,7 @@ /// /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode, KeyEventKind}; use ratatui::layout::{Constraint, Layout, Position}; use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::text::{Line, Span, Text}; @@ -129,9 +129,9 @@ impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; - if let Event::Key(key) = event::read()? { + if let Some(key) = event::read()?.as_key_press_event() { match self.input_mode { InputMode::Normal => match key.code { KeyCode::Char('e') => { @@ -157,7 +157,7 @@ impl App { } } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let vertical = Layout::vertical([ Constraint::Length(1), Constraint::Length(3), diff --git a/examples/apps/weather/src/main.rs b/examples/apps/weather/src/main.rs index aacdd2a7..c174dbbf 100644 --- a/examples/apps/weather/src/main.rs +++ b/examples/apps/weather/src/main.rs @@ -9,7 +9,7 @@ //! [`BarChart`]: https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use rand::{rng, Rng}; use ratatui::layout::{Constraint, Layout}; use ratatui::style::{Color, Style, Stylize}; @@ -42,22 +42,23 @@ impl App { fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { while !self.should_exit { - terminal.draw(|frame| self.draw(frame))?; + terminal.draw(|frame| self.render(frame))?; self.handle_events()?; } Ok(()) } fn handle_events(&mut self) -> Result<()> { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { - self.should_exit = true; - } + if event::read()? + .as_key_press_event() + .is_some_and(|key| key.code == KeyCode::Char('q')) + { + self.should_exit = true; } Ok(()) } - fn draw(&self, frame: &mut Frame) { + fn render(&self, frame: &mut Frame) { let [title, main] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]) .spacing(1) .areas(frame.area()); diff --git a/examples/apps/widget-ref-container/src/main.rs b/examples/apps/widget-ref-container/src/main.rs index a969c1a0..a2603d56 100644 --- a/examples/apps/widget-ref-container/src/main.rs +++ b/examples/apps/widget-ref-container/src/main.rs @@ -30,8 +30,8 @@ fn main() -> Result<()> { fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { terminal.draw(render)?; - if matches!(event::read()?, event::Event::Key(_)) { - break Ok(()); + if event::read()?.is_key_press() { + return Ok(()); } } } diff --git a/ratatui-widgets/examples/barchart-grouped.rs b/ratatui-widgets/examples/barchart-grouped.rs index 613b618f..6ace2e94 100644 --- a/ratatui-widgets/examples/barchart-grouped.rs +++ b/ratatui-widgets/examples/barchart-grouped.rs @@ -17,7 +17,7 @@ use core::iter::zip; use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::style::{Color, Stylize}; use ratatui::text::{Line, Span}; @@ -35,15 +35,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with a barchart on the left and right side. -fn draw(frame: &mut Frame) { +/// Render the UI with a barchart on the left and right side. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Fill(1); 2]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/barchart.rs b/ratatui-widgets/examples/barchart.rs index cd580567..61415fe7 100644 --- a/ratatui-widgets/examples/barchart.rs +++ b/ratatui-widgets/examples/barchart.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::Stylize; use ratatui::text::{Line, Span}; @@ -33,15 +33,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with a title and two barcharts. -fn draw(frame: &mut Frame) { +/// Render the UI with a title and two barcharts. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Length(28), Constraint::Fill(1)]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/block.rs b/ratatui-widgets/examples/block.rs index 71d5aaf6..a9444dda 100644 --- a/ratatui-widgets/examples/block.rs +++ b/ratatui-widgets/examples/block.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::text::{Line, Span}; @@ -33,15 +33,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with various blocks. -fn draw(frame: &mut Frame) { +/// Render the UI with various blocks. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Percentage(33); 3]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/calendar.rs b/ratatui-widgets/examples/calendar.rs index 03810b00..3a285688 100644 --- a/ratatui-widgets/examples/calendar.rs +++ b/ratatui-widgets/examples/calendar.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -35,15 +35,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with 2 monthly calendars side by side. -fn draw(frame: &mut Frame) { +/// Render the UI with 2 monthly calendars side by side. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Percentage(50); 2]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/canvas.rs b/ratatui-widgets/examples/canvas.rs index b67631ab..d4b94a26 100644 --- a/ratatui-widgets/examples/canvas.rs +++ b/ratatui-widgets/examples/canvas.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Stylize}; use ratatui::symbols::Marker; @@ -35,15 +35,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with a canvas widget. -fn draw(frame: &mut Frame) { +/// Render the UI with a canvas widget. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Percentage(100)]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/chart.rs b/ratatui-widgets/examples/chart.rs index b33dc0a1..ceef4b8d 100644 --- a/ratatui-widgets/examples/chart.rs +++ b/ratatui-widgets/examples/chart.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Stylize}; use ratatui::symbols::Marker; @@ -34,15 +34,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with a chart. -fn draw(frame: &mut Frame) { +/// Render the UI with a chart. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/gauge.rs b/ratatui-widgets/examples/gauge.rs index 8d480a5b..17897960 100644 --- a/ratatui-widgets/examples/gauge.rs +++ b/ratatui-widgets/examples/gauge.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Modifier, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -33,15 +33,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with various progress bars. -fn draw(frame: &mut Frame) { +/// Render the UI with various progress bars. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([ Constraint::Length(1), Constraint::Max(2), diff --git a/ratatui-widgets/examples/line_gauge.rs b/ratatui-widgets/examples/line_gauge.rs index b92deb3b..bf02ee58 100644 --- a/ratatui-widgets/examples/line_gauge.rs +++ b/ratatui-widgets/examples/line_gauge.rs @@ -17,7 +17,7 @@ use core::time::Duration; use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use crossterm::event::{self, KeyCode}; use ratatui::buffer::Buffer; use ratatui::layout::Constraint::{Length, Min}; use ratatui::layout::{Layout, Rect}; @@ -71,27 +71,26 @@ impl App { fn handle_events(&mut self) -> Result<()> { let timeout = Duration::from_secs_f32(1.0 / 20.0); if event::poll(timeout)? { - if let Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char(' ') => { - // toggle start / stop - if self.state == AppState::Stop { - self.state = AppState::Start; - } else { - self.state = AppState::Stop; - } - } - KeyCode::Char('r') => self.reset(), - KeyCode::Char('q') => self.state = AppState::Quit, - _ => {} - } + if let Some(key) = event::read()?.as_key_press_event() { + match key.code { + KeyCode::Char(' ') => self.toggle_start(), + KeyCode::Char('r') => self.reset(), + KeyCode::Char('q') => self.state = AppState::Quit, + _ => {} } } } Ok(()) } + fn toggle_start(&mut self) { + self.state = if self.state == AppState::Start { + AppState::Stop + } else { + AppState::Start + }; + } + const fn reset(&mut self) { self.progress = 0.0; self.progress_columns = 0; diff --git a/ratatui-widgets/examples/list.rs b/ratatui-widgets/examples/list.rs index 3d777b40..2fc406aa 100644 --- a/ratatui-widgets/examples/list.rs +++ b/ratatui-widgets/examples/list.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Modifier, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -32,23 +32,22 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { - let mut list_state = ListState::default(); - list_state.select_first(); + let mut list_state = ListState::default().with_selected(Some(0)); loop { - terminal.draw(|frame| draw(frame, &mut list_state))?; - if let Event::Key(key) = event::read()? { + terminal.draw(|frame| render(frame, &mut list_state))?; + if let Some(key) = event::read()?.as_key_press_event() { match key.code { - KeyCode::Char('q') => break Ok(()), - KeyCode::Down | KeyCode::Char('j') => list_state.select_next(), - KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(), + KeyCode::Char('j') | KeyCode::Down => list_state.select_next(), + KeyCode::Char('k') | KeyCode::Up => list_state.select_previous(), + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), _ => {} } } } } -/// Draw the UI with various lists. -fn draw(frame: &mut Frame, list_state: &mut ListState) { +/// Render the UI with various lists. +fn render(frame: &mut Frame, list_state: &mut ListState) { let vertical = Layout::vertical([ Constraint::Length(1), Constraint::Fill(1), diff --git a/ratatui-widgets/examples/logo.rs b/ratatui-widgets/examples/logo.rs index 6db2b38a..4113d947 100644 --- a/ratatui-widgets/examples/logo.rs +++ b/ratatui-widgets/examples/logo.rs @@ -17,7 +17,7 @@ use std::env::args; use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout}; use ratatui::widgets::{RatatuiLogo, RatatuiLogoSize}; use ratatui::{DefaultTerminal, Frame, TerminalOptions, Viewport}; @@ -41,15 +41,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal, size: RatatuiLogoSize) -> Result<()> { loop { - terminal.draw(|frame| draw(frame, size))?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(|frame| render(frame, size))?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with a logo. -fn draw(frame: &mut Frame, size: RatatuiLogoSize) { +/// Render the UI with a logo. +fn render(frame: &mut Frame, size: RatatuiLogoSize) { let [top, bottom] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).areas(frame.area()); diff --git a/ratatui-widgets/examples/paragraph.rs b/ratatui-widgets/examples/paragraph.rs index 18117f6c..3a46bd03 100644 --- a/ratatui-widgets/examples/paragraph.rs +++ b/ratatui-widgets/examples/paragraph.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Alignment, Constraint, Layout, Rect}; use ratatui::style::{Color, Stylize}; use ratatui::text::{Line, Masked, Span}; @@ -33,15 +33,15 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with various text. -fn draw(frame: &mut Frame) { +/// Render the UI with various text. +fn render(frame: &mut Frame) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let horizontal = Layout::horizontal([Constraint::Percentage(50); 2]).spacing(1); let [top, main] = vertical.areas(frame.area()); diff --git a/ratatui-widgets/examples/scrollbar.rs b/ratatui-widgets/examples/scrollbar.rs index aeeca40e..f0409ba6 100644 --- a/ratatui-widgets/examples/scrollbar.rs +++ b/ratatui-widgets/examples/scrollbar.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Layout, Margin, Rect}; use ratatui::style::{Color, Stylize}; use ratatui::symbols::scrollbar::Set; @@ -36,22 +36,22 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { let mut vertical = ScrollbarState::new(100); let mut horizontal = ScrollbarState::new(100); loop { - terminal.draw(|frame| draw(frame, &mut vertical, &mut horizontal))?; - if let Event::Key(key) = event::read()? { + terminal.draw(|frame| render(frame, &mut vertical, &mut horizontal))?; + if let Some(key) = event::read()?.as_key_press_event() { match key.code { - KeyCode::Char('q') => break Ok(()), - KeyCode::Down | KeyCode::Char('j') => vertical.next(), - KeyCode::Up | KeyCode::Char('k') => vertical.prev(), - KeyCode::Right | KeyCode::Char('l') => horizontal.next(), - KeyCode::Left | KeyCode::Char('h') => horizontal.prev(), + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), + KeyCode::Char('j') | KeyCode::Down => vertical.next(), + KeyCode::Char('k') | KeyCode::Up => vertical.prev(), + KeyCode::Char('l') | KeyCode::Right => horizontal.next(), + KeyCode::Char('h') | KeyCode::Left => horizontal.prev(), _ => {} } } } } -/// Draw the UI with vertical/horizontal scrollbars. -fn draw(frame: &mut Frame, vertical: &mut ScrollbarState, horizontal: &mut ScrollbarState) { +/// Render the UI with vertical/horizontal scrollbars. +fn render(frame: &mut Frame, vertical: &mut ScrollbarState, horizontal: &mut ScrollbarState) { let vertical_layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let [top, main] = vertical_layout.areas(frame.area()); diff --git a/ratatui-widgets/examples/sparkline.rs b/ratatui-widgets/examples/sparkline.rs index 43137709..ae45c014 100644 --- a/ratatui-widgets/examples/sparkline.rs +++ b/ratatui-widgets/examples/sparkline.rs @@ -17,7 +17,7 @@ use core::time::Duration; use color_eyre::Result; -use crossterm::event::{self, Event}; +use crossterm::event; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -35,23 +35,26 @@ fn main() -> Result<()> { /// Run the application. fn run(mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(draw)?; - if event::poll(Duration::from_millis(16))? && matches!(event::read()?, Event::Key(_)) { - break Ok(()); + terminal.draw(render)?; + // Ensure that the animations renders at 50 FPS (GIF speed) + if !event::poll(Duration::from_secs_f64(1.0 / 50.0))? { + continue; + } + if event::read()?.is_key_press() { + return Ok(()); } } } -/// Draw the UI with various sparklines. -fn draw(frame: &mut Frame) { - let vertical = Layout::vertical([ +/// Render the UI with various sparklines. +fn render(frame: &mut Frame) { + let constraints = [ Constraint::Length(1), Constraint::Max(2), Constraint::Fill(1), Constraint::Fill(1), - ]) - .spacing(1); - let [top, first, second, _] = vertical.areas(frame.area()); + ]; + let [top, first, second, _] = Layout::vertical(constraints).spacing(1).areas(frame.area()); let title = Line::from_iter([ Span::from("Sparkline Widget").bold(), diff --git a/ratatui-widgets/examples/table.rs b/ratatui-widgets/examples/table.rs index f67bbc68..3cb46e68 100644 --- a/ratatui-widgets/examples/table.rs +++ b/ratatui-widgets/examples/table.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Constraint, Layout, Rect}; use ratatui::style::{Color, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -36,22 +36,24 @@ fn run(mut terminal: DefaultTerminal) -> Result<()> { table_state.select_first(); table_state.select_first_column(); loop { - terminal.draw(|frame| draw(frame, &mut table_state))?; - if let Event::Key(key) = event::read()? { + terminal.draw(|frame| render(frame, &mut table_state))?; + if let Some(key) = event::read()?.as_key_press_event() { match key.code { - KeyCode::Char('q') => break Ok(()), - KeyCode::Down | KeyCode::Char('j') => table_state.select_next(), - KeyCode::Up | KeyCode::Char('k') => table_state.select_previous(), - KeyCode::Right | KeyCode::Char('l') => table_state.select_next_column(), - KeyCode::Left | KeyCode::Char('h') => table_state.select_previous_column(), + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), + KeyCode::Char('j') | KeyCode::Down => table_state.select_next(), + KeyCode::Char('k') | KeyCode::Up => table_state.select_previous(), + KeyCode::Char('l') | KeyCode::Right => table_state.select_next_column(), + KeyCode::Char('h') | KeyCode::Left => table_state.select_previous_column(), + KeyCode::Char('g') => table_state.select_first(), + KeyCode::Char('G') => table_state.select_last(), _ => {} } } } } -/// Draw the UI with a table. -fn draw(frame: &mut Frame, table_state: &mut TableState) { +/// Render the UI with a table. +fn render(frame: &mut Frame, table_state: &mut TableState) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let [top, main] = vertical.areas(frame.area()); @@ -66,6 +68,10 @@ fn draw(frame: &mut Frame, table_state: &mut TableState) { /// Render a table with some rows and columns. pub fn render_table(frame: &mut Frame, area: Rect, table_state: &mut TableState) { + let header = Row::new(["Ingredient", "Quantity", "Macros"]) + .style(Style::new().bold()) + .bottom_margin(1); + let rows = [ Row::new(["Eggplant", "1 medium", "25 kcal, 6g carbs, 1g protein"]), Row::new(["Tomato", "2 large", "44 kcal, 10g carbs, 2g protein"]), @@ -73,27 +79,19 @@ pub fn render_table(frame: &mut Frame, area: Rect, table_state: &mut TableState) Row::new(["Bell Pepper", "1 medium", "24 kcal, 6g carbs, 1g protein"]), Row::new(["Garlic", "2 cloves", "9 kcal, 2g carbs, 0.4g protein"]), ]; - + let footer = Row::new([ + "Ratatouille Recipe", + "", + "135 kcal, 31g carbs, 6.4g protein", + ]); let widths = [ Constraint::Percentage(30), Constraint::Percentage(20), Constraint::Percentage(50), ]; - let table = Table::new(rows, widths) - .header( - Row::new(["Ingredient", "Quantity", "Macros"]) - .style(Style::new().bold()) - .bottom_margin(1), - ) - .footer( - Row::new([ - "Ratatouille Recipe", - "", - "135 kcal, 31g carbs, 6.4g protein", - ]) - .italic(), - ) + .header(header) + .footer(footer.italic()) .column_spacing(1) .style(Color::White) .row_highlight_style(Style::new().on_black().bold()) diff --git a/ratatui-widgets/examples/tabs.rs b/ratatui-widgets/examples/tabs.rs index 5a229047..16616c0d 100644 --- a/ratatui-widgets/examples/tabs.rs +++ b/ratatui-widgets/examples/tabs.rs @@ -15,7 +15,7 @@ //! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md use color_eyre::Result; -use crossterm::event::{self, Event, KeyCode}; +use crossterm::event::{self, KeyCode}; use ratatui::layout::{Alignment, Constraint, Layout, Offset, Rect}; use ratatui::style::{Color, Style, Stylize}; use ratatui::text::{Line, Span}; @@ -34,22 +34,22 @@ fn main() -> Result<()> { fn run(mut terminal: DefaultTerminal) -> Result<()> { let mut selected_tab = 0; loop { - terminal.draw(|frame| draw(frame, selected_tab))?; - if let Event::Key(key) = event::read()? { + terminal.draw(|frame| render(frame, selected_tab))?; + if let Some(key) = event::read()?.as_key_press_event() { match key.code { - KeyCode::Char('q') => break Ok(()), - KeyCode::Right | KeyCode::Char('l') | KeyCode::Tab => { + KeyCode::Char('l') | KeyCode::Right | KeyCode::Tab => { selected_tab = (selected_tab + 1) % 3; } - KeyCode::Left | KeyCode::Char('h') => selected_tab = (selected_tab + 2) % 3, + KeyCode::Char('h') | KeyCode::Left => selected_tab = (selected_tab + 2) % 3, + KeyCode::Char('q') | KeyCode::Esc => return Ok(()), _ => {} } } } } -/// Draw the UI with tabs. -fn draw(frame: &mut Frame, selected_tab: usize) { +/// Render the UI with tabs. +fn render(frame: &mut Frame, selected_tab: usize) { let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1); let [top, main] = vertical.areas(frame.area());