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//! 
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 '!' => "│
107 ╵
108 ╵",
109 '"' => "╭╭",
110 '#' => "┼─┼
111 ┼─┼",
112 '$' => "╭┼╴
113 └┼┐
114 ╶┼╯",
115 '%' => "o╱
116 ╱o",
117 '&' => "╭─╮
118 ╭╲╯
119 ╰─╲",
120 '\'' => "╭",
121 '(' => "╭
122 │
123 ╰",
124 ')' => "╮
125 │
126 ╯",
127 '*' => "
128
129 *
130 ",
131 '+' => "
132 ╷
133 ╶┼╴
134 ╵",
135 ',' => "
136
137
138 ╯",
139 '-' => "
140
141 ──
142 ",
143 '.' => "
144
145 .
146 ",
147 '/' => "
148 ╱
149 ╱
150 ",
151 '0' => "╭─╮
152 │╱│
153 ╰─╯",
154 '1' => "
155 ╶┐
156 │
157 ─┴─",
158 '2' => "╶─╮
159 ┌─┘
160 └─╴",
161 '3' => "╶─╮
162 ╶─┤
163 ╶─╯",
164 '4' => "╷ ╷
165 ╰─┤
166 ╵",
167 '5' => "┌─╴
168 └─╮
169 ╰─╯",
170 '6' => "╭─╴
171 ├─╮
172 ╰─╯",
173 '7' => "╶─┐
174 ╱
175 ╵ ",
176 '8' => "╭─╮
177 ├─┤
178 ╰─╯",
179 '9' => "╭─╮
180 ╰─┤
181 ╶─╯",
182 ':' => "╷
183 ╵
184 │
185 ",
186 ';' => "╷
187 ╵
188 ╯",
189 '<' => "
190 ╱
191 ╲
192 ",
193 '=' => "
194 ──
195 ──",
196 '>' => "
197 ╲
198 ╱
199 ",
200 '?' => "
201 ╶─╮
202 ╭╯
203 ╷",
204 '@' => "╭─╮
205 ╭╮│
206 ╰┴╯",
207 'A' => "╭─╮
208 ├─┤
209 ╵ ╵",
210 'B' => "┌╮
211 ├┴╮
212 ╰─╯",
213 'C' => "╭─╮
214 │
215 ╰─╯",
216 'D' => "┌─╮
217 │ │
218 └─╯",
219 'E' => "┌─╴
220 ├─
221 └─╴",
222 'F' => "┌─╴
223 ├─
224 ╵ ",
225 'G' => "╭─╮
226 │─╮
227 ╰─╯",
228 'H' => "╷ ╷
229 ├─┤
230 ╵ ╵",
231 'I' => "╶┬╴
232 │
233 ╶┴╴",
234 'J' => " ╶┐
235 │
236 ╰─╯",
237 'K' => "╷╭
238 ├┴╮
239 ╵ ╵",
240 'L' => "╷
241 │
242 └──",
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 ╶┬╴
266 │
267 ╵",
268 'U' => "╷ ╷
269 │ │
270 ╰─╯",
271 'V' => "╷ ╷
272 │ │
273 └─╯",
274 'W' => "╷╷╷
275 │││
276 ╰┴╯",
277 'X' => "╮ ╭
278 ╰─╮
279 ╯ ╰",
280 'Y' => "╮ ╭
281 ╰┬╯
282 ╵",
283 'Z' => "╶─╮
284 ╱
285 ╰─╴",
286 '[' => "┌─
287 │
288 └─",
289 '\\' => "
290 ╲
291 ╲
292 ",
293 ']' => "─┐
294 │
295 ─┘",
296 '^' => "╱╲",
297 '_' => "
298
299 ──",
300 '`' => "╮",
301 '{' => "
302 ╭
303 ┤
304 ╰",
305 '|' => "│
306 │
307 │",
308 '}' => "╮
309 ├
310 ╯",
311 '~' => "
312 ╭╮
313 ╰╯",
314 )
315});