aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/island/IslandClockModule.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/modules/island/IslandClockModule.qml')
-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 }
+ }
+}