aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-18 09:39:27 +0200
committerDanilo M. <danix@danix.xyz>2026-09-18 09:39:27 +0200
commitd0393fffb4191e6b29252690381d8dbb48314c77 (patch)
treefa723dc5e5bf894473656985609ddd7c565a50aa
parentc64bf67e15b075182deb4eebead8315c9081ac46 (diff)
downloadconky-theme-udt-d0393fffb4191e6b29252690381d8dbb48314c77.tar.gz
conky-theme-udt-d0393fffb4191e6b29252690381d8dbb48314c77.zip
feat: date the calendar rows and pin a colour legend
Three changes to the calendar card: The month grid drops further below the header. The weekday initials sat close enough to read as part of the header rather than as the top of the grid. Event rows carry the date before the time, day/month with no year: the window is a week and the grid above already names the month, so a year would spend three characters on the one field that cannot vary. A legend of the calendar colours is pinned along the card's bottom, and the event rows now end above it rather than at the card's inner edge. It lists every calendar khal declared a colour for, in name order so it does not reshuffle between frames, since pairs() walks a hash. The row budget also counted one row more than the drawing loop would draw, which suppressed the '+N more' line exactly when it was needed: the loop cut the row the count had already spent. Both now test the same condition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
-rw-r--r--README.md15
-rw-r--r--widgets/calendar.lua91
2 files changed, 92 insertions, 14 deletions
diff --git a/README.md b/README.md
index c3a4261..5ec6b61 100644
--- a/README.md
+++ b/README.md
@@ -154,16 +154,25 @@ takes its calendar's colour; today takes a filled pill, coloured by its own
calendar when something is on and `highlight` otherwise, with the number over
it in the heading colour because a calendar colour loses too much contrast
against the fill. Past days in the month dim: the grid is mostly about what is
-still coming. Below the grid, one row per appointment, start time left in the
-calendar's colour and title right in the body colour. Colour-coding the whole
+still coming. Below the grid, one row per appointment: date and start time
+left in the calendar's colour, title right in the body colour. The date is
+day/month with no year, since the window is a week and the grid above already
+names the month. Colour-coding the whole
row makes three calendars read as three unrelated cards, so the time carries
the code and the title stays readable. All-day events read `all day`, never
`00:00`, because midnight is a legitimate start time.
+A legend of the calendar colours is pinned along the card's bottom, in name
+order so it does not reshuffle between frames, and the event rows end above it
+rather than running under it. It lists every calendar khal declared a colour
+for, including ones with nothing on this week: it is a colour key, not a second
+list of what is coming.
+
Events come from a cache file written by `bin/calendar-sample.sh`, which shells
out to `khal`. The card shows as many rows as the cell affords and ends with
`+N more` when the week does not fit, since a card that silently drops the rest
-of the week defeats its own purpose.
+of the week defeats its own purpose. A cell too short for even one row draws
+the grid alone, without the legend, rather than a clipped half-row.
**Calendar colours come from khal's own config**, parsed out of the
`[calendars]` section by the sampler, then mapped onto the board's palette
diff --git a/widgets/calendar.lua b/widgets/calendar.lua
index 82e10d3..6194447 100644
--- a/widgets/calendar.lua
+++ b/widgets/calendar.lua
@@ -41,11 +41,15 @@ local function cal_colour(name, colors, khal_colors)
return colors[role] or colors.value
end
--- 'Dentist' at 13:00 -> '13:00', an all-day event -> 'all day'.
--- Never '00:00' for an all-day event: midnight is a legitimate start time and
--- the two must not read the same.
+-- 'Dentist' on 24 September at 13:00 -> '24/09 13:00', an all-day event ->
+-- '26/09 all day'. Never '00:00' for an all-day event: midnight is a
+-- legitimate start time and the two must not read the same.
+--
+-- Day/month, no year: the grid above already names the month, and the window
+-- is a week, so a year would be three characters of the row spent on the one
+-- field that cannot vary.
local function when(e)
- return e.start or 'all day'
+ return string.format('%02d/%02d %s', e.d, e.m, e.start or 'all day')
end
-- The failure face: the sampler has not run. Named, as the other cached cards
@@ -66,6 +70,33 @@ local function draw_empty(cr, inner, colors, label_f, note, hint)
card.text(cr, inner.x, inner.y + S + S * label_f * 2.6 + S * 0.72, hint)
end
+-- The legend: one coloured dot and name per calendar, along the card's bottom.
+-- Wraps to a second line when the names do not fit, and drops any that still
+-- do not: a legend spilling off the card is worse than a short one, since the
+-- colours are also readable from the grid above.
+local function draw_legend(cr, inner, y, size, names, colors, khal_colors)
+ card.font(cr, card.FONT_MONO, size, false)
+ local r = size * 0.30
+ local x = inner.x
+ local right = inner.x + inner.w
+ for _, name in ipairs(names) do
+ local w = r * 2 + size * 0.45 + card.advance(cr, name)
+ if x + w > right then
+ -- Next line, if the card has one to give.
+ x = inner.x
+ y = y + size * 1.5
+ if y > inner.y + inner.h then return end
+ end
+ card.rgba(cr, cal_colour(name, colors, khal_colors))
+ cairo_new_path(cr)
+ cairo_arc(cr, x + r, y - size * 0.32, r, 0, math.pi * 2)
+ cairo_fill(cr)
+ card.rgba(cr, colors.label, 0.85)
+ card.text(cr, x + r * 2 + size * 0.45, y, name)
+ x = x + w + size * 0.9
+ end
+end
+
function M.draw(cr, rect, colors)
local inner = card.card(cr, rect, colors)
local LABEL_F = 0.30
@@ -113,7 +144,9 @@ function M.draw(cr, rect, colors)
card.text(cr, inner.x, inner.y + label_size, 'CALENDAR')
-- === Month grid =========================================================
- local gy = inner.y + big_size * 1.5
+ -- 2.3, not 1.5: the weekday row sat close enough to the header that the
+ -- initials read as part of it rather than as the top of the grid.
+ local gy = inner.y + big_size * 2.3
local row_h = day_size * 1.55
-- Weekday initials. Monday-first, matching khal's firstweekday = 0.
@@ -173,10 +206,25 @@ function M.draw(cr, rect, colors)
-- === Event table ========================================================
-- Everything below the grid, however much that is. A card too short for even
-- one row draws the grid alone rather than a clipped half-row.
- local avail = (inner.y + inner.h) - grid_bottom
local ev_size = clamp(day_size * 0.78, 7, 18)
local ev_step = ev_size * 1.75
- if avail < ev_step then return end
+
+ -- The legend is pinned to the card's bottom, so the event rows end above it
+ -- rather than running under it. Only calendars that khal declared a colour
+ -- for appear, in the config's own order, which is why the names are sorted
+ -- rather than taken from a hash walk: pairs() would reorder the legend
+ -- between frames.
+ local legend = {}
+ for name in pairs(cal.colors) do legend[#legend + 1] = name end
+ table.sort(legend)
+
+ local legend_size = ev_size * 0.82
+ local legend_h = #legend > 0 and legend_size * 2.0 or 0
+ local floor_y = inner.y + inner.h - legend_h
+
+ local avail = floor_y - grid_bottom
+ if avail < ev_step then legend = {}; legend_h = 0; floor_y = inner.y + inner.h end
+ if (floor_y - grid_bottom) < ev_step then return end
local ey = grid_bottom + ev_size * 1.1
@@ -184,6 +232,10 @@ function M.draw(cr, rect, colors)
card.font(cr, card.FONT_UI, ev_size, false)
card.rgba(cr, colors.label, 0.8)
card.text(cr, inner.x, ey, 'nothing this week')
+ if #legend > 0 then
+ draw_legend(cr, inner, inner.y + inner.h - legend_size * 0.5, legend_size,
+ legend, colors, cal.colors)
+ end
return
end
@@ -211,14 +263,26 @@ function M.draw(cr, rect, colors)
-- A card that silently drops the rest of the week is worse than one showing
-- fewer events and admitting it: the whole point of the card is knowing
-- what is coming.
- local room = math.floor(((inner.y + inner.h) - ey + ev_step) / ev_step)
+ --
+ -- Counted from the same test the drawing loop uses (a row at `ey` fits when
+ -- ey <= floor_y), not an independent division: the two disagreeing by one
+ -- row is what made the overflow line vanish exactly when it was needed,
+ -- since the loop cut the row the count had already spent.
+ local room = 0
+ local probe = ey
+ while probe <= floor_y do room = room + 1; probe = probe + ev_step end
+
local shown = #cal.events
- if room < shown then shown = math.max(room - 1, 1) end
+ -- The overflow line costs a row, so it is only worth taking when it reports
+ -- more than the row it displaces.
+ if room < shown then
+ shown = (room >= 2) and (room - 1) or room
+ end
local hidden = #cal.events - shown
for i = 1, shown do
local e = cal.events[i]
- if ey + ev_step > inner.y + inner.h then break end
+ if ey > floor_y then break end
local colour = cal_colour(e.calendar, colors, cal.colors)
card.font(cr, card.FONT_MONO, ev_size, false)
@@ -237,11 +301,16 @@ function M.draw(cr, rect, colors)
-- The overflow line, in the label colour so it reads as chrome rather than
-- as another appointment.
- if hidden > 0 and ey + ev_step <= inner.y + inner.h + ev_step then
+ if hidden > 0 and ey <= floor_y then
card.font(cr, card.FONT_UI, ev_size * 0.92, false)
card.rgba(cr, colors.label, 0.8)
card.text(cr, inner.x, ey, string.format('+%d more', hidden))
end
+
+ if #legend > 0 then
+ draw_legend(cr, inner, inner.y + inner.h - legend_size * 0.5, legend_size,
+ legend, colors, cal.colors)
+ end
end
return M