diff options
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/Clock.qml')
-rw-r--r-- | accounts/gkleen@sif/shell/quickshell/Clock.qml | 295 |
1 files changed, 188 insertions, 107 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml index 65b842b3..d0c9178b 100644 --- a/accounts/gkleen@sif/shell/quickshell/Clock.qml +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml | |||
@@ -4,182 +4,263 @@ import Quickshell | |||
4 | import Custom as Custom | 4 | import Custom as Custom |
5 | import QtQuick.Controls | 5 | import QtQuick.Controls |
6 | import QtQuick.Layouts | 6 | import QtQuick.Layouts |
7 | import Quickshell.Widgets | ||
7 | 8 | ||
8 | Item { | 9 | Item { |
10 | id: clockItem | ||
11 | |||
12 | property bool calendarPopup: true | ||
13 | |||
9 | width: clock.contentWidth | 14 | width: clock.contentWidth |
10 | height: parent.height | 15 | height: parent.height |
11 | anchors.verticalCenter: parent.verticalCenter | 16 | anchors.verticalCenter: parent.verticalCenter |
12 | 17 | ||
13 | MouseArea { | 18 | WrapperMouseArea { |
14 | id: clockMouseArea | 19 | id: clockMouseArea |
15 | 20 | ||
16 | anchors.fill: parent | 21 | anchors.fill: parent |
17 | hoverEnabled: true | 22 | hoverEnabled: true |
18 | enabled: true | 23 | enabled: clockItem.calendarPopup |
19 | } | ||
20 | |||
21 | Text { | ||
22 | id: clock | ||
23 | color: "white" | ||
24 | 24 | ||
25 | anchors.verticalCenter: parent.verticalCenter | 25 | property real angleRem: 0 |
26 | property real sensitivity: 1 / 120 | ||
26 | 27 | ||
27 | Custom.Chrono { | 28 | function scrollYear(event) { |
28 | id: chrono | 29 | angleRem += event.angleDelta.y; |
29 | format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}" | 30 | const d = Math.round(angleRem * sensitivity); |
31 | yearCalendar.year += d; | ||
32 | angleRem -= d / sensitivity; | ||
30 | } | 33 | } |
31 | 34 | ||
32 | text: chrono.date | 35 | onWheel: event => scrollYear(event) |
36 | |||
37 | Item { | ||
38 | anchors.fill: parent | ||
39 | |||
40 | Text { | ||
41 | id: clock | ||
42 | color: "white" | ||
43 | |||
44 | anchors.verticalCenter: parent.verticalCenter | ||
33 | 45 | ||
34 | font.pointSize: 10 | 46 | Custom.Chrono { |
35 | font.family: "Fira Sans" | 47 | id: chrono |
36 | font.features: { "tnum": 1 } | 48 | |
49 | onDateChanged: clock.text = format("W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}") | ||
50 | } | ||
51 | |||
52 | font.pointSize: 10 | ||
53 | font.family: "Fira Sans" | ||
54 | font.features: { "tnum": 1 } | ||
55 | } | ||
56 | } | ||
37 | } | 57 | } |
38 | 58 | ||
39 | PopupWindow { | 59 | PopupWindow { |
60 | id: tooltip | ||
61 | |||
62 | property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
63 | |||
40 | anchor { | 64 | anchor { |
41 | item: clockMouseArea | 65 | item: clockMouseArea |
42 | edges: Edges.Bottom | 66 | edges: Edges.Bottom | Edges.Left |
43 | } | 67 | } |
44 | visible: clockMouseArea.containsMouse | 68 | visible: false |
45 | 69 | ||
46 | implicitWidth: yearCalendar.implicitWidth + 16 | 70 | onNextVisibleChanged: hangTimer.restart() |
47 | implicitHeight: yearCalendar.implicitHeight + 16 | ||
48 | color: "black" | ||
49 | 71 | ||
50 | GridLayout { | 72 | Timer { |
51 | property int year: { const d = new Date(); return d.getFullYear(); } | 73 | id: hangTimer |
74 | interval: 100 | ||
75 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
76 | } | ||
52 | 77 | ||
53 | id: yearCalendar | 78 | implicitWidth: clockTooltipContent.width |
79 | implicitHeight: clockTooltipContent.height | ||
80 | color: "black" | ||
54 | 81 | ||
55 | columns: 3 | 82 | onVisibleChanged: { |
56 | columnSpacing: 16 | 83 | yearCalendar.year = chrono.date.getFullYear(); |
57 | rowSpacing: 16 | 84 | clockMouseArea.angleRem = 0; |
85 | } | ||
58 | 86 | ||
59 | anchors.centerIn: parent | 87 | WrapperMouseArea { |
88 | id: tooltipMouseArea | ||
60 | 89 | ||
61 | Repeater { | 90 | hoverEnabled: true |
62 | model: 12 | 91 | enabled: true |
63 | 92 | ||
64 | GridLayout { | 93 | onWheel: event => clockMouseArea.scrollYear(event) |
65 | columns: 2 | ||
66 | 94 | ||
67 | required property int index | 95 | anchors.fill: parent |
68 | property int month: index | ||
69 | 96 | ||
70 | id: monthCalendar | 97 | WrapperItem { |
98 | id: clockTooltipContent | ||
71 | 99 | ||
72 | Layout.alignment: Qt.AlignTop | Qt.AlignRight | 100 | margin: 8 |
73 | Layout.fillWidth: false | 101 | leftMargin: 0 |
74 | 102 | ||
103 | ColumnLayout { | ||
75 | Text { | 104 | Text { |
76 | Layout.column: 1 | 105 | id: yearLabel |
77 | Layout.fillWidth: true | ||
78 | 106 | ||
79 | horizontalAlignment: Text.AlignHCenter | 107 | horizontalAlignment: Text.AlignHCenter |
80 | 108 | ||
81 | font.pointSize: 10 | 109 | font.pointSize: 14 |
82 | font.family: "Fira Sans" | 110 | font.family: "Fira Sans" |
111 | font.features: { "tnum": 1 } | ||
112 | color: "white" | ||
83 | 113 | ||
84 | text: { | 114 | text: yearCalendar.year |
85 | const date = Date.fromLocaleDateString(Qt.locale(), `${yearCalendar.year}-${monthCalendar.month + 1}-01`, "yyyy-M-dd"); | ||
86 | return date.toLocaleString(Qt.locale("en_DK"), "MMMM") | ||
87 | } | ||
88 | 115 | ||
89 | color: "#ffead3" | 116 | Layout.fillWidth: true |
117 | Layout.bottomMargin: 8 | ||
90 | } | 118 | } |
91 | 119 | ||
92 | DayOfWeekRow { | 120 | GridLayout { |
93 | locale: grid.locale | 121 | property int year: chrono.date.getFullYear() |
94 | 122 | ||
95 | Layout.row: 1 | 123 | id: yearCalendar |
96 | Layout.column: 1 | ||
97 | Layout.fillWidth: true | ||
98 | 124 | ||
99 | delegate: Text { | 125 | columns: 3 |
100 | required property string shortName | 126 | columnSpacing: 16 |
127 | rowSpacing: 16 | ||
101 | 128 | ||
102 | font.pointSize: 10 | 129 | Layout.alignment: Qt.AlignHCenter |
103 | font.family: "Fira Mono" | 130 | Layout.fillWidth: false |
104 | 131 | ||
105 | text: shortName | 132 | Repeater { |
106 | color: "#ffcc66" | 133 | model: 12 |
107 | 134 | ||
108 | horizontalAlignment: Text.AlignRight | 135 | GridLayout { |
109 | verticalAlignment: Text.AlignVCenter | 136 | columns: 2 |
110 | } | 137 | |
111 | } | 138 | required property int index |
139 | property int month: index | ||
112 | 140 | ||
113 | WeekNumberColumn { | 141 | id: monthCalendar |
114 | month: grid.month | ||
115 | year: grid.year | ||
116 | locale: grid.locale | ||
117 | 142 | ||
118 | Layout.fillHeight: true | 143 | Layout.alignment: Qt.AlignTop | Qt.AlignRight |
144 | Layout.fillWidth: false | ||
119 | 145 | ||
120 | delegate: Text { | 146 | Text { |
121 | required property int weekNumber | 147 | Layout.column: 1 |
148 | Layout.fillWidth: true | ||
122 | 149 | ||
123 | opacity: { | 150 | horizontalAlignment: Text.AlignHCenter |
124 | const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); | ||
125 | const dayOfWeek = simple.getDay(); | ||
126 | const isoWeekStart = simple; | ||
127 | 151 | ||
128 | isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); | 152 | font.pointSize: 10 |
129 | if (dayOfWeek > 4) { | 153 | font.family: "Fira Sans" |
130 | isoWeekStart.setDate(isoWeekStart.getDate() + 7); | 154 | |
155 | text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM") | ||
156 | |||
157 | color: "#ffead3" | ||
131 | } | 158 | } |
132 | 159 | ||
133 | for (let i = 0; i < 7; i++) { | 160 | DayOfWeekRow { |
134 | const dayInWeek = new Date(isoWeekStart); | 161 | locale: grid.locale |
135 | dayInWeek.setDate(dayInWeek.getDate() + i); | 162 | |
136 | if (dayInWeek.getMonth() == monthCalendar.month) | 163 | Layout.row: 1 |
137 | return 1; | 164 | Layout.column: 1 |
165 | Layout.fillWidth: true | ||
166 | |||
167 | delegate: WrapperItem { | ||
168 | required property string shortName | ||
169 | |||
170 | width: dowLabel.contentWidth + 6 | ||
171 | |||
172 | Text { | ||
173 | id: dowLabel | ||
174 | |||
175 | anchors.fill: parent | ||
176 | |||
177 | font.pointSize: 10 | ||
178 | font.family: "Fira Sans" | ||
179 | |||
180 | text: parent.shortName | ||
181 | color: "#ffcc66" | ||
182 | |||
183 | horizontalAlignment: Text.AlignHCenter | ||
184 | verticalAlignment: Text.AlignVCenter | ||
185 | } | ||
186 | } | ||
138 | } | 187 | } |
139 | 188 | ||
140 | return 0; | 189 | WeekNumberColumn { |
141 | } | 190 | month: grid.month |
191 | year: grid.year | ||
192 | locale: grid.locale | ||
142 | 193 | ||
143 | font.pointSize: 10 | 194 | Layout.fillHeight: true |
144 | font.family: "Fira Sans" | ||
145 | font.features: { "tnum": 1 } | ||
146 | 195 | ||
147 | text: weekNumber | 196 | delegate: Text { |
148 | color: "#99ffdd" | 197 | required property int weekNumber |
149 | 198 | ||
150 | horizontalAlignment: Text.AlignRight | 199 | opacity: { |
151 | verticalAlignment: Text.AlignVCenter | 200 | const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); |
152 | } | 201 | const dayOfWeek = simple.getDay(); |
153 | } | 202 | const isoWeekStart = simple; |
154 | 203 | ||
155 | MonthGrid { | 204 | isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); |
156 | id: grid | 205 | if (dayOfWeek > 4) { |
206 | isoWeekStart.setDate(isoWeekStart.getDate() + 7); | ||
207 | } | ||
157 | 208 | ||
158 | year: yearCalendar.year | 209 | for (let i = 0; i < 7; i++) { |
159 | month: monthCalendar.month | 210 | const dayInWeek = new Date(isoWeekStart); |
160 | locale: Qt.locale("en_DK") | 211 | dayInWeek.setDate(dayInWeek.getDate() + i); |
212 | if (dayInWeek.getMonth() == monthCalendar.month) | ||
213 | return 1; | ||
214 | } | ||
161 | 215 | ||
162 | Layout.fillWidth: true | 216 | return 0; |
163 | Layout.fillHeight: true | 217 | } |
218 | |||
219 | font.pointSize: 10 | ||
220 | font.family: "Fira Sans" | ||
221 | font.features: { "tnum": 1 } | ||
222 | |||
223 | text: weekNumber | ||
224 | color: "#99ffdd" | ||
225 | |||
226 | horizontalAlignment: Text.AlignRight | ||
227 | verticalAlignment: Text.AlignVCenter | ||
228 | } | ||
229 | } | ||
164 | 230 | ||
165 | delegate: Text { | 231 | MonthGrid { |
166 | required property var model | 232 | id: grid |
167 | 233 | ||
168 | opacity: model.month === monthCalendar.month ? 1 : 0 | 234 | year: yearCalendar.year |
235 | month: monthCalendar.month | ||
236 | locale: Qt.locale("en_DK") | ||
169 | 237 | ||
170 | font.pointSize: 10 | 238 | Layout.fillWidth: true |
171 | font.family: "Fira Sans" | 239 | Layout.fillHeight: true |
172 | font.features: { "tnum": 1 } | ||
173 | 240 | ||
174 | text: model.day | 241 | delegate: Text { |
175 | color: model.today ? "#ff6699" : "white" | 242 | required property var model |
176 | 243 | ||
177 | horizontalAlignment: Text.AlignRight | 244 | opacity: model.month === monthCalendar.month ? 1 : 0 |
178 | verticalAlignment: Text.AlignVCenter | 245 | |
246 | font.pointSize: 10 | ||
247 | font.family: "Fira Sans" | ||
248 | font.features: { "tnum": 1 } | ||
249 | |||
250 | property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day | ||
251 | |||
252 | text: model.day | ||
253 | color: today ? "#ff6699" : "white" | ||
254 | |||
255 | horizontalAlignment: Text.AlignRight | ||
256 | verticalAlignment: Text.AlignVCenter | ||
257 | } | ||
258 | } | ||
259 | } | ||
179 | } | 260 | } |
180 | } | 261 | } |
181 | } | 262 | } |
182 | } | 263 | } |
183 | } | 264 | } |
184 | } | 265 | } |
185 | } \ No newline at end of file | 266 | } |