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