-- Cache: ~/.cache total and its four largest subdirectories. -- -- Reads a cache file written by bin/cache-sample.sh. du walks the tree and -- costs about 100ms, which cannot happen on a 2-second draw. local card = require 'lib.card' local data = require 'lib.data' local CACHE = (os.getenv('XDG_CACHE_HOME') or (os.getenv('HOME') .. '/.cache')) .. '/udt/cachesize.txt' local M = {} function M.draw(cr, rect, colors) local inner = card.card(cr, rect, colors) local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end -- Fluid type. One base size S drives every element. The height budget makes -- the content fill the cell; the measured width fit pulls S down only where -- the header or a row would overrun the card. A tall cell gets larger type -- instead of empty space, and nothing collides. local LABEL_F, ROW_F = 0.30, 0.32 local c = data.du_parse(data.slurp(CACHE) or '') if not c then local S = clamp(inner.h * 0.94 / (1.78 + 2 * 0.72), 10, 72) S = math.min(S, card.fit_unit(cr, inner.w * 0.96, { { { 'CACHE', card.FONT_MONO, LABEL_F } }, { { 'no cache data', card.FONT_UI, 0.36 } }, { { 'bin/cache-sample.sh', card.FONT_MONO, ROW_F } }, }, 100)) card.font(cr, card.FONT_MONO, S * LABEL_F, false) card.rgba(cr, colors.label) card.text(cr, inner.x, inner.y + S * LABEL_F, 'CACHE') card.font(cr, card.FONT_UI, S * 0.36, true) card.text(cr, inner.x, inner.y + S + S * LABEL_F * 2.6, 'no cache data') card.font(cr, card.FONT_MONO, S * ROW_F, false) card.text(cr, inner.x, inner.y + S + S * LABEL_F * 2.6 + S * 0.72, 'bin/cache-sample.sh') return end local items = c.items or {} local names = {} for i, e in ipairs(items) do -- By character, not byte: a cache directory can carry an accented name and -- a byte slice cuts one in half, which Cairo draws as a replacement box. names[i] = card.truncate(e.name, 18) end local row_n = #items local S_h = inner.h * 0.94 / (1.78 + row_n * 0.64) local groups = { { { 'CACHE', card.FONT_MONO, LABEL_F }, { c.total.label, card.FONT_HEAVY, 1.0 } }, } for i = 1, row_n do groups[#groups + 1] = { { names[i], card.FONT_MONO, ROW_F }, { items[i].label, card.FONT_MONO, ROW_F } } end local S = clamp(math.min(S_h, card.fit_unit(cr, inner.w * 0.96, groups, 100)), 10, 72) local label_size = S * LABEL_F local row_size = S * ROW_F card.font(cr, card.FONT_MONO, label_size, false) card.rgba(cr, colors.label) card.text(cr, inner.x, inner.y + label_size, 'CACHE') -- The total shares the header line with the label, set on the same baseline -- and right-aligned: the width carries the number instead of an otherwise -- empty row beneath it. card.font(cr, card.FONT_HEAVY, S, false) card.rgba(cr, colors.body) card.text_right(cr, inner.x + inner.w, inner.y + S, c.total.label) local ey = inner.y + S + label_size * 2.6 local step = row_size * 2.0 card.font(cr, card.FONT_MONO, row_size, false) for i, e in ipairs(items) do if ey + step > inner.y + inner.h then break end -- The biggest offender is the point of the card, so it carries colour: -- critical when it is over half the total, warning otherwise. The rest -- stay in the ordinary value colour so the eye lands on the one to delete. local colour = colors.value if i == 1 then local share = c.total.bytes > 0 and (e.bytes / c.total.bytes) or 0 colour = share > 0.5 and colors.critical or colors.warning end card.rgba(cr, colors.label) card.text(cr, inner.x, ey, names[i]) card.rgba(cr, colour) card.text_right(cr, inner.x + inner.w, ey, e.label) ey = ey + step end end return M