diff --git a/Modules/Bar/Calendar/AnalogClock.qml b/Modules/Bar/Calendar/AnalogClock.qml new file mode 100644 index 00000000..1fc87f2d --- /dev/null +++ b/Modules/Bar/Calendar/AnalogClock.qml @@ -0,0 +1,107 @@ +import QtQuick +import qs.Commons +import Quickshell + + +Item { + property var now + property color backgroundColor: Color.mPrimary + property color clockColor: Color.mOnPrimary + property color secondHandColor: Color.mError + property real markAlpha: 0.7 + 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: { + 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() + } +} diff --git a/Modules/Bar/Calendar/CalendarPanel.qml b/Modules/Bar/Calendar/CalendarPanel.qml index 20e5a7e6..9941f309 100644 --- a/Modules/Bar/Calendar/CalendarPanel.qml +++ b/Modules/Bar/Calendar/CalendarPanel.qml @@ -215,204 +215,13 @@ NPanel { } // Analog clock - Item { - id: clockItem + ClockLoader { anchors.right: parent.right anchors.rightMargin: Style.marginM anchors.verticalCenter: parent.verticalCenter + progressColor: Color.mOnPrimary Layout.alignment: Qt.AlignVCenter - height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2 - width: clockItem.height - - Canvas { - id: clockCanvas - visible: Settings.data.location.analogClockInCalendar - anchors.fill: parent - - property int hours: now.getHours() - property int minutes: now.getMinutes() - property int seconds: now.getSeconds() - property real markAlpha: 0.7 - property color secondHandColor: { - var defaultColor = Color.mError - var backgroundL = Color.mPrimary.hslLightness - var hourMarkL = (Color.mOnPrimary.hslLightness * markAlpha) + (backgroundL * (1.0 - markAlpha)) - - var bestWorstContrast = -1 - var bestColor = defaultColor - - var candidates = [Color.mSecondary, Color.mTertiary, Color.mError] - - for (var i = 0; i < candidates.length; i++) { - var candidateColor = candidates[i] - var candidateL = candidateColor.hslLightness - - var diffBackground = Math.abs(backgroundL - candidateL) - var diffHourMark = Math.abs(hourMarkL - candidateL) - - var currentWorstContrast = Math.min(diffBackground, diffHourMark) - - if (currentWorstContrast > bestWorstContrast) { - bestWorstContrast = currentWorstContrast - bestColor = candidateColor - } - } - - return bestColor - } - - onPaint: { - 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(Color.mOnPrimary, 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 = Color.mOnPrimary - 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 = Color.mOnPrimary - 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 = Color.mOnPrimary - 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's seconds circular progress - Canvas { - id: secondsProgress - visible: !Settings.data.location.analogClockInCalendar - 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(Color.mOnPrimary, 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 = Color.mOnPrimary - ctx.lineCap = "round" - ctx.stroke() - } - } - - // Digital clock - ColumnLayout { - visible: !Settings.data.location.analogClockInCalendar - anchors.centerIn: parent - spacing: -Style.marginXXS - - NText { - text: { - var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(now, "hh AP") : Qt.locale().toString(now, "HH") - return t.split(" ")[0] - } - - pointSize: Style.fontSizeXS - font.weight: Style.fontWeightBold - color: Color.mOnPrimary - family: Settings.data.ui.fontFixed - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: Qt.formatTime(now, "mm") - pointSize: Style.fontSizeXXS - font.weight: Style.fontWeightBold - color: Color.mOnPrimary - family: Settings.data.ui.fontFixed - Layout.alignment: Qt.AlignHCenter - } - } + now: root.now } } diff --git a/Modules/Bar/Calendar/ClockLoader.qml b/Modules/Bar/Calendar/ClockLoader.qml new file mode 100644 index 00000000..ae027702 --- /dev/null +++ b/Modules/Bar/Calendar/ClockLoader.qml @@ -0,0 +1,79 @@ +import QtQuick +import qs.Commons +import qs.Services +import Quickshell + +Item { + id: clockRoot + + property var now + + // Default colors + property color backgroundColor: Color.mPrimary + property color clockColor: Color.mOnPrimary + readonly property real markAlpha: 0.7 // alpha value of hour markers in AnalogClock + property color secondHandColor: { + var defaultColor = Color.mError + var backgroundL = backgroundColor.hslLightness + var hourMarkL + + if (Settings.data.location.analogClockInCalendar) { + hourMarkL = (clockColor.hslLightness * markAlpha) + (backgroundL * (1.0 - markAlpha)) + } else { + hourMarkL = backgroundL + } + + var bestWorstContrast = -1 + var bestColor = defaultColor + + var candidates = [ + Color.mSecondary, + Color.mTertiary, + Color.mError, + ] + + for (var i = 0; i < candidates.length; i++) { + var candidateColor = candidates[i] + var candidateL = candidateColor.hslLightness + + var diffBackground = Math.abs(backgroundL - candidateL) + var diffHourMark = Math.abs(hourMarkL - candidateL) + + var currentWorstContrast = Math.min(diffBackground, diffHourMark) + + if (currentWorstContrast > bestWorstContrast) { + bestWorstContrast = currentWorstContrast + bestColor = candidateColor + } + } + + return bestColor + } + property color progressColor: clockRoot.secondHandColor + + + height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2 + width: clockRoot.height + + Loader { + id: clockLoader + anchors.fill: parent + + source: Settings.data.location.analogClockInCalendar ? "AnalogClock.qml" : "DigitalClock.qml" + + onLoaded: { + item.now = Qt.binding(function() { return clockRoot.now }) + item.backgroundColor = Qt.binding(function() { return clockRoot.backgroundColor }) + item.clockColor = Qt.binding(function() { return clockRoot.clockColor }) + if (item.hasOwnProperty("secondHandColor")) { + item.secondHandColor = Qt.binding(function() { return clockRoot.secondHandColor }) + } + if (item.hasOwnProperty("progressColor")) { + item.progressColor = Qt.binding(function() { return clockRoot.progressColor }) + } + if (item.hasOwnProperty("markAlpha")) { + item.markAlpha = Qt.binding(function() { return clockRoot.markAlpha }) + } + } + } +} diff --git a/Modules/Bar/Calendar/DigitalClock.qml b/Modules/Bar/Calendar/DigitalClock.qml new file mode 100644 index 00000000..f5ff2ccb --- /dev/null +++ b/Modules/Bar/Calendar/DigitalClock.qml @@ -0,0 +1,79 @@ +import QtQuick +import QtQuick.Layouts +import qs.Commons +import qs.Widgets +import Quickshell + + +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 ? Qt.locale().toString(now, "hh AP") : Qt.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 + } + } +} diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml index 92d7a443..370fa202 100644 --- a/Modules/LockScreen/LockScreen.qml +++ b/Modules/LockScreen/LockScreen.qml @@ -12,6 +12,7 @@ import qs.Commons import qs.Services import qs.Widgets import qs.Modules.Audio +import qs.Modules.Bar.Calendar Loader { id: lockScreen @@ -363,80 +364,15 @@ Loader { Layout.fillWidth: true } - Item { + // Clock + ClockLoader { + now: Time.date Layout.preferredWidth: 70 Layout.preferredHeight: 70 Layout.alignment: Qt.AlignVCenter - - // Seconds circular progress - Canvas { - id: secondsProgress - anchors.fill: parent - - property real progress: Time.date.getSeconds() / 60 - onProgressChanged: requestPaint() - - Connections { - target: Time - function onDateChanged() { - const total = Time.date.getSeconds() * 1000 + Time.date.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(Color.mOnSurface, 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 = Color.mPrimary - ctx.lineCap = "round" - ctx.stroke() - } - } - - // Digital clock - ColumnLayout { - anchors.centerIn: parent - spacing: 0 - - NText { - text: { - var t = Settings.data.location.use12hourFormat ? Qt.locale().toString(Time.date, "hh AP") : Qt.locale().toString(Time.date, "HH") - return t - } - pointSize: Style.fontSizeM - font.weight: Style.fontWeightBold - family: Settings.data.ui.fontFixed - color: Color.mOnSurface - horizontalAlignment: Text.AlignHCenter - Layout.alignment: Qt.AlignHCenter - } - - NText { - text: Qt.formatTime(Time.date, "mm") - pointSize: Style.fontSizeM - font.weight: Style.fontWeightBold - family: Settings.data.ui.fontFixed - color: Color.mOnSurfaceVariant - horizontalAlignment: Text.AlignHCenter - Layout.alignment: Qt.AlignHCenter - } - } + backgroundColor: Color.mSurface + clockColor: Color.mOnSurface + secondHandColor: Color.mPrimary } } }