-- 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 -- 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) -- 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 y1 = inner.y + size * 0.78 -- Tight leading, as in the mockup: the numerals nearly touch. local y2 = y1 + size * 0.92 card.text(cr, x, y1, os.date('%H')) card.text(cr, x, y2, os.date('%M')) -- 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) 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, date_size, true) local parts = { { os.date('%d'), colors.value }, { ' / ', colors.label }, { os.date('%b'):upper(), colors.value }, { ' / ', colors.label }, { os.date('%Y'), colors.value }, } local dx = x for _, p in ipairs(parts) do card.rgba(cr, p[2]) card.text(cr, dx, dy, p[1]) -- advance(), not measure(): the ink width of ' / ' omits its spaces. dx = dx + card.advance(cr, p[1]) end end return M