aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md27
-rw-r--r--widgets/clock.lua41
2 files changed, 58 insertions, 10 deletions
diff --git a/README.md b/README.md
index 912aa18..b51e9fa 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,33 @@ bug here: ink width ignores leading and trailing spaces, so `' / '` measures 6px
of ink against a 14px advance, and stepping a cursor by the ink width renders
the date as `16 /SEP /2026`.
+### Widgets are fluid
+
+A widget is handed a rectangle and must fill it, whatever shape the grid makes
+it. That is what lets you retune `COLS`/`ROWS` or move a card without touching
+widget code. Three rules, all learned by getting them wrong:
+
+**Derive every size and offset from the rect.** No fixed pixel drops between
+elements. A fixed `+34` under a numeral that scales leaves a dead band in a tall
+cell and overlaps in a short one.
+
+**Scale type off the WIDTH, not the height.** Height is what changes when you
+add a row to the grid, and a card that shrinks its text every time its cell gets
+shorter reads as broken beside its neighbours. Width is also usually what
+constrains the text, since rows run edge to edge. Clamp it: `math.max(lo,
+math.min(hi, inner.w * k))`.
+
+**Decide where the slack goes, and let one element absorb it.** The clock pins
+its date block to the bottom and gives the numerals everything above. The
+weather card stacks from the top and lets the arc take the remainder. Either is
+fine; what fails is leaving the slack wherever it lands.
+
+An element that cannot shrink below some size should drop out rather than
+overlap. The weather arc does this: below 70px it is not drawn at all, because a
+curve crossing the stat rows is worse than no curve. Check a widget at several
+spans before believing it is fluid, which the offscreen renderer above makes
+cheap.
+
## Development
Run it in the foreground to see Lua output, which is the only debugging channel:
diff --git a/widgets/clock.lua b/widgets/clock.lua
index 978715e..c3e9712 100644
--- a/widgets/clock.lua
+++ b/widgets/clock.lua
@@ -1,18 +1,37 @@
-- Clock: hour stacked over minute, weekday, slashed date.
--
-- Shape follows idea2.png; every colour comes from the palette.
+--
+-- Fluid: every size and offset derives from the rect it is handed, and the
+-- date block pins to the bottom so the slack in a tall cell falls between the
+-- numerals and the date rather than piling up under everything. A widget is
+-- given a rectangle and should fill it, whatever shape the grid makes it.
local card = require 'lib.card'
local M = {}
+local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end
+
function M.draw(cr, rect, colors)
local inner = card.card(cr, rect, colors)
+ local x = inner.x
+ local bottom = inner.y + inner.h
- -- Numeral size is derived from the cell, not fixed, so the same widget fills
- -- a 1x2 cell on either monitor instead of needing a per-screen constant.
- -- Two stacked numerals plus the date block: allow 40% of the height each.
- local size = math.min(inner.h * 0.40, inner.w * 0.95)
+ -- The date block is sized first, because the numerals get whatever is left.
+ -- Both scale off the WIDTH: height changes with the grid, and a widget that
+ -- shrinks its type every time its cell gets shorter reads as broken next to
+ -- its neighbours. Width is also what actually constrains the date line,
+ -- which runs edge to edge.
+ local day_size = clamp(inner.w * 0.055, 12, 26)
+ local date_size = clamp(inner.w * 0.050, 11, 24)
+ local date_band = day_size * 1.6 + date_size * 1.8
+
+ -- Two stacked numerals fill the space above the date band. Whichever of
+ -- height and width binds first wins, so a wide short cell is limited by its
+ -- height and a narrow tall one by its width, and neither overflows.
+ local avail = (bottom - date_band) - inner.y
+ local size = math.min(avail * 0.46, inner.w * 0.95)
card.font(cr, card.FONT_HEAVY, size, false)
card.rgba(cr, colors.body)
@@ -20,7 +39,6 @@ function M.draw(cr, rect, colors)
-- Baselines. Cairo's y is the baseline, so the first sits one cap-height
-- down; 0.78 of the font size approximates cap height for Noto Sans and
-- avoids measuring every frame.
- local x = inner.x
local y1 = inner.y + size * 0.78
-- Tight leading, as in the mockup: the numerals nearly touch.
local y2 = y1 + size * 0.92
@@ -28,16 +46,19 @@ function M.draw(cr, rect, colors)
card.text(cr, x, y1, os.date('%H'))
card.text(cr, x, y2, os.date('%M'))
- -- Weekday, small caps.
- card.font(cr, card.FONT_UI, 15, true)
+ -- Weekday and date sit against the bottom edge, not at a fixed drop from the
+ -- numerals. That is what puts the slack of a tall cell in the middle of the
+ -- card instead of leaving a dead band under the date.
+ local dy = bottom - date_size * 0.3
+ local wy = dy - date_size * 1.8
+
+ card.font(cr, card.FONT_UI, day_size, true)
card.rgba(cr, colors.body)
- local wy = y2 + 34
card.text(cr, x, wy, os.date('%A'):upper())
-- Date as "16 / SEP / 2026". The slashes take the dimmer label colour so the
-- numerals read first, which is what gives the mockup's date line its rhythm.
- card.font(cr, card.FONT_UI, 14, true)
- local dy = wy + 24
+ card.font(cr, card.FONT_UI, date_size, true)
local parts = {
{ os.date('%d'), colors.value },
{ ' / ', colors.label },