diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 19:01:50 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 19:01:50 +0200 |
| commit | d9fb5c42e8a8deed21d93b6961a0775acea5540a (patch) | |
| tree | a19aac68ab66f37b6a3e2a20b063598a83f602be | |
| parent | db50ae8b65f6be6b2c6d9e2a9a019c8b21adfaab (diff) | |
| download | conky-theme-udt-d9fb5c42e8a8deed21d93b6961a0775acea5540a.tar.gz conky-theme-udt-d9fb5c42e8a8deed21d93b6961a0775acea5540a.zip | |
fix: restore the line width card.ring sets, add card.truncate
A ring left its line width behind, so the next stroke on the card inherited
a hairline several pixels thick. It already restored the cap for the same
reason.
card.truncate replaces byte slicing, which cuts a multi-byte character in
half and emits an invalid sequence Cairo draws as a box.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| -rw-r--r-- | lib/card.lua | 28 | ||||
| -rw-r--r-- | test/test_data.lua | 28 |
2 files changed, 54 insertions, 2 deletions
diff --git a/lib/card.lua b/lib/card.lua index c83e6bc..f25c393 100644 --- a/lib/card.lua +++ b/lib/card.lua @@ -188,6 +188,7 @@ function M.ring(cr, cx, cy, r, frac, colour, colors, width) -- and cairo_arc() joins to it with a straight line, so without this every -- ring after a label is drawn with a stray chord to the text baseline. cairo_new_path(cr) + local prev_width = cairo_get_line_width(cr) cairo_set_line_width(cr, width) cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND) @@ -200,9 +201,11 @@ function M.ring(cr, cx, cy, r, frac, colour, colors, width) cairo_arc(cr, cx, cy, r, -math.pi / 2, -math.pi / 2 + math.pi * 2 * frac) cairo_stroke(cr) end - -- Leave the cap as it was found: a later stroke inheriting ROUND would get - -- visibly rounded ends on the card's hairlines. + -- Leave the line state as it was found. A later stroke inheriting ROUND gets + -- visibly rounded ends on the card's hairlines, and one inheriting this + -- width gets a hairline several pixels thick. cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT) + cairo_set_line_width(cr, prev_width) end -- The largest base size S at which every row of pieces still fits max_w, @@ -246,4 +249,25 @@ function M.human(bytes) return string.format('%.1f%s', n, units[i]) end +-- Shorten a string to `limit` characters, appending an ellipsis when it cuts. +-- +-- By character, never by byte: Lua's string.sub counts bytes, so slicing a +-- UTF-8 name mid-sequence emits an invalid byte, which Cairo draws as a +-- replacement box. A name long enough to need shortening is exactly the kind +-- likely to carry an accent. +-- +-- This counts codepoints, not rendered width, so it does not account for a +-- double-width CJK glyph. That is the right trade here: the strings it cuts +-- are cache directory names and hardware model strings. Measure with +-- M.measure when true rendered width matters. +function M.truncate(s, limit) + s = tostring(s or '') + limit = tonumber(limit) or 0 + if limit <= 0 then return '' end + if utf8.len(s) == nil then return s:sub(1, limit) end -- not valid UTF-8: byte-slice + if utf8.len(s) <= limit then return s end + local cut = utf8.offset(s, limit) -- byte index of the limit'th character + return s:sub(1, cut - 1) .. '\u{2026}' +end + return M diff --git a/test/test_data.lua b/test/test_data.lua index a90413b..4d3280c 100644 --- a/test/test_data.lua +++ b/test/test_data.lua @@ -6,6 +6,13 @@ package.path = './?.lua;' .. package.path local data = require 'lib.data' +-- lib.card uses Cairo globals (cairo_*, CAIRO_*) without requiring 'cairo' +-- itself, so those globals must exist before it loads. Conky provides them; +-- plain lua needs its loadable Cairo binding on the cpath first, exactly as +-- test/render.lua does. +package.cpath = '/usr/lib64/conky/lib?.so;' .. package.cpath +require 'cairo' + local function read(path) local f = assert(io.open(path, 'r')) local s = f:read('*a') @@ -150,4 +157,25 @@ assert(cache.items[1].bytes / cache.total.bytes > 0.5, assert(data.du_parse('') == nil, 'empty input gives nil') assert(data.du_parse(nil) == nil, 'nil gives nil') +-- === card.truncate ======================================================== +-- Truncation is by CHARACTER, not byte: slicing a UTF-8 string mid-sequence +-- emits an invalid byte that Cairo draws as a replacement box, and the name +-- that needed shortening is exactly the kind that carries accents. +local card = require 'lib.card' + +assert(card.truncate('short', 10) == 'short', 'a short string is unchanged') +assert(card.truncate('exactlyten', 10) == 'exactlyten', 'a string at the limit is unchanged') +assert(card.truncate('abcdefghijkl', 10) == 'abcdefghi\u{2026}', + 'a long string is cut to limit-1 plus an ellipsis, got ' .. tostring(card.truncate('abcdefghijkl', 10))) + +-- The multi-byte case: ten accented characters are 20 bytes, so a byte-based +-- slice would cut one in half and produce invalid UTF-8. +local accented = string.rep('\u{00E9}', 12) +local cut = card.truncate(accented, 10) +assert(cut == string.rep('\u{00E9}', 9) .. '\u{2026}', + 'accented input must cut on a character boundary, got ' .. tostring(cut)) + +assert(card.truncate(nil, 10) == '', 'nil truncates to empty') +assert(card.truncate('abc', 0) == '', 'a zero limit gives empty') + print('test_data: all assertions passed') |
