blizz_ui/components/
hint.rs1use std::io::Write;
2
3use crossterm::{
4 queue,
5 style::{Color, Print, ResetColor, SetForegroundColor},
6};
7
8use crate::{Component, Renderer};
9
10const ANSI_GRAYSCALE_START: u8 = 232;
11const GRAYSCALE_RANGE: f64 = 15.0;
12
13pub struct HintComponent {
14 pub text: &'static str,
15 pub fade: f64,
16}
17
18impl HintComponent {
19 pub fn new(text: &'static str) -> Self {
20 Self { text, fade: 0.0 }
21 }
22}
23
24#[cfg(not(tarpaulin_include))]
25impl Component for HintComponent {
26 fn render<W: Write>(&self, renderer: &mut Renderer<W>) -> std::io::Result<u16> {
27 if self.fade <= 0.0 {
28 return Ok(0);
29 }
30 renderer.with_panel(|writer, _panel, _rng| {
31 let color = fade_color(self.fade);
32 queue!(
33 writer,
34 SetForegroundColor(color),
35 Print(self.text),
36 ResetColor
37 )?;
38 Ok(1)
39 })
40 }
41}
42
43fn fade_color(fade: f64) -> Color {
44 if fade >= 1.0 {
45 return Color::DarkGrey;
46 }
47 Color::AnsiValue(ANSI_GRAYSCALE_START + (fade * GRAYSCALE_RANGE).round() as u8)
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use crate::LayoutPanel;
54 use crate::test_helpers::test_renderer;
55
56 #[test]
57 fn new_starts_invisible() {
58 let hint = HintComponent::new("(hit enter)");
59 assert_eq!(hint.fade, 0.0);
60 assert_eq!(hint.text, "(hit enter)");
61 }
62
63 #[test]
64 fn fade_color_returns_dark_grey_at_full() {
65 assert_eq!(fade_color(1.0), Color::DarkGrey);
66 assert_eq!(fade_color(1.5), Color::DarkGrey);
67 }
68
69 #[test]
70 fn fade_color_returns_ansi_gradient_below_one() {
71 let color = fade_color(0.5);
72 match color {
73 Color::AnsiValue(v) => {
74 assert!(v >= 232);
75 assert!(v <= 247);
76 }
77 _ => panic!("expected AnsiValue"),
78 }
79 }
80
81 #[test]
82 fn fade_color_at_zero_is_darkest() {
83 assert_eq!(fade_color(0.0), Color::AnsiValue(ANSI_GRAYSCALE_START));
84 }
85
86 #[test]
87 fn render_skips_when_invisible() {
88 let hint = HintComponent::new("test");
89 let panel = LayoutPanel::centered(80, 4, 10);
90 let mut renderer = test_renderer();
91 renderer.draw(&hint, panel).unwrap();
92 assert!(renderer.writer.is_empty());
93 }
94
95 #[test]
96 fn render_writes_when_visible() {
97 let mut hint = HintComponent::new("hello");
98 hint.fade = 1.0;
99 let panel = LayoutPanel::centered(80, 5, 10);
100 let mut renderer = test_renderer();
101 renderer.draw(&hint, panel).unwrap();
102 assert!(!renderer.writer.is_empty());
103 let output = String::from_utf8(renderer.writer).unwrap();
104 assert!(output.contains("hello"));
105 }
106}