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/joshka/tui-widgets
35//! [Coverage]: https://app.codecov.io/gh/joshka/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/joshka/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/joshka/tui-widgets/blob/main/LICENSE-MIT
42//! [Coverage Badge]:
43//! https://img.shields.io/codecov/c/github/joshka/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/joshka/tui-widgets
47//! [API Docs]: https://docs.rs/tui-box-text/
48//! [Examples]: https://github.com/joshka/tui-widgets/tree/main/tui-box-text/examples
49//! [Changelog]: https://github.com/joshka/tui-widgets/blob/main/tui-box-text/CHANGELOG.md
50//! [Contributing]: https://github.com/joshka/tui-widgets/blob/main/CONTRIBUTING.md
51//! [Joshka]: https://github.com/joshka
52//! [tui-widgets]: https://crates.io/crates/tui-widgets
53
54use std::collections::HashMap;
55use std::iter::zip;
56use std::sync::LazyLock;
57
58use ratatui_core::buffer::Buffer;
59use ratatui_core::layout::Rect;
60use ratatui_core::widgets::Widget;
61
62pub struct BoxChar(char);
63
64impl BoxChar {
65 pub const fn new(c: char) -> Self {
66 Self(c)
67 }
68}
69
70impl Widget for &BoxChar {
71 fn render(self, area: Rect, buf: &mut Buffer) {
72 let c = self
73 .0
74 .to_uppercase() // TODO: add support for lower case characters
75 .next()
76 .and_then(|c| CHARS.get(&c))
77 .unwrap_or(&" ");
78 let lines = c.lines().collect::<Vec<_>>();
79 for (line, row) in zip(lines, area.rows()) {
80 for (char, cell) in zip(line.chars(), row.columns()) {
81 buf[cell.as_position()].set_symbol(&char.to_string());
82 }
83 }
84 }
85}
86
87/// A macro for creating a hash table that maps single characters to strings.
88macro_rules! char_table {
89 ( $($char:expr => $repr:expr),* $(,)? ) => {
90 {
91 let mut table = ::std::collections::HashMap::new();
92 $(
93 table.insert($char, ::indoc::indoc! {$repr});
94 )*
95 table
96 }
97 };
98}
99
100/// A hash table that maps single characters to strings that are 3 lines high and made up of box
101/// drawing characters.
102static CHARS: LazyLock<HashMap<char, &str>> = LazyLock::new(|| {
103 char_table!(
104 ' ' => " ",
105 '!' => "│
106 ╵
107 ╵",
108 '"' => "╭╭",
109 '#' => "┼─┼
110 ┼─┼",
111 '$' => "╭┼╴
112 └┼┐
113 ╶┼╯",
114 '%' => "o╱
115 ╱o",
116 '&' => "╭─╮
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 '0' => "╭─╮
151 │╱│
152 ╰─╯",
153 '1' => "
154 ╶┐
155 │
156 ─┴─",
157 '2' => "╶─╮
158 ┌─┘
159 └─╴",
160 '3' => "╶─╮
161 ╶─┤
162 ╶─╯",
163 '4' => "╷ ╷
164 ╰─┤
165 ╵",
166 '5' => "┌─╴
167 └─╮
168 ╰─╯",
169 '6' => "╭─╴
170 ├─╮
171 ╰─╯",
172 '7' => "╶─┐
173 ╱
174 ╵ ",
175 '8' => "╭─╮
176 ├─┤
177 ╰─╯",
178 '9' => "╭─╮
179 ╰─┤
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 'A' => "╭─╮
207 ├─┤
208 ╵ ╵",
209 'B' => "┌╮
210 ├┴╮
211 ╰─╯",
212 'C' => "╭─╮
213 │
214 ╰─╯",
215 'D' => "┌─╮
216 │ │
217 └─╯",
218 'E' => "┌─╴
219 ├─
220 └─╴",
221 'F' => "┌─╴
222 ├─
223 ╵ ",
224 'G' => "╭─╮
225 │─╮
226 ╰─╯",
227 'H' => "╷ ╷
228 ├─┤
229 ╵ ╵",
230 'I' => "╶┬╴
231 │
232 ╶┴╴",
233 'J' => " ╶┐
234 │
235 ╰─╯",
236 'K' => "╷╭
237 ├┴╮
238 ╵ ╵",
239 'L' => "╷
240 │
241 └──",
242 'M' => "╭┬╮
243 │││
244 ╵╵╵",
245 'N' => "╭─╮
246 │ │
247 ╵ ╵",
248 'O' => "╭─╮
249 │ │
250 ╰─╯",
251 'P' => "┌─╮
252 ├─╯
253 ╵ ",
254 'Q' => "╭─╮
255 │ │
256 ╰─╳",
257 'R' => "┌─╮
258 ├┬╯
259 ╵╰ ",
260 'S' => "╭─╮
261 ╰─╮
262 ╰─╯",
263 'T' => "
264 ╶┬╴
265 │
266 ╵",
267 'U' => "╷ ╷
268 │ │
269 ╰─╯",
270 'V' => "╷ ╷
271 │ │
272 └─╯",
273 'W' => "╷╷╷
274 │││
275 ╰┴╯",
276 'X' => "╮ ╭
277 ╰─╮
278 ╯ ╰",
279 'Y' => "╮ ╭
280 ╰┬╯
281 ╵",
282 'Z' => "╶─╮
283 ╱
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});