aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/island
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-13 09:55:00 +0100
committerDanilo M. <danix@danix.xyz>2026-11-13 09:55:00 +0100
commit7829a18d8049087eaa963fb922a8701cc9851979 (patch)
tree0ce6c851a96ff4f337a47ac40d6253a9f8f7f079 /desktop/modules/island
parent70d2b0c0e2e83dd2f17bd49dc2f06d39a7f7f4cf (diff)
downloadquickshell-7829a18d8049087eaa963fb922a8701cc9851979.tar.gz
quickshell-7829a18d8049087eaa963fb922a8701cc9851979.zip
feat(island): date and time format settings
The clock pill hard-coded "ddd dd/MM" and "HH:mm", and the calendar its day headings. A new Date and time page in the Island bar group picks a 24- or 12-hour clock and one of 22 date formats, grouped by order (day, month or year first) and each shown as today's date. The calendar's headings and editor dates keep spelling the month out and follow the chosen order; agenda times and the hour picker go compact on the 12-hour clock ("9:30p", "1p"), where width is short. A preview mocks the pill and panel with the unsaved choice. The formats live in Cal.js (DATE_GROUPS), which Prefs imports to validate island.dateFormat. Event times still store and show as 24-hour HH:MM, since that is what calctl speaks, but normTime now also accepts am and pm ("9.30pm", "12am"), rejecting an hour outside 1 to 12 with one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/island')
-rw-r--r--desktop/modules/island/IslandClockModule.qml247
1 files changed, 247 insertions, 0 deletions
diff --git a/desktop/modules/island/IslandClockModule.qml b/desktop/modules/island/IslandClockModule.qml
new file mode 100644
index 0000000..ef8c204
--- /dev/null
+++ b/desktop/modules/island/IslandClockModule.qml
@@ -0,0 +1,247 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+import Quickshell
+import QtQuick
+import "../.."
+import "../appearance"
+import "../../island/calendar/Cal.js" as Cal
+
+// How the island shows time and dates: the clock pill, the calendar's day
+// headings, agenda times and hour picker.
+Module {
+ id: mod
+ name: "island-clock"
+ label: "Date and time"
+ // nf-md-clock_outline, present in Inconsolata Nerd Font.
+ icon: "\u{f0150}"
+ settings: true
+ group: "Island bar"
+ keywords: ["island", "clock", "time", "date", "format", "12-hour", "24-hour", "am", "pm", "day", "month"]
+ searchItems: [{ text: "Clock", key: "h12" }, { text: "Date format", key: "dateFormat" }]
+
+ property bool h12: Prefs.island.h12
+ property string dateFormat: Prefs.island.dateFormat
+ dirty: h12 !== Prefs.island.h12 || dateFormat !== Prefs.island.dateFormat
+ function refresh() { if (!mod.dirty) mod.discard(); }
+ function discard() { mod.h12 = Prefs.island.h12; mod.dateFormat = Prefs.island.dateFormat; }
+
+ page: Component {
+ Page {
+ title: "Date and time"
+ actions: Component {
+ SaveBar {
+ dirty: mod.dirty
+ onSave: Prefs.setIsland({ h12: mod.h12, dateFormat: mod.dateFormat })
+ onDiscard: mod.discard()
+ }
+ }
+ Flow {
+ id: body
+ width: parent.width
+ spacing: 32
+ SystemClock { id: now; precision: SystemClock.Minutes }
+ readonly property string today: Cal.today(now.date)
+ // Two made-up events today, one each side of noon.
+ readonly property var events: [
+ { title: "Standup", allDay: false, start: today + "T09:30", end: today + "T10:00", color: "blue" },
+ { title: "Dinner", allDay: false, start: today + "T20:30", end: today + "T22:00", color: "green" }]
+
+ Column {
+ id: controls
+ width: 3 * 120 + 2 * 12
+ spacing: 6
+
+ Heading { text: "Clock" }
+ Hint { text: "The clock pill, the agenda and the hour picker. Event times can also be typed with am and pm (9.30pm)." }
+ Row {
+ objectName: "h12"
+ spacing: 6
+ Tab { text: "24-hour"; selected: !mod.h12; onClicked: mod.h12 = false }
+ Tab { text: "12-hour"; selected: mod.h12; onClicked: mod.h12 = true }
+ }
+
+ Item { width: 1; height: 14 }
+ Heading { text: "Date" }
+ Hint { text: "The clock pill. The calendar spells the month out, in the same order." }
+ Row {
+ objectName: "dateFormat"
+ spacing: 12
+ Repeater {
+ model: Cal.DATE_GROUPS
+ delegate: Column {
+ required property var modelData
+ width: 120
+ spacing: 2
+ Text {
+ text: modelData.title
+ color: Theme.subtext
+ bottomPadding: 4
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ Repeater {
+ model: modelData.formats
+ delegate: Rectangle {
+ required property string modelData
+ readonly property bool on: mod.dateFormat === modelData
+ width: 120
+ height: 26
+ radius: 6
+ color: on ? Qt.alpha(Theme.accent, 0.25)
+ : pick.containsMouse ? Qt.alpha(Theme.surface, 0.8) : "transparent"
+ border.width: on ? 1 : 0
+ border.color: Qt.alpha(Theme.accent, 0.5)
+ Text {
+ anchors { left: parent.left; leftMargin: 8; verticalCenter: parent.verticalCenter }
+ text: Qt.formatDate(now.date, parent.modelData)
+ color: parent.on ? Theme.text : Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ MouseArea {
+ id: pick
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: Qt.PointingHandCursor
+ onClicked: mod.dateFormat = parent.modelData
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Beside the controls when there is room, below them when not.
+ Column {
+ width: Math.max(220, Math.min(400, body.width - controls.width - body.spacing))
+ spacing: 6
+ Heading { text: "Preview" }
+ Hint { text: "With the unsaved choice." }
+ // A mock of the island: the clock pill at rest, and the
+ // calendar panel's heading, agenda and hour picker below it.
+ Rectangle {
+ width: parent.width
+ height: mock.implicitHeight + 32
+ radius: 12
+ color: Qt.alpha(Theme.surface, 0.35)
+
+ Column {
+ id: mock
+ anchors { fill: parent; margins: 16 }
+ spacing: 14
+
+ Rectangle {
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: pillRow.implicitWidth + 28
+ height: 34
+ radius: 17
+ color: Qt.alpha(Theme.base, 0.92)
+ border.width: 1
+ border.color: Qt.alpha(Theme.text, 0.12)
+ Row {
+ id: pillRow
+ anchors.centerIn: parent
+ spacing: 10
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: Qt.formatDateTime(now.date, Cal.shortDateFmt(mod.dateFormat))
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ }
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: Qt.formatDateTime(now.date, Cal.timeFmt(mod.h12))
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize; weight: Font.DemiBold }
+ }
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: panelCol.implicitHeight + 24
+ radius: 17
+ color: Qt.alpha(Theme.base, 0.92)
+ border.width: 1
+ border.color: Qt.alpha(Theme.text, 0.12)
+ Column {
+ id: panelCol
+ anchors { fill: parent; margins: 12 }
+ spacing: 6
+ Text {
+ text: Qt.formatDate(now.date, Cal.dayFmt(mod.dateFormat, true))
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true }
+ }
+ Repeater {
+ model: body.events
+ delegate: Row {
+ required property var modelData
+ spacing: 8
+ Rectangle { width: 3; height: 16; radius: 1; color: Theme[modelData.color] }
+ Text {
+ width: mod.h12 ? 92 : 82
+ text: Cal.timeLabel(modelData, body.today, mod.h12)
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 }
+ }
+ Text {
+ text: modelData.title
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ }
+ }
+ Text {
+ text: "When " + Qt.formatDate(now.date, Cal.dayFmt(mod.dateFormat, false))
+ color: Theme.subtext
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ Row {
+ spacing: 2
+ Repeater {
+ model: [8, 9, 10, 11, 12]
+ delegate: Rectangle {
+ required property int modelData
+ width: 34
+ height: 26
+ radius: 6
+ color: modelData === 9 ? Qt.alpha(Theme.accent, 0.35) : "transparent"
+ Text {
+ anchors.centerIn: parent
+ text: Cal.hourLabel(parent.modelData, mod.h12)
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ component Heading: Text {
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3; bold: true }
+ color: Theme.text
+ }
+
+ component Hint: Text {
+ width: parent ? parent.width : 0
+ wrapMode: Text.WordWrap
+ color: Theme.overlay
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
+ }
+}