Seconds per clock, and type that scales with the bar height

Seconds move from one bar-wide switch to a per-clock setting. A clock's own
`showSeconds` wins, the top-level one is the default for clocks with no
opinion, and the centre clock gets `local.showSeconds` since it has no entry in
`clocks[]`. An explicit false on a clock beats a true default, so the check is
for the property's presence rather than its truthiness.

The tick rate follows what is actually displayed: `anyShowsSeconds()` decides
between a per-second and a per-minute SystemClock, so a bar showing no seconds
does not wake the shell sixty times as often to redraw an identical minute.

`barSize` is now editable from the popup, and everything drawn on the bar is
sized against the theme's own bar height rather than pinned to the theme's
12px type. A taller bar carries proportionally larger text, gaps and rule; at
the theme's own height the scale is exactly 1, so nothing moves. Heights are
clamped to 12-120 rather than rejected, so a value the editor can produce can
never be one the config layer silently discards.

Fixes a latent bug this surfaced: updateRow() rebuilt a draft row from a fixed
pair of fields, so editing a clock's label would have thrown away its own
seconds setting. It now copies every key the row had.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MhxtL7WzL5XmuTd8GHbcTY
This commit is contained in:
Harvey
2026-09-04 18:02:36 +02:00
co-authored by Claude Opus 5
parent b5f4569233
commit 8364fe2141
6 changed files with 249 additions and 29 deletions
+93 -7
View File
@@ -24,6 +24,9 @@ Item {
property int workStart: 8
property int workEnd: 18
property bool dimOutside: true
property bool defaultSeconds: false
property bool localSeconds: false
property int barSize: 0
readonly property color background: Color.popups.background
readonly property color foreground: Color.popups.text
@@ -56,24 +59,38 @@ Item {
var cfg = root.configFile ? root.configFile.config : null
var clocks = cfg && Array.isArray(cfg.clocks) ? cfg.clocks : []
var copy = []
for (var i = 0; i < clocks.length; i++)
copy.push({ label: String(clocks[i].label || ""), timezone: String(clocks[i].timezone || "") })
for (var i = 0; i < clocks.length; i++) {
var row = { label: String(clocks[i].label || ""), timezone: String(clocks[i].timezone || "") }
if (typeof clocks[i].showSeconds === "boolean") row.showSeconds = clocks[i].showSeconds
copy.push(row)
}
root.draft = copy
if (cfg && cfg.workingHours) {
root.workStart = Number(cfg.workingHours.start)
root.workEnd = Number(cfg.workingHours.end)
}
root.dimOutside = cfg ? cfg.dimOutsideWorkingHours !== false : true
root.defaultSeconds = cfg ? cfg.showSeconds === true : false
root.localSeconds = Model.showsSeconds(cfg ? cfg.local : null, root.defaultSeconds)
root.barSize = cfg && cfg.barSize > 0 ? cfg.barSize : 0
}
// What a row's toggle shows: its own setting when it has one, otherwise the
// bar-wide default it is inheriting.
function rowShowsSeconds(row) { return Model.showsSeconds(row, root.defaultSeconds) }
// QML only re-renders a var array when the reference changes, so every
// mutation rebuilds it rather than editing in place.
function replaceDraft(next) { root.draft = next }
// Copies every key the row already had. Rebuilding it from a fixed pair of
// fields silently dropped anything else on it — editing a label would have
// thrown away that clock's own seconds setting.
function updateRow(index, key, value) {
var next = root.draft.slice()
if (!next[index]) return
var row = { label: next[index].label, timezone: next[index].timezone }
var row = {}
for (var k in next[index]) row[k] = next[index][k]
row[key] = value
next[index] = row
replaceDraft(next)
@@ -99,7 +116,11 @@ Item {
var tz = String(root.draft[i].timezone || "").replace(/^\s+|\s+$/g, "")
if (!tz) continue
var label = String(root.draft[i].label || "").replace(/^\s+|\s+$/g, "")
out.push({ label: label || tz.split("/").pop().replace(/_/g, " "), timezone: tz })
var row = { label: label || tz.split("/").pop().replace(/_/g, " "), timezone: tz }
// Only clocks the user actually set keep an explicit flag; the rest go
// on inheriting the bar-wide default.
if (typeof root.draft[i].showSeconds === "boolean") row.showSeconds = root.draft[i].showSeconds
out.push(row)
}
return out
}
@@ -116,8 +137,9 @@ Item {
workdays: cfg && cfg.workingHours ? cfg.workingHours.workdays : [1, 2, 3, 4, 5]
},
dimOutsideWorkingHours: root.dimOutside,
showSeconds: cfg ? cfg.showSeconds === true : false,
barSize: cfg ? cfg.barSize : 0
showSeconds: root.defaultSeconds,
local: { showSeconds: root.localSeconds },
barSize: root.barSize
})
close()
}
@@ -211,7 +233,7 @@ Item {
spacing: Style.space(8)
TextField {
Layout.preferredWidth: Style.space(74)
Layout.preferredWidth: Style.space(66)
text: row.modelData.label
placeholderText: "Label"
foreground: root.foreground
@@ -245,6 +267,18 @@ Item {
font.pixelSize: Style.font.caption
}
Button {
text: "SS"
bordered: true
selected: root.rowShowsSeconds(row.modelData)
tooltipText: root.rowShowsSeconds(row.modelData)
? "Showing seconds — click for HH:MM"
: "Show seconds on this clock"
foreground: root.foreground
accent: root.accent
onClicked: root.updateRow(row.index, "showSeconds", !root.rowShowsSeconds(row.modelData))
}
Button {
iconText: "󰅙"
tooltipText: "Remove this clock"
@@ -346,6 +380,58 @@ Item {
PanelSeparator { Layout.fillWidth: true }
PanelSectionHeader {
Layout.fillWidth: true
text: "Bar"
}
RowLayout {
Layout.fillWidth: true
spacing: Style.space(8)
Text {
Layout.fillWidth: true
text: "Seconds on your local clock"
color: root.foreground
elide: Text.ElideRight
font.family: Style.font.family
font.pixelSize: Style.font.body
}
ToggleSwitch {
checked: root.localSeconds
foreground: root.foreground
accent: root.accent
onToggled: root.localSeconds = !root.localSeconds
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.space(8)
Text {
Layout.fillWidth: true
text: "Bar height — 0 follows the theme. Text scales with it."
color: root.muted
elide: Text.ElideRight
font.family: Style.font.family
font.pixelSize: Style.font.caption
}
NumberField {
Layout.preferredWidth: Style.spacing.numberFieldWidth
value: root.barSize
from: 0
to: 120
foreground: root.foreground
accent: root.accent
onModified: function (v) { root.barSize = v }
}
}
PanelSeparator { Layout.fillWidth: true }
RowLayout {
Layout.fillWidth: true
spacing: Style.space(8)