Ratatui is a crate for building terminal user interfaces in Rust.
In this post we’ll show some simple widgets that come built-in with ratatui.
Code
:dep ratatui ="0.26.2":dep ratatui-macros ="0.4.0"fn span_to_html(s:ratatui::text::Span) ->String{letmut html =String::new(); html.push_str("<span style=\"");// Set foreground colorifletSome(color) =&s.style.fg { html.push_str(&format!("color: {};", color));}// Set background colorifletSome(color) =&s.style.bg { html.push_str(&format!("background-color: {};", color));}// Add modifiersmatch s.style.add_modifier {ratatui::style::Modifier::BOLD => html.push_str("font-weight: bold;"),ratatui::style::Modifier::ITALIC => html.push_str("font-style: italic;"),ratatui::style::Modifier::UNDERLINED => html.push_str("text-decoration: underline;"), _ =>{}} html.push_str("\">"); html.push_str(&s.content); html.push_str("</span>"); html}fn buffer_to_html(buf:&ratatui::buffer::Buffer) ->String{fn escape_special_html_characters(text:&str) ->String{ text.replace("&","&").replace("<","<").replace(">",">").replace("\"",""").replace("'","'")}letmut html =String::from("<pre><code>");let w = buf.area.width;let h = buf.area.height;for y in0..h {for x in0..w {let s = buf.get(x, y).symbol();let escaped = escape_special_html_characters(s);let style = buf.get(x, y).style();let span =ratatui::text::Span::styled(s, style); html.push_str(&span_to_html(span));} html.push('\n');} html.push_str("</code></pre>"); html }fn show_html<D>(content: D) where D:std::fmt::Display{println!(r#"EVCXR_BEGIN_CONTENT text/html<div style="display: flex; justify-content:start; gap: 1em; margin: 1em">{}</div>EVCXR_END_CONTENT"#, content);}
Widget primitives
Block
The simplest widget is the Block widget, which is essentially just borders.
useratatui::widgets::Widget;let (x, y, width, height) = (0,0,50,5);let area =ratatui::layout::Rect::new(x, y, width, height);letmut buf =ratatui::buffer::Buffer::empty(area);ratatui::widgets::Block::bordered().render(area,&mut buf);show_html(buffer_to_html(&buf))
Most widgets accept a Block as a fluent setter. We saw from earlier that the Paragraph has a .block() method that accepts a Block.
let paragraph =Paragraph::new(text).block(block).centered();
Blocks can have different kinds of borders:
let (x, y, width, height) = (0,0,50,5);let area =ratatui::layout::Rect::new(x, y, width, height);letmut buf =ratatui::buffer::Buffer::empty(area);ratatui::widgets::Block::bordered().border_type(ratatui::widgets::BorderType::Double).render(area.inner(&ratatui::layout::Margin::new(2,1)),&mut buf);ratatui::widgets::Block::bordered().borders(ratatui::widgets::Borders::TOP |ratatui::widgets::Borders::BOTTOM).render(area,&mut buf);show_html(buffer_to_html(&buf))
And Block can have multiple titles in different locations:
useratatui_macros::line;let (x, y, width, height) = (0,0,50,5);let area =ratatui::layout::Rect::new(x, y, width, height);letmut buf =ratatui::buffer::Buffer::empty(area);let block =ratatui::widgets::Block::bordered().title("Top Left") // accepts anything that can be converted to a `Title` or a `Line`.title(ratatui::text::Line::from("Top Center").centered()) // explicitly need to use `Line` if you want alignment.title(line!["Top Right"].right_aligned()) // you can use the `line!` macro to make it shorter.title(ratatui::widgets::block::Title::from("Bottom Right") // explicitly using `Title` gives you most control.alignment(ratatui::layout::Alignment::Right).position(ratatui::widgets::block::title::Position::Bottom) ).title_bottom(ratatui::text::Line::from("Bottom Center").centered()) // shorthand functions for bottom position.title_bottom("Bottom Left");// aligned to the left by defaultblock.render(area,&mut buf);show_html(buffer_to_html(&buf))