Skip to main content

shuyi_ui/widgets/name_entry/
glyph.rs

1//! The glyph look of the name boxes: the typing box and the letter grid,
2//! each a double-bordered box with its title in the top border and the
3//! name so far with a cursor. Everything about the look lives here;
4//! nothing here decides what a box shows. Another look is another
5//! `paint_*` of the same views.
6
7use super::{GridView, TypingView};
8use crate::frame::color::{EngineColor, Palette};
9use crate::frame::glyph_buffer::{BoxStyle, Cell, GlyphBuffer, Rect};
10use crate::prelude::*;
11
12/// Height of the typing box: the border, the name, the hint and a blank
13/// row around each.
14pub const TYPING_HEIGHT: i32 = 7;
15
16/// The x that centres `text` in `rect` of `buf`.
17fn centred_in(buf: &GlyphBuffer, rect: Rect, text: &str) -> i32 {
18    rect.x + (rect.w - buf.text_cells(text)) / 2
19}
20
21/// A double-bordered box at `rect` with `title` in its top border.
22fn paint_box(palette: &Palette, buf: &mut GlyphBuffer, rect: Rect, title: &str) {
23    let c = |e| palette.engine(e);
24    let bg = c(EngineColor::Panel);
25    buf.fill_rect(rect, Cell::new(' ', c(EngineColor::Text), bg));
26    buf.draw_box(rect, BoxStyle::Double, c(EngineColor::Border), bg);
27    let title = format!(" {title} ");
28    buf.print(rect.x + 2, rect.y, &title, c(EngineColor::Highlight), bg);
29}
30
31/// The typing box at `rect` ([`TYPING_HEIGHT`] tall): the name so far
32/// with a cursor, and the hint to type.
33pub fn paint_typing(palette: &Palette, buf: &mut GlyphBuffer, rect: Rect, typing: &TypingView) {
34    let c = |e| palette.engine(e);
35    let bg = c(EngineColor::Panel);
36    paint_box(palette, buf, rect, &typing.title);
37    let shown = format!("{}_", typing.typed);
38    let x = centred_in(buf, rect, &shown);
39    buf.print(x, rect.y + 2, &shown, c(EngineColor::Text), bg);
40    let hint = &typing.hint;
41    let x = centred_in(buf, rect, hint);
42    buf.print(x, rect.y + 4, hint, c(EngineColor::TextDim), bg);
43}
44
45/// The height of the grid's box for `grid`: the border, the name, and two
46/// rows per row of cells.
47pub fn grid_height(grid: &GridView) -> i32 {
48    4 + 2 * i32::try_from(grid.rows.len()).unwrap_or(0)
49}
50
51/// The name grid's box from `rect`'s top left, `rect.w` wide and as tall
52/// as [`grid_height`]: the name so far with a cursor, then the grid, the
53/// focused cell as a lit bar.
54pub fn paint_grid(palette: &Palette, buf: &mut GlyphBuffer, rect: Rect, grid: &GridView) {
55    let c = |e| palette.engine(e);
56    let bg = c(EngineColor::Panel);
57    let rect = Rect::new(rect.x, rect.y, rect.w, grid_height(grid));
58    paint_box(palette, buf, rect, &grid.title);
59    let shown = format!("{}_", grid.spelled);
60    let x = centred_in(buf, rect, &shown);
61    buf.print(x, rect.y + 2, &shown, c(EngineColor::Text), bg);
62    for (r, labels) in grid.rows.iter().enumerate() {
63        let y = rect.y + 4 + i32::try_from(r * 2).unwrap_or(0);
64        // Each cell is its label with a space either side.
65        let widths: Vec<i32> = labels.iter().map(|l| buf.text_cells(l) + 2).collect();
66        let total: i32 = widths.iter().sum();
67        let mut x = rect.x + (rect.w - total) / 2;
68        for (col, (label, w)) in labels.iter().zip(&widths).enumerate() {
69            let (fg, cell_bg) = if grid.focus == (r, col) {
70                (bg, c(EngineColor::Focus))
71            } else {
72                (c(EngineColor::Text), bg)
73            };
74            buf.print(x, y, &format!(" {label} "), fg, cell_bg);
75            x += w;
76        }
77    }
78}