Add max-rows
Scrolling left the overlay's height entirely derived — as many rows as fit in 90% of the display — so with enough windows it is always nearly full-height, and there was no way to ask for a compact strip instead. max-rows caps the viewport and scrolls the rest, which makes it the symmetric partner to max-columns; rows are also the natural unit when tiles are a fixed size, where a height in ppt would flip the row count about as the tile size changes. Measured on a 1280x1440 display with 14 tiles at 18ppt: uncapped gives a 981px surface showing 3 of 4 rows, max-rows = 2 gives 657px, and 1 gives 333px — each a row's pitch apart. The test I wrote for it was wrong before the code was: four tiles make a 2x2 grid under the ceil(sqrt(n)) rule, not the 4x1 I had assumed.
This commit is contained in:
@@ -153,6 +153,7 @@ border-width = 2px
|
|||||||
tile-width = 18ppt # largest a thumbnail may be
|
tile-width = 18ppt # largest a thumbnail may be
|
||||||
tile-height = 20ppt # defaults to the display's aspect
|
tile-height = 20ppt # defaults to the display's aspect
|
||||||
max-columns = 4
|
max-columns = 4
|
||||||
|
max-rows = 3 # default: however many the display fits
|
||||||
|
|
||||||
font = monospace
|
font = monospace
|
||||||
font-size = 13.3
|
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
|
of the windows on it — a 16:9 cell wastes about half its area on a portrait
|
||||||
monitor.
|
monitor.
|
||||||
|
|
||||||
When there are more rows than the display can show, **the grid scrolls**: the
|
Nothing sets the overlay's height directly: it is as many rows as fit in 90% of
|
||||||
tile size you asked for is honoured and a scrollbar appears in the right margin.
|
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
|
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
|
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
|
what stops a long list costing bandwidth for pixels nobody sees. Only a tile too
|
||||||
|
|||||||
+4
-1
@@ -53,7 +53,9 @@ config:
|
|||||||
|
|
||||||
tile-width = 18ppt # how big a thumbnail is
|
tile-width = 18ppt # how big a thumbnail is
|
||||||
tile-height = 20ppt # defaults to the display's aspect
|
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 = monospace # also --font
|
||||||
font-size = 13.3
|
font-size = 13.3
|
||||||
@@ -159,6 +161,7 @@ impl Args {
|
|||||||
tile_w: tile_w.max(1),
|
tile_w: tile_w.max(1),
|
||||||
tile_h: tile_h.max(1),
|
tile_h: tile_h.max(1),
|
||||||
max_cols: cfg.max_columns.unwrap_or(base.max_cols).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),
|
labels: self.labels.or(cfg.labels).unwrap_or(base.labels),
|
||||||
font: self
|
font: self
|
||||||
.font
|
.font
|
||||||
|
|||||||
+5
-3
@@ -88,6 +88,7 @@ pub struct Config {
|
|||||||
pub tile_width: Option<Length>,
|
pub tile_width: Option<Length>,
|
||||||
pub tile_height: Option<Length>,
|
pub tile_height: Option<Length>,
|
||||||
pub max_columns: Option<i32>,
|
pub max_columns: Option<i32>,
|
||||||
|
pub max_rows: Option<i32>,
|
||||||
pub font: Option<String>,
|
pub font: Option<String>,
|
||||||
pub font_size: Option<f32>,
|
pub font_size: Option<f32>,
|
||||||
pub labels: Option<bool>,
|
pub labels: Option<bool>,
|
||||||
@@ -140,9 +141,8 @@ impl Config {
|
|||||||
"border-width" => self.border_width = Some(Length::parse(value)?),
|
"border-width" => self.border_width = Some(Length::parse(value)?),
|
||||||
"tile-width" => self.tile_width = Some(Length::parse(value)?),
|
"tile-width" => self.tile_width = Some(Length::parse(value)?),
|
||||||
"tile-height" => self.tile_height = Some(Length::parse(value)?),
|
"tile-height" => self.tile_height = Some(Length::parse(value)?),
|
||||||
"max-columns" => {
|
"max-columns" => self.max_columns = Some(number(value)?),
|
||||||
self.max_columns = Some(number(value)?);
|
"max-rows" => self.max_rows = Some(number(value)?),
|
||||||
}
|
|
||||||
"font" => self.font = Some(value.to_string()),
|
"font" => self.font = Some(value.to_string()),
|
||||||
"font-size" => self.font_size = Some(number(value)?),
|
"font-size" => self.font_size = Some(number(value)?),
|
||||||
"labels" => self.labels = Some(boolean(value)?),
|
"labels" => self.labels = Some(boolean(value)?),
|
||||||
@@ -230,6 +230,7 @@ border-width = 2px
|
|||||||
|
|
||||||
tile-width = 18ppt
|
tile-width = 18ppt
|
||||||
max-columns = 4
|
max-columns = 4
|
||||||
|
max-rows = 3
|
||||||
|
|
||||||
live = current
|
live = current
|
||||||
fps = 30
|
fps = 30
|
||||||
@@ -242,6 +243,7 @@ labels = no
|
|||||||
assert_eq!(cfg.border_width, Some(Length::Px(2)));
|
assert_eq!(cfg.border_width, Some(Length::Px(2)));
|
||||||
assert_eq!(cfg.tile_width, Some(Length::Ppt(18.0)));
|
assert_eq!(cfg.tile_width, Some(Length::Ppt(18.0)));
|
||||||
assert_eq!(cfg.max_columns, Some(4));
|
assert_eq!(cfg.max_columns, Some(4));
|
||||||
|
assert_eq!(cfg.max_rows, Some(3));
|
||||||
assert_eq!(cfg.fps, Some(30));
|
assert_eq!(cfg.fps, Some(30));
|
||||||
assert_eq!(cfg.labels, Some(false));
|
assert_eq!(cfg.labels, Some(false));
|
||||||
assert!(cfg.live.is_some());
|
assert!(cfg.live.is_some());
|
||||||
|
|||||||
+26
-1
@@ -25,6 +25,9 @@ pub struct Theme {
|
|||||||
/// Margin between the grid and the window edge.
|
/// Margin between the grid and the window edge.
|
||||||
pub margin: i32,
|
pub margin: i32,
|
||||||
pub max_cols: 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<i32>,
|
||||||
/// Gap between a thumbnail and its label (rasi `element { spacing }`).
|
/// Gap between a thumbnail and its label (rasi `element { spacing }`).
|
||||||
pub spacing: i32,
|
pub spacing: i32,
|
||||||
/// Label font family, resolved against the system's fonts. The default is
|
/// Label font family, resolved against the system's fonts. The default is
|
||||||
@@ -53,6 +56,7 @@ impl Default for Theme {
|
|||||||
gap: 15,
|
gap: 15,
|
||||||
margin: 12,
|
margin: 12,
|
||||||
max_cols: 4,
|
max_cols: 4,
|
||||||
|
max_rows: None,
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
font: crate::text::SYSTEM_MONO.to_string(),
|
font: crate::text::SYSTEM_MONO.to_string(),
|
||||||
font_px: 13.3,
|
font_px: 13.3,
|
||||||
@@ -111,7 +115,8 @@ impl Layout {
|
|||||||
let fit_cols = ((room_w + t.gap) / (elem_w + t.gap)).max(1);
|
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);
|
cols = cols.clamp(1, t.max_cols.min(fit_cols)).max(1);
|
||||||
let rows = (n + cols - 1) / cols;
|
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 {
|
Self {
|
||||||
cols,
|
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]
|
#[test]
|
||||||
fn revealing_moves_the_viewport_as_little_as_possible() {
|
fn revealing_moves_the_viewport_as_little_as_possible() {
|
||||||
let t = Theme::default();
|
let t = Theme::default();
|
||||||
|
|||||||
Reference in New Issue
Block a user