1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: MIT
3
4import { DemoPalette, PushButton } from "./common.slint";
5
6export struct PrinterQueueItem {
7 status: string,
8 progress: int,
9 title: string,
10 owner: string,
11 pages: int,
12 size: string, // number instead and format in .slint?
13 submission-date: string
14}
15
16export global PrinterQueue {
17 in property <[PrinterQueueItem]> printer-queue: [
18 { status: "printing", progress: 63, title: "210106-FinalPresentation.pdf", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" },
19 { status: "waiting", title: "Adressliste.docx", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" },
20 { status: "waiting", title: "Salaries.pdf", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" },
21 ];
22
23 public pure function statusString(status: string) -> string {
24 if (status == "printing") {
25 @tr("PRINTING")
26 } else if (status == "waiting") {
27 @tr("WAITING...")
28 } else {
29 @tr("Unknown job status")
30 }
31 }
32
33 callback start-job(string);
34 callback cancel-job(int);
35 callback pause-job(int);
36}
37
38
39component PrintQueueDetailsLabel inherits Text {
40 font-weight: 500;
41 color: DemoPalette.control-foreground;
42 horizontal-stretch: 0;
43 font-size: DemoPalette.base-font-size * 0.9375;
44}
45
46component PrintQueueSeparator inherits Rectangle {
47 height: 1px;
48 border-width: 1px;
49 border-color: #BDC0D1;
50 horizontal-stretch: 2;
51}
52
53component PrintDetails inherits GridLayout {
54 in property <PrinterQueueItem> queue-item;
55
56 spacing: 3px;
57
58 Row {
59 PrintQueueDetailsLabel {
60 text: @tr("Job Owner");
61 }
62
63 Text {
64 text: root.queue-item.owner;
65 color: DemoPalette.secondary-foreground-color;
66 overflow: elide;
67 horizontal-stretch: 1;
68 font-size: DemoPalette.base-font-size * 0.9375;
69 }
70 }
71 Row {
72 PrintQueueSeparator {
73 colspan: 2;
74 }
75 }
76
77 Row {
78 PrintQueueDetailsLabel {
79 text: @tr("Pages");
80 }
81
82 Text {
83 text: root.queue-item.pages;
84 color: DemoPalette.secondary-foreground-color;
85 overflow: elide;
86 horizontal-stretch: 1;
87 font-size: DemoPalette.base-font-size * 0.9375;
88 }
89 }
90
91 Row {
92 PrintQueueSeparator {
93 colspan: 2;
94 }
95 }
96
97 Row {
98 PrintQueueDetailsLabel {
99 text: @tr("Size");
100 }
101
102 Text {
103 text: root.queue-item.pages;
104 color: DemoPalette.secondary-foreground-color;
105 overflow: elide;
106 horizontal-stretch: 1;
107 font-size: DemoPalette.base-font-size * 0.9375;
108 }
109 }
110
111 Row {
112 PrintQueueSeparator {
113 colspan: 2;
114 }
115 }
116
117 Row {
118 PrintQueueDetailsLabel {
119 text: @tr("Submitted");
120 }
121
122 Text {
123 text: root.queue-item.submission-date;
124 color: DemoPalette.secondary-foreground-color;
125 overflow: elide;
126 horizontal-stretch: 1;
127 font-size: DemoPalette.base-font-size * 0.9375;
128 }
129 }
130}
131
132component NarrowPrintQueueElement inherits Rectangle {
133 in property <PrinterQueueItem> queue-item;
134 in-out property <bool> expanded;
135
136 callback show-job-details();
137
138 private property <float> expanded-opacity: 0;
139
140 border-color: DemoPalette.control-outline-color;
141 border-radius: 14px;
142 border-width: 2px;
143 background: DemoPalette.printer-queue-item-background-color;
144 clip: true;
145 height: always-visible.min-height + layout.padding * 2;
146
147 states [
148 expanded when root.expanded : {
149 height: layout.min-height;
150 expanded-opacity: 1;
151
152 in {
153 animate height { duration: 200ms; easing: ease; }
154 animate expanded-opacity { duration: 200ms; }
155 }
156 out {
157 animate height { duration: 200ms; easing: ease; }
158 animate expanded-opacity { duration: 200ms; }
159 }
160 }
161 ]
162
163 TouchArea {
164 clicked => {
165 root.expanded = !root.expanded;
166 }
167 }
168
169 Rectangle {
170 height: 100%;
171 layout := VerticalLayout {
172 padding: root.border-radius;
173 spacing: 4px;
174 alignment: start;
175
176 always-visible := VerticalLayout {
177 padding: 0;
178 spacing: parent.spacing;
179
180 Text {
181 // TODO: text-transform: uppercase
182 text: {
183 if (root.queue-item.progress > 0) {
184 @tr("{}% - {}",root.queue-item.progress, PrinterQueue.statusString(root.queue-item.status))
185 } else {
186 PrinterQueue.statusString(root.queue-item.status)
187 }
188 }
189 color: DemoPalette.status-label-text-color;
190 font-size: DemoPalette.base-font-size * 0.75;
191 font-weight: 800;
192 letter-spacing: 1.56px;
193 }
194
195 Text {
196 text: root.queue-item.title;
197 overflow: elide;
198 color: DemoPalette.text-foreground-color;
199 font-weight: 800;
200 font-size: DemoPalette.base-font-size * 1.125;
201 }
202 }
203
204 if (root.expanded || root.expanded-opacity > 0) : PrintDetails {
205 padding: 0px;
206 padding-bottom: root.border-radius / 2;
207 queue-item: root.queue-item;
208 opacity: root.expanded-opacity;
209 }
210
211 if (root.expanded || root.expanded-opacity > 0) : HorizontalLayout {
212 Rectangle {
213 horizontal-stretch: 0;
214 width: 10%;
215 }
216
217 PushButton {
218 clicked => {
219 root.show-job-details();
220 }
221 opacity: root.expanded-opacity;
222 text: @tr("More ");
223 }
224
225 Rectangle {
226 horizontal-stretch: 0;
227 width: 10%;
228 }
229 }
230 }
231 }
232}
233
234component NarrowPrinterQueueList inherits Flickable {
235 callback show-job-details(int);
236
237 VerticalLayout {
238 alignment: start;
239 padding: 0px;
240 spacing: 13.5px;
241
242 for queue-item[idx] in PrinterQueue.printer-queue: NarrowPrintQueueElement {
243 show-job-details => {
244 root.show-job-details(idx)
245 }
246
247 width: root.width;
248 queue-item: queue-item;
249 }
250 }
251}
252
253component ProgressBar inherits Rectangle {
254 in property <int> progress;
255
256 // FIXME: The intermediate rectangle is needed to allow the surrounding
257 // layout to freely resize the progress bar without affecting the design-intended
258 // height of 6px. The alternative of specifying a `max-height: 6px` will unfortunately
259 // also affect the width calculation and make it vanish altogether.
260 Rectangle {
261 y: parent.height / 2 - 3px;
262 height: 6px;
263
264 border-radius: 3px;
265 background: DemoPalette.neutral-box;
266
267 Rectangle {
268 x:0;
269 width: max(6px, root.progress * parent.width / 100);
270 border-radius: parent.border-radius;
271 background: DemoPalette.control-foreground;
272 }
273 }
274}
275
276component WidePrintQueueElement inherits Rectangle {
277 in property <PrinterQueueItem> queue-item;
278
279 callback cancel-job();
280 callback pause-job();
281
282 border-color: DemoPalette.neutral-box;
283 border-radius: 14px;
284 border-width: 2px;
285 background: DemoPalette.printer-queue-item-background-color;
286
287 GridLayout {
288 padding: parent.border-radius;
289 spacing: 3px;
290
291 HorizontalLayout {
292 width: 48%;
293 Text {
294 // TODO: text-transform: uppercase
295 text: {
296 if (root.queue-item.progress > 0) {
297 @tr("{}% - {}",root.queue-item.progress, PrinterQueue.statusString(root.queue-item.status))
298 } else {
299 PrinterQueue.statusString(root.queue-item.status)
300 }
301 }
302 color: DemoPalette.status-label-text-color;
303 font-size: DemoPalette.base-font-size * 0.75;
304 font-weight: 700;
305 letter-spacing: 1.56px;
306 horizontal-stretch: 1;
307 }
308
309 ProgressBar {
310 // Each progress bar should have the same size
311 // In principle it should be the smaller size which would fit next to the text foe each entry
312 // But since it is too hard to compute, just hardcode that for now
313 width: 50%; // 164px;
314 progress: root.queue-item.progress;
315 }
316 }
317
318 Text {
319 col: 0;
320 row: 1;
321 text: root.queue-item.title;
322 color: DemoPalette.text-foreground-color;
323 overflow: elide;
324 font-weight: 700;
325 font-size: DemoPalette.base-font-size * 1.125;
326 horizontal-stretch: 1;
327 }
328
329 HorizontalLayout {
330 col: 0;
331 row: 3;
332 spacing: 14px;
333 horizontal-stretch: 1;
334 vertical-stretch: 0;
335
336 PushButton {
337 clicked => { root.pause-job(); }
338
339 text: @tr("Pause");
340 icon: @image-url("images/pause.svg");
341 }
342
343 PushButton {
344 clicked => { root.cancel-job(); }
345 primary: false;
346 text: @tr("Delete");
347 icon: @image-url("images/delete.svg");
348 }
349 }
350
351 PrintDetails {
352 row: 0;
353 col: 2;
354 rowspan: 4;
355 width: 40%;
356 padding: 0px;
357 padding-bottom: root.border-radius / 2;
358 queue-item: root.queue-item;
359 horizontal-stretch: 1;
360 }
361 }
362}
363
364
365export component WidePrinterQueueList inherits Flickable {
366 VerticalLayout {
367 alignment: start;
368 padding: 0px;
369 spacing: 13.5px;
370
371 for queue-item[idx] in PrinterQueue.printer-queue: WidePrintQueueElement {
372 cancel-job => { PrinterQueue.cancel-job(idx) }
373 pause-job => { PrinterQueue.pause-job(idx) }
374
375 queue-item: queue-item;
376 }
377 }
378}
379
380export component PrinterQueueView inherits Rectangle {
381 callback show-job-details(int);
382
383 border-radius: 27px;
384 background: DemoPalette.night-mode ? DemoPalette.printer-action-background-color : #F4F6FF;
385
386 VerticalLayout {
387 padding: 16px;
388 spacing: 16px;
389
390 Text {
391 text: @tr("Printing Queue");
392 color: DemoPalette.text-foreground-color;
393 font-size: DemoPalette.base-font-size * 1.5;
394 font-weight: 700;
395 }
396
397 queue-list := NarrowPrinterQueueList {
398 show-job-details(idx) => {
399 root.show-job-details(idx)
400 }
401
402 width: root.width - 2*parent.padding; // FIXME why do we need this? bug in layout?
403 }
404 }
405}
406