summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/shell/quickshell
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/shell/quickshell')
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Bar.qml6
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Clock.qml309
-rw-r--r--accounts/gkleen@sif/shell/quickshell/LockSurface.qml223
-rw-r--r--accounts/gkleen@sif/shell/quickshell/Lockscreen.qml222
-rw-r--r--accounts/gkleen@sif/shell/quickshell/displaymanager.qml115
5 files changed, 522 insertions, 353 deletions
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml
index aab1607f..38225d74 100644
--- a/accounts/gkleen@sif/shell/quickshell/Bar.qml
+++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml
@@ -7,8 +7,6 @@ PanelWindow {
7 7
8 required property var screen 8 required property var screen
9 9
10 property var calendarMouseArea: clock.calendarMouseArea
11
12 anchors { 10 anchors {
13 top: true 11 top: true
14 left: true 12 left: true
@@ -80,8 +78,6 @@ PanelWindow {
80 width: 4 78 width: 4
81 } 79 }
82 80
83 Clock { 81 Clock {}
84 id: clock
85 }
86 } 82 }
87} \ No newline at end of file 83} \ No newline at end of file
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml
index d0c9178b..382af168 100644
--- a/accounts/gkleen@sif/shell/quickshell/Clock.qml
+++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml
@@ -22,18 +22,6 @@ Item {
22 hoverEnabled: true 22 hoverEnabled: true
23 enabled: clockItem.calendarPopup 23 enabled: clockItem.calendarPopup
24 24
25 property real angleRem: 0
26 property real sensitivity: 1 / 120
27
28 function scrollYear(event) {
29 angleRem += event.angleDelta.y;
30 const d = Math.round(angleRem * sensitivity);
31 yearCalendar.year += d;
32 angleRem -= d / sensitivity;
33 }
34
35 onWheel: event => scrollYear(event)
36
37 Item { 25 Item {
38 anchors.fill: parent 26 anchors.fill: parent
39 27
@@ -56,204 +44,233 @@ Item {
56 } 44 }
57 } 45 }
58 46
59 PopupWindow { 47 Loader {
60 id: tooltip 48 id: tooltipLoader
61 49
62 property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse 50 active: false
63 51
64 anchor { 52 Connections {
65 item: clockMouseArea 53 target: clockMouseArea
66 edges: Edges.Bottom | Edges.Left 54 function onContainsMouseChanged() {
55 if (clockMouseArea.containsMouse)
56 tooltipLoader.active = true;
57 }
67 } 58 }
68 visible: false
69 59
70 onNextVisibleChanged: hangTimer.restart() 60 sourceComponent: PopupWindow {
61 id: tooltip
71 62
72 Timer { 63 property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse
73 id: hangTimer
74 interval: 100
75 onTriggered: tooltip.visible = tooltip.nextVisible
76 }
77 64
78 implicitWidth: clockTooltipContent.width 65 anchor {
79 implicitHeight: clockTooltipContent.height 66 item: clockMouseArea
80 color: "black" 67 edges: Edges.Bottom | Edges.Left
68 }
69 visible: false
81 70
82 onVisibleChanged: { 71 onNextVisibleChanged: hangTimer.restart()
83 yearCalendar.year = chrono.date.getFullYear();
84 clockMouseArea.angleRem = 0;
85 }
86 72
87 WrapperMouseArea { 73 Timer {
88 id: tooltipMouseArea 74 id: hangTimer
75 interval: 100
76 onTriggered: tooltip.visible = tooltip.nextVisible
77 }
89 78
90 hoverEnabled: true 79 implicitWidth: clockTooltipContent.width
91 enabled: true 80 implicitHeight: clockTooltipContent.height
81 color: "black"
92 82
93 onWheel: event => clockMouseArea.scrollYear(event) 83 onVisibleChanged: {
84 yearCalendar.year = chrono.date.getFullYear();
85 yearCalendar.angleRem = 0;
86 }
94 87
95 anchors.fill: parent 88 WrapperMouseArea {
89 id: tooltipMouseArea
96 90
97 WrapperItem { 91 hoverEnabled: true
98 id: clockTooltipContent 92 enabled: true
99 93
100 margin: 8 94 onWheel: event => yearCalendar.scrollYear(event)
101 leftMargin: 0
102 95
103 ColumnLayout { 96 anchors.fill: parent
104 Text {
105 id: yearLabel
106 97
107 horizontalAlignment: Text.AlignHCenter 98 WrapperItem {
99 id: clockTooltipContent
108 100
109 font.pointSize: 14 101 margin: 8
110 font.family: "Fira Sans" 102 leftMargin: 0
111 font.features: { "tnum": 1 }
112 color: "white"
113 103
114 text: yearCalendar.year 104 ColumnLayout {
105 Text {
106 id: yearLabel
115 107
116 Layout.fillWidth: true 108 horizontalAlignment: Text.AlignHCenter
117 Layout.bottomMargin: 8
118 }
119 109
120 GridLayout { 110 font.pointSize: 14
121 property int year: chrono.date.getFullYear() 111 font.family: "Fira Sans"
112 font.features: { "tnum": 1 }
113 color: "white"
122 114
123 id: yearCalendar 115 text: yearCalendar.year
124 116
125 columns: 3 117 Layout.fillWidth: true
126 columnSpacing: 16 118 Layout.bottomMargin: 8
127 rowSpacing: 16 119 }
128 120
129 Layout.alignment: Qt.AlignHCenter 121 GridLayout {
130 Layout.fillWidth: false 122 property int year: chrono.date.getFullYear()
131 123
132 Repeater { 124 id: yearCalendar
133 model: 12
134 125
135 GridLayout { 126 columns: 3
136 columns: 2 127 columnSpacing: 16
128 rowSpacing: 16
137 129
138 required property int index 130 Layout.alignment: Qt.AlignHCenter
139 property int month: index 131 Layout.fillWidth: false
140 132
141 id: monthCalendar 133 property real angleRem: 0
134 property real sensitivity: 1 / 120
142 135
143 Layout.alignment: Qt.AlignTop | Qt.AlignRight 136 function scrollYear(event) {
144 Layout.fillWidth: false 137 angleRem += event.angleDelta.y;
138 const d = Math.round(angleRem * sensitivity);
139 yearCalendar.year += d;
140 angleRem -= d / sensitivity;
141 }
145 142
146 Text { 143 Connections {
147 Layout.column: 1 144 target: clockMouseArea
148 Layout.fillWidth: true 145 function onWheel(event) { yearCalendar.scrollYear(event); }
146 }
149 147
150 horizontalAlignment: Text.AlignHCenter 148 Repeater {
149 model: 12
151 150
152 font.pointSize: 10 151 GridLayout {
153 font.family: "Fira Sans" 152 columns: 2
154 153
155 text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM") 154 required property int index
155 property int month: index
156 156
157 color: "#ffead3" 157 id: monthCalendar
158 }
159 158
160 DayOfWeekRow { 159 Layout.alignment: Qt.AlignTop | Qt.AlignRight
161 locale: grid.locale 160 Layout.fillWidth: false
162 161
163 Layout.row: 1 162 Text {
164 Layout.column: 1 163 Layout.column: 1
165 Layout.fillWidth: true 164 Layout.fillWidth: true
166 165
167 delegate: WrapperItem { 166 horizontalAlignment: Text.AlignHCenter
168 required property string shortName
169 167
170 width: dowLabel.contentWidth + 6 168 font.pointSize: 10
169 font.family: "Fira Sans"
171 170
172 Text { 171 text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM")
173 id: dowLabel
174 172
175 anchors.fill: parent 173 color: "#ffead3"
174 }
176 175
177 font.pointSize: 10 176 DayOfWeekRow {
178 font.family: "Fira Sans" 177 locale: grid.locale
179 178
180 text: parent.shortName 179 Layout.row: 1
181 color: "#ffcc66" 180 Layout.column: 1
181 Layout.fillWidth: true
182 182
183 horizontalAlignment: Text.AlignHCenter 183 delegate: WrapperItem {
184 verticalAlignment: Text.AlignVCenter 184 required property string shortName
185 }
186 }
187 }
188 185
189 WeekNumberColumn { 186 width: dowLabel.contentWidth + 6
190 month: grid.month
191 year: grid.year
192 locale: grid.locale
193 187
194 Layout.fillHeight: true 188 Text {
189 id: dowLabel
195 190
196 delegate: Text { 191 anchors.fill: parent
197 required property int weekNumber
198 192
199 opacity: { 193 font.pointSize: 10
200 const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); 194 font.family: "Fira Sans"
201 const dayOfWeek = simple.getDay();
202 const isoWeekStart = simple;
203 195
204 isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); 196 text: parent.shortName
205 if (dayOfWeek > 4) { 197 color: "#ffcc66"
206 isoWeekStart.setDate(isoWeekStart.getDate() + 7);
207 }
208 198
209 for (let i = 0; i < 7; i++) { 199 horizontalAlignment: Text.AlignHCenter
210 const dayInWeek = new Date(isoWeekStart); 200 verticalAlignment: Text.AlignVCenter
211 dayInWeek.setDate(dayInWeek.getDate() + i);
212 if (dayInWeek.getMonth() == monthCalendar.month)
213 return 1;
214 } 201 }
215
216 return 0;
217 } 202 }
203 }
218 204
219 font.pointSize: 10 205 WeekNumberColumn {
220 font.family: "Fira Sans" 206 month: grid.month
221 font.features: { "tnum": 1 } 207 year: grid.year
222 208 locale: grid.locale
223 text: weekNumber
224 color: "#99ffdd"
225 209
226 horizontalAlignment: Text.AlignRight 210 Layout.fillHeight: true
227 verticalAlignment: Text.AlignVCenter
228 }
229 }
230 211
231 MonthGrid { 212 delegate: Text {
232 id: grid 213 required property int weekNumber
233 214
234 year: yearCalendar.year 215 opacity: {
235 month: monthCalendar.month 216 const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7);
236 locale: Qt.locale("en_DK") 217 const dayOfWeek = simple.getDay();
218 const isoWeekStart = simple;
237 219
238 Layout.fillWidth: true 220 isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1);
239 Layout.fillHeight: true 221 if (dayOfWeek > 4) {
222 isoWeekStart.setDate(isoWeekStart.getDate() + 7);
223 }
240 224
241 delegate: Text { 225 for (let i = 0; i < 7; i++) {
242 required property var model 226 const dayInWeek = new Date(isoWeekStart);
227 dayInWeek.setDate(dayInWeek.getDate() + i);
228 if (dayInWeek.getMonth() == monthCalendar.month)
229 return 1;
230 }
243 231
244 opacity: model.month === monthCalendar.month ? 1 : 0 232 return 0;
233 }
245 234
246 font.pointSize: 10 235 font.pointSize: 10
247 font.family: "Fira Sans" 236 font.family: "Fira Sans"
248 font.features: { "tnum": 1 } 237 font.features: { "tnum": 1 }
249 238
250 property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day 239 text: weekNumber
251 240 color: "#99ffdd"
252 text: model.day
253 color: today ? "#ff6699" : "white"
254 241
255 horizontalAlignment: Text.AlignRight 242 horizontalAlignment: Text.AlignRight
256 verticalAlignment: Text.AlignVCenter 243 verticalAlignment: Text.AlignVCenter
244 }
245 }
246
247 MonthGrid {
248 id: grid
249
250 year: yearCalendar.year
251 month: monthCalendar.month
252 locale: Qt.locale("en_DK")
253
254 Layout.fillWidth: true
255 Layout.fillHeight: true
256
257 delegate: Text {
258 required property var model
259
260 opacity: model.month === monthCalendar.month ? 1 : 0
261
262 font.pointSize: 10
263 font.family: "Fira Sans"
264 font.features: { "tnum": 1 }
265
266 property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day
267
268 text: model.day
269 color: today ? "#ff6699" : "white"
270
271 horizontalAlignment: Text.AlignRight
272 verticalAlignment: Text.AlignVCenter
273 }
257 } 274 }
258 } 275 }
259 } 276 }
diff --git a/accounts/gkleen@sif/shell/quickshell/LockSurface.qml b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml
new file mode 100644
index 00000000..18698725
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml
@@ -0,0 +1,223 @@
1import Quickshell.Widgets
2import QtQuick.Effects
3import QtQuick.Layouts
4import QtQuick
5import QtQuick.Controls
6import QtQuick.Controls.Fusion
7import qs.Services
8import QtQml
9
10Item {
11 id: lockSurface
12
13 property var screen
14 property list<var> messages: []
15 property bool responseRequired: false
16 property bool responseVisible: false
17 property string currentText: ""
18 property bool authRunning: false
19
20 signal response(string responseText)
21
22 anchors.fill: parent
23
24 Item {
25 id: background
26
27 anchors.fill: parent
28
29 property Img current: one
30 property string source: selector.selected
31
32 WallpaperSelector {
33 id: selector
34 seed: lockSurface.screen?.name || ""
35 }
36
37 onSourceChanged: {
38 if (!source)
39 current = null;
40 else if (current === one)
41 two.update()
42 else
43 one.update()
44 }
45
46 Img { id: one }
47 Img { id: two }
48
49 component Img: Item {
50 id: img
51
52 property string source
53
54 function update() {
55 source = background.source || ""
56 }
57
58 anchors.fill: parent
59
60 Image {
61 id: imageSource
62
63 source: img.source
64 sourceSize: Qt.size(parent.width, parent.height)
65 fillMode: Image.PreserveAspectCrop
66 smooth: true
67 visible: false
68 asynchronous: true
69 cache: false
70
71 onStatusChanged: {
72 if (status === Image.Ready) {
73 background.current = img
74 }
75 }
76 }
77
78 MultiEffect {
79 id: imageEffect
80
81 source: imageSource
82 anchors.fill: parent
83 blurEnabled: true
84 blur: 1
85 blurMax: 64
86 blurMultiplier: 2
87
88 opacity: 0
89
90 states: State {
91 name: "visible"
92 when: background.current === img
93
94 PropertyChanges {
95 imageEffect.opacity: 1
96 }
97 StateChangeScript {
98 name: "unloadOther"
99 script: {
100 if (img === one)
101 two.source = ""
102 if (img === two)
103 one.source = ""
104 }
105 }
106 }
107
108 transitions: Transition {
109 SequentialAnimation {
110 NumberAnimation {
111 target: imageEffect
112 properties: "opacity"
113 duration: 5000
114 easing.type: Easing.OutCubic
115 }
116 ScriptAction {
117 scriptName: "unloadOther"
118 }
119 }
120 }
121 }
122 }
123 }
124
125 Item {
126 anchors {
127 top: lockSurface.top
128 left: lockSurface.left
129 right: lockSurface.right
130 }
131
132 implicitWidth: lockSurface.width
133 implicitHeight: 21
134
135 Rectangle {
136 anchors.fill: parent
137 color: Qt.rgba(0, 0, 0, 0.75)
138 }
139
140 Clock {
141 anchors.centerIn: parent
142 calendarPopup: false
143 }
144 }
145
146 WrapperRectangle {
147 id: unlockUi
148
149 Keys.onPressed: event => {
150 if (!lockSurface.authRunning) {
151 event.accepted = true;
152 lockSurface.authRunning = true;
153 }
154 }
155 focus: !passwordBox.visible
156
157 visible: lockSurface.authRunning
158
159 color: Qt.rgba(0, 0, 0, 0.75)
160 margin: 8
161
162 anchors.centerIn: parent
163
164 ColumnLayout {
165 spacing: 4
166
167 BusyIndicator {
168 visible: running
169 running: !Array.from(lockSurface.messages).length && !lockSurface.responseRequired
170 }
171
172 Repeater {
173 model: lockSurface.messages
174
175 Text {
176 required property var modelData
177
178 font.pointSize: 10
179 font.family: "Fira Sans"
180 color: modelData.error ? "#f28a21" : "#ffffff"
181
182 text: String(modelData.text).trim()
183
184 Layout.fillWidth: true
185 horizontalAlignment: Text.AlignHCenter
186 }
187 }
188
189 TextField {
190 id: passwordBox
191
192 visible: lockSurface.responseRequired
193 echoMode: lockSurface.responseVisible ? TextInput.Normal : TextInput.Password
194 inputMethodHints: Qt.ImhSensitiveData
195
196 onTextChanged: lockSurface.currentText = passwordBox.text
197 onAccepted: {
198 passwordBox.readOnly = true;
199 lockSurface.response(lockSurface.currentText);
200 }
201
202 Connections {
203 target: lockSurface
204 function onCurrentTextChanged() {
205 passwordBox.text = lockSurface.currentText
206 }
207 }
208 Connections {
209 target: lockSurface
210 function onResponseRequiredChanged() {
211 if (lockSurface.responseRequired)
212 passwordBox.readOnly = false;
213 passwordBox.focus = true;
214 passwordBox.selectAll();
215 }
216 }
217
218 Layout.topMargin: 4
219 Layout.fillWidth: true
220 }
221 }
222 }
223}
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml
index 7cb1cc67..cc82a275 100644
--- a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml
+++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml
@@ -2,14 +2,7 @@ import Quickshell
2import Quickshell.Wayland 2import Quickshell.Wayland
3import Quickshell.Io 3import Quickshell.Io
4import Quickshell.Services.Pam 4import Quickshell.Services.Pam
5import Quickshell.Widgets 5import QtQml
6import QtQuick.Effects
7import QtQuick.Layouts
8import QtQuick
9import QtQuick.Controls
10import QtQuick.Controls.Fusion
11import qs.Services
12// import QtQml.Models
13 6
14Scope { 7Scope {
15 id: lockscreen 8 id: lockscreen
@@ -53,205 +46,30 @@ Scope {
53 WlSessionLockSurface { 46 WlSessionLockSurface {
54 id: lockSurface 47 id: lockSurface
55 48
56 color: "#000000" 49 color: "black"
57 50
58 Item { 51 LockSurface {
59 id: background 52 id: surfaceContent
60 53
61 anchors.fill: parent 54 onResponse: responseText => pam.respond(responseText)
62 55 onAuthRunningChanged: {
63 property Img current: one 56 if (authRunning)
64 property string source: selector.selected 57 pam.start();
65
66 WallpaperSelector {
67 id: selector
68 seed: lockSurface.screen?.name || ""
69 }
70
71 onSourceChanged: {
72 if (!source)
73 current = null;
74 else if (current === one)
75 two.update()
76 else
77 one.update()
78 }
79
80 Img { id: one }
81 Img { id: two }
82
83 component Img: Item {
84 id: img
85
86 property string source
87
88 function update() {
89 source = background.source || ""
90 }
91
92 anchors.fill: parent
93
94 Image {
95 id: imageSource
96
97 source: img.source
98 sourceSize: Qt.size(parent.width, parent.height)
99 fillMode: Image.PreserveAspectCrop
100 smooth: true
101 visible: false
102 asynchronous: true
103 cache: false
104
105 onStatusChanged: {
106 if (status === Image.Ready) {
107 background.current = img
108 }
109 }
110 }
111
112 MultiEffect {
113 id: imageEffect
114
115 source: imageSource
116 anchors.fill: parent
117 blurEnabled: true
118 blur: 1
119 blurMax: 64
120 blurMultiplier: 2
121
122 opacity: 0
123
124 states: State {
125 name: "visible"
126 when: background.current === img
127
128 PropertyChanges {
129 imageEffect.opacity: 1
130 }
131 StateChangeScript {
132 name: "unloadOther"
133 script: {
134 if (img === one)
135 two.source = ""
136 if (img === two)
137 one.source = ""
138 }
139 }
140 }
141
142 transitions: Transition {
143 SequentialAnimation {
144 NumberAnimation {
145 target: imageEffect
146 properties: "opacity"
147 duration: 5000
148 easing.type: Easing.OutCubic
149 }
150 ScriptAction {
151 scriptName: "unloadOther"
152 }
153 }
154 }
155 }
156 }
157 }
158
159 Item {
160 anchors {
161 top: lockSurface.top
162 left: lockSurface.left
163 right: lockSurface.right
164 }
165
166 implicitWidth: lockSurface.width
167 implicitHeight: 21
168
169 Rectangle {
170 anchors.fill: parent
171 color: Qt.rgba(0, 0, 0, 0.75)
172 } 58 }
173 59 Connections {
174 Clock { 60 target: pam
175 anchors.centerIn: parent 61 function onMessagesChanged() { surfaceContent.messages = pam.messages; }
176 calendarPopup: false 62 function onResponseRequiredChanged() { surfaceContent.responseRequired = pam.responseRequired; }
63 function onActiveChanged() { surfaceContent.authRunning = pam.active; }
177 } 64 }
178 } 65 onCurrentTextChanged: lockscreen.currentText = currentText
179 66 Connections {
180 WrapperRectangle { 67 target: lockscreen
181 id: unlockUi 68 function onCurrentTextChanged() { surfaceContent.currentText = lockscreen.currentText; }
182
183 Keys.onPressed: event => {
184 if (!pam.active) {
185 event.accepted = true;
186 pam.start();
187 }
188 } 69 }
189 focus: !passwordBox.visible 70 Connections {
190 71 target: lockSurface
191 visible: pam.active 72 function onScreenChanged() { surfaceContent.screen = lockSurface.screen; }
192
193 color: Qt.rgba(0, 0, 0, 0.75)
194 margin: 8
195
196 anchors.centerIn: parent
197
198 ColumnLayout {
199 spacing: 4
200
201 BusyIndicator {
202 visible: running
203 running: !Array.from(pam.messages).length && !pam.responseRequired
204 }
205
206 Repeater {
207 model: pam.messages
208
209 Text {
210 required property var modelData
211
212 font.pointSize: 10
213 font.family: "Fira Sans"
214 color: modelData.error ? "#f28a21" : "#ffffff"
215
216 text: modelData.text
217
218 Layout.fillWidth: true
219 horizontalAlignment: Text.AlignHCenter
220 }
221 }
222
223 TextField {
224 id: passwordBox
225
226 visible: pam.responseRequired
227 echoMode: pam.responseVisible ? TextInput.Normal : TextInput.Password
228 inputMethodHints: Qt.ImhSensitiveData
229
230 onTextChanged: lockscreen.currentText = passwordBox.text
231 onAccepted: {
232 passwordBox.readOnly = true;
233 pam.respond(lockscreen.currentText);
234 }
235
236 Connections {
237 target: lockscreen
238 function onCurrentTextChanged() {
239 passwordBox.text = lockscreen.currentText
240 }
241 }
242 Connections {
243 target: pam
244 function onResponseRequiredChanged() {
245 if (pam.responseRequired)
246 passwordBox.readOnly = false;
247 passwordBox.focus = true;
248 passwordBox.selectAll();
249 }
250 }
251
252 Layout.topMargin: 4
253 Layout.fillWidth: true
254 }
255 } 73 }
256 } 74 }
257 } 75 }
diff --git a/accounts/gkleen@sif/shell/quickshell/displaymanager.qml b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml
new file mode 100644
index 00000000..b452c03d
--- /dev/null
+++ b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml
@@ -0,0 +1,115 @@
1//@ pragma UseQApplication
2
3import Quickshell
4import Quickshell.Wayland
5import Quickshell.Io
6import Quickshell.Services.Greetd
7import QtQml
8
9
10ShellRoot {
11 id: displaymanager
12
13 settings.watchFiles: false
14
15 property string currentText: ""
16 property string username: @username@
17 property list<string> command: @niri_session@
18 property list<var> messages: []
19 property bool responseRequired: false
20 property bool responseVisible: false
21
22 signal startAuth()
23
24 onStartAuth: {
25 if (Greetd.state !== GreetdState.Inactive)
26 Greetd.cancelSession();
27 displaymanager.messages = [];
28 Greetd.createSession(displaymanager.username);
29 }
30
31 Connections {
32 target: Greetd
33 function onStateChanged() {
34 console.log("greetd state: ", GreetdState.toString(Greetd.state));
35 if (Greetd.state === GreetdState.ReadyToLaunch)
36 Greetd.launch(displaymanager.command);
37 }
38 function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) {
39 displaymanager.responseVisible = echoResponse;
40 displaymanager.responseRequired = responseRequired;
41 displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": error }]);
42 }
43 function onAuthFailure(message: string) {
44 displaymanager.responseRequired = false;
45 displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": true }]);
46 }
47 }
48
49 Component.onCompleted: {
50 if (Greetd.state !== GreetdState.Inactive)
51 Greetd.cancelSession();
52 }
53
54 Variants {
55 model: Quickshell.screens
56
57 delegate: Scope {
58 id: screenScope
59
60 required property var modelData
61
62 PanelWindow {
63 color: "black"
64
65 screen: screenScope.modelData
66
67 WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
68
69 anchors.top: true
70 anchors.bottom: true
71 anchors.left: true
72 anchors.right: true
73
74 LockSurface {
75 id: surfaceContent
76
77 screen: screenScope.modelData
78
79 onCurrentTextChanged: displaymanager.currentText = currentText
80 Connections {
81 target: displaymanager
82 function onCurrentTextChanged() { surfaceContent.currentText = displaymanager.currentText; }
83 function onMessagesChanged() { surfaceContent.messages = Array.from(displaymanager.messages); }
84 function onResponseRequiredChanged() { surfaceContent.responseRequired = displaymanager.responseRequired; }
85 function onResponseVisibleChanged() { surfaceContent.responseVisible = displaymanager.responseVisible; }
86 }
87
88 onResponse: responseText => Greetd.respond(responseText);
89 Connections {
90 target: Greetd
91 function onStateChanged() {
92 if (Greetd.state === GreetdState.Authenticating) {
93 surfaceContent.authRunning = true;
94 } else {
95 surfaceContent.authRunning = false;
96 }
97 }
98 }
99
100 onAuthRunningChanged: {
101 if (surfaceContent.authRunning && Greetd.state !== GreetdState.Authenticating)
102 displaymanager.startAuth();
103 }
104 Component.onCompleted: {
105 surfaceContent.authRunning = Greetd.state === GreetdState.Authenticating
106 surfaceContent.messages = Array.from(displaymanager.messages);
107 surfaceContent.responseVisible = displaymanager.responseVisible;
108 surfaceContent.responseRequired = displaymanager.responseRequired;
109 surfaceContent.currentText = displaymanager.currentText;
110 }
111 }
112 }
113 }
114 }
115}