Skip to main content

blizz_ui/
box_chrome.rs

1//! Shared box-drawing primitives for bordered UI components (text entry, selector).
2
3pub const CORNER_TOP_LEFT: &str = "\u{256d}";
4pub const CORNER_TOP_RIGHT: &str = "\u{256e}";
5pub const CORNER_BOTTOM_LEFT: &str = "\u{2570}";
6pub const CORNER_BOTTOM_RIGHT: &str = "\u{256f}";
7pub const VERTICAL: &str = "\u{2502}";
8pub const HORIZONTAL: &str = "\u{2500}";
9
10/// Total columns consumed by border chrome: "│ " + " │" = 4.
11pub const CHROME_WIDTH: u16 = 4;
12/// Columns consumed by the left border before content starts ("│ " = 2).
13pub const CONTENT_OFFSET: u16 = 2;
14/// Space padding inside the vertical bars (one space each side).
15pub const INNER_PADDING: usize = 2;
16/// Number of border rows in a box (top border + bottom border).
17pub const BORDER_ROWS: u16 = 2;
18
19/// Snap a width to the nearest even number, using `min_width` when rounding
20/// would collapse a nonzero value to zero. Used during partial-open animation
21/// so the box stays symmetric as it grows.
22pub fn snap_even(raw: u16, min_width: u16) -> u16 {
23  let even = (raw / 2) * 2;
24  if even == 0 && raw > 0 {
25    min_width
26  } else {
27    even
28  }
29}
30
31/// Plain top border: `╭──────╮`
32pub fn top_border(inner_width: u16) -> String {
33  let fill: String = HORIZONTAL.repeat(inner_width as usize + INNER_PADDING);
34  format!("{CORNER_TOP_LEFT}{fill}{CORNER_TOP_RIGHT}")
35}
36
37/// Plain bottom border: `╰──────╯`
38pub fn bottom_border(inner_width: u16) -> String {
39  let fill: String = HORIZONTAL.repeat(inner_width as usize + INNER_PADDING);
40  format!("{CORNER_BOTTOM_LEFT}{fill}{CORNER_BOTTOM_RIGHT}")
41}
42
43#[cfg(test)]
44mod tests {
45  use super::*;
46
47  #[test]
48  fn top_border_has_correct_corners() {
49    let border = top_border(10);
50    assert!(border.starts_with(CORNER_TOP_LEFT));
51    assert!(border.ends_with(CORNER_TOP_RIGHT));
52  }
53
54  #[test]
55  fn bottom_border_has_correct_corners() {
56    let border = bottom_border(10);
57    assert!(border.starts_with(CORNER_BOTTOM_LEFT));
58    assert!(border.ends_with(CORNER_BOTTOM_RIGHT));
59  }
60
61  #[test]
62  fn border_fill_width_includes_inner_padding() {
63    let border = top_border(5);
64    let fill_chars = border.chars().count() - 2; // minus two corner chars
65    assert_eq!(fill_chars, 5 + INNER_PADDING);
66  }
67
68  #[test]
69  fn snap_even_rounds_down_to_even() {
70    assert_eq!(snap_even(5, 2), 4);
71    assert_eq!(snap_even(7, 2), 6);
72    assert_eq!(snap_even(4, 2), 4);
73  }
74
75  #[test]
76  fn snap_even_uses_min_when_rounding_collapses_to_zero() {
77    assert_eq!(snap_even(1, 2), 2);
78    assert_eq!(snap_even(0, 2), 0);
79  }
80}