summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell/quickshell/Bar.qml
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell/Bar.qml')
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Bar.qml507
1 files changed, 6 insertions, 501 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml
index 9cb58b93..2f323858 100644
--- a/accounts/gkleen@sif/shell/quickshell/Bar.qml
+++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml
@@ -1,13 +1,5 @@
1import Quickshell 1import Quickshell
2import Quickshell.Io
3import Quickshell.Services.SystemTray
4import Quickshell.Widgets
5import Custom as Custom
6import QtQuick 2import QtQuick
7import qs.Services
8import QtQuick.Controls
9import QtQuick.Layouts
10import QtQml
11 3
12 4
13PanelWindow { 5PanelWindow {
@@ -46,74 +38,7 @@ PanelWindow {
46 anchors.verticalCenter: parent.verticalCenter 38 anchors.verticalCenter: parent.verticalCenter
47 spacing: 8 39 spacing: 8
48 40
49 Row { 41 WorkspaceSwitcher {}
50 id: workspaces
51
52 property var ignoreWorkspaces: @ignore_workspaces@
53
54 height: parent.height
55 anchors.verticalCenter: parent.verticalCenter
56 spacing: 0
57
58 Repeater {
59 model: {
60 let currWorkspaces = NiriService.workspaces;
61 const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces);
62 currWorkspaces = currWorkspaces.filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name));
63 currWorkspaces.sort((a, b) => {
64 if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x)
65 return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x
66 if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y)
67 return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y
68 return a.idx - b.idx;
69 });
70 return currWorkspaces;
71 }
72
73 Rectangle {
74 property var workspaceData: modelData
75
76 width: wsLabel.contentWidth + 8
77 color: {
78 if (mouseArea.containsMouse) {
79 return "#33808080";
80 }
81 return "transparent";
82 }
83 height: parent.height
84 anchors.verticalCenter: parent.verticalCenter
85
86 MouseArea {
87 id: mouseArea
88
89 anchors.fill: parent
90 hoverEnabled: true
91 cursorShape: Qt.PointingHandCursor
92 enabled: true
93 onClicked: {
94 NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {})
95 }
96 }
97
98 Text {
99 id: wsLabel
100
101 font.pointSize: 10
102 font.family: "Fira Sans"
103 color: {
104 if (workspaceData.is_active)
105 return "#23fd00";
106 if (workspaceData.active_window_id === null)
107 return "#555";
108 return "white";
109 }
110 anchors.centerIn: parent
111
112 text: workspaceData.name ? workspaceData.name : workspaceData.idx
113 }
114 }
115 }
116 }
117 } 42 }
118 43
119 Row { 44 Row {
@@ -124,91 +49,8 @@ PanelWindow {
124 anchors.centerIn: parent 49 anchors.centerIn: parent
125 spacing: 5 50 spacing: 5
126 51
127 Item { 52 ActiveWindowDisplay {
128 id: activeWindowDisplay 53 maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8
129
130 property var activeWindow: {
131 let currWindowId = Array.from(NiriService.workspaces).find(ws => {
132 return ws.output === bar.screen.name && ws.is_active;
133 })?.active_window_id;
134
135 return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null;
136 }
137 property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null
138
139 anchors.verticalCenter: parent.verticalCenter
140 width: activeWindowDisplayContent.width
141 height: parent.height
142
143 Row {
144 id: activeWindowDisplayContent
145
146 width: childrenRect.width
147 height: parent.height
148 anchors.verticalCenter: parent.verticalCenter
149 spacing: 8
150
151 IconImage {
152 id: activeWindowIcon
153
154 height: 14
155 width: 14
156
157 anchors.verticalCenter: parent.verticalCenter
158
159 source: {
160 let icon = activeWindowDisplay.windowEntry?.icon
161 if (typeof icon === 'string' || icon instanceof String) {
162 if (icon.includes("?path=")) {
163 const split = icon.split("?path=")
164 if (split.length !== 2)
165 return icon
166 const name = split[0]
167 const path = split[1]
168 const fileName = name.substring(
169 name.lastIndexOf("/") + 1)
170 return `file://${path}/${fileName}`
171 } else
172 icon = Quickshell.iconPath(icon);
173 return icon
174 }
175 return ""
176 }
177 asynchronous: true
178 smooth: true
179 mipmap: true
180 }
181
182 Text {
183 id: windowTitle
184
185 width: Math.min(implicitWidth, bar.width - 2*Math.max(left.width, right.width) - 2*8 - activeWindowIcon.width - activeWindowDisplayContent.spacing)
186
187 property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" }
188
189 elide: Text.ElideRight
190 maximumLineCount: 1
191 color: "white"
192 anchors.verticalCenter: parent.verticalCenter
193 text: {
194 if (!activeWindowDisplay.activeWindow)
195 return "";
196
197 var title = activeWindowDisplay.activeWindow.title;
198 var appName = activeWindowDisplay.windowEntry?.name;
199 if (appAliases[appName])
200 appName = appAliases[appName];
201 if (appName && title.endsWith(appName)) {
202 const oldTitle = title;
203 title = title.substring(0, title.length - appName.length);
204 title = title.replace(/\s*(—|-)\s*$/, "");
205 if (!title)
206 title = oldTitle;
207 }
208 return title;
209 }
210 }
211 }
212 } 54 }
213 } 55 }
214 56
@@ -222,357 +64,20 @@ PanelWindow {
222 anchors.verticalCenter: parent.verticalCenter 64 anchors.verticalCenter: parent.verticalCenter
223 spacing: 0 65 spacing: 0
224 66
225 Item { 67 SystemTray {}
226 anchors.verticalCenter: parent.verticalCenter
227 width: systemTrayRow.childrenRect.width
228 height: parent.height
229 clip: true
230
231 Row {
232 id: systemTrayRow
233 anchors.centerIn: parent
234 width: childrenRect.width
235 height: parent.height
236 spacing: 0
237
238 Repeater {
239 model: SystemTray.items.values
240
241 delegate: Item {
242 property var trayItem: modelData
243 property string iconSource: {
244 let icon = trayItem && trayItem.icon
245 if (typeof icon === 'string' || icon instanceof String) {
246 if (icon.includes("?path=")) {
247 const split = icon.split("?path=")
248 if (split.length !== 2)
249 return icon
250 const name = split[0]
251 const path = split[1]
252 const fileName = name.substring(
253 name.lastIndexOf("/") + 1)
254 return `file://${path}/${fileName}`
255 }
256 return icon
257 }
258 return ""
259 }
260
261 width: 16
262 height: parent.height
263 anchors.verticalCenter: parent.verticalCenter
264
265 IconImage {
266 anchors.centerIn: parent
267 width: parent.width
268 height: parent.width
269 source: parent.iconSource
270 asynchronous: true
271 smooth: true
272 mipmap: true
273 }
274
275 MouseArea {
276 id: trayItemArea
277
278 anchors.fill: parent
279 acceptedButtons: Qt.LeftButton | Qt.RightButton
280 hoverEnabled: true
281 cursorShape: Qt.PointingHandCursor
282 onClicked: mouse => {
283 if (!trayItem)
284 return
285
286 if (mouse.button === Qt.LeftButton
287 && !trayItem.onlyMenu) {
288 trayItem.activate()
289 return
290 }
291
292 if (trayItem.hasMenu) {
293 var globalPos = mapToGlobal(0, 0)
294 var currentScreen = screen || Screen
295 var screenX = currentScreen.x || 0
296 var relativeX = globalPos.x - screenX
297 menuAnchor.menu = trayItem.menu
298 menuAnchor.anchor.window = bar
299 menuAnchor.anchor.rect = Qt.rect(
300 relativeX,
301 21,
302 parent.width, 1)
303 menuAnchor.open()
304 }
305 }
306 }
307 }
308 }
309 }
310 QsMenuAnchor {
311 id: menuAnchor
312 }
313 }
314 68
315 Item { 69 Item {
316 height: parent.height 70 height: parent.height
317 width: 4 71 width: 4
318 } 72 }
319 73
320 Rectangle { 74 KeyboardLayout {}
321 id: kbdWidget
322
323 property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" }
324
325 width: kbdLabel.contentWidth + 8
326 color: {
327 if (kbdMouseArea.containsMouse) {
328 return "#33808080";
329 }
330 return "transparent";
331 }
332 height: parent.height
333 anchors.verticalCenter: parent.verticalCenter
334
335 MouseArea {
336 id: kbdMouseArea
337
338 anchors.fill: parent
339 hoverEnabled: true
340 cursorShape: Qt.PointingHandCursor
341 enabled: true
342 onClicked: {
343 NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {})
344 }
345 }
346
347 Text {
348 id: kbdLabel
349
350 font.pointSize: 10
351 font.family: "Fira Sans"
352 color: {
353 if (NiriService.keyboardLayouts?.current_idx === 0)
354 return "#555";
355 return "white";
356 }
357 anchors.centerIn: parent
358
359 text: {
360 const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx];
361 if (!currentLayout)
362 return "";
363 return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout;
364 }
365 }
366
367 PopupWindow {
368 anchor {
369 item: kbdMouseArea
370 edges: Edges.Bottom
371 }
372 visible: kbdMouseArea.containsMouse
373
374 implicitWidth: kbdTooltipText.contentWidth + 4
375 implicitHeight: kbdTooltipText.contentHeight + 4
376 color: "black"
377
378 Text {
379 id: kbdTooltipText
380
381 anchors.centerIn: parent
382
383 font.pointSize: 10
384 font.family: "Fira Sans"
385 color: "white"
386
387 text: {
388 const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx];
389 return currentLayout || "";
390 }
391 }
392 }
393 }
394 75
395 Item { 76 Item {
396 height: parent.height 77 height: parent.height
397 width: 4 78 width: 4
398 } 79 }
399 80
400 Item { 81 Clock {}
401 width: clock.contentWidth
402 height: parent.height
403 anchors.verticalCenter: parent.verticalCenter
404
405 MouseArea {
406 id: clockMouseArea
407
408 anchors.fill: parent
409 hoverEnabled: true
410 enabled: true
411 }
412
413 Text {
414 id: clock
415 color: "white"
416
417 anchors.verticalCenter: parent.verticalCenter
418
419 Custom.Chrono {
420 id: chrono
421 format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}"
422 }
423
424 text: chrono.date
425
426 font.pointSize: 10
427 font.family: "Fira Sans"
428 font.features: { "tnum": 1 }
429 }
430
431 PopupWindow {
432 anchor {
433 item: clockMouseArea
434 edges: Edges.Bottom
435 }
436 visible: clockMouseArea.containsMouse
437
438 implicitWidth: yearCalendar.implicitWidth + 16
439 implicitHeight: yearCalendar.implicitHeight + 16
440 color: "black"
441
442 GridLayout {
443 property int year: { const d = new Date(); return d.getFullYear(); }
444
445 id: yearCalendar
446
447 columns: 3
448 columnSpacing: 16
449 rowSpacing: 16
450
451 anchors.centerIn: parent
452
453 Repeater {
454 model: 12
455
456 Column {
457 required property int index
458 property int month: index
459
460 id: monthCalendar
461
462 width: parent.width
463
464 Text {
465 width: parent.width
466
467 horizontalAlignment: Text.AlignHCenter
468
469 font.pointSize: 10
470 font.family: "Fira Sans"
471
472 text: {
473 const date = Date.fromLocaleDateString(Qt.locale(), `${yearCalendar.year}-${monthCalendar.month + 1}-01`, "yyyy-M-dd");
474 return date.toLocaleString(Qt.locale("en_DK"), "MMMM")
475 }
476
477 color: "#ffead3"
478 }
479
480 GridLayout {
481 columns: 2
482
483 DayOfWeekRow {
484 locale: grid.locale
485
486 Layout.column: 1
487 Layout.fillWidth: true
488
489 delegate: Text {
490 required property string shortName
491
492 font.pointSize: 10
493 font.family: "Fira Mono"
494
495 text: shortName
496 color: "#ffcc66"
497
498 horizontalAlignment: Text.AlignRight
499 verticalAlignment: Text.AlignVCenter
500 }
501 }
502
503 WeekNumberColumn {
504 month: grid.month
505 year: grid.year
506 locale: grid.locale
507
508 Layout.fillHeight: true
509
510 delegate: Text {
511 required property int weekNumber
512
513 opacity: {
514 const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7);
515 const dayOfWeek = simple.getDay();
516 const isoWeekStart = simple;
517
518 isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1);
519 if (dayOfWeek > 4) {
520 isoWeekStart.setDate(isoWeekStart.getDate() + 7);
521 }
522
523 for (let i = 0; i < 7; i++) {
524 const dayInWeek = isoWeekStart;
525 dayInWeek.setDate(dayInWeek.getDate() + i);
526 if (dayInWeek.getMonth() == monthCalendar.month)
527 return 1;
528 }
529
530 return 0;
531 }
532
533 font.pointSize: 10
534 font.family: "Fira Sans"
535 font.features: { "tnum": 1 }
536
537 text: weekNumber
538 color: "#99ffdd"
539
540 horizontalAlignment: Text.AlignRight
541 verticalAlignment: Text.AlignVCenter
542 }
543 }
544
545 MonthGrid {
546 id: grid
547
548 year: yearCalendar.year
549 month: monthCalendar.month
550 locale: Qt.locale("en_DK")
551
552 Layout.fillWidth: true
553 Layout.fillHeight: true
554
555 delegate: Text {
556 required property var model
557
558 opacity: model.month === monthCalendar.month ? 1 : 0
559
560 font.pointSize: 10
561 font.family: "Fira Sans"
562 font.features: { "tnum": 1 }
563
564 text: model.day
565 color: model.today ? "#ff6699" : "white"
566
567 horizontalAlignment: Text.AlignRight
568 verticalAlignment: Text.AlignVCenter
569 }
570 }
571 }
572 }
573 }
574 }
575 }
576 }
577 } 82 }
578} \ No newline at end of file 83} \ No newline at end of file