Files
2026-10-20 12:23:25 -04:00

430 lines
20 KiB
QML

import QtQuick
import qs.Common
import qs.Services
import qs.DCommon.Widgets
import qs.Modules.Settings.Widgets
Item {
id: root
property var parentModal: null
readonly property string _systemDefaultLabel: I18n.tr("System default")
readonly property var presetDateFormats: ["ddd d", "ddd MMM d", "MMM d", "M/d", "d/M", "ddd d MMM yyyy", "yyyy-MM-dd", "dddd, MMMM d"]
property bool editingBarFormat: false
property bool editingLockFormat: false
function isCustomFormat(format) {
return !!format && !presetDateFormats.includes(format);
}
function weekStartQt() {
if (SettingsData.firstDayOfWeek < 0 || SettingsData.firstDayOfWeek >= 7)
return Qt.locale().firstDayOfWeek;
return SettingsData.firstDayOfWeek;
}
function weekStartJs() {
return weekStartQt() % 7;
}
function _dayNames() {
return Array(7).fill(0).map((_, i) => new Date(Date.UTC(2026, 2, 1 + i, 0, 0, 0)).toLocaleDateString(I18n.locale(), "dddd")).map(d => d[0].toUpperCase() + d.slice(1));
}
SettingsPage {
id: mainColumn
SettingsCard {
tab: "time"
tags: ["time", "clock", "format", "24hour"]
title: I18n.tr("Time")
settingKey: "timeFormat"
iconName: "schedule"
SettingsDropdownRow {
tab: "time"
tags: ["time", "24hour", "12hour", "format", "locale"]
settingKey: "clockFormat"
text: I18n.tr("Format", "noun, time or date format setting label")
readonly property var _sample: new Date(2000, 0, 1, 13, 0, 0)
readonly property string _twelve: Qt.formatTime(_sample, "h:mm AP")
readonly property string _twentyFour: Qt.formatTime(_sample, "HH:mm")
options: [I18n.tr("System default"), _twelve, _twentyFour]
currentValue: {
switch (SettingsData.clockFormat) {
case "12h":
return _twelve;
case "24h":
return _twentyFour;
default:
return I18n.tr("System default");
}
}
onValueChanged: value => {
if (value === _twelve) {
SettingsData.set("clockFormat", "12h");
return;
}
if (value === _twentyFour) {
SettingsData.set("clockFormat", "24h");
return;
}
SettingsData.set("clockFormat", "auto");
}
}
SettingsToggleRow {
tab: "time"
tags: ["time", "seconds", "clock"]
settingKey: "showSeconds"
text: I18n.tr("Show seconds")
checked: SettingsData.showSeconds
onToggled: checked => SettingsData.set("showSeconds", checked)
}
SettingsToggleRow {
enabled: !SettingsData.use24HourClock
tab: "time"
tags: ["time", "12hour", "format", "padding", "leading", "zero"]
settingKey: "padHours12Hour"
text: I18n.tr("Leading zero on hours")
checked: SettingsData.padHours12Hour
onToggled: checked => SettingsData.set("padHours12Hour", checked)
}
}
SettingsCard {
tab: "time"
tags: ["date", "format", "calendar"]
title: I18n.tr("Date")
settingKey: "dateFormat"
iconName: "calendar_today"
SettingsToggleRow {
tab: "time"
tags: ["show", "week"]
settingKey: "showWeekNumber"
text: I18n.tr("Show week number")
checked: SettingsData.showWeekNumber
onToggled: checked => SettingsData.set("showWeekNumber", checked)
}
SettingsDropdownRow {
tab: "time"
tags: ["first", "day", "week"]
settingKey: "firstDayOfWeek"
text: I18n.tr("First day of week")
options: [root._systemDefaultLabel].concat(root._dayNames())
currentValue: {
if (SettingsData.firstDayOfWeek < 0 || SettingsData.firstDayOfWeek >= 7)
return root._systemDefaultLabel;
return root._dayNames()[root.weekStartJs()];
}
onValueChanged: value => {
if (value === root._systemDefaultLabel) {
SettingsData.set("firstDayOfWeek", -1);
return;
}
SettingsData.set("firstDayOfWeek", root._dayNames().indexOf(value));
}
}
SettingsDropdownRow {
tab: "time"
tags: ["date", "format", "topbar"]
settingKey: "clockDateFormat"
text: I18n.tr("Bar format")
description: I18n.tr("Preview: %1", "date format setting description, %1 is the current date formatted").arg(SettingsData.clockDateFormat ? new Date().toLocaleDateString(I18n.locale(), SettingsData.clockDateFormat) : new Date().toLocaleDateString(I18n.locale(), "ddd d"))
options: [I18n.tr("System default", "date format option"), I18n.tr("Day date", "date format option"), I18n.tr("Day month date", "date format option"), I18n.tr("Month date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with year", "date format option"), I18n.tr("ISO date", "date format option"), I18n.tr("Full day & month", "date format option"), I18n.tr("Custom", "date format option") + "…"]
currentValue: {
if (!SettingsData.clockDateFormat || SettingsData.clockDateFormat.length === 0)
return I18n.tr("System default", "date format option");
const presets = [
{
"format": "ddd d",
"label": I18n.tr("Day date", "date format option")
},
{
"format": "ddd MMM d",
"label": I18n.tr("Day month date", "date format option")
},
{
"format": "MMM d",
"label": I18n.tr("Month date", "date format option")
},
{
"format": "M/d",
"label": I18n.tr("Numeric (M/D)", "date format option")
},
{
"format": "d/M",
"label": I18n.tr("Numeric (D/M)", "date format option")
},
{
"format": "ddd d MMM yyyy",
"label": I18n.tr("Full with year", "date format option")
},
{
"format": "yyyy-MM-dd",
"label": I18n.tr("ISO date", "date format option")
},
{
"format": "dddd, MMMM d",
"label": I18n.tr("Full day & month", "date format option")
}
];
const match = presets.find(p => p.format === SettingsData.clockDateFormat);
return match ? match.label : I18n.tr("Custom") + ": " + SettingsData.clockDateFormat;
}
onValueChanged: value => {
const formatMap = {};
formatMap[I18n.tr("System default", "date format option")] = "";
formatMap[I18n.tr("Day date", "date format option")] = "ddd d";
formatMap[I18n.tr("Day month date", "date format option")] = "ddd MMM d";
formatMap[I18n.tr("Month date", "date format option")] = "MMM d";
formatMap[I18n.tr("Numeric (M/D)", "date format option")] = "M/d";
formatMap[I18n.tr("Numeric (D/M)", "date format option")] = "d/M";
formatMap[I18n.tr("Full with year", "date format option")] = "ddd d MMM yyyy";
formatMap[I18n.tr("ISO date", "date format option")] = "yyyy-MM-dd";
formatMap[I18n.tr("Full day & month", "date format option")] = "dddd, MMMM d";
root.editingBarFormat = value === I18n.tr("Custom", "date format option") + "…";
if (root.editingBarFormat)
return;
SettingsData.set("clockDateFormat", formatMap[value]);
}
}
DTextField {
id: customFormatInput
outlined: true
leftIconName: "calendar_today"
labelText: I18n.tr("Format")
visible: root.editingBarFormat || root.isCustomFormat(SettingsData.clockDateFormat)
placeholderText: I18n.tr("Enter custom top bar format (e.g., ddd MMM d)")
text: SettingsData.clockDateFormat
onTextChanged: {
if (visible && text)
SettingsData.set("clockDateFormat", text);
}
}
SettingsDropdownRow {
tab: "time"
tags: ["date", "format", "lock", "screen"]
settingKey: "lockDateFormat"
text: I18n.tr("Lock screen format")
description: I18n.tr("Preview: %1").arg(SettingsData.lockDateFormat ? new Date().toLocaleDateString(I18n.locale(), SettingsData.lockDateFormat) : new Date().toLocaleDateString(I18n.locale(), Locale.LongFormat))
options: [I18n.tr("System default", "date format option"), I18n.tr("Day date", "date format option"), I18n.tr("Day month date", "date format option"), I18n.tr("Month date", "date format option"), I18n.tr("Numeric (M/D)", "date format option"), I18n.tr("Numeric (D/M)", "date format option"), I18n.tr("Full with year", "date format option"), I18n.tr("ISO date", "date format option"), I18n.tr("Full day & month", "date format option"), I18n.tr("Custom", "date format option") + "…"]
currentValue: {
if (!SettingsData.lockDateFormat || SettingsData.lockDateFormat.length === 0)
return I18n.tr("System default", "date format option");
const presets = [
{
"format": "ddd d",
"label": I18n.tr("Day date", "date format option")
},
{
"format": "ddd MMM d",
"label": I18n.tr("Day month date", "date format option")
},
{
"format": "MMM d",
"label": I18n.tr("Month date", "date format option")
},
{
"format": "M/d",
"label": I18n.tr("Numeric (M/D)", "date format option")
},
{
"format": "d/M",
"label": I18n.tr("Numeric (D/M)", "date format option")
},
{
"format": "ddd d MMM yyyy",
"label": I18n.tr("Full with year", "date format option")
},
{
"format": "yyyy-MM-dd",
"label": I18n.tr("ISO date", "date format option")
},
{
"format": "dddd, MMMM d",
"label": I18n.tr("Full day & month", "date format option")
}
];
const match = presets.find(p => p.format === SettingsData.lockDateFormat);
return match ? match.label : I18n.tr("Custom") + ": " + SettingsData.lockDateFormat;
}
onValueChanged: value => {
const formatMap = {};
formatMap[I18n.tr("System default", "date format option")] = "";
formatMap[I18n.tr("Day date", "date format option")] = "ddd d";
formatMap[I18n.tr("Day month date", "date format option")] = "ddd MMM d";
formatMap[I18n.tr("Month date", "date format option")] = "MMM d";
formatMap[I18n.tr("Numeric (M/D)", "date format option")] = "M/d";
formatMap[I18n.tr("Numeric (D/M)", "date format option")] = "d/M";
formatMap[I18n.tr("Full with year", "date format option")] = "ddd d MMM yyyy";
formatMap[I18n.tr("ISO date", "date format option")] = "yyyy-MM-dd";
formatMap[I18n.tr("Full day & month", "date format option")] = "dddd, MMMM d";
root.editingLockFormat = value === I18n.tr("Custom", "date format option") + "…";
if (root.editingLockFormat)
return;
SettingsData.set("lockDateFormat", formatMap[value]);
}
}
DTextField {
id: customLockFormatInput
outlined: true
leftIconName: "calendar_today"
labelText: I18n.tr("Format")
visible: root.editingLockFormat || root.isCustomFormat(SettingsData.lockDateFormat)
placeholderText: I18n.tr("Enter custom lock screen format (e.g., dddd, MMMM d)")
text: SettingsData.lockDateFormat
onTextChanged: {
if (visible && text)
SettingsData.set("lockDateFormat", text);
}
}
SettingsRow {
visible: customFormatInput.visible || customLockFormatInput.visible
body: Column {
width: parent.width
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Format legend")
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
font.weight: Theme.fontWeightMedium
}
Row {
width: parent.width
spacing: Theme.spacingL
Column {
width: (parent.width - Theme.spacingL) / 2
spacing: Theme.spacingXXS
StyledText {
text: I18n.tr("• d - Day (1-31)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• dd - Day (01-31)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• ddd - Day name (Mon)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• dddd - Day name (Monday)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• M - Month (1-12)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
Column {
width: (parent.width - Theme.spacingL) / 2
spacing: Theme.spacingXXS
StyledText {
text: I18n.tr("• MM - Month (01-12)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• MMM - Month (Jan)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• MMMM - Month (January)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• yy - Year (24)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
StyledText {
text: I18n.tr("• yyyy - Year (2024)")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}
}
}
SettingsCard {
tab: "time"
title: I18n.tr("Calendar")
settingKey: "calendarSettings"
iconName: "event"
tags: ["calendar", "backend", "tasks", "events"]
SettingsDropdownRow {
tab: "time"
tags: ["calendar", "backend", "daemon", "khal", "dankcalendar", "events"]
settingKey: "calendarBackend"
text: I18n.tr("Calendar backend")
readonly property var _backendValues: ["auto", "khal", "dankcal"]
readonly property var _backendLabels: [I18n.tr("Auto", "calendar backend option"), I18n.tr("khal", "calendar backend option"), I18n.tr("Dux Calendar", "calendar backend option")]
options: _backendLabels
currentValue: _backendLabels[Math.max(0, _backendValues.indexOf(SettingsData.calendarBackend))]
onValueChanged: value => {
const idx = _backendLabels.indexOf(value);
if (idx < 0)
return;
SettingsData.set("calendarBackend", _backendValues[idx]);
}
}
SettingsDropdownRow {
tab: "time"
tags: ["calendar", "tasks", "list", "default", "caldav", "todo"]
settingKey: "defaultTaskCalendarId"
visible: CalendarService.isDankActive && _taskLists.length > 0
text: I18n.tr("Default task list")
readonly property var _taskLists: (CalendarService.calendars || []).filter(c => c.holdsTasks && !c.readOnly && !c.hidden)
readonly property var _labels: [I18n.tr("Auto")].concat(_taskLists.map(c => c.name))
options: _labels
currentValue: {
const idx = _taskLists.findIndex(c => c.id === SettingsData.defaultTaskCalendarId);
return idx >= 0 ? _labels[idx + 1] : I18n.tr("Auto");
}
onValueChanged: value => {
const idx = _labels.indexOf(value);
SettingsData.set("defaultTaskCalendarId", idx > 0 ? _taskLists[idx - 1].id : "");
}
}
}
SettingsCard {
SettingsNavRow {
tab: "time"
settingKey: "weather"
tags: ["weather", "location", "units"]
title: I18n.tr("Weather")
iconName: "partly_cloudy_day"
onClicked: keyboard => root.parentModal?.navigateTo("weather", keyboard)
}
}
}
}