// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT import { DemoPalette, PushButton } from "./common.slint"; export struct PrinterQueueItem { status: string, progress: int, title: string, owner: string, pages: int, size: string, // number instead and format in .slint? submission-date: string } export global PrinterQueue { in property <[PrinterQueueItem]> printer-queue: [ { status: "printing", progress: 63, title: "210106-FinalPresentation.pdf", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" }, { status: "waiting", title: "Adressliste.docx", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" }, { status: "waiting", title: "Salaries.pdf", owner: "info@slint.dev", pages: 6, size: "143kb", submission-date: "11:41 25/01/21" }, ]; public pure function statusString(status: string) -> string { if (status == "printing") { @tr("PRINTING") } else if (status == "waiting") { @tr("WAITING...") } else { @tr("Unknown job status") } } callback start-job(string); callback cancel-job(int); callback pause-job(int); } component PrintQueueDetailsLabel inherits Text { font-weight: 500; color: DemoPalette.control-foreground; horizontal-stretch: 0; font-size: DemoPalette.base-font-size * 0.9375; } component PrintQueueSeparator inherits Rectangle { height: 1px; border-width: 1px; border-color: #BDC0D1; horizontal-stretch: 2; } component PrintDetails inherits GridLayout { in property queue-item; spacing: 3px; Row { PrintQueueDetailsLabel { text: @tr("Job Owner"); } Text { text: root.queue-item.owner; color: DemoPalette.secondary-foreground-color; overflow: elide; horizontal-stretch: 1; font-size: DemoPalette.base-font-size * 0.9375; } } Row { PrintQueueSeparator { colspan: 2; } } Row { PrintQueueDetailsLabel { text: @tr("Pages"); } Text { text: root.queue-item.pages; color: DemoPalette.secondary-foreground-color; overflow: elide; horizontal-stretch: 1; font-size: DemoPalette.base-font-size * 0.9375; } } Row { PrintQueueSeparator { colspan: 2; } } Row { PrintQueueDetailsLabel { text: @tr("Size"); } Text { text: root.queue-item.pages; color: DemoPalette.secondary-foreground-color; overflow: elide; horizontal-stretch: 1; font-size: DemoPalette.base-font-size * 0.9375; } } Row { PrintQueueSeparator { colspan: 2; } } Row { PrintQueueDetailsLabel { text: @tr("Submitted"); } Text { text: root.queue-item.submission-date; color: DemoPalette.secondary-foreground-color; overflow: elide; horizontal-stretch: 1; font-size: DemoPalette.base-font-size * 0.9375; } } } component NarrowPrintQueueElement inherits Rectangle { in property queue-item; in-out property expanded; callback show-job-details(); private property expanded-opacity: 0; border-color: DemoPalette.secondary; border-radius: 14px; border-width: 2px; background: DemoPalette.background; clip: true; height: always-visible.min-height + layout.padding * 2; states [ expanded when root.expanded : { height: layout.min-height; expanded-opacity: 1; in { animate height { duration: 200ms; easing: ease; } animate expanded-opacity { duration: 200ms; } } out { animate height { duration: 200ms; easing: ease; } animate expanded-opacity { duration: 200ms; } } } ] TouchArea { clicked => { root.expanded = !root.expanded; } } Rectangle { height: 100%; layout := VerticalLayout { padding: root.border-radius; spacing: 4px; alignment: start; always-visible := VerticalLayout { padding: 0; spacing: parent.spacing; Text { // TODO: text-transform: uppercase text: { if (root.queue-item.progress > 0) { @tr("{}% - {}",root.queue-item.progress, PrinterQueue.statusString(root.queue-item.status)) } else { PrinterQueue.statusString(root.queue-item.status) } } color: DemoPalette.text-secondary; font-size: DemoPalette.base-font-size * 0.75; font-weight: 800; letter-spacing: 1.56px; } Text { text: root.queue-item.title; overflow: elide; color: DemoPalette.text-primary; font-weight: 800; font-size: DemoPalette.base-font-size * 1.125; } } if (root.expanded || root.expanded-opacity > 0) : PrintDetails { padding: 0px; padding-bottom: root.border-radius / 2; queue-item: root.queue-item; opacity: root.expanded-opacity; } if (root.expanded || root.expanded-opacity > 0) : HorizontalLayout { Rectangle { horizontal-stretch: 0; width: 10%; } PushButton { clicked => { root.show-job-details(); } opacity: root.expanded-opacity; text: @tr("More "); } Rectangle { horizontal-stretch: 0; width: 10%; } } } } } component NarrowPrinterQueueList inherits Flickable { callback show-job-details(int); VerticalLayout { alignment: start; padding: 0px; spacing: 13.5px; for queue-item[idx] in PrinterQueue.printer-queue: NarrowPrintQueueElement { show-job-details => { root.show-job-details(idx) } width: root.width; queue-item: queue-item; } } } component ProgressBar inherits Rectangle { in property progress; // FIXME: The intermediate rectangle is needed to allow the surrounding // layout to freely resize the progress bar without affecting the design-intended // height of 6px. The alternative of specifying a `max-height: 6px` will unfortunately // also affect the width calculation and make it vanish altogether. Rectangle { y: parent.height / 2 - 3px; height: 6px; border-radius: 3px; background: DemoPalette.neutral-box; Rectangle { x:0; width: max(6px, root.progress * parent.width / 100); border-radius: parent.border-radius; background: DemoPalette.control-foreground; } } } component WidePrintQueueElement inherits Rectangle { in property queue-item; callback cancel-job(); callback pause-job(); border-color: DemoPalette.neutral-box; border-radius: 14px; border-width: 2px; background: DemoPalette.background; GridLayout { padding: parent.border-radius; spacing: 3px; HorizontalLayout { width: 48%; Text { // TODO: text-transform: uppercase text: { if (root.queue-item.progress > 0) { @tr("{}% - {}",root.queue-item.progress, PrinterQueue.statusString(root.queue-item.status)) } else { PrinterQueue.statusString(root.queue-item.status) } } color: DemoPalette.text-secondary; font-size: DemoPalette.base-font-size * 0.75; font-weight: 700; letter-spacing: 1.56px; horizontal-stretch: 1; } ProgressBar { // Each progress bar should have the same size // In principle it should be the smaller size which would fit next to the text foe each entry // But since it is too hard to compute, just hardcode that for now width: 50%; // 164px; progress: root.queue-item.progress; } } Text { col: 0; row: 1; text: root.queue-item.title; color: DemoPalette.text-primary; overflow: elide; font-weight: 700; font-size: DemoPalette.base-font-size * 1.125; horizontal-stretch: 1; } HorizontalLayout { col: 0; row: 3; spacing: 14px; horizontal-stretch: 1; vertical-stretch: 0; PushButton { clicked => { root.pause-job(); } text: @tr("Pause"); icon: @image-url("images/pause.svg"); } PushButton { clicked => { root.cancel-job(); } primary: false; text: @tr("Delete"); icon: @image-url("images/delete.svg"); } } PrintDetails { row: 0; col: 2; rowspan: 4; width: 40%; padding: 0px; padding-bottom: root.border-radius / 2; queue-item: root.queue-item; horizontal-stretch: 1; } } } export component WidePrinterQueueList inherits Flickable { VerticalLayout { alignment: start; padding: 0px; spacing: 13.5px; for queue-item[idx] in PrinterQueue.printer-queue: WidePrintQueueElement { cancel-job => { PrinterQueue.cancel-job(idx) } pause-job => { PrinterQueue.pause-job(idx) } queue-item: queue-item; } } } export component PrinterQueueView inherits Rectangle { callback show-job-details(int); border-radius: 27px; background: DemoPalette.background.darker(20%); VerticalLayout { padding: 16px; spacing: 16px; Text { text: @tr("Printing Queue"); color: DemoPalette.text-primary; font-size: DemoPalette.base-font-size * 1.5; font-weight: 700; } queue-list := NarrowPrinterQueueList { show-job-details(idx) => { root.show-job-details(idx) } width: root.width - 2*parent.padding; // FIXME why do we need this? bug in layout? } } }