MonitorPrinterCard.qml 18 KB

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