Block
Demonstrates the Block
widget.
git clone https://github.com/ratatui-org/ratatui.git --branch latestcd ratatuicargo run --example=block --features=crossterm
//! # [Ratatui] Block example//!//! The latest version of this example is available in the [examples] folder in the repository.//!//! Please note that the examples are designed to be run against the `main` branch of the Github//! repository. This means that you may not be able to compile with the latest release version on//! crates.io, or the one that you have installed locally.//!//! See the [examples readme] for more information on finding examples that match the version of the//! library you are using.//!//! [Ratatui]: https://github.com/ratatui-org/ratatui//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{ error::Error, io::{stdout, Stdout}, ops::ControlFlow, time::Duration,};
use itertools::Itertools;use ratatui::{ crossterm::{ event::{self, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }, prelude::*, widgets::{ block::{Position, Title}, Block, BorderType, Borders, Padding, Paragraph, Wrap, },};
// These type aliases are used to make the code more readable by reducing repetition of the generic// types. They are not necessary for the functionality of the code.type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;type Result<T> = std::result::Result<T, Box<dyn Error>>;
fn main() -> Result<()> { let mut terminal = setup_terminal()?; let result = run(&mut terminal); restore_terminal(terminal)?;
if let Err(err) = result { eprintln!("{err:?}"); } Ok(())}
fn setup_terminal() -> Result<Terminal> { enable_raw_mode()?; let mut stdout = stdout(); execute!(stdout, EnterAlternateScreen)?; let backend = CrosstermBackend::new(stdout); let terminal = Terminal::new(backend)?; Ok(terminal)}
fn restore_terminal(mut terminal: Terminal) -> Result<()> { disable_raw_mode()?; execute!(terminal.backend_mut(), LeaveAlternateScreen)?; Ok(())}
fn run(terminal: &mut Terminal) -> Result<()> { loop { terminal.draw(ui)?; if handle_events()?.is_break() { return Ok(()); } }}
fn handle_events() -> Result<ControlFlow<()>> { if event::poll(Duration::from_millis(100))? { if let Event::Key(key) = event::read()? { if key.code == KeyCode::Char('q') { return Ok(ControlFlow::Break(())); } } } Ok(ControlFlow::Continue(()))}
fn ui(frame: &mut Frame) { let (title_area, layout) = calculate_layout(frame.size());
render_title(frame, title_area);
let paragraph = placeholder_paragraph();
render_borders(¶graph, Borders::ALL, frame, layout[0][0]); render_borders(¶graph, Borders::NONE, frame, layout[0][1]); render_borders(¶graph, Borders::LEFT, frame, layout[1][0]); render_borders(¶graph, Borders::RIGHT, frame, layout[1][1]); render_borders(¶graph, Borders::TOP, frame, layout[2][0]); render_borders(¶graph, Borders::BOTTOM, frame, layout[2][1]);
render_border_type(¶graph, BorderType::Plain, frame, layout[3][0]); render_border_type(¶graph, BorderType::Rounded, frame, layout[3][1]); render_border_type(¶graph, BorderType::Double, frame, layout[4][0]); render_border_type(¶graph, BorderType::Thick, frame, layout[4][1]);
render_styled_block(¶graph, frame, layout[5][0]); render_styled_borders(¶graph, frame, layout[5][1]); render_styled_title(¶graph, frame, layout[6][0]); render_styled_title_content(¶graph, frame, layout[6][1]); render_multiple_titles(¶graph, frame, layout[7][0]); render_multiple_title_positions(¶graph, frame, layout[7][1]); render_padding(¶graph, frame, layout[8][0]); render_nested_blocks(¶graph, frame, layout[8][1]);}
/// Calculate the layout of the UI elements.////// Returns a tuple of the title area and the main areas.fn calculate_layout(area: Rect) -> (Rect, Vec<Vec<Rect>>) { let main_layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]); let block_layout = Layout::vertical([Constraint::Max(4); 9]); let [title_area, main_area] = main_layout.areas(area); let main_areas = block_layout .split(main_area) .iter() .map(|&area| { Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]) .split(area) .to_vec() }) .collect_vec(); (title_area, main_areas)}
fn render_title(frame: &mut Frame, area: Rect) { frame.render_widget( Paragraph::new("Block example. Press q to quit") .dark_gray() .alignment(Alignment::Center), area, );}
fn placeholder_paragraph() -> Paragraph<'static> { let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; Paragraph::new(text.dark_gray()).wrap(Wrap { trim: true })}
fn render_borders(paragraph: &Paragraph, border: Borders, frame: &mut Frame, area: Rect) { let block = Block::new() .borders(border) .title(format!("Borders::{border:#?}")); frame.render_widget(paragraph.clone().block(block), area);}
fn render_border_type( paragraph: &Paragraph, border_type: BorderType, frame: &mut Frame, area: Rect,) { let block = Block::bordered() .border_type(border_type) .title(format!("BorderType::{border_type:#?}")); frame.render_widget(paragraph.clone().block(block), area);}fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .border_style(Style::new().blue().on_white().bold().italic()) .title("Styled borders"); frame.render_widget(paragraph.clone().block(block), area);}
fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .style(Style::new().blue().on_white().bold().italic()) .title("Styled block"); frame.render_widget(paragraph.clone().block(block), area);}
// Note: this currently renders incorrectly, see https://github.com/ratatui-org/ratatui/issues/349fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .title("Styled title") .title_style(Style::new().blue().on_white().bold().italic()); frame.render_widget(paragraph.clone().block(block), area);}
fn render_styled_title_content(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let title = Line::from(vec![ "Styled ".blue().on_white().bold().italic(), "title content".red().on_white().bold().italic(), ]); let block = Block::bordered().title(title); frame.render_widget(paragraph.clone().block(block), area);}
fn render_multiple_titles(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .title("Multiple".blue().on_white().bold().italic()) .title("Titles".red().on_white().bold().italic()); frame.render_widget(paragraph.clone().block(block), area);}
fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .title( Title::from("top left") .position(Position::Top) .alignment(Alignment::Left), ) .title( Title::from("top center") .position(Position::Top) .alignment(Alignment::Center), ) .title( Title::from("top right") .position(Position::Top) .alignment(Alignment::Right), ) .title( Title::from("bottom left") .position(Position::Bottom) .alignment(Alignment::Left), ) .title( Title::from("bottom center") .position(Position::Bottom) .alignment(Alignment::Center), ) .title( Title::from("bottom right") .position(Position::Bottom) .alignment(Alignment::Right), ); frame.render_widget(paragraph.clone().block(block), area);}
fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let block = Block::bordered() .padding(Padding::new(5, 10, 1, 2)) .title("Padding"); frame.render_widget(paragraph.clone().block(block), area);}
fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) { let outer_block = Block::bordered().title("Outer block"); let inner_block = Block::bordered().title("Inner block"); let inner = outer_block.inner(area); frame.render_widget(outer_block, area); frame.render_widget(paragraph.clone().block(inner_block), inner);}