cbtop/bricks/panels/
cpu.rs1use crate::brick::{Brick, BrickAssertion, BrickBudget, BrickVerification};
4use presentar_core::{Canvas, Point, Rect, TextStyle, Widget};
5use presentar_terminal::{BrailleGraph, GraphMode, Meter, Theme};
6use std::any::Any;
7
8pub struct CpuPanelBrick {
9 pub cpu_data: Vec<f64>,
10 pub intensity: f64,
11 pub theme: Theme,
12}
13
14impl CpuPanelBrick {
15 pub fn new() -> Self {
16 Self {
17 cpu_data: Vec::new(),
18 intensity: 0.0,
19 theme: Theme::tokyo_night(),
20 }
21 }
22
23 pub fn paint(&self, canvas: &mut dyn Canvas, width: f32, _height: f32) {
24 let label_style = TextStyle {
25 color: self.theme.foreground,
26 ..Default::default()
27 };
28 let dim_style = TextStyle {
29 color: self.theme.dim,
30 ..Default::default()
31 };
32
33 canvas.draw_text("CPU Monitor", Point::new(2.0, 2.0), &label_style);
34
35 if !self.cpu_data.is_empty() {
37 let cpu_usage = self.cpu_data.last().copied().unwrap_or(0.0);
38 let mut graph = BrailleGraph::new(self.cpu_data.clone())
39 .with_color(self.theme.cpu_color(cpu_usage))
40 .with_range(0.0, 100.0)
41 .with_mode(GraphMode::Braille);
42 graph.layout(Rect::new(2.0, 3.0, width - 4.0, 8.0));
43 graph.paint(canvas);
44 }
45
46 canvas.draw_text(
48 "Per-Core Usage (simulated)",
49 Point::new(2.0, 12.0),
50 &label_style,
51 );
52 for i in 0..8 {
53 let y = 13.0 + i as f32;
54 let usage = 30.0 + (i as f64 * 7.0) + (self.intensity * 50.0);
55 let usage = usage.min(100.0);
56
57 canvas.draw_text(&format!("Core {}: ", i), Point::new(2.0, y), &dim_style);
58
59 let mut meter = Meter::new(usage, 100.0).with_color(self.theme.cpu_color(usage));
61 meter.layout(Rect::new(12.0, y, 20.0, 1.0));
62 meter.paint(canvas);
63
64 let pct_style = TextStyle {
66 color: self.theme.cpu_color(usage),
67 ..Default::default()
68 };
69 canvas.draw_text(&format!("{:5.1}%", usage), Point::new(34.0, y), &pct_style);
70 }
71 }
72}
73
74impl Brick for CpuPanelBrick {
75 fn brick_name(&self) -> &'static str {
76 "cpu_panel"
77 }
78
79 fn assertions(&self) -> Vec<BrickAssertion> {
80 vec![
81 BrickAssertion::MinWidth(40),
82 BrickAssertion::MinHeight(15),
83 BrickAssertion::max_latency_ms(8),
84 ]
85 }
86
87 fn budget(&self) -> BrickBudget {
88 BrickBudget::FRAME_60FPS
89 }
90
91 fn verify(&self) -> BrickVerification {
92 let mut v = BrickVerification::new();
93 for assertion in self.assertions() {
94 v.check(&assertion);
95 }
96 v
97 }
98
99 fn as_any(&self) -> &dyn Any {
100 self
101 }
102}
103
104impl Default for CpuPanelBrick {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use presentar_core::RecordingCanvas;
114
115 #[test]
116 fn test_cpu_panel_brick_name() {
117 let panel = CpuPanelBrick::new();
118 assert_eq!(panel.brick_name(), "cpu_panel");
119 }
120
121 #[test]
122 fn test_cpu_panel_has_assertions() {
123 let panel = CpuPanelBrick::new();
124 assert!(!panel.assertions().is_empty());
125 }
126
127 #[test]
128 fn test_cpu_panel_paint_empty() {
129 let panel = CpuPanelBrick::new();
130 let mut canvas = RecordingCanvas::new();
131
132 panel.paint(&mut canvas, 80.0, 24.0);
133
134 assert!(!canvas.is_empty());
136 assert!(canvas.command_count() >= 10);
138 }
139
140 #[test]
141 fn test_cpu_panel_paint_with_data() {
142 let mut panel = CpuPanelBrick::new();
143 panel.cpu_data = vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0];
144
145 let mut canvas = RecordingCanvas::new();
146 panel.paint(&mut canvas, 80.0, 24.0);
147
148 assert!(canvas.command_count() >= 15);
150 }
151
152 #[test]
153 fn test_cpu_panel_paint_with_intensity() {
154 let mut panel = CpuPanelBrick::new();
155 panel.intensity = 0.8; let mut canvas = RecordingCanvas::new();
158 panel.paint(&mut canvas, 80.0, 24.0);
159
160 assert!(!canvas.is_empty());
162 }
163
164 #[test]
165 fn test_cpu_panel_paint_high_usage() {
166 let mut panel = CpuPanelBrick::new();
167 panel.cpu_data = vec![90.0, 95.0, 92.0, 88.0, 91.0];
169 panel.intensity = 1.0; let mut canvas = RecordingCanvas::new();
172 panel.paint(&mut canvas, 80.0, 24.0);
173
174 assert!(canvas.command_count() >= 10);
176 }
177
178 #[test]
179 fn test_cpu_panel_default() {
180 let panel = CpuPanelBrick::default();
181 assert!(panel.cpu_data.is_empty());
182 assert_eq!(panel.intensity, 0.0);
183 }
184
185 #[test]
186 fn test_cpu_panel_verify() {
187 let panel = CpuPanelBrick::new();
188 let verification = panel.verify();
189 assert!(verification.is_valid());
190 }
191
192 #[test]
193 fn test_cpu_panel_budget() {
194 let panel = CpuPanelBrick::new();
195 let budget = panel.budget();
196 assert_eq!(budget.total_ms(), 16); }
198}