@@ -207,11 +207,16 @@ impl TerminalBounds {
}
pub fn num_lines(&self) -> usize {
- (self.bounds.size.height / self.line_height).floor() as usize
+ // Tolerance to prevent f32 precision from losing a row:
+ // `N * line_height / line_height` can be N-epsilon, which floor()
+ // would round down, pushing the first line into invisible scrollback.
+ let raw = self.bounds.size.height / self.line_height;
+ raw.next_up().floor() as usize
}
pub fn num_columns(&self) -> usize {
- (self.bounds.size.width / self.cell_width).floor() as usize
+ let raw = self.bounds.size.width / self.cell_width;
+ raw.next_up().floor() as usize
}
pub fn height(&self) -> Pixels {
@@ -3364,5 +3369,59 @@ mod tests {
scroll_by(-1);
}
}
+
+ #[test]
+ fn test_num_lines_float_precision() {
+ let line_heights = [
+ 20.1f32, 16.7, 18.3, 22.9, 14.1, 15.6, 17.8, 19.4, 21.3, 23.7,
+ ];
+ for &line_height in &line_heights {
+ for n in 1..=100 {
+ let height = n as f32 * line_height;
+ let bounds = TerminalBounds::new(
+ px(line_height),
+ px(8.0),
+ Bounds {
+ origin: Point::default(),
+ size: Size {
+ width: px(800.0),
+ height: px(height),
+ },
+ },
+ );
+ assert_eq!(
+ bounds.num_lines(),
+ n,
+ "num_lines() should be {n} for height={height}, line_height={line_height}"
+ );
+ }
+ }
+ }
+
+ #[test]
+ fn test_num_columns_float_precision() {
+ let cell_widths = [8.1f32, 7.3, 9.7, 6.9, 10.1];
+ for &cell_width in &cell_widths {
+ for n in 1..=200 {
+ let width = n as f32 * cell_width;
+ let bounds = TerminalBounds::new(
+ px(20.0),
+ px(cell_width),
+ Bounds {
+ origin: Point::default(),
+ size: Size {
+ width: px(width),
+ height: px(400.0),
+ },
+ },
+ );
+ assert_eq!(
+ bounds.num_columns(),
+ n,
+ "num_columns() should be {n} for width={width}, cell_width={cell_width}"
+ );
+ }
+ }
+ }
}
}