返回 CodeWhale
braille_frame.rs
根目录 / vendor / codewhale-ratatui / src / components / braille_frame.rs
1 //! Transparent packed Braille supplied by the host's existing simulation.
2 //! No clock, activity classification, state transition or color inference.
3
4 use ratatui::{
5 buffer::Buffer,
6 layout::{Alignment, Rect},
7 style::Style,
8 widgets::{Paragraph, Widget, Wrap},
9 };
10 use unicode_width::UnicodeWidthStr;
11
12 /// Raw-caption companion raster. Tiny viewports retain the complete wrapped
13 /// non-color cue; zero cells remain transparent. Ink includes host modifiers.
14 #[derive(Clone, Copy, Debug)]
15 pub struct BrailleFrame<'a> {
16 pub cells: &'a [u8],
17 pub caption: &'a str,
18 pub style: Style,
19 }
20
21 /// Shared with Whale's semantic-caption/ombre renderer. Coordinates are the
22 /// original grid coordinates even when the buffer clips its edges.
23 pub(crate) fn paint_braille_cells(
24 area: Rect,
25 buf: &mut Buffer,
26 cells: &[u8],
27 mut ink: impl FnMut(u16) -> Style,
28 ) {
29 let visible = area.intersection(buf.area);
30 for y in visible.y..visible.bottom() {
31 let row = y - area.y;
32 let style = ink(row);
33 for x in visible.x..visible.right() {
34 let index = usize::from(row) * usize::from(area.width) + usize::from(x - area.x);
35 let bits = cells.get(index).copied().unwrap_or(0);
36 if bits != 0
37 && let Some(cell) = buf.cell_mut((x, y))
38 {
39 let glyph = char::from_u32(0x2800 + u32::from(bits)).expect("braille");
40 let mut encoded = [0; 4];
41 cell.set_symbol(glyph.encode_utf8(&mut encoded))
42 .set_style(style);
43 }
44 }
45 }
46 }
47
48 impl Widget for BrailleFrame<'_> {
49 fn render(self, area: Rect, buf: &mut Buffer) {
50 if area.is_empty() {
51 return;
52 }
53 if self.caption.width() > usize::from(area.width) || area.height < 4 {
54 Paragraph::new(self.caption)
55 .style(self.style)
56 .alignment(Alignment::Center)
57 .wrap(Wrap { trim: false })
58 .render(area, buf);
59 return;
60 }
61 paint_braille_cells(
62 Rect {
63 height: area.height - 1,
64 ..area
65 },
66 buf,
67 self.cells,
68 |_| self.style,
69 );
70 let x = area
71 .x
72 .saturating_add((area.width - self.caption.width() as u16) / 2);
73 let y = area.bottom().saturating_sub(1);
74 if buf.area.contains((x, y).into()) {
75 buf.set_stringn(x, y, self.caption, usize::from(area.width), self.style);
76 } else if y >= buf.area.y && y < buf.area.bottom() && x < buf.area.right() {
77 let mut caption = Buffer::empty(Rect::new(0, 0, area.width, 1));
78 for offset in 0..area.width {
79 if let Some(cell) = buf.cell((x.saturating_add(offset), y)) {
80 caption[(offset, 0)] = cell.clone();
81 }
82 }
83 let (end, _) =
84 caption.set_stringn(0, 0, self.caption, usize::from(area.width), self.style);
85 for offset in 0..end {
86 if let Some(cell) = buf.cell_mut((x.saturating_add(offset), y)) {
87 *cell = caption[(offset, 0)].clone();
88 }
89 }
90 }
91 }
92 }
93
93 lines RUST