diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 13:03:18 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 13:03:18 +0200 |
| commit | ad40e5c153fa2ccd9162bc970eabd5a11db11a32 (patch) | |
| tree | c6b2c57ea1deab1f2eae373a95004cdcba67c90e | |
| parent | adecc8de0e90b3856c8807e1c3610c70879545ad (diff) | |
| download | conky-theme-udt-ad40e5c153fa2ccd9162bc970eabd5a11db11a32.tar.gz conky-theme-udt-ad40e5c153fa2ccd9162bc970eabd5a11db11a32.zip | |
feat: fluid type for the gpu and cache cards
The big value moves to the right of the heading, on the same baseline,
so the card's width carries it instead of leaving the right half empty.
Type now scales to fill the cell: card.fit_unit measures the rows and
returns the largest size that fits the width, and the widget takes the
smaller of that and its height budget, shrinking only where a row would
collide.
| -rw-r--r-- | README.md | 24 | ||||
| -rw-r--r-- | lib/card.lua | 28 | ||||
| -rw-r--r-- | widgets/cache.lua | 73 | ||||
| -rw-r--r-- | widgets/gpu.lua | 48 |
4 files changed, 132 insertions, 41 deletions
@@ -181,11 +181,16 @@ widget code. Three rules, all learned by getting them wrong: 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))`. +**Fill the cell with type, and shrink only on collision.** Height is what +changes when you add a row to the grid. Size type off the width alone and a tall +cell is left sparse; size it off the height alone and a short cell shrinks the +text to a whisper. The robust way is a measured fit: `card.fit_unit()` returns +the largest base size at which every row still fits the width, and the widget +takes the smaller of that and its height budget. Larger cell, larger type; a row +that would collide pulls the size back down. The GPU and cache cards do this. A +card that cannot afford the measuring pass can still key off width with a clamp, +`math.max(lo, math.min(hi, inner.w * k))`; the clock, weather, system and disks +cards do. **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 @@ -314,9 +319,12 @@ for nil, string, table, boolean, NaN and negative arguments. It matters because the parser's input is a file written by a network fetch, and a raise here is a blank dashboard rather than a message. -**Card type scales off the cell WIDTH, not its height.** Height changes with the -grid, and scaling off it made the weather text shrink to a whisper beside the -clock's numerals when its cell got shorter. +**Type keyed on one dimension alone leaves the card broken or sparse.** The +weather text once shrank to a whisper beside the clock when its cell got shorter +because the size keyed on height; a card keyed only on width leaves a tall cell +half empty. Fill the cell instead: `card.fit_unit()` measures the rows and +returns the largest size that fits the width, the widget takes the smaller of +that and its height budget, and a row that would collide pulls the size down. **A bare `df` would silently mis-read every disk figure.** The user's shell aliases `df` to `df -h`, so `bin/disks-sample.sh` calls `/usr/bin/df -P -B1` by diff --git a/lib/card.lua b/lib/card.lua index 185703c..c83e6bc 100644 --- a/lib/card.lua +++ b/lib/card.lua @@ -205,6 +205,34 @@ function M.ring(cr, cx, cy, r, frac, colour, colors, width) cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT) end +-- The largest base size S at which every row of pieces still fits max_w, +-- measured at a reference size and scaled. +-- +-- `groups` is a list of rows; each row is a list of { text, font, factor } +-- pieces drawn at factor*S on that row. A widget uses this to derive one +-- fluid size for the whole card: the cell's height decides how large the +-- content grows, and this pulls S back down only where a row would overrun +-- the width, so type fills the card instead of leaving empty space and never +-- collides. Returns math.huge when there is nothing to measure, leaving the +-- caller's height budget in charge. +function M.fit_unit(cr, max_w, groups, ref) + if not (max_w and max_w > 0) then return math.huge end + ref = ref or 100 + local S = math.huge + for _, row in ipairs(groups) do + local w = 0 + for _, piece in ipairs(row) do + M.font(cr, piece[2], ref * piece[3], false) + w = w + (M.measure(cr, piece[1])) + end + if w > 0 then + local fit = ref * max_w / w + if fit < S then S = fit end + end + end + return S +end + -- Bytes to a short human string: 1181116006 -> '1.1G'. -- The card formats its own numbers because the samplers pass raw bytes. function M.human(bytes) diff --git a/widgets/cache.lua b/widgets/cache.lua index 0209f1a..40025eb 100644 --- a/widgets/cache.lua +++ b/widgets/cache.lua @@ -14,32 +14,68 @@ 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 - local label_size = clamp(inner.w * 0.036, 10, 16) - local big_size = clamp(inner.w * 0.095, 22, 46) - - card.font(cr, card.FONT_MONO, label_size, false) - card.rgba(cr, colors.label) - card.text(cr, inner.x, inner.y + label_size, 'CACHE') + -- 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.font(cr, card.FONT_UI, label_size * 1.2, true) - card.text(cr, inner.x, inner.y + label_size * 3.4, 'no cache data') - card.font(cr, card.FONT_MONO, label_size, false) - card.text(cr, inner.x, inner.y + label_size * 5, 'bin/cache-sample.sh') + card.text(cr, inner.x, inner.y + S, '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 - card.font(cr, card.FONT_HEAVY, big_size, false) - card.rgba(cr, colors.body) - card.text(cr, inner.x, inner.y + label_size + big_size, c.total.label) + local items = c.items or {} + local names = {} + for i, e in ipairs(items) do + local name = e.name + if #name > 18 then name = name:sub(1, 17) .. '\u{2026}' end + names[i] = name + end - local ey = inner.y + label_size + big_size + label_size * 2 - local step = label_size * 2.6 + 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) - for i, e in ipairs(c.items) do + card.rgba(cr, colors.label) + card.text(cr, inner.x, inner.y + S, '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 @@ -50,10 +86,7 @@ function M.draw(cr, rect, colors) colour = share > 0.5 and colors.critical or colors.warning end card.rgba(cr, colors.label) - -- Long names are truncated rather than allowed to collide with the size. - local name = e.name - if #name > 18 then name = name:sub(1, 17) .. '\u{2026}' end - card.text(cr, inner.x, ey, name) + 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 diff --git a/widgets/gpu.lua b/widgets/gpu.lua index 87e22f3..7933e3b 100644 --- a/widgets/gpu.lua +++ b/widgets/gpu.lua @@ -24,26 +24,48 @@ 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 - local label_size = clamp(inner.w * 0.030, 11, 18) - local big_size = clamp(inner.w * 0.085, 26, 56) - local row_size = clamp(inner.w * 0.030, 11, 17) + local pkg = data.sensor('xe', 'temp2_input') + local big_txt = pkg and string.format('%d\u{00B0}', pkg) or '--' + + -- Fluid type. One base size S drives every element. The cell's height sets + -- how large the content grows; the measured width fit pulls S back down + -- only where the header or a row would overrun the card. So a tall cell + -- gets larger type instead of empty space, and nothing ever collides. + local LABEL_F, ROW_F = 0.32, 0.34 + local row_n = #TEMPS + 1 -- VRAM + FAN + local S_h = inner.h * 0.94 / (2.088 + row_n * 0.646) + local groups = { + { { 'GPU', card.FONT_MONO, LABEL_F }, { big_txt, card.FONT_HEAVY, 1.0 } }, + { { 'ARC B580', card.FONT_MONO, LABEL_F } }, + } + for _, t in ipairs(TEMPS) do + groups[#groups + 1] = { { t[3], card.FONT_MONO, ROW_F }, + { '888\u{00B0}', card.FONT_MONO, ROW_F } } + end + groups[#groups + 1] = { { 'FAN', card.FONT_MONO, ROW_F }, + { '8888 RPM', card.FONT_MONO, ROW_F } } + 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 local y = inner.y + -- The package temperature is the headline, since there is no load to show. + -- It shares the header line with the label, set on the same baseline and + -- right-aligned, so the card's width carries the number rather than leaving + -- the right half empty under a left-set value. + card.font(cr, card.FONT_HEAVY, S, false) + card.rgba(cr, pkg and card.threshold(pkg, 75, 85, colors) or colors.label) + card.text_right(cr, inner.x + inner.w, y + S, big_txt) + card.font(cr, card.FONT_MONO, label_size, false) card.rgba(cr, colors.label) - card.text(cr, inner.x, y + label_size, 'GPU') - card.rgba(cr, colors.value) - card.text_right(cr, inner.x + inner.w, y + label_size, 'ARC B580') + card.text(cr, inner.x, y + S, 'GPU') - -- The package temperature is the headline, since there is no load to show. - local pkg = data.sensor('xe', 'temp2_input') - card.font(cr, card.FONT_HEAVY, big_size, false) - card.rgba(cr, pkg and card.threshold(pkg, 75, 85, colors) or colors.label) - card.text(cr, inner.x, y + label_size + big_size, - pkg and string.format('%d\u{00B0}', pkg) or '--') + card.rgba(cr, colors.value) + card.text(cr, inner.x, y + S + label_size * 1.7, 'ARC B580') - local ey = y + label_size + big_size + 18 + local ey = y + S + label_size * 3.4 local step = row_size * 1.9 card.font(cr, card.FONT_MONO, row_size, false) |
