mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-06-02 18:42:04 +00:00
Merge pull request #577 from MrDowntempo/feature/boomer-clock
Added option for an analog style clock on the calendar screen.
This commit is contained in:
@@ -630,6 +630,10 @@
|
||||
"show-events": {
|
||||
"label": "Kalenderereignisse anzeigen",
|
||||
"description": "Ereignisse im Kalender-Panel anzeigen."
|
||||
},
|
||||
"use-analog": {
|
||||
"description": "Eine Analoguhr auf dem Kalenderbildschirm anzeigen.",
|
||||
"label": "Analoge Uhr verwenden"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -630,6 +630,10 @@
|
||||
"show-events": {
|
||||
"label": "Show calendar events",
|
||||
"description": "Display events in the calendar panel."
|
||||
},
|
||||
"use-analog": {
|
||||
"label": "Use analog style clock",
|
||||
"description": "Show an analog style clock on the calendar screen."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -630,6 +630,10 @@
|
||||
"show-events": {
|
||||
"label": "Mostrar eventos del calendario",
|
||||
"description": "Mostrar eventos en el panel del calendario."
|
||||
},
|
||||
"use-analog": {
|
||||
"description": "Mostrar un reloj de estilo analógico en la pantalla del calendario.",
|
||||
"label": "Usar reloj de estilo analógico"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -630,6 +630,10 @@
|
||||
"show-events": {
|
||||
"label": "Afficher les événements du calendrier",
|
||||
"description": "Afficher les événements dans le panneau du calendrier."
|
||||
},
|
||||
"use-analog": {
|
||||
"description": "Afficher une horloge de style analogique sur l'écran du calendrier.",
|
||||
"label": "Utiliser une horloge de style analogique."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -592,6 +592,10 @@
|
||||
"show-events": {
|
||||
"label": "Mostrar eventos do calendário",
|
||||
"description": "Exibir eventos no painel do calendário."
|
||||
},
|
||||
"use-analog": {
|
||||
"description": "Mostrar um relógio estilo analógico na tela do calendário.",
|
||||
"label": "Use um relógio de estilo analógico."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -630,6 +630,10 @@
|
||||
"show-events": {
|
||||
"label": "显示日历事件",
|
||||
"description": "在日历面板中显示事件。"
|
||||
},
|
||||
"use-analog": {
|
||||
"description": "在日历屏幕上显示一个模拟时钟。",
|
||||
"label": "使用模拟时钟样式"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -203,6 +203,7 @@ Singleton {
|
||||
property bool use12hourFormat: false
|
||||
property bool showWeekNumberInCalendar: false
|
||||
property bool showCalendarEvents: true
|
||||
property bool analogClockInCalendar: false
|
||||
}
|
||||
|
||||
// screen recorder
|
||||
|
||||
@@ -95,16 +95,16 @@ NPanel {
|
||||
text: {
|
||||
if (!Settings.data.location.weatherEnabled)
|
||||
return ""
|
||||
if (!weatherReady)
|
||||
return ""
|
||||
var temp = LocationService.data.weather.current_weather.temperature
|
||||
var suffix = "C"
|
||||
if (Settings.data.location.useFahrenheit) {
|
||||
temp = LocationService.celsiusToFahrenheit(temp)
|
||||
suffix = "F"
|
||||
}
|
||||
temp = Math.round(temp)
|
||||
return `${temp}°${suffix}`
|
||||
if (!weatherReady)
|
||||
return ""
|
||||
var temp = LocationService.data.weather.current_weather.temperature
|
||||
var suffix = "C"
|
||||
if (Settings.data.location.useFahrenheit) {
|
||||
temp = LocationService.celsiusToFahrenheit(temp)
|
||||
suffix = "F"
|
||||
}
|
||||
temp = Math.round(temp)
|
||||
return `${temp}°${suffix}`
|
||||
}
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: Style.fontWeightBold
|
||||
@@ -174,10 +174,10 @@ NPanel {
|
||||
text: {
|
||||
if (!Settings.data.location.weatherEnabled)
|
||||
return ""
|
||||
if (!weatherReady)
|
||||
return I18n.tr("calendar.weather.loading")
|
||||
const chunks = Settings.data.location.name.split(",")
|
||||
return chunks[0]
|
||||
if (!weatherReady)
|
||||
return I18n.tr("calendar.weather.loading")
|
||||
const chunks = Settings.data.location.name.split(",")
|
||||
return chunks[0]
|
||||
}
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: Style.fontWeightMedium
|
||||
@@ -202,19 +202,147 @@ NPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Digital clock with circular progress
|
||||
// Analog clock
|
||||
Item {
|
||||
id: clockItem
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Style.marginM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
height: Math.round((Style.fontSizeXXXL * 1.9) / 2 * Style.uiScaleRatio) * 2
|
||||
width: clockItem.height
|
||||
|
||||
// Seconds circular progress
|
||||
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()
|
||||
@@ -251,6 +379,7 @@ NPanel {
|
||||
|
||||
// Digital clock
|
||||
ColumnLayout {
|
||||
visible: !Settings.data.location.analogClockInCalendar
|
||||
anchors.centerIn: parent
|
||||
spacing: -Style.marginXXS
|
||||
|
||||
@@ -279,7 +408,6 @@ NPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// ... (rest of the file is unchanged) ...
|
||||
RowLayout {
|
||||
visible: weatherReady
|
||||
Layout.fillWidth: true
|
||||
@@ -429,14 +557,14 @@ NPanel {
|
||||
if (!CalendarService.available || CalendarService.events.length === 0)
|
||||
return false
|
||||
|
||||
const targetDate = new Date(year, month, day)
|
||||
const targetStart = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()).getTime() / 1000
|
||||
const targetEnd = targetStart + 86400 // +24 hours
|
||||
const targetDate = new Date(year, month, day)
|
||||
const targetStart = new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()).getTime() / 1000
|
||||
const targetEnd = targetStart + 86400 // +24 hours
|
||||
|
||||
return CalendarService.events.some(event => {
|
||||
// Check if event starts or overlaps with this day
|
||||
return (event.start >= targetStart && event.start < targetEnd) || (event.end > targetStart && event.end <= targetEnd) || (event.start < targetStart && event.end > targetEnd)
|
||||
})
|
||||
return CalendarService.events.some(event => {
|
||||
// Check if event starts or overlaps with this day
|
||||
return (event.start >= targetStart && event.start < targetEnd) || (event.end > targetStart && event.end <= targetEnd) || (event.start < targetStart && event.end > targetEnd)
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to get events for a specific date
|
||||
@@ -444,13 +572,13 @@ NPanel {
|
||||
if (!CalendarService.available || CalendarService.events.length === 0)
|
||||
return []
|
||||
|
||||
const targetDate = new Date(year, month, day)
|
||||
const targetStart = Math.floor(new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()).getTime() / 1000)
|
||||
const targetEnd = targetStart + 86400 // +24 hours
|
||||
const targetDate = new Date(year, month, day)
|
||||
const targetStart = Math.floor(new Date(targetDate.getFullYear(), targetDate.getMonth(), targetDate.getDate()).getTime() / 1000)
|
||||
const targetEnd = targetStart + 86400 // +24 hours
|
||||
|
||||
return CalendarService.events.filter(event => {
|
||||
return (event.start >= targetStart && event.start < targetEnd) || (event.end > targetStart && event.end <= targetEnd) || (event.start < targetStart && event.end > targetEnd)
|
||||
})
|
||||
return CalendarService.events.filter(event => {
|
||||
return (event.start >= targetStart && event.start < targetEnd) || (event.end > targetStart && event.end <= targetEnd) || (event.start < targetStart && event.end > targetEnd)
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to check if an event is all-day
|
||||
@@ -550,9 +678,9 @@ NPanel {
|
||||
color: {
|
||||
if (model.today)
|
||||
return Color.mOnSecondary
|
||||
if (model.month === grid.month)
|
||||
return Color.mOnSurface
|
||||
return Color.mOnSurfaceVariant
|
||||
if (model.month === grid.month)
|
||||
return Color.mOnSurface
|
||||
return Color.mOnSurfaceVariant
|
||||
}
|
||||
opacity: model.month === grid.month ? 1.0 : 0.4
|
||||
pointSize: Style.fontSizeM
|
||||
@@ -617,15 +745,6 @@ NPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getISOWeekNumber(date) {
|
||||
const target = new Date(date.getTime())
|
||||
target.setHours(0, 0, 0, 0)
|
||||
const dayOfWeek = target.getDay() || 7
|
||||
target.setDate(target.getDate() + 4 - dayOfWeek)
|
||||
const yearStart = new Date(target.getFullYear(), 0, 1)
|
||||
const weekNumber = Math.ceil(((target - yearStart) / 86400000 + 1) / 7)
|
||||
return weekNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,13 @@ ColumnLayout {
|
||||
checked: Settings.data.location.showCalendarEvents
|
||||
onToggled: checked => Settings.data.location.showCalendarEvents = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.location.date-time.use-analog.label")
|
||||
description: I18n.tr("settings.location.date-time.use-analog.description")
|
||||
checked: Settings.data.location.analogClockInCalendar
|
||||
onToggled: checked => Settings.data.location.analogClockInCalendar = checked
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
|
||||
Reference in New Issue
Block a user