From 3c69d5b4da9c3586a72e823770acc57dec0f7f7d Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Mon, 3 Nov 2025 18:32:46 +0100 Subject: [PATCH] NClock: factorize NClockAnalog/Digital i18n: rephrase analog clock translation --- Assets/Translations/de.json | 2 +- Assets/Translations/en.json | 2 +- Assets/Translations/es.json | 2 +- Assets/Translations/fr.json | 2 +- Assets/Translations/pt.json | 2 +- Assets/Translations/zh-CN.json | 2 +- Modules/Bar/Calendar/CalendarPanel.qml | 3 +- Modules/LockScreen/LockScreen.qml | 3 +- Widgets/NClock.qml | 273 +++++++++++++++++++++++++ 9 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 Widgets/NClock.qml diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 8d320903..536ca936 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -693,7 +693,7 @@ "description": "Ereignisse im Kalender-Panel anzeigen." }, "use-analog": { - "description": "Eine Analoguhr auf dem Kalenderbildschirm anzeigen.", + "description": "Eine Analoguhr im Kalenderfenster und auf dem Sperrbildschirm anzeigen.", "label": "Analoge Uhr verwenden" }, "first-day-of-week": { diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 4839d622..2650fcc7 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -699,7 +699,7 @@ }, "use-analog": { "label": "Use analog style clock", - "description": "Show an analog style clock on the calendar screen." + "description": "Show an analog style clock on the calendar window and lock screen." } } }, diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 79f8d2df..96df1dd8 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -693,7 +693,7 @@ "description": "Mostrar eventos en el panel del calendario." }, "use-analog": { - "description": "Mostrar un reloj de estilo analógico en la pantalla del calendario.", + "description": "Mostrar un reloj de estilo analógico en la ventana del calendario y en la pantalla de bloqueo.", "label": "Usar reloj de estilo analógico" }, "first-day-of-week": { diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 44467e4d..7578df55 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -693,7 +693,7 @@ "description": "Afficher les événements dans le panneau du calendrier." }, "use-analog": { - "description": "Afficher une horloge de style analogique sur l'écran du calendrier.", + "description": "Afficher une horloge de style analogique dans la fenêtre du calendrier et sur l'écran de verrouillage.", "label": "Utiliser une horloge de style analogique." }, "first-day-of-week": { diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 9f09faba..e2c99b7f 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -655,7 +655,7 @@ "description": "Exibir eventos no painel do calendário." }, "use-analog": { - "description": "Mostrar um relógio estilo analógico na tela do calendário.", + "description": "Mostrar um relógio estilo analógico na janela do calendário e na tela de bloqueio.", "label": "Use um relógio de estilo analógico." }, "first-day-of-week": { diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 7077817a..9930341b 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -693,7 +693,7 @@ "description": "在日历面板中显示事件。" }, "use-analog": { - "description": "在日历屏幕上显示一个模拟时钟。", + "description": "在日历窗口和锁定屏幕上显示模拟时钟。", "label": "使用模拟时钟样式" }, "first-day-of-week": { diff --git a/Modules/Bar/Calendar/CalendarPanel.qml b/Modules/Bar/Calendar/CalendarPanel.qml index a2317d61..307300ba 100644 --- a/Modules/Bar/Calendar/CalendarPanel.qml +++ b/Modules/Bar/Calendar/CalendarPanel.qml @@ -201,11 +201,12 @@ NPanel { } // Analog clock - ClockLoader { + NClock { id: clockLoader anchors.right: parent.right anchors.rightMargin: Style.marginXL anchors.verticalCenter: parent.verticalCenter + clockStyle: Settings.data.location.analogClockInCalendar ? "analog" : "digital" progressColor: Color.mOnPrimary Layout.alignment: Qt.AlignVCenter now: root.now diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml index 61c16980..59bac6b3 100644 --- a/Modules/LockScreen/LockScreen.qml +++ b/Modules/LockScreen/LockScreen.qml @@ -365,8 +365,9 @@ Loader { } // Clock - ClockLoader { + NClock { now: Time.date + clockStyle: Settings.data.location.analogClockInCalendar ? "analog" : "digital" Layout.preferredWidth: 70 Layout.preferredHeight: 70 Layout.alignment: Qt.AlignVCenter diff --git a/Widgets/NClock.qml b/Widgets/NClock.qml new file mode 100644 index 00000000..c77774e9 --- /dev/null +++ b/Widgets/NClock.qml @@ -0,0 +1,273 @@ +import QtQuick +import QtQuick.Layouts +import qs.Commons +import qs.Widgets +import qs.Services +import Quickshell +import "../Helpers/ColorsConvert.js" as ColorsConvert + +Item { + id: root + + property var now: Time.date + + // Style: "analog" or "digital" + property string clockStyle: "analog" + + // Color properties + property color backgroundColor: Color.mPrimary + property color clockColor: Color.mOnPrimary + + property color secondHandColor: { + var defaultColor = Color.mError + var bestContrast = 1.0 // 1.0 is "no contrast" + var bestColor = defaultColor + var candidates = [Color.mSecondary, Color.mTertiary, Color.mError] + + const minContrast = 1.149 + + for (var i = 0; i < candidates.length; i++) { + var candidate = candidates[i] + var contrastClock = ColorsConvert.getContrastRatio(candidate.toString(), clockColor.toString()) + if (contrastClock < minContrast) { + continue + } + var contrastBg = ColorsConvert.getContrastRatio(candidate.toString(), backgroundColor.toString()) + if (contrastBg < minContrast) { + continue + } + + var currentWorstContrast = Math.min(contrastBg, contrastClock) + + if (currentWorstContrast > bestContrast) { + bestContrast = currentWorstContrast + bestColor = candidate + } + } + + return bestColor + } + + property color progressColor: root.secondHandColor + + height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2 + width: root.height + + Loader { + id: clockLoader + anchors.fill: parent + + sourceComponent: root.clockStyle === "analog" ? analogClockComponent : digitalClockComponent + + onLoaded: { + item.now = Qt.binding(function () { + return root.now + }) + item.backgroundColor = Qt.binding(function () { + return root.backgroundColor + }) + item.clockColor = Qt.binding(function () { + return root.clockColor + }) + if (item.hasOwnProperty("secondHandColor")) { + item.secondHandColor = Qt.binding(function () { + return root.secondHandColor + }) + } + if (item.hasOwnProperty("progressColor")) { + item.progressColor = Qt.binding(function () { + return root.progressColor + }) + } + } + } + + // Analog Clock Component + component NClockAnalog: Item { + property var now + property color backgroundColor: Color.mPrimary + property color clockColor: Color.mOnPrimary + property color secondHandColor: Color.mError + anchors.fill: parent + + Canvas { + id: clockCanvas + anchors.fill: parent + + property int hours: now.getHours() + property int minutes: now.getMinutes() + property int seconds: now.getSeconds() + + onPaint: { + const markAlpha = 0.7 + var ctx = getContext("2d") + ctx.reset() + ctx.translate(width / 2, height / 2) + var radius = Math.min(width, height) / 2 + + // Hour marks + ctx.strokeStyle = Qt.alpha(clockColor, markAlpha) + ctx.lineWidth = 2 * Style.uiScaleRatio + var scaleFactor = 0.7 + + for (var i = 0; i < 12; i++) { + var scaleFactor = 0.8 + if (i % 3 === 0) { + scaleFactor = 0.65 + } + ctx.save() + ctx.rotate(i * Math.PI / 6) + ctx.beginPath() + ctx.moveTo(0, -radius * scaleFactor) + ctx.lineTo(0, -radius) + ctx.stroke() + ctx.restore() + } + + // Hour hand + ctx.save() + var hourAngle = (hours % 12 + minutes / 60) * Math.PI / 6 + ctx.rotate(hourAngle) + ctx.strokeStyle = clockColor + ctx.lineWidth = 3 * Style.uiScaleRatio + ctx.lineCap = "round" + ctx.beginPath() + ctx.moveTo(0, 0) + ctx.lineTo(0, -radius * 0.6) + ctx.stroke() + ctx.restore() + + // Minute hand + ctx.save() + var minuteAngle = (minutes + seconds / 60) * Math.PI / 30 + ctx.rotate(minuteAngle) + ctx.strokeStyle = clockColor + ctx.lineWidth = 2 * Style.uiScaleRatio + ctx.lineCap = "round" + ctx.beginPath() + ctx.moveTo(0, 0) + ctx.lineTo(0, -radius * 0.9) + ctx.stroke() + ctx.restore() + + // Second hand + ctx.save() + var secondAngle = seconds * Math.PI / 30 + ctx.rotate(secondAngle) + ctx.strokeStyle = secondHandColor + ctx.lineWidth = 1.6 * Style.uiScaleRatio + ctx.lineCap = "round" + ctx.beginPath() + ctx.moveTo(0, 0) + ctx.lineTo(0, -radius) + ctx.stroke() + ctx.restore() + + // Center dot + ctx.beginPath() + ctx.arc(0, 0, 3 * Style.uiScaleRatio, 0, 2 * Math.PI) + ctx.fillStyle = clockColor + ctx.fill() + } + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: { + clockCanvas.hours = now.getHours() + clockCanvas.minutes = now.getMinutes() + clockCanvas.seconds = now.getSeconds() + clockCanvas.requestPaint() + } + } + + Component.onCompleted: requestPaint() + } + } + + // Digital Clock Component + component NClockDigital: Item { + property var now + property color backgroundColor: Color.mPrimary + property color clockColor: Color.mOnPrimary + property color progressColor: Color.mError + + anchors.fill: parent + + // Digital clock's seconds circular progress + Canvas { + id: secondsProgress + anchors.fill: parent + property real progress: now.getSeconds() / 60 + onProgressChanged: requestPaint() + Connections { + target: Time + function onDateChanged() { + const total = now.getSeconds() * 1000 + now.getMilliseconds() + secondsProgress.progress = total / 60000 + } + } + onPaint: { + var ctx = getContext("2d") + var centerX = width / 2 + var centerY = height / 2 + var radius = Math.min(width, height) / 2 - 3 + ctx.reset() + + // Background circle + ctx.beginPath() + ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI) + ctx.lineWidth = 2.5 + ctx.strokeStyle = Qt.alpha(clockColor, 0.15) + ctx.stroke() + + // Progress arc + ctx.beginPath() + ctx.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + progress * 2 * Math.PI) + ctx.lineWidth = 2.5 + ctx.strokeStyle = progressColor + ctx.lineCap = "round" + ctx.stroke() + } + } + + // Digital clock + ColumnLayout { + anchors.centerIn: parent + spacing: -Style.marginXXS + + NText { + text: { + var t = Settings.data.location.use12hourFormat ? I18n.locale.toString(now, "hh AP") : I18n.locale.toString(now, "HH") + return t.split(" ")[0] + } + + pointSize: Style.fontSizeXS + font.weight: Style.fontWeightBold + color: clockColor + family: Settings.data.ui.fontFixed + Layout.alignment: Qt.AlignHCenter + } + + NText { + text: Qt.formatTime(now, "mm") + pointSize: Style.fontSizeXXS + font.weight: Style.fontWeightBold + color: clockColor + family: Settings.data.ui.fontFixed + Layout.alignment: Qt.AlignHCenter + } + } + } + + Component { + id: analogClockComponent + NClockAnalog {} + } + + Component { + id: digitalClockComponent + NClockDigital {} + } +}