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