DarkModeService: Add settings and manual scheduling mode.

This commit is contained in:
Leopold Luley
2025-10-16 17:23:32 +02:00
parent a6c0a9fc99
commit 49f4ab114f
4 changed files with 189 additions and 13 deletions

View File

@@ -354,6 +354,9 @@ Singleton {
property bool useWallpaperColors: false
property string predefinedScheme: "Noctalia (default)"
property bool darkMode: true
property string schedulingMode: "off"
property string manualSunrise: "06:30"
property string manualSunset: "18:30"
property string matugenSchemeType: "scheme-fruit-salad"
property bool generateTemplatesForPredefined: true
}

View File

@@ -13,6 +13,24 @@ ColumnLayout {
property var schemeColorsCache: ({})
property int cacheVersion: 0 // Increment to trigger UI updates
// Time dropdown options (00:00 .. 23:30)
ListModel {
id: timeOptions
}
Component.onCompleted: {
for (var h = 0; h < 24; h++) {
for (var m = 0; m < 60; m += 30) {
var hh = ("0" + h).slice(-2)
var mm = ("0" + m).slice(-2)
var key = hh + ":" + mm
timeOptions.append({
"key": key,
"name": key
})
}
}
}
spacing: Style.marginL
// Helper function to extract scheme name from path
@@ -148,6 +166,77 @@ ColumnLayout {
}
}
NComboBox {
label: "Dark Mode Schedule"
description: "Enables automatic switching between light and dark mode"
model: [{
"name": "Off",
"key": "off"
}, {
"name": "Manual",
"key": "manual"
}, {
"name": "Sunrise/Sunset",
"key": "location"
}]
currentKey: Settings.data.colorSchemes.schedulingMode
onSelected: key => {
Settings.data.colorSchemes.schedulingMode = key
AppThemeService.generate()
}
}
// Manual scheduling
ColumnLayout {
spacing: Style.marginS
visible: Settings.data.colorSchemes.schedulingMode === "manual"
NLabel {
label: I18n.tr("settings.display.night-light.manual-schedule.label")
description: I18n.tr("settings.display.night-light.manual-schedule.description")
}
RowLayout {
Layout.fillWidth: false
spacing: Style.marginS
NText {
text: I18n.tr("settings.display.night-light.manual-schedule.sunrise")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
NComboBox {
model: timeOptions
currentKey: Settings.data.colorSchemes.manualSunrise
placeholder: I18n.tr("settings.display.night-light.manual-schedule.select-start")
onSelected: key => Settings.data.colorSchemes.manualSunrise = key
minimumWidth: 120
}
Item {
Layout.preferredWidth: 20
}
NText {
text: I18n.tr("settings.display.night-light.manual-schedule.sunset")
pointSize: Style.fontSizeM
color: Color.mOnSurfaceVariant
}
NComboBox {
model: timeOptions
currentKey: Settings.data.colorSchemes.manualSunset
placeholder: I18n.tr("settings.display.night-light.manual-schedule.select-stop")
onSelected: key => Settings.data.colorSchemes.manualSunset = key
minimumWidth: 120
}
}
}
// Use Wallpaper Colors
NToggle {
label: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label")

View File

@@ -13,31 +13,118 @@ Singleton {
Connections {
target: LocationService.data
enabled: Settings.data.colorSchemes.schedulingMode == "location"
function onWeatherChanged() {
if (LocationService.data.weather !== null) {
const changes = root.collectChanges(LocationService.data.weather)
const changes = root.collectWeatherChanges(LocationService.data.weather)
if (!root.initComplete) {
root.initComplete = true
root.resetDarkMode(changes)
root.applyCurrentMode(changes)
}
root.scheduleChange(changes)
root.scheduleNextMode(changes)
}
}
}
Connections {
target: Settings.data.colorSchemes
enabled: Settings.data.colorSchemes.schedulingMode == "manual"
function onManualSunriseChanged() {
const changes = root.collectManualChanges()
root.applyCurrentMode(changes)
root.scheduleNextMode(changes)
}
function onManualSunsetChanged() {
const changes = root.collectManualChanges()
root.applyCurrentMode(changes)
root.scheduleNextMode(changes)
}
}
Connections {
target: Settings.data.colorSchemes
function onSchedulingModeChanged() {
root.init()
}
}
Timer {
id: timer
onTriggered: {
Settings.data.colorSchemes.darkMode = root.nextDarkModeState
if (LocationService.data.weather !== null) {
const changes = root.collectChanges(LocationService.data.weather)
root.scheduleChange(changes)
const changes = root.collectWeatherChanges(LocationService.data.weather)
root.scheduleNextMode(changes)
}
}
}
function collectChanges(weather) {
function init() {
Logger.log("DarkModeService", "Service started")
if (Settings.data.colorSchemes.schedulingMode == "manual") {
const changes = collectManualChanges()
initComplete = true
applyCurrentMode(changes)
scheduleNextMode(changes)
}
if (Settings.data.colorSchemes.schedulingMode == "location" && LocationService.data.weather) {
const changes = collectWeatherChanges(LocationService.data.weather)
initComplete = true
applyCurrentMode(changes)
scheduleNextMode(changes)
}
}
function parseTime(timeString) {
const parts = timeString.split(":").map(Number)
return {
"hour": parts[0],
"minute": parts[1]
}
}
function collectManualChanges() {
const sunriseTime = parseTime(Settings.data.colorSchemes.manualSunrise)
const sunsetTime = parseTime(Settings.data.colorSchemes.manualSunset)
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth()
const day = now.getDate()
const yesterdaysSunset = new Date(year, month, day - 1, sunsetTime.hour, sunsetTime.minute)
const todaysSunrise = new Date(year, month, day, sunriseTime.hour, sunriseTime.minute)
const todaysSunset = new Date(year, month, day, sunsetTime.hour, sunsetTime.minute)
const tomorrowsSunrise = new Date(year, month, day + 1, sunriseTime.hour, sunriseTime.minute)
return [{
"time": yesterdaysSunset.getTime(),
"darkMode": true
}, {
"time": todaysSunrise.getTime(),
"darkMode": false
}, {
"time": todaysSunset.getTime(),
"darkMode": true
}, {
"time": tomorrowsSunrise.getTime(),
"darkMode": false
}]
}
function collectWeatherChanges(weather) {
const changes = []
if (Date.now() < Date.parse(weather.daily.sunrise[0])) {
// The sun has not risen yet
changes.push({
"time": Date.now() - 1,
"darkMode": true
})
}
for (var i = 0; i < weather.daily.sunrise.length; i++) {
changes.push({
"time": Date.parse(weather.daily.sunrise[i]),
@@ -48,10 +135,11 @@ Singleton {
"darkMode": true
})
}
return changes
}
function resetDarkMode(changes) {
function applyCurrentMode(changes) {
const now = Date.now()
// changes.findLast(change => change.time < now) // not available in QML...
@@ -68,7 +156,7 @@ Singleton {
}
}
function scheduleChange(changes) {
function scheduleNextMode(changes) {
const now = Date.now()
const nextChange = changes.find(change => change.time > now)
if (nextChange) {
@@ -78,8 +166,4 @@ Singleton {
Logger.log("DarkModeService", `Scheduled: darkmode=${nextChange.darkMode} in ${timer.interval} ms`)
}
}
function init() {
Logger.log("DarkModeService", "Service started")
}
}

View File

@@ -69,7 +69,7 @@ Singleton {
Timer {
id: updateTimer
interval: 20 * 1000
running: Settings.data.location.weatherEnabled
running: Settings.data.location.weatherEnabled || Settings.data.colorSchemes.schedulingMode == "location"
repeat: true
onTriggered: {
updateWeather()