MonitorPrinterCard.qml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. // Copyright (c) 2019 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.3
  4. import QtQuick.Controls 2.0
  5. import QtQuick.Dialogs 1.1
  6. import UM 1.3 as UM
  7. import Cura 1.0 as Cura
  8. /**
  9. * A Printer Card is has two main components: the printer portion and the print job portion, the latter being paired in
  10. * the UI when a print job is paired a printer in-cluster.
  11. *
  12. * NOTE: For most labels, a fixed height with vertical alignment is used to make layouts more deterministic (like the
  13. * fixed-size textboxes used in original mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted
  14. * with '// FIXED-LINE-HEIGHT:'.
  15. */
  16. Item
  17. {
  18. id: base
  19. // The printer which all printer data is derived from
  20. property var printer: null
  21. property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here
  22. // If the printer card's controls are enabled. This is used by the carousel to prevent opening the context menu or
  23. // camera while the printer card is not "in focus"
  24. property var enabled: true
  25. // If the printer is a cloud printer or not. Other items base their enabled state off of this boolean. In the future
  26. // they might not need to though.
  27. property bool cloudConnection: Cura.MachineManager.activeMachineIsUsingCloudConnection
  28. width: 834 * screenScaleFactor // TODO: Theme!
  29. height: childrenRect.height
  30. Rectangle
  31. {
  32. id: background
  33. anchors.fill: parent
  34. color: UM.Theme.getColor("monitor_card_background")
  35. border
  36. {
  37. color: UM.Theme.getColor("monitor_card_border")
  38. width: borderSize // TODO: Remove once themed
  39. }
  40. radius: 2 * screenScaleFactor // TODO: Theme!
  41. }
  42. // Printer portion
  43. Item
  44. {
  45. id: printerInfo
  46. width: parent.width
  47. height: 144 * screenScaleFactor // TODO: Theme!
  48. Row
  49. {
  50. anchors
  51. {
  52. left: parent.left
  53. leftMargin: 36 * screenScaleFactor // TODO: Theme!
  54. verticalCenter: parent.verticalCenter
  55. }
  56. spacing: 18 * screenScaleFactor // TODO: Theme!
  57. Rectangle
  58. {
  59. id: printerImage
  60. width: 108 * screenScaleFactor // TODO: Theme!
  61. height: 108 * screenScaleFactor // TODO: Theme!
  62. color: printer ? "transparent" : UM.Theme.getColor("monitor_skeleton_loading")
  63. radius: 8 // TODO: Theme!
  64. Image
  65. {
  66. anchors.fill: parent
  67. fillMode: Image.PreserveAspectFit
  68. source: printer ? "../png/" + printer.type + ".png" : ""
  69. mipmap: true
  70. }
  71. }
  72. Item
  73. {
  74. anchors
  75. {
  76. verticalCenter: parent.verticalCenter
  77. }
  78. width: 180 * screenScaleFactor // TODO: Theme!
  79. height: childrenRect.height
  80. Rectangle
  81. {
  82. id: printerNameLabel
  83. color: printer ? "transparent" : UM.Theme.getColor("monitor_skeleton_loading")
  84. height: 18 * screenScaleFactor // TODO: Theme!
  85. width: parent.width
  86. radius: 2 * screenScaleFactor // TODO: Theme!
  87. Label
  88. {
  89. text: printer && printer.name ? printer.name : ""
  90. color: UM.Theme.getColor("text")
  91. elide: Text.ElideRight
  92. font: UM.Theme.getFont("large") // 16pt, bold
  93. width: parent.width
  94. visible: printer
  95. // FIXED-LINE-HEIGHT:
  96. height: parent.height
  97. verticalAlignment: Text.AlignVCenter
  98. renderType: Text.NativeRendering
  99. }
  100. }
  101. Rectangle
  102. {
  103. color: UM.Theme.getColor("monitor_skeleton_loading")
  104. height: 18 * screenScaleFactor // TODO: Theme!
  105. radius: 2 * screenScaleFactor // TODO: Theme!
  106. visible: !printer
  107. width: 48 * screenScaleFactor // TODO: Theme!
  108. }
  109. MonitorPrinterPill
  110. {
  111. id: printerFamilyPill
  112. anchors
  113. {
  114. top: printerNameLabel.bottom
  115. topMargin: UM.Theme.getSize("narrow_margin").height
  116. left: printerNameLabel.left
  117. }
  118. text: printer ? printer.type : ""
  119. }
  120. Item
  121. {
  122. id: managePrinterLink
  123. anchors {
  124. top: printerFamilyPill.bottom
  125. topMargin: UM.Theme.getSize("narrow_margin").height
  126. }
  127. height: 18 * screenScaleFactor // TODO: Theme!
  128. width: childrenRect.width
  129. Label
  130. {
  131. id: managePrinterText
  132. anchors.verticalCenter: managePrinterLink.verticalCenter
  133. color: UM.Theme.getColor("text_link")
  134. font: UM.Theme.getFont("default")
  135. text: catalog.i18nc("@label link to Connect and Cloud interfaces", "Manage printer")
  136. renderType: Text.NativeRendering
  137. }
  138. UM.RecolorImage
  139. {
  140. id: externalLinkIcon
  141. anchors
  142. {
  143. left: managePrinterText.right
  144. leftMargin: UM.Theme.getSize("narrow_margin").width
  145. verticalCenter: managePrinterText.verticalCenter
  146. }
  147. color: UM.Theme.getColor("text_link")
  148. source: UM.Theme.getIcon("external_link")
  149. width: 12 * screenScaleFactor
  150. height: 12 * screenScaleFactor
  151. }
  152. }
  153. MouseArea
  154. {
  155. anchors.fill: managePrinterLink
  156. onClicked: OutputDevice.openPrintJobControlPanel()
  157. onEntered:
  158. {
  159. manageQueueText.font.underline = true
  160. }
  161. onExited:
  162. {
  163. manageQueueText.font.underline = false
  164. }
  165. }
  166. }
  167. MonitorPrinterConfiguration
  168. {
  169. id: printerConfiguration
  170. anchors.verticalCenter: parent.verticalCenter
  171. buildplate: printer ? catalog.i18nc("@label", "Glass") : null // 'Glass' as a default
  172. configurations:
  173. {
  174. var configs = []
  175. if (printer)
  176. {
  177. configs = configs.concat(printer.printerConfiguration.extruderConfigurations)
  178. }
  179. else
  180. {
  181. configs.push(null, null)
  182. }
  183. return configs
  184. }
  185. height: 72 * screenScaleFactor // TODO: Theme!te theRect's x property
  186. }
  187. }
  188. MonitorContextMenuButton
  189. {
  190. id: contextMenuButton
  191. anchors
  192. {
  193. right: parent.right
  194. rightMargin: 12 * screenScaleFactor // TODO: Theme!
  195. top: parent.top
  196. topMargin: 12 * screenScaleFactor // TODO: Theme!
  197. }
  198. width: 36 * screenScaleFactor // TODO: Theme!
  199. height: 36 * screenScaleFactor // TODO: Theme!
  200. enabled: OutputDevice.supportsPrintJobActions
  201. onClicked: enabled ? contextMenu.switchPopupState() : {}
  202. visible:
  203. {
  204. if (!printer || !printer.activePrintJob) {
  205. return false
  206. }
  207. var states = ["queued", "error", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"]
  208. return states.indexOf(printer.activePrintJob.state) !== -1
  209. }
  210. }
  211. MonitorContextMenu
  212. {
  213. id: contextMenu
  214. printJob: printer ? printer.activePrintJob : null
  215. target: contextMenuButton
  216. }
  217. // For cloud printing, add this mouse area over the disabled contextButton to indicate that it's not available
  218. MouseArea
  219. {
  220. id: contextMenuDisabledButtonArea
  221. anchors.fill: contextMenuButton
  222. hoverEnabled: contextMenuButton.visible && !contextMenuButton.enabled
  223. onEntered: contextMenuDisabledInfo.open()
  224. onExited: contextMenuDisabledInfo.close()
  225. enabled: !contextMenuButton.enabled
  226. }
  227. MonitorInfoBlurb
  228. {
  229. id: contextMenuDisabledInfo
  230. text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
  231. target: contextMenuButton
  232. }
  233. CameraButton
  234. {
  235. id: cameraButton
  236. anchors
  237. {
  238. right: parent.right
  239. rightMargin: 20 * screenScaleFactor // TODO: Theme!
  240. bottom: parent.bottom
  241. bottomMargin: 20 * screenScaleFactor // TODO: Theme!
  242. }
  243. iconSource: "../svg/icons/camera.svg"
  244. enabled: !cloudConnection
  245. visible: printer
  246. }
  247. // For cloud printing, add this mouse area over the disabled cameraButton to indicate that it's not available
  248. //Warning message is commented out because it's factually incorrect. Fix CURA-7637 to allow camera connections via cloud.
  249. /* MouseArea
  250. {
  251. id: cameraDisabledButtonArea
  252. anchors.fill: cameraButton
  253. hoverEnabled: cameraButton.visible && !cameraButton.enabled
  254. onEntered: cameraDisabledInfo.open()
  255. onExited: cameraDisabledInfo.close()
  256. enabled: !cameraButton.enabled
  257. }
  258. MonitorInfoBlurb
  259. {
  260. id: cameraDisabledInfo
  261. text: catalog.i18nc("@info", "The webcam is not available because you are monitoring a cloud printer.")
  262. target: cameraButton
  263. }*/
  264. }
  265. // Divider
  266. Rectangle
  267. {
  268. anchors
  269. {
  270. top: printJobInfo.top
  271. left: printJobInfo.left
  272. right: printJobInfo.right
  273. }
  274. height: borderSize // Remove once themed
  275. color: background.border.color
  276. }
  277. // Print job portion
  278. Rectangle
  279. {
  280. id: printJobInfo
  281. anchors
  282. {
  283. top: printerInfo.bottom
  284. topMargin: -borderSize * screenScaleFactor // TODO: Theme!
  285. }
  286. border
  287. {
  288. color: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? UM.Theme.getColor("warning") : "transparent" // TODO: Theme!
  289. width: borderSize // TODO: Remove once themed
  290. }
  291. color: "transparent" // TODO: Theme!
  292. height: 84 * screenScaleFactor + borderSize // TODO: Remove once themed
  293. width: parent.width
  294. Row
  295. {
  296. anchors
  297. {
  298. fill: parent
  299. topMargin: 12 * screenScaleFactor + borderSize // TODO: Theme!
  300. bottomMargin: 12 * screenScaleFactor // TODO: Theme!
  301. leftMargin: 36 * screenScaleFactor // TODO: Theme!
  302. }
  303. height: childrenRect.height
  304. spacing: 18 * screenScaleFactor // TODO: Theme!
  305. Label
  306. {
  307. id: printerStatus
  308. anchors
  309. {
  310. verticalCenter: parent.verticalCenter
  311. }
  312. color: printer ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  313. font: UM.Theme.getFont("large_bold") // 16pt, bold
  314. text: {
  315. if (!printer) {
  316. return catalog.i18nc("@label:status", "Loading...")
  317. }
  318. if (printer.state == "disabled")
  319. {
  320. return catalog.i18nc("@label:status", "Unavailable")
  321. }
  322. if (printer.state == "unreachable")
  323. {
  324. return catalog.i18nc("@label:status", "Unreachable")
  325. }
  326. if (!printer.activePrintJob && printer.state == "idle")
  327. {
  328. return catalog.i18nc("@label:status", "Idle")
  329. }
  330. if (!printer.activePrintJob && printer.state == "pre_print")
  331. {
  332. return catalog.i18nc("@label:status", "Preparing...")
  333. }
  334. if (!printer.activePrintJob && printer.state == "printing")
  335. {
  336. // The print job isn't quite updated yet.
  337. return catalog.i18nc("@label:status", "Printing")
  338. }
  339. return ""
  340. }
  341. visible: text !== ""
  342. renderType: Text.NativeRendering
  343. }
  344. Item
  345. {
  346. anchors
  347. {
  348. verticalCenter: parent.verticalCenter
  349. }
  350. width: printerImage.width
  351. height: 60 * screenScaleFactor // TODO: Theme!
  352. MonitorPrintJobPreview
  353. {
  354. anchors.centerIn: parent
  355. printJob: printer ? printer.activePrintJob : null
  356. size: parent.height
  357. }
  358. visible: printer && printer.activePrintJob && !printerStatus.visible
  359. }
  360. Item
  361. {
  362. anchors
  363. {
  364. verticalCenter: parent.verticalCenter
  365. }
  366. width: 180 * screenScaleFactor // TODO: Theme!
  367. height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
  368. visible: printer && printer.activePrintJob && !printerStatus.visible
  369. Label
  370. {
  371. id: printerJobNameLabel
  372. color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  373. elide: Text.ElideRight
  374. font: UM.Theme.getFont("large") // 16pt, bold
  375. text: printer && printer.activePrintJob ? printer.activePrintJob.name : catalog.i18nc("@label", "Untitled")
  376. width: parent.width
  377. // FIXED-LINE-HEIGHT:
  378. height: 18 * screenScaleFactor // TODO: Theme!
  379. verticalAlignment: Text.AlignVCenter
  380. renderType: Text.NativeRendering
  381. }
  382. Label
  383. {
  384. id: printerJobOwnerLabel
  385. anchors
  386. {
  387. top: printerJobNameLabel.bottom
  388. topMargin: UM.Theme.getSize("narrow_margin").height
  389. left: printerJobNameLabel.left
  390. }
  391. color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  392. elide: Text.ElideRight
  393. font: UM.Theme.getFont("default") // 12pt, regular
  394. text: printer && printer.activePrintJob ? printer.activePrintJob.owner : catalog.i18nc("@label", "Anonymous")
  395. width: parent.width
  396. // FIXED-LINE-HEIGHT:
  397. height: 18 * screenScaleFactor // TODO: Theme!
  398. verticalAlignment: Text.AlignVCenter
  399. renderType: Text.NativeRendering
  400. }
  401. }
  402. MonitorPrintJobProgressBar
  403. {
  404. anchors
  405. {
  406. verticalCenter: parent.verticalCenter
  407. }
  408. printJob: printer && printer.activePrintJob
  409. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0 && !printerStatus.visible
  410. }
  411. Label
  412. {
  413. anchors
  414. {
  415. verticalCenter: parent.verticalCenter
  416. }
  417. font: UM.Theme.getFont("default")
  418. text: catalog.i18nc("@label:status", "Requires configuration changes")
  419. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible
  420. color: UM.Theme.getColor("text")
  421. // FIXED-LINE-HEIGHT:
  422. height: 18 * screenScaleFactor // TODO: Theme!
  423. verticalAlignment: Text.AlignVCenter
  424. renderType: Text.NativeRendering
  425. }
  426. }
  427. Button
  428. {
  429. id: detailsButton
  430. anchors
  431. {
  432. verticalCenter: parent.verticalCenter
  433. right: parent.right
  434. rightMargin: 18 * screenScaleFactor // TODO: Theme!
  435. }
  436. background: Rectangle
  437. {
  438. color: UM.Theme.getColor("monitor_secondary_button_shadow")
  439. radius: 2 * screenScaleFactor // Todo: Theme!
  440. Rectangle
  441. {
  442. anchors.fill: parent
  443. anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
  444. color: detailsButton.hovered ? UM.Theme.getColor("monitor_secondary_button_hover") : UM.Theme.getColor("monitor_secondary_button")
  445. radius: 2 * screenScaleFactor // Todo: Theme!
  446. }
  447. }
  448. contentItem: Label
  449. {
  450. anchors.fill: parent
  451. anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme!
  452. color: UM.Theme.getColor("monitor_secondary_button_text")
  453. font: UM.Theme.getFont("medium") // 14pt, regular
  454. text: catalog.i18nc("@action:button", "Details");
  455. verticalAlignment: Text.AlignVCenter
  456. horizontalAlignment: Text.AlignHCenter
  457. height: 18 * screenScaleFactor // TODO: Theme!
  458. renderType: Text.NativeRendering
  459. }
  460. implicitHeight: 32 * screenScaleFactor // TODO: Theme!
  461. implicitWidth: 96 * screenScaleFactor // TODO: Theme!
  462. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible
  463. onClicked: base.enabled ? overrideConfirmationDialog.open() : {}
  464. enabled: OutputDevice.supportsPrintJobActions
  465. }
  466. // For cloud printing, add this mouse area over the disabled details button to indicate that it's not available
  467. MouseArea
  468. {
  469. id: detailsButtonDisabledButtonArea
  470. anchors.fill: detailsButton
  471. hoverEnabled: detailsButton.visible && !detailsButton.enabled
  472. onEntered: overrideButtonDisabledInfo.open()
  473. onExited: overrideButtonDisabledInfo.close()
  474. enabled: !detailsButton.enabled
  475. }
  476. MonitorInfoBlurb
  477. {
  478. id: overrideButtonDisabledInfo
  479. text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
  480. target: detailsButton
  481. }
  482. }
  483. MonitorConfigOverrideDialog
  484. {
  485. id: overrideConfirmationDialog
  486. printer: base.printer
  487. }
  488. }