aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/clock.lua
blob: b7c80d94be6d62edcdb9ca14f39423cd8766b978 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
-- 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_CLOCK, size, true)
  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