Skip to main content

cbtop/brick/
widget.rs

1//! Widget trait and layout primitives for TUI rendering.
2
3/// Widget trait - measure/layout/paint cycle
4pub trait Widget {
5    /// Measure desired size given constraints
6    fn measure(&self, constraints: &Constraints) -> Size;
7
8    /// Layout with allocated size
9    fn layout(&mut self, size: Size);
10
11    /// Paint to canvas
12    fn paint(&self, canvas: &mut dyn Canvas);
13}
14
15/// Size in terminal cells
16#[derive(Debug, Clone, Copy, Default, PartialEq)]
17pub struct Size {
18    pub width: f32,
19    pub height: f32,
20}
21
22impl Size {
23    pub const fn new(width: f32, height: f32) -> Self {
24        Self { width, height }
25    }
26
27    pub const ZERO: Self = Self {
28        width: 0.0,
29        height: 0.0,
30    };
31}
32
33/// Point in terminal cells
34#[derive(Debug, Clone, Copy, Default, PartialEq)]
35pub struct Point {
36    pub x: f32,
37    pub y: f32,
38}
39
40impl Point {
41    pub const fn new(x: f32, y: f32) -> Self {
42        Self { x, y }
43    }
44
45    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
46}
47
48/// Rectangle in terminal cells
49#[derive(Debug, Clone, Copy, Default, PartialEq)]
50pub struct Rect {
51    pub x: f32,
52    pub y: f32,
53    pub width: f32,
54    pub height: f32,
55}
56
57impl Rect {
58    pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
59        Self {
60            x,
61            y,
62            width,
63            height,
64        }
65    }
66
67    pub fn from_size(size: Size) -> Self {
68        Self {
69            x: 0.0,
70            y: 0.0,
71            width: size.width,
72            height: size.height,
73        }
74    }
75
76    pub fn size(&self) -> Size {
77        Size::new(self.width, self.height)
78    }
79
80    pub fn top_left(&self) -> Point {
81        Point::new(self.x, self.y)
82    }
83}
84
85/// Layout constraints
86#[derive(Debug, Clone, Copy)]
87pub struct Constraints {
88    pub min_width: f32,
89    pub max_width: f32,
90    pub min_height: f32,
91    pub max_height: f32,
92}
93
94impl Constraints {
95    pub const fn new(min_width: f32, max_width: f32, min_height: f32, max_height: f32) -> Self {
96        Self {
97            min_width,
98            max_width,
99            min_height,
100            max_height,
101        }
102    }
103
104    pub fn tight(size: Size) -> Self {
105        Self {
106            min_width: size.width,
107            max_width: size.width,
108            min_height: size.height,
109            max_height: size.height,
110        }
111    }
112
113    pub fn loose(size: Size) -> Self {
114        Self {
115            min_width: 0.0,
116            max_width: size.width,
117            min_height: 0.0,
118            max_height: size.height,
119        }
120    }
121
122    pub fn constrain(&self, size: Size) -> Size {
123        Size {
124            width: size.width.clamp(self.min_width, self.max_width),
125            height: size.height.clamp(self.min_height, self.max_height),
126        }
127    }
128}
129
130impl Default for Constraints {
131    fn default() -> Self {
132        Self {
133            min_width: 0.0,
134            max_width: f32::INFINITY,
135            min_height: 0.0,
136            max_height: f32::INFINITY,
137        }
138    }
139}
140
141/// Color representation (24-bit RGB)
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
143pub struct Color {
144    pub r: u8,
145    pub g: u8,
146    pub b: u8,
147}
148
149impl Color {
150    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
151        Self { r, g, b }
152    }
153
154    // Standard colors
155    pub const BLACK: Self = Self::rgb(0, 0, 0);
156    pub const WHITE: Self = Self::rgb(255, 255, 255);
157    pub const RED: Self = Self::rgb(255, 0, 0);
158    pub const GREEN: Self = Self::rgb(0, 255, 0);
159    pub const BLUE: Self = Self::rgb(0, 0, 255);
160    pub const YELLOW: Self = Self::rgb(255, 255, 0);
161    pub const CYAN: Self = Self::rgb(0, 255, 255);
162    pub const MAGENTA: Self = Self::rgb(255, 0, 255);
163    pub const GRAY: Self = Self::rgb(128, 128, 128);
164    pub const DARK_GRAY: Self = Self::rgb(64, 64, 64);
165    pub const LIGHT_GRAY: Self = Self::rgb(192, 192, 192);
166
167    // Andon colors (Toyota Way visual management)
168    pub const ANDON_GREEN: Self = Self::rgb(0, 200, 0);
169    pub const ANDON_YELLOW: Self = Self::rgb(255, 200, 0);
170    pub const ANDON_RED: Self = Self::rgb(255, 50, 50);
171}
172
173/// Text style for rendering
174#[derive(Debug, Clone, Copy, Default)]
175pub struct TextStyle {
176    pub color: Color,
177    pub background: Option<Color>,
178    pub bold: bool,
179    pub italic: bool,
180    pub underline: bool,
181}
182
183impl TextStyle {
184    pub const fn new() -> Self {
185        Self {
186            color: Color::WHITE,
187            background: None,
188            bold: false,
189            italic: false,
190            underline: false,
191        }
192    }
193
194    pub const fn color(mut self, color: Color) -> Self {
195        self.color = color;
196        self
197    }
198
199    pub const fn background(mut self, color: Color) -> Self {
200        self.background = Some(color);
201        self
202    }
203
204    pub const fn bold(mut self) -> Self {
205        self.bold = true;
206        self
207    }
208}
209
210/// Canvas trait for rendering (presentar-style)
211pub trait Canvas {
212    /// Fill rectangle with solid color
213    fn fill_rect(&mut self, rect: Rect, color: Color);
214
215    /// Stroke rectangle outline
216    fn stroke_rect(&mut self, rect: Rect, color: Color, width: f32);
217
218    /// Draw text at position
219    fn draw_text(&mut self, text: &str, pos: Point, style: &TextStyle);
220
221    /// Draw line between points
222    fn draw_line(&mut self, from: Point, to: Point, color: Color, width: f32);
223
224    /// Fill circle
225    fn fill_circle(&mut self, center: Point, radius: f32, color: Color);
226
227    /// Stroke circle outline
228    fn stroke_circle(&mut self, center: Point, radius: f32, color: Color, width: f32);
229
230    /// Draw path (connected line segments)
231    fn draw_path(&mut self, points: &[Point], color: Color, width: f32);
232
233    /// Get canvas size
234    fn size(&self) -> Size;
235
236    /// Clear canvas with color
237    fn clear(&mut self, color: Color);
238}