Skip to main content

par_term/pane/types/
bounds.rs

1//! `PaneBounds` — pixel-space bounding box for a pane.
2
3/// Bounds of a pane in pixels
4#[derive(Debug, Clone, Copy, Default)]
5pub struct PaneBounds {
6    /// X position in pixels from left edge of content area
7    pub x: f32,
8    /// Y position in pixels from top of content area (below tab bar)
9    pub y: f32,
10    /// Width in pixels
11    pub width: f32,
12    /// Height in pixels
13    pub height: f32,
14}
15
16impl PaneBounds {
17    /// Create new bounds
18    pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
19        Self {
20            x,
21            y,
22            width,
23            height,
24        }
25    }
26
27    /// Check if a point is inside these bounds
28    pub fn contains(&self, px: f32, py: f32) -> bool {
29        px >= self.x && px < self.x + self.width && py >= self.y && py < self.y + self.height
30    }
31
32    /// Get the center point of the bounds
33    pub fn center(&self) -> (f32, f32) {
34        (self.x + self.width / 2.0, self.y + self.height / 2.0)
35    }
36
37    /// Calculate grid dimensions (cols, rows) given cell dimensions
38    pub fn grid_size(&self, cell_width: f32, cell_height: f32) -> (usize, usize) {
39        let cols = (self.width / cell_width).floor() as usize;
40        let rows = (self.height / cell_height).floor() as usize;
41        (cols.max(1), rows.max(1))
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    // ── PaneBounds::contains ──────────────────────────────────────────────
50
51    #[test]
52    fn test_contains_interior_point() {
53        let b = PaneBounds::new(10.0, 20.0, 100.0, 80.0);
54        assert!(b.contains(50.0, 50.0));
55    }
56
57    #[test]
58    fn test_contains_top_left_corner() {
59        let b = PaneBounds::new(10.0, 20.0, 100.0, 80.0);
60        // Inclusive lower bound
61        assert!(b.contains(10.0, 20.0));
62    }
63
64    #[test]
65    fn test_contains_exclusive_right_edge() {
66        let b = PaneBounds::new(0.0, 0.0, 100.0, 100.0);
67        // Right/bottom edge is exclusive
68        assert!(!b.contains(100.0, 50.0));
69        assert!(!b.contains(50.0, 100.0));
70    }
71
72    #[test]
73    fn test_contains_outside_left() {
74        let b = PaneBounds::new(10.0, 10.0, 50.0, 50.0);
75        assert!(!b.contains(9.9, 30.0));
76    }
77
78    #[test]
79    fn test_contains_outside_above() {
80        let b = PaneBounds::new(10.0, 10.0, 50.0, 50.0);
81        assert!(!b.contains(30.0, 9.9));
82    }
83
84    // ── PaneBounds::center ────────────────────────────────────────────────
85
86    #[test]
87    fn test_center_unit_box() {
88        let b = PaneBounds::new(0.0, 0.0, 2.0, 2.0);
89        assert_eq!(b.center(), (1.0, 1.0));
90    }
91
92    #[test]
93    fn test_center_offset_box() {
94        let b = PaneBounds::new(10.0, 20.0, 40.0, 60.0);
95        assert_eq!(b.center(), (30.0, 50.0));
96    }
97
98    // ── PaneBounds::grid_size ─────────────────────────────────────────────
99
100    #[test]
101    fn test_grid_size_exact_division() {
102        // 800 x 600 pixels at 8x16 cell size → 100 cols × 37 rows
103        let b = PaneBounds::new(0.0, 0.0, 800.0, 600.0);
104        let (cols, rows) = b.grid_size(8.0, 16.0);
105        assert_eq!(cols, 100);
106        assert_eq!(rows, 37);
107    }
108
109    #[test]
110    fn test_grid_size_fractional_truncated() {
111        // 85 / 8 = 10.625 → truncated to 10
112        let b = PaneBounds::new(0.0, 0.0, 85.0, 100.0);
113        let (cols, _) = b.grid_size(8.0, 16.0);
114        assert_eq!(cols, 10);
115    }
116
117    #[test]
118    fn test_grid_size_minimum_one() {
119        // Very small pane — must return at least 1×1
120        let b = PaneBounds::new(0.0, 0.0, 1.0, 1.0);
121        let (cols, rows) = b.grid_size(8.0, 16.0);
122        assert_eq!(cols, 1);
123        assert_eq!(rows, 1);
124    }
125
126    #[test]
127    fn test_grid_size_zero_dimensions_minimum_one() {
128        let b = PaneBounds::new(0.0, 0.0, 0.0, 0.0);
129        let (cols, rows) = b.grid_size(8.0, 16.0);
130        assert_eq!(cols, 1);
131        assert_eq!(rows, 1);
132    }
133
134    // ── PaneBounds::default ───────────────────────────────────────────────
135
136    #[test]
137    fn test_default_is_zero() {
138        let b = PaneBounds::default();
139        assert_eq!(b.x, 0.0);
140        assert_eq!(b.y, 0.0);
141        assert_eq!(b.width, 0.0);
142        assert_eq!(b.height, 0.0);
143    }
144}