Cairn · module

Cairn.UI.Grid

lean/Cairn/UI/Grid.lean

Grids that follow their box

A Grid lays equal cards out in rows, as many columns as fit the box's width. It is plain arithmetic on the box's size, so a view builds it from the window size the runtime keeps (UI.size, updated by every resize event): the host only reports the size and Lean decides every rect. Resizing the window changes the size, the view runs again and the cards reflow.

  • Columns are as many cards of minWidth as fit with the gaps between them, and at least one (columns). The cards then share the width equally, so they grow until another column fits.
  • Cards keep their aspect ratio (ratioW : ratioH), plus a fixed caption height under it that doesn't scale. When the rows would be taller than the box, every card shrinks so they fit: nothing scrolls, and nothing is drawn outside the box.
  • Gaps are fixed: gap between columns and between rows. (Only when the gaps between rows alone are taller than the box are the rows packed with none.)
  • Reading order: card i is in row i / columns, column i % columns, so the cards keep their order on every resize; only where the rows break changes.
  • The used width is centred in the box; rows start at its top.

Grid.node builds the view: a column of rows of fixed-size cells, each clipping its card, laid out by the ordinary layout pass. The geometry's theorems are at the end of this file: the column count is the largest that fits and at least one, the cells never overlap and stay inside the box, in reading order. They live here, not with the layout proofs, so an app's claims can cite them. Cairn.Layout.Grid proves the cells land exactly on Grid.rects and that hit testing a point in a cell answers from that cell alone.

Imports Cairn.UI.Node

Order

Structures 1

structure

Cairn.Grid

lean/Cairn/UI/Grid.lean:36
Grid : Type

A grid of equal cards whose column count follows the width of its box.

Fields
minWidth : Nat

The narrowest a card is laid out when there's room for it: there are as many columns as cards of this width fit (at least one; taken as 1 when 0).

ratioW : Nat

The cards' aspect ratio, width to height (each taken as 1 when 0).

ratioH : Nat
gap : Nat

Points between columns and between rows.

caption : Nat

A fixed height under the scaled part of each card (a caption), not scaled with it.

Instances

Repr Inhabited

Definitions 24

def

Cairn.Grid.minW

lean/Cairn/UI/Grid.lean:52
minW (g : Grid) : Nat
def

Cairn.Grid.rw

lean/Cairn/UI/Grid.lean:53
rw (g : Grid) : Nat
def

Cairn.Grid.rh

lean/Cairn/UI/Grid.lean:54
rh (g : Grid) : Nat
def

Cairn.Grid.Fits

lean/Cairn/UI/Grid.lean:58
Fits (g : Grid) (W k : Nat) : Prop

k cards of the minimum width, with the gaps between them, fit in W.

Instances

Decidable

def

Cairn.Grid.columns

lean/Cairn/UI/Grid.lean:62
columns (g : Grid) (W : Nat) : Nat

Columns: the most cards of the minimum width that fit in W with their gaps, and at least one (columns_fits, le_columns).

def

Cairn.Grid.rows

lean/Cairn/UI/Grid.lean:68
rows (g : Grid) (W n : Nat) : Nat

Rows for n cards.

def

Cairn.Grid.vgap

lean/Cairn/UI/Grid.lean:71
vgap (g : Grid) (W H n : Nat) : Nat

The gap between rows: gap, unless the gaps alone are taller than the box.

def

Cairn.Grid.widthShare

lean/Cairn/UI/Grid.lean:74
widthShare (g : Grid) (W : Nat) : Nat

The width each column gets when the cards share the box's width.

def

Cairn.Grid.heightShare

lean/Cairn/UI/Grid.lean:77
heightShare (g : Grid) (W H n : Nat) : Nat

The height each row gets when the rows share the box's height.

def

Cairn.Grid.cardW

lean/Cairn/UI/Grid.lean:81
cardW (g : Grid) (W H n : Nat) : Nat

A card's width: its share of the width, or less if the rows would be too tall.

def

Cairn.Grid.cardH

lean/Cairn/UI/Grid.lean:85
cardH (g : Grid) (W H n : Nat) : Nat

A card's height: its width at the aspect ratio, plus the caption (cardH_aspect).

def

Cairn.Grid.usedW

lean/Cairn/UI/Grid.lean:89
usedW (g : Grid) (W H n : Nat) : Nat

The width the columns take, gaps included.

def

Cairn.Grid.offset

lean/Cairn/UI/Grid.lean:92
offset (g : Grid) (W H n : Nat) : Nat

Where the first column starts, centring the used width.

def

Cairn.Grid.cell

lean/Cairn/UI/Grid.lean:97
cell (g : Grid) (x y : Int) (W H n i : Nat) : Rect

Card i's rect, of n in the W × H box at (x, y): row i / columns, column i % columns.

def

Cairn.Grid.rects

lean/Cairn/UI/Grid.lean:104
rects (g : Grid) (x y : Int) (W H n : Nat) : List Rect

Every card's rect, in order.

def

Cairn.Grid.minCardH

lean/Cairn/UI/Grid.lean:118
minCardH (g : Grid) : Nat

The least height of a card minWidth wide: its scaled part rounded up, plus the caption.

def

Cairn.Grid.minBox

lean/Cairn/UI/Grid.lean:122
minBox (g : Grid) (n cols : Nat) : Nat × Nat

The smallest box with cols columns in which every one of n cards is at least minWidth wide, as width and height (minW_le_cardW).

def

Cairn.Grid.chunksAux

lean/Cairn/UI/Grid.lean:132
chunksAux {α : Type} (c : Nat) : Nat → List α → List (List α)

xs in runs of c (the last may be shorter). fuel bounds the recursion; chunks gives enough.

def

Cairn.Grid.chunks

lean/Cairn/UI/Grid.lean:137
chunks {α : Type} (c : Nat) (xs : List α) : List (List α)
def

Cairn.Grid.cellNode

lean/Cairn/UI/Grid.lean:141
cellNode (w h : Nat) (card : Node) : Node

A cell: exactly the card's rect, clipping the card to it, so nothing in one cell paints or takes a click in another.

def

Cairn.Grid.rowStyle

lean/Cairn/UI/Grid.lean:145
rowStyle (g : Grid) (W H n : Nat) : Style

A row's box: the used width, a card high, cells gap apart.

def

Cairn.Grid.rowNode

lean/Cairn/UI/Grid.lean:149
rowNode (g : Grid) (W H n : Nat) (cells : List Node) : Node

A row of cells.

def

Cairn.Grid.style

lean/Cairn/UI/Grid.lean:152
style (g : Grid) (W H n : Nat) : Style

The grid's box: W × H, rows vgap apart, the used width centred.

def

Cairn.Grid.node

lean/Cairn/UI/Grid.lean:159
node {α : Type} (g : Grid) (W H : Nat) (card : Nat → Nat → α → Node)
  (xs : List α) : Node

The grid as a view: W × H points (a fixed box), card i drawn by card w h xs[i] at w × h points in its cell. The cards keep the order of xs.

Theorems 34

theorem

Cairn.Grid.minW_pos

provedlean/Cairn/UI/Grid.lean:174
minW_pos (g : Grid) : 1 ≤ g.minW
theorem

Cairn.Grid.rw_pos

provedlean/Cairn/UI/Grid.lean:175
rw_pos (g : Grid) : 1 ≤ g.rw
theorem

Cairn.Grid.rh_pos

provedlean/Cairn/UI/Grid.lean:176
rh_pos (g : Grid) : 1 ≤ g.rh
theorem

Cairn.Grid.columns_pos

provedlean/Cairn/UI/Grid.lean:180
columns_pos (g : Grid) (W : Nat) : 1 ≤ g.columns W
theorem

Cairn.Grid.fits_iff

provedlean/Cairn/UI/Grid.lean:183
fits_iff (g : Grid) (W k : Nat) (hk : 1 ≤ k) :
  g.Fits W k ↔ k * (g.minW + g.gap) ≤ W + g.gap

k columns fit exactly when k cards and their gaps fit in the width plus one gap.

theorem

Cairn.Grid.columns_fits

provedlean/Cairn/UI/Grid.lean:189
columns_fits (g : Grid) (W : Nat) : g.columns W = 1 ∨ g.Fits W (g.columns W)

The column count fits: one column, or that many cards of the minimum width fit.

theorem

Cairn.Grid.le_columns

provedlean/Cairn/UI/Grid.lean:198
le_columns (g : Grid) (W k : Nat) (h : g.Fits W k) : k ≤ g.columns W

No column count that fits is larger.

theorem

Cairn.Grid.not_fits_succ

provedlean/Cairn/UI/Grid.lean:207
not_fits_succ (g : Grid) (W : Nat) : ¬g.Fits W (g.columns W + 1)

One more column would not fit.

theorem

Cairn.Grid.minW_le_widthShare

provedlean/Cairn/UI/Grid.lean:211
minW_le_widthShare (g : Grid) (W : Nat) (h : g.Fits W 1) :
  g.minW ≤ g.widthShare W

With room for one card of the minimum width, each column's share is at least that wide.

theorem

Cairn.Grid.gaps_le_W

provedlean/Cairn/UI/Grid.lean:225
gaps_le_W (g : Grid) (W : Nat) : g.gap * (g.columns W - 1) ≤ W
theorem

Cairn.Grid.usedW_le

provedlean/Cairn/UI/Grid.lean:231
usedW_le (g : Grid) (W H n : Nat) : g.usedW W H n ≤ W

The columns and their gaps fit the width.

theorem

Cairn.Grid.offset_usedW_le

provedlean/Cairn/UI/Grid.lean:243
offset_usedW_le (g : Grid) (W H n : Nat) : g.offset W H n + g.usedW W H n ≤ W
theorem

Cairn.Grid.vgaps_le

provedlean/Cairn/UI/Grid.lean:247
vgaps_le (g : Grid) (W H n : Nat) : g.vgap W H n * (g.rows W n - 1) ≤ H
theorem

Cairn.Grid.rows_le

provedlean/Cairn/UI/Grid.lean:251
rows_le (g : Grid) (W H n : Nat) (hn : 0 < g.rows W n) :
  (g.rows W n - 1) * (g.cardH W H n + g.vgap W H n) + g.cardH W H n ≤ H

The rows and their gaps fit the height.

theorem

Cairn.Grid.cardH_aspect

provedlean/Cairn/UI/Grid.lean:267
cardH_aspect (g : Grid) (W H n : Nat) (h : g.caption ≤ g.heightShare W H n) :
  g.cardH W H n = g.cardW W H n * g.rh / g.rw + g.caption

Aspect kept: when the box is tall enough for the captions, a card is as tall as its width at the aspect ratio, plus the caption.

theorem

Cairn.Grid.vgap_eq

provedlean/Cairn/UI/Grid.lean:279
vgap_eq (g : Grid) (W H n : Nat) (h : g.gap * (g.rows W n - 1) ≤ H) :
  g.vgap W H n = g.gap

Gaps fixed: whenever the gaps between rows fit the height, rows are gap apart.

theorem

Cairn.Grid.row_lt_rows

provedlean/Cairn/UI/Grid.lean:284
row_lt_rows (g : Grid) (W n i : Nat) (hi : i < n) : i / g.columns W < g.rows W n
theorem

Cairn.Grid.mul_le_of_le

provedlean/Cairn/UI/Grid.lean:291
mul_le_of_le {a b s : Nat} (h : a ≤ b) : a * s ≤ b * s
theorem

Cairn.Grid.pitch_lt

provedlean/Cairn/UI/Grid.lean:294
pitch_lt {a b s w : Nat} (h : a < b) (hw : w ≤ s) : a * s + w ≤ b * s

a < b rows (or columns) of pitch s ≥ w apart: the earlier ends before the later starts.

theorem

Cairn.Grid.cell_inside

provedlean/Cairn/UI/Grid.lean:299
cell_inside (g : Grid) (x y : Int) (W H n i : Nat) (hi : i < n) :
  (g.cell x y W H n i).inside { x := x, y := y, w := ↑W, h := ↑H }

Inside: every card's rect is inside the grid's box.

theorem

Cairn.Grid.cell_before

provedlean/Cairn/UI/Grid.lean:321
cell_before (g : Grid) (x y : Int) (W H n i j : Nat) (hij : i < j) :
  have a := g.cell x y W H n i;
  have b := g.cell x y W H n j;
  a.y + a.h ≤ b.y ∨ a.y = b.y ∧ a.x + a.w ≤ b.x

Reading order: a later card is in a lower row (it starts below the earlier one's bottom), or in the same row and to the right.

theorem

Cairn.Grid.cell_disjoint

provedlean/Cairn/UI/Grid.lean:342
cell_disjoint (g : Grid) (x y : Int) (W H n i j : Nat) (hij : i ≠ j) :
  (g.cell x y W H n i).disjoint (g.cell x y W H n j)

No overlap: two different cards' rects are disjoint.

theorem

Cairn.Grid.rects_length

provedlean/Cairn/UI/Grid.lean:348
rects_length (g : Grid) (x y : Int) (W H n : Nat) :
  (g.rects x y W H n).length = n
theorem

Cairn.Grid.rects_getElem

provedlean/Cairn/UI/Grid.lean:351
rects_getElem (g : Grid) (x y : Int) (W H n i : Nat)
  (h : i < (g.rects x y W H n).length) : (g.rects x y W H n)[i] = g.cell x y W H n i
theorem

Cairn.Grid.rects_pairwise

provedlean/Cairn/UI/Grid.lean:356
rects_pairwise (g : Grid) (x y : Int) (W H n : Nat) :
  List.Pairwise (fun a b => a.disjoint b ∧ (a.y + a.h ≤ b.y ∨ a.y = b.y ∧ a.x + a.w ≤ b.x))
    (g.rects x y W H n)

The rects in order: pairwise disjoint, and each in reading order before the next.

theorem

Cairn.Grid.rects_inside

provedlean/Cairn/UI/Grid.lean:365
rects_inside (g : Grid) (x y : Int) (W H n : Nat) (r : Rect) :
  r ∈ g.rects x y W H n → r.inside { x := x, y := y, w := ↑W, h := ↑H }
theorem

Cairn.Grid.ceil_spec

provedlean/Cairn/UI/Grid.lean:382
ceil_spec (a b : Nat) (hb : 1 ≤ b) :
  a ≤ (a + b - 1) / b * b ∧ (0 < a → ((a + b - 1) / b - 1) * b < a)

Rounding up: q = ⌈a / b⌉ covers a (a ≤ q * b) and one less doesn't ((q - 1) * b < a when a > 0).

theorem

Cairn.Grid.ceil_div_anti

provedlean/Cairn/UI/Grid.lean:394
ceil_div_anti (n c C : Nat) (hc : 1 ≤ c) (hC : c ≤ C) :
  (n + C - 1) / C ≤ (n + c - 1) / c

Rows only get fewer with more columns: ⌈n / C⌉ ≤ ⌈n / c⌉ when 1 ≤ c ≤ C.

theorem

Cairn.Grid.le_columns_minBox

provedlean/Cairn/UI/Grid.lean:404
le_columns_minBox (g : Grid) (n cols W : Nat) (hW : (g.minBox n cols).fst ≤ W) :
  max 1 cols ≤ g.columns W

The columns of a box at least minBox's width: at least cols.

theorem

Cairn.Grid.minW_le_cardW

provedlean/Cairn/UI/Grid.lean:409
minW_le_cardW (g : Grid) (n cols W H : Nat) (hn : 0 < n)
  (hW : (g.minBox n cols).fst ≤ W) (hH : (g.minBox n cols).snd ≤ H) : g.minW ≤ g.cardW W H n

At or above minBox, every card is at least minWidth wide.

theorem

Cairn.Grid.minBox_cells

provedlean/Cairn/UI/Grid.lean:451
minBox_cells (g : Grid) (x y : Int) (n cols W H : Nat)
  (hW : (g.minBox n cols).fst ≤ W) (hH : (g.minBox n cols).snd ≤ H) (r : Rect) :
  r ∈ g.rects x y W H n → ↑g.minW ≤ r.w

Every card's rect is at least minWidth wide in a box at least minBox.

theorem

Cairn.Grid.columns_minBox

provedlean/Cairn/UI/Grid.lean:460
columns_minBox (g : Grid) (n cols : Nat) :
  g.columns (g.minBox n cols).fst = max 1 cols

minBox has exactly cols columns (at least one).

theorem

Cairn.Grid.minBox_narrower

provedlean/Cairn/UI/Grid.lean:472
minBox_narrower (g : Grid) (n cols W H : Nat) (hW : W < (g.minBox n cols).fst) :
  g.columns W < max 1 cols ∨ g.cardW W H n < g.minW

No narrower box will do: narrower than minBox, a box has fewer than cols columns, or (at one column) cards narrower than minWidth.

theorem

Cairn.Grid.minBox_shorter

provedlean/Cairn/UI/Grid.lean:499
minBox_shorter (g : Grid) (n cols W H : Nat) (hn : 0 < n)
  (hcols : g.columns W = max 1 cols) (hgap : g.gap ≤ g.minCardH)
  (hH : H < (g.minBox n cols).snd) : g.cardW W H n < g.minW

No shorter box will do: with cols columns and a gap no taller than a card, a box shorter than minBox has cards narrower than minWidth.

Instances 4

instance

Cairn.instReprGrid

lean/Cairn/UI/Grid.lean:47
instReprGrid : Repr Grid
instance

Cairn.instDecidableEqGrid

lean/Cairn/UI/Grid.lean:47
instDecidableEqGrid : DecidableEq Grid
instance

Cairn.instInhabitedGrid

lean/Cairn/UI/Grid.lean:47
instInhabitedGrid : Inhabited Grid
instance

Cairn.Grid.instDecidableFits

lean/Cairn/UI/Grid.lean:65
instDecidableFits (g : Grid) (W k : Nat) : Decidable (g.Fits W k)