1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
// 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 Quickshell.Io
import Quickshell.Widgets
import QtQuick
import "../.."
import "User.js" as U
// The drawer's greeting: avatar, a random greeting, real name. It re-reads each time
// it becomes visible, which is every drawer open and every return from a
// page, so a save on the user page shows here on the way back.
Rectangle {
id: row
signal clicked
property var info: ({})
implicitHeight: 64
radius: 12
// A header, not a card: nothing behind it until hovered, so it does not
// read as one more notification in the list below.
color: area.containsMouse ? Qt.alpha(Theme.accent, 0.12) : "transparent"
function read() {
reader.running = false;
reader.running = true;
}
onVisibleChanged: if (visible) read()
Component.onCompleted: read()
// Read on creation, which is every drawer open, so an edit to the file
// shows on the next one with no watch.
property string greeting: "Hello " + Quickshell.env("USER")
FileView {
path: (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/quickshell/greetings"
printErrors: false
onLoaded: row.greeting = U.greeting(text(), Quickshell.env("USER"))
onLoadFailed: row.greeting = U.greeting("", Quickshell.env("USER"))
}
Process {
id: reader
command: [Qt.resolvedUrl("userctl").toString().replace(/^file:\/\//, ""), "get"]
// Cleared first: the icon path never changes, so only a source going
// through "" makes an uncached Image load the new picture.
stdout: StdioCollector {
onStreamFinished: { row.info = ({}); row.info = U.parse(text); }
}
}
ClippingRectangle {
id: face
anchors { left: parent.left; leftMargin: 12; verticalCenter: parent.verticalCenter }
width: 44
height: 44
radius: 22
color: Theme.surfaceAlt
Image {
id: pic
anchors.fill: parent
source: row.info.avatar ? "file://" + row.info.avatar.split("/").map(encodeURIComponent).join("/") : ""
fillMode: Image.PreserveAspectCrop
sourceSize: Qt.size(88, 88)
cache: false
}
Text {
anchors.centerIn: parent
visible: pic.status !== Image.Ready
text: ""
font { family: Theme.iconFamily; pixelSize: 20 }
color: Theme.subtext
}
}
Column {
anchors { left: face.right; leftMargin: 12; right: parent.right; rightMargin: 12
verticalCenter: parent.verticalCenter }
spacing: 2
Text {
text: row.greeting
font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 2; bold: true }
color: Theme.text
}
Text {
width: parent.width
visible: text !== ""
text: row.info.name ?? ""
elide: Text.ElideRight
font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
color: Theme.subtext
}
}
MouseArea {
id: area
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: row.clicked()
}
}
|