diff --git a/README.md b/README.md index 8bab87a..07ce9c3 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ border-width = 2px tile-width = 18ppt # largest a thumbnail may be tile-height = 20ppt # defaults to the display's aspect max-columns = 4 +max-rows = 3 # default: however many the display fits font = monospace font-size = 13.3 @@ -175,8 +176,13 @@ the width and the height follows the display's aspect, which is roughly the shap of the windows on it — a 16:9 cell wastes about half its area on a portrait monitor. -When there are more rows than the display can show, **the grid scrolls**: the -tile size you asked for is honoured and a scrollbar appears in the right margin. +Nothing sets the overlay's height directly: it is as many rows as fit in 90% of +the display, so below that threshold the window hugs the grid. `max-rows` caps it +if you would rather have a compact strip that scrolls sooner than a full-height +overlay — the symmetric partner to `max-columns`. + +When there are more rows than can be shown, **the grid scrolls**: the tile size +you asked for is honoured and a scrollbar appears in the right margin. Any move keeps the selection in view, `PgUp`/`PgDn` jump a screen, and tiles scrolled out of sight are unmapped — so live capture skips them too, which is what stops a long list costing bandwidth for pixels nobody sees. Only a tile too diff --git a/src/cli.rs b/src/cli.rs index 3596fd5..08df19b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -53,7 +53,9 @@ config: tile-width = 18ppt # how big a thumbnail is tile-height = 20ppt # defaults to the display's aspect - max-columns = 4 # rows beyond the screen scroll + max-columns = 4 + max-rows = 3 # default: as many as the display fits; + # rows beyond that scroll font = monospace # also --font font-size = 13.3 @@ -159,6 +161,7 @@ impl Args { tile_w: tile_w.max(1), tile_h: tile_h.max(1), max_cols: cfg.max_columns.unwrap_or(base.max_cols).max(1), + max_rows: cfg.max_rows.map(|r| r.max(1)).or(base.max_rows), labels: self.labels.or(cfg.labels).unwrap_or(base.labels), font: self .font diff --git a/src/config.rs b/src/config.rs index 05878b8..561a4da 100644 --- a/src/config.rs +++ b/src/config.rs @@ -88,6 +88,7 @@ pub struct Config { pub tile_width: Option, pub tile_height: Option, pub max_columns: Option, + pub max_rows: Option, pub font: Option, pub font_size: Option, pub labels: Option, @@ -140,9 +141,8 @@ impl Config { "border-width" => self.border_width = Some(Length::parse(value)?), "tile-width" => self.tile_width = Some(Length::parse(value)?), "tile-height" => self.tile_height = Some(Length::parse(value)?), - "max-columns" => { - self.max_columns = Some(number(value)?); - } + "max-columns" => self.max_columns = Some(number(value)?), + "max-rows" => self.max_rows = Some(number(value)?), "font" => self.font = Some(value.to_string()), "font-size" => self.font_size = Some(number(value)?), "labels" => self.labels = Some(boolean(value)?), @@ -230,6 +230,7 @@ border-width = 2px tile-width = 18ppt max-columns = 4 +max-rows = 3 live = current fps = 30 @@ -242,6 +243,7 @@ labels = no assert_eq!(cfg.border_width, Some(Length::Px(2))); assert_eq!(cfg.tile_width, Some(Length::Ppt(18.0))); assert_eq!(cfg.max_columns, Some(4)); + assert_eq!(cfg.max_rows, Some(3)); assert_eq!(cfg.fps, Some(30)); assert_eq!(cfg.labels, Some(false)); assert!(cfg.live.is_some()); diff --git a/src/theme.rs b/src/theme.rs index b2f31ea..3e4ad84 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -25,6 +25,9 @@ pub struct Theme { /// Margin between the grid and the window edge. pub margin: i32, pub max_cols: i32, + /// Cap on rows shown at once. Without it the viewport is as tall as the + /// display allows; with it the grid stays compact and scrolls sooner. + pub max_rows: Option, /// Gap between a thumbnail and its label (rasi `element { spacing }`). pub spacing: i32, /// Label font family, resolved against the system's fonts. The default is @@ -53,6 +56,7 @@ impl Default for Theme { gap: 15, margin: 12, max_cols: 4, + max_rows: None, spacing: 10, font: crate::text::SYSTEM_MONO.to_string(), font_px: 13.3, @@ -111,7 +115,8 @@ impl Layout { let fit_cols = ((room_w + t.gap) / (elem_w + t.gap)).max(1); cols = cols.clamp(1, t.max_cols.min(fit_cols)).max(1); let rows = (n + cols - 1) / cols; - let visible_rows = ((room_h + t.gap) / (elem_h + t.gap)).clamp(1, rows.max(1)); + let fits = (room_h + t.gap) / (elem_h + t.gap); + let visible_rows = fits.min(t.max_rows.unwrap_or(fits)).clamp(1, rows.max(1)); Self { cols, @@ -472,6 +477,26 @@ mod tests { ); } + #[test] + fn max_rows_keeps_the_grid_compact() { + let mut t = Theme::default(); + let full = Layout::new(&t, 30, (1280, 1440)); + t.max_rows = Some(2); + let capped = Layout::new(&t, 30, (1280, 1440)); + assert!( + capped.visible_rows == 2 && full.visible_rows > 2, + "{capped:?}" + ); + assert!(capped.height < full.height, "a shorter overlay"); + assert!(capped.scrollable()); + // The cap cannot invent rows: four tiles make a 2x2 grid, and a cap of + // five leaves it alone. + t.max_rows = Some(5); + let few = Layout::new(&t, 4, (1280, 1440)); + assert_eq!((few.cols, few.rows, few.visible_rows), (2, 2, 2), "{few:?}"); + assert!(!few.scrollable()); + } + #[test] fn revealing_moves_the_viewport_as_little_as_possible() { let t = Theme::default();