Skip to main content

cbtop/bricks/panels/load/
render.rs

1//! Rendering logic for the load control panel.
2
3use crate::brick::{BrickGrade, BrickScore};
4use presentar_core::{Canvas, Color, Point, Rect, TextStyle};
5use presentar_terminal::{Meter, Widget};
6
7use super::LoadControlPanelBrick;
8
9impl LoadControlPanelBrick {
10    /// Render a score bar component
11    pub(super) fn render_score_bar(value: u8, max: u8, width: usize) -> String {
12        BrickScore::render_bar(value, max, width)
13    }
14
15    /// Format problem size for display
16    pub(super) fn format_size(size: usize) -> String {
17        if size >= 1024 {
18            format!("{}K", size / 1024)
19        } else {
20            format!("{}", size)
21        }
22    }
23
24    /// Paint the load control panel
25    pub fn paint(&self, canvas: &mut dyn Canvas, width: f32, _height: f32) {
26        let label_style = TextStyle {
27            color: self.theme.foreground,
28            ..Default::default()
29        };
30        let dim_style = TextStyle {
31            color: self.theme.dim,
32            ..Default::default()
33        };
34        let selected_style = TextStyle {
35            color: Color::new(0.3, 0.8, 1.0, 1.0), // Cyan for selected
36            ..Default::default()
37        };
38        let running_style = TextStyle {
39            color: Color::new(0.3, 1.0, 0.5, 1.0), // Green for running
40            ..Default::default()
41        };
42        let stopped_style = TextStyle {
43            color: Color::new(1.0, 0.5, 0.3, 1.0), // Orange for stopped
44            ..Default::default()
45        };
46        let error_style = TextStyle {
47            color: Color::new(1.0, 0.2, 0.2, 1.0), // Red for errors
48            ..Default::default()
49        };
50
51        canvas.draw_text("Load Control", Point::new(2.0, 2.0), &label_style);
52
53        // Status indicator
54        let status_text = if self.is_running {
55            "RUNNING"
56        } else {
57            "STOPPED"
58        };
59        let status_style = if self.is_running {
60            &running_style
61        } else {
62            &stopped_style
63        };
64        canvas.draw_text(status_text, Point::new(width - 12.0, 2.0), status_style);
65
66        // Backend selection
67        let backend_style = if self.selected_item == 0 {
68            &selected_style
69        } else {
70            &dim_style
71        };
72        canvas.draw_text("Backend:", Point::new(2.0, 4.0), backend_style);
73        canvas.draw_text(
74            &format!("< {} >", self.backend.name()),
75            Point::new(12.0, 4.0),
76            &label_style,
77        );
78
79        // Workload selection
80        let workload_style = if self.selected_item == 1 {
81            &selected_style
82        } else {
83            &dim_style
84        };
85        canvas.draw_text("Workload:", Point::new(2.0, 5.0), workload_style);
86        canvas.draw_text(
87            &format!("< {} >", self.workload.short_name()),
88            Point::new(12.0, 5.0),
89            &label_style,
90        );
91
92        // Intensity slider
93        let intensity_style = if self.selected_item == 2 {
94            &selected_style
95        } else {
96            &dim_style
97        };
98        canvas.draw_text("Intensity:", Point::new(2.0, 6.0), intensity_style);
99        let intensity_color = self.theme.cpu_color(self.intensity);
100        let mut intensity_meter = Meter::new(self.intensity, 100.0).with_color(intensity_color);
101        intensity_meter.layout(Rect::new(12.0, 6.0, width - 30.0, 1.0));
102        intensity_meter.paint(canvas);
103        canvas.draw_text(
104            &format!("{:.0}%", self.intensity),
105            Point::new(width - 16.0, 6.0),
106            &label_style,
107        );
108
109        // Problem size
110        let size_style = if self.selected_item == 3 {
111            &selected_style
112        } else {
113            &dim_style
114        };
115        canvas.draw_text("Size:", Point::new(2.0, 7.0), size_style);
116        canvas.draw_text(
117            &format!("< {} >", Self::format_size(self.problem_size)),
118            Point::new(12.0, 7.0),
119            &label_style,
120        );
121
122        // Start/Stop button indicator
123        let button_style = if self.selected_item == 4 {
124            &selected_style
125        } else {
126            &dim_style
127        };
128        let button_text = if self.is_running { "[STOP]" } else { "[START]" };
129        canvas.draw_text(button_text, Point::new(2.0, 9.0), button_style);
130
131        // Error display
132        if let Some(ref err) = self.error {
133            canvas.draw_text("Error:", Point::new(2.0, 11.0), &error_style);
134            canvas.draw_text(err, Point::new(9.0, 11.0), &error_style);
135        }
136
137        // ComputeBrick Score section
138        self.paint_score_or_stats(canvas, width, &label_style, &dim_style);
139
140        // Help text
141        canvas.draw_text(
142            "Use arrow keys to navigate, Enter to toggle",
143            Point::new(2.0, 20.0),
144            &dim_style,
145        );
146    }
147
148    /// Paint score section or fallback statistics
149    fn paint_score_or_stats(
150        &self,
151        canvas: &mut dyn Canvas,
152        width: f32,
153        label_style: &TextStyle,
154        dim_style: &TextStyle,
155    ) {
156        if let Some(ref score) = self.brick_score {
157            let grade = score.grade();
158            let grade_color = match grade {
159                BrickGrade::A | BrickGrade::B => Color::new(0.3, 1.0, 0.5, 1.0), // Green
160                BrickGrade::C => Color::new(1.0, 0.8, 0.3, 1.0),                 // Yellow
161                BrickGrade::D | BrickGrade::F => Color::new(1.0, 0.3, 0.3, 1.0), // Red
162            };
163            let grade_style = TextStyle {
164                color: grade_color,
165                ..Default::default()
166            };
167
168            canvas.draw_text("ComputeBrick Score", Point::new(2.0, 13.0), label_style);
169            canvas.draw_text(
170                &format!("{}/100 ({})", score.total(), grade.letter()),
171                Point::new(22.0, 13.0),
172                &grade_style,
173            );
174            canvas.draw_text(
175                &format!("{:.2} GFLOP/s", self.gflops),
176                Point::new(width - 18.0, 13.0),
177                label_style,
178            );
179
180            // Score breakdown bars
181            let bar_width = 20;
182            canvas.draw_text("Performance:", Point::new(2.0, 15.0), dim_style);
183            canvas.draw_text(
184                &Self::render_score_bar(score.performance, 40, bar_width),
185                Point::new(14.0, 15.0),
186                label_style,
187            );
188            canvas.draw_text(
189                &format!("{}/40", score.performance),
190                Point::new(36.0, 15.0),
191                dim_style,
192            );
193
194            canvas.draw_text("Efficiency:", Point::new(2.0, 16.0), dim_style);
195            canvas.draw_text(
196                &Self::render_score_bar(score.efficiency, 25, bar_width),
197                Point::new(14.0, 16.0),
198                label_style,
199            );
200            canvas.draw_text(
201                &format!("{}/25", score.efficiency),
202                Point::new(36.0, 16.0),
203                dim_style,
204            );
205
206            canvas.draw_text("Correctness:", Point::new(2.0, 17.0), dim_style);
207            canvas.draw_text(
208                &Self::render_score_bar(score.correctness, 20, bar_width),
209                Point::new(14.0, 17.0),
210                label_style,
211            );
212            canvas.draw_text(
213                &format!("{}/20", score.correctness),
214                Point::new(36.0, 17.0),
215                dim_style,
216            );
217
218            canvas.draw_text("Stability:", Point::new(2.0, 18.0), dim_style);
219            canvas.draw_text(
220                &Self::render_score_bar(score.stability, 15, bar_width),
221                Point::new(14.0, 18.0),
222                label_style,
223            );
224            canvas.draw_text(
225                &format!("{}/15", score.stability),
226                Point::new(36.0, 18.0),
227                dim_style,
228            );
229        } else {
230            // Statistics section (fallback when no score available)
231            canvas.draw_text("Statistics", Point::new(2.0, 13.0), label_style);
232
233            canvas.draw_text("Iterations:", Point::new(2.0, 14.0), dim_style);
234            canvas.draw_text(
235                &format!("{}", self.stats.iterations),
236                Point::new(14.0, 14.0),
237                label_style,
238            );
239
240            canvas.draw_text("Ops/sec:", Point::new(2.0, 15.0), dim_style);
241            canvas.draw_text(
242                &format!("{:.1}", self.stats.ops_per_sec),
243                Point::new(14.0, 15.0),
244                label_style,
245            );
246
247            canvas.draw_text("Throughput:", Point::new(2.0, 16.0), dim_style);
248            canvas.draw_text(
249                &format!("{:.2} GB/s", self.stats.throughput_gbs),
250                Point::new(14.0, 16.0),
251                label_style,
252            );
253
254            canvas.draw_text("Avg Latency:", Point::new(2.0, 17.0), dim_style);
255            canvas.draw_text(
256                &format!("{:.1} us", self.stats.avg_latency_us),
257                Point::new(14.0, 17.0),
258                label_style,
259            );
260
261            canvas.draw_text("P99 Latency:", Point::new(2.0, 18.0), dim_style);
262            canvas.draw_text(
263                &format!("{:.1} us", self.stats.p99_latency_us),
264                Point::new(14.0, 18.0),
265                label_style,
266            );
267        }
268    }
269}