// State glyphs, drawn with cairo alone. // // The panel is monochrome, so shape is the only channel left and these four // have to stay apart at 14 px. Kept free of St/Clutter/GNOME imports so the // shapes can be rendered to a file and looked at, rather than guessed about. /** Draw `state` filling the given box, in the colour passed as {r,g,b,a} 0..1. */ export function drawState(cr, state, width, height, color) { const { r, g, b, a } = color; const LINE = 1.75; const cx = width / 2; const cy = height / 2; // Centreline of the ring. A stroke straddles it, so a disc filled to this // radius reads visibly smaller than a ring drawn at it -- the filled states // below use `outer` instead, which keeps every glyph the same size. const radius = Math.min(width, height) / 2 - LINE; const outer = radius + LINE / 2; cr.setLineWidth(LINE); cr.setSourceRGBA(r, g, b, a); switch (state) { case 'blocked': // Disc inside a ring: the most ink of the four, for the only state // where a session is stuck until you act. The inner disc has to be // big enough to register at 14 px, or this reads as plain "working". cr.arc(cx, cy, radius, 0, 2 * Math.PI); cr.stroke(); // 0.55 of the ring radius: below that the centre stops registering at // 14 px and this collapses into the "working" ring; above it the gap // closes and it collapses into the "waiting" disc. Checked by // rendering, not guessed. cr.arc(cx, cy, radius * 0.55, 0, 2 * Math.PI); cr.fill(); break; case 'waiting': cr.arc(cx, cy, outer, 0, 2 * Math.PI); cr.fill(); break; case 'busy': cr.arc(cx, cy, radius, 0, 2 * Math.PI); cr.stroke(); break; default: // Idle: same ring as "working", but dashed and dimmed. Reading as a // fainter version of a state you already know is the point -- nobody // is waiting on it. cr.setDash([2, 2], 0); cr.setSourceRGBA(r, g, b, a * 0.5); cr.arc(cx, cy, radius, 0, 2 * Math.PI); cr.stroke(); cr.setDash([], 0); break; } }