Skip to main content

cbtop/bricks/panels/
help.rs

1//! Help panel brick (Layer 3)
2
3use crate::brick::{Brick, BrickAssertion, BrickBudget, BrickVerification};
4use presentar_core::{Canvas, Point, TextStyle};
5use presentar_terminal::Theme;
6use std::any::Any;
7
8pub struct HelpPanelBrick {
9    pub theme: Theme,
10}
11
12impl HelpPanelBrick {
13    pub fn new() -> Self {
14        Self {
15            theme: Theme::tokyo_night(),
16        }
17    }
18
19    pub fn paint(&self, canvas: &mut dyn Canvas, _width: f32, _height: f32) {
20        let title_style = TextStyle {
21            color: self.theme.foreground,
22            ..Default::default()
23        };
24        let key_style = TextStyle {
25            color: self.theme.cpu.sample(0.3), // Use gradient for accent color
26            ..Default::default()
27        };
28        let desc_style = TextStyle {
29            color: self.theme.dim,
30            ..Default::default()
31        };
32
33        canvas.draw_text(
34            "Help - Keyboard Controls",
35            Point::new(2.0, 2.0),
36            &title_style,
37        );
38
39        let controls = [
40            ("1-9", "Switch panels"),
41            ("Space", "Start/Stop load generation"),
42            ("+/-", "Increase/Decrease intensity"),
43            ("b", "Cycle backend (SIMD/wgpu/CUDA/All)"),
44            ("w", "Cycle workload type"),
45            ("[/]", "Decrease/Increase problem size"),
46            ("r", "Reset statistics"),
47            ("q/Esc", "Quit"),
48        ];
49
50        for (i, (key, desc)) in controls.iter().enumerate() {
51            let y = 4.0 + i as f32;
52            canvas.draw_text(&format!("{:>8}", key), Point::new(4.0, y), &key_style);
53            canvas.draw_text(&format!("  {}", desc), Point::new(12.0, y), &desc_style);
54        }
55    }
56}
57
58impl Brick for HelpPanelBrick {
59    fn brick_name(&self) -> &'static str {
60        "help_panel"
61    }
62
63    fn assertions(&self) -> Vec<BrickAssertion> {
64        vec![BrickAssertion::MinWidth(30), BrickAssertion::MinHeight(10)]
65    }
66
67    fn budget(&self) -> BrickBudget {
68        BrickBudget::FRAME_60FPS
69    }
70
71    fn verify(&self) -> BrickVerification {
72        let mut v = BrickVerification::new();
73        for assertion in self.assertions() {
74            v.check(&assertion);
75        }
76        v
77    }
78
79    fn as_any(&self) -> &dyn Any {
80        self
81    }
82}
83
84impl Default for HelpPanelBrick {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use presentar_core::RecordingCanvas;
94
95    #[test]
96    fn test_help_panel_brick_name() {
97        let panel = HelpPanelBrick::new();
98        assert_eq!(panel.brick_name(), "help_panel");
99    }
100
101    #[test]
102    fn test_help_panel_has_assertions() {
103        let panel = HelpPanelBrick::new();
104        assert!(!panel.assertions().is_empty());
105    }
106
107    #[test]
108    fn test_help_panel_paint() {
109        let panel = HelpPanelBrick::new();
110        let mut canvas = RecordingCanvas::new();
111
112        panel.paint(&mut canvas, 80.0, 24.0);
113
114        // Should draw title and all keyboard controls
115        // 1 title + 8 controls (each has key + description)
116        assert!(!canvas.is_empty());
117        assert!(canvas.command_count() >= 10);
118    }
119
120    #[test]
121    fn test_help_panel_default() {
122        let panel = HelpPanelBrick::default();
123        assert_eq!(panel.brick_name(), "help_panel");
124    }
125
126    #[test]
127    fn test_help_panel_verify() {
128        let panel = HelpPanelBrick::new();
129        let verification = panel.verify();
130        assert!(verification.is_valid());
131    }
132
133    #[test]
134    fn test_help_panel_budget() {
135        let panel = HelpPanelBrick::new();
136        let budget = panel.budget();
137        assert_eq!(budget.total_ms(), 16); // FRAME_60FPS
138    }
139}