Skip to main content

tui_box_text/
lib.rs

1//! A [Ratatui] widget to draw delightfully boxy text with line-drawing characters. Part of the
2//! [tui-widgets] suite by [Joshka].
3//!
4//! ![Demo](https://vhs.charm.sh/vhs-6ldj2r9v3mIaSzk8H7Jp8t.gif)
5//!
6//! [![Crate badge]][Crate]
7//! [![Docs Badge]][Docs]
8//! [![Deps Badge]][Dependency Status]
9//! [![License Badge]][License]
10//! [![Coverage Badge]][Coverage]
11//! [![Discord Badge]][Ratatui Discord]
12//!
13//! [GitHub Repository] · [API Docs] · [Examples] · [Changelog] · [Contributing]
14//!
15//! # Usage
16//!
17//! Create a `BoxChar` and render it into a region of your frame.
18//!
19//! ```rust
20//! use tui_box_text::BoxChar;
21//!
22//! # fn draw(frame: &mut ratatui::Frame) {
23//! let letter = BoxChar::new('A');
24//! frame.render_widget(&letter, frame.area());
25//! # }
26//! ```
27//!
28//! # More widgets
29//!
30//! For the full suite of widgets, see [tui-widgets].
31//!
32//! [Crate]: https://crates.io/crates/tui-box-text
33//! [Docs]: https://docs.rs/tui-box-text/
34//! [Dependency Status]: https://deps.rs/repo/github/ratatui/tui-widgets
35//! [Coverage]: https://app.codecov.io/gh/ratatui/tui-widgets
36//! [Ratatui Discord]: https://discord.gg/pMCEU9hNEj
37//! [Crate badge]: https://img.shields.io/crates/v/tui-box-text?logo=rust&style=flat
38//! [Docs Badge]: https://img.shields.io/docsrs/tui-box-text?logo=rust&style=flat
39//! [Deps Badge]: https://deps.rs/repo/github/ratatui/tui-widgets/status.svg?style=flat
40//! [License Badge]: https://img.shields.io/crates/l/tui-box-text?style=flat
41//! [License]: https://github.com/ratatui/tui-widgets/blob/main/LICENSE-MIT
42//! [Coverage Badge]:
43//!     https://img.shields.io/codecov/c/github/ratatui/tui-widgets?logo=codecov&style=flat
44//! [Discord Badge]: https://img.shields.io/discord/1070692720437383208?logo=discord&style=flat
45//!
46//! [GitHub Repository]: https://github.com/ratatui/tui-widgets
47//! [API Docs]: https://docs.rs/tui-box-text/
48//! [Examples]: https://github.com/ratatui/tui-widgets/tree/main/tui-box-text/examples
49//! [Changelog]: https://github.com/ratatui/tui-widgets/blob/main/tui-box-text/CHANGELOG.md
50//! [Contributing]: https://github.com/ratatui/tui-widgets/blob/main/CONTRIBUTING.md
51//! [Joshka]: https://github.com/joshka
52//! [Ratatui]: https://ratatui.rs
53//! [tui-widgets]: https://crates.io/crates/tui-widgets
54
55use std::collections::HashMap;
56use std::iter::zip;
57use std::sync::LazyLock;
58
59use ratatui_core::buffer::Buffer;
60use ratatui_core::layout::Rect;
61use ratatui_core::widgets::Widget;
62
63pub struct BoxChar(char);
64
65impl BoxChar {
66    pub const fn new(c: char) -> Self {
67        Self(c)
68    }
69}
70
71impl Widget for &BoxChar {
72    fn render(self, area: Rect, buf: &mut Buffer) {
73        let c = self
74            .0
75            .to_uppercase() // TODO: add support for lower case characters
76            .next()
77            .and_then(|c| CHARS.get(&c))
78            .unwrap_or(&" ");
79        let lines = c.lines().collect::<Vec<_>>();
80        for (line, row) in zip(lines, area.rows()) {
81            for (char, cell) in zip(line.chars(), row.columns()) {
82                buf[cell.as_position()].set_symbol(&char.to_string());
83            }
84        }
85    }
86}
87
88/// A macro for creating a hash table that maps single characters to strings.
89macro_rules! char_table {
90    ( $($char:expr_2021 => $repr:expr_2021),* $(,)? ) => {
91        {
92            let mut table = ::std::collections::HashMap::new();
93            $(
94                table.insert($char, ::indoc::indoc! {$repr});
95            )*
96            table
97        }
98    };
99}
100
101/// A hash table that maps single characters to strings that are 3 lines high and made up of box
102/// drawing characters.
103static CHARS: LazyLock<HashMap<char, &str>> = LazyLock::new(|| {
104    char_table!(
105        ' ' => " ",
106        '!' => "│
107108                ╵",
109        '"' => "╭╭",
110        '#' => "┼─┼
111                ┼─┼",
112        '$' => "╭┼╴
113                └┼┐
114                ╶┼╯",
115        '%' => "o╱
116                ╱o",
117        '&' => "╭─╮
118                ╭╲╯
119                ╰─╲",
120        '\'' => "╭",
121        '(' => "╭
122123                ╰",
124        ')' => "╮
125126                ╯",
127        '*' => "
128        
129                *
130                ",
131        '+' => "
132133                ╶┼╴
134                 ╵",
135        ',' => "
136
137                
138                ╯",
139        '-' => "
140
141                ──
142                 ",
143        '.' => "
144
145                .
146                 ",
147        '/' => "
148149150                ",
151        '0' => "╭─╮
152                │╱│
153                ╰─╯",
154        '1' => "
155                 ╶┐
156157                 ─┴─",
158        '2' => "╶─╮
159                ┌─┘
160                └─╴",
161        '3' => "╶─╮
162                ╶─┤
163                ╶─╯",
164        '4' => "╷ ╷
165                ╰─┤
166                  ╵",
167        '5' => "┌─╴
168                └─╮
169                ╰─╯",
170        '6' => "╭─╴
171                ├─╮
172                ╰─╯",
173        '7' => "╶─┐
174175                ╵  ",
176        '8' => "╭─╮
177                ├─┤
178                ╰─╯",
179        '9' => "╭─╮
180                ╰─┤
181                ╶─╯",
182        ':' => "╷
183184185                 ",
186        ';' => "╷
187188                ╯",
189        '<' => "
190191192                 ",
193        '=' => "
194                ──
195                ──",
196        '>' => "
197198199                 ",
200        '?' => "
201                ╶─╮
202                 ╭╯
203                 ╷",
204        '@' => "╭─╮
205                ╭╮│
206                ╰┴╯",
207        'A' => "╭─╮
208                ├─┤
209                ╵ ╵",
210        'B' => "┌╮
211                ├┴╮
212                ╰─╯",
213        'C' => "╭─╮
214215                ╰─╯",
216        'D' => "┌─╮
217                │ │
218                └─╯",
219        'E' => "┌─╴
220                ├─
221                └─╴",
222        'F' => "┌─╴
223                ├─
224                ╵  ",
225        'G' => "╭─╮
226                │─╮
227                ╰─╯",
228        'H' => "╷ ╷
229                ├─┤
230                ╵ ╵",
231        'I' => "╶┬╴
232233                ╶┴╴",
234        'J' => " ╶┐
235236                ╰─╯",
237        'K' => "╷╭
238                ├┴╮
239                ╵ ╵",
240        'L' => "╷
241242                └──",
243        'M' => "╭┬╮
244                │││
245                ╵╵╵",
246        'N' => "╭─╮
247                │ │
248                ╵ ╵",
249        'O' => "╭─╮
250                │ │
251                ╰─╯",
252        'P' => "┌─╮
253                ├─╯
254                ╵  ",
255        'Q' => "╭─╮
256                │ │
257                ╰─╳",
258        'R' => "┌─╮
259                ├┬╯
260                ╵╰ ",
261        'S' => "╭─╮
262                ╰─╮ 
263                ╰─╯",
264        'T' => "
265                ╶┬╴
266267                 ╵",
268        'U' => "╷ ╷
269                │ │
270                ╰─╯",
271        'V' => "╷ ╷
272                │ │
273                └─╯",
274        'W' => "╷╷╷
275                │││
276                ╰┴╯",
277        'X' => "╮ ╭
278                ╰─╮ 
279                ╯ ╰",
280        'Y' => "╮ ╭
281                ╰┬╯ 
282                 ╵",
283        'Z' => "╶─╮
284285                ╰─╴",
286        '[' => "┌─
287288                └─",
289        '\\' => "
290291292                ",
293        ']' => "─┐
294295                ─┘",
296        '^' => "╱╲",
297        '_' => "
298
299                ──",
300        '`' => "╮",
301        '{' => "
302303304                ╰",
305        '|' => "│
306307                │",
308        '}' => "╮
309310                ╯",
311        '~' => "
312                ╭╮
313                 ╰╯",
314    )
315});