MonitorPrinterCard.qml 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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 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. buildplate: printer ? catalog.i18nc("@label", "Glass") : null // 'Glass' as a default
  160. configurations:
  161. {
  162. var configs = []
  163. if (printer)
  164. {
  165. configs = configs.concat(printer.printerConfiguration.extruderConfigurations)
  166. }
  167. else
  168. {
  169. configs.push(null, null)
  170. }
  171. return configs
  172. }
  173. height: 72 * screenScaleFactor // TODO: Theme!te theRect's x property
  174. }
  175. }
  176. MonitorContextMenuButton
  177. {
  178. id: contextMenuButton
  179. anchors
  180. {
  181. right: parent.right
  182. rightMargin: 12 * screenScaleFactor // TODO: Theme!
  183. top: parent.top
  184. topMargin: 12 * screenScaleFactor // TODO: Theme!
  185. }
  186. width: 36 * screenScaleFactor // TODO: Theme!
  187. height: 36 * screenScaleFactor // TODO: Theme!
  188. enabled: OutputDevice.supportsPrintJobActions
  189. onClicked: enabled ? contextMenu.switchPopupState() : {}
  190. visible:
  191. {
  192. if(!printer || !printer.activePrintJob)
  193. {
  194. return false;
  195. }
  196. if(!contextMenu.hasItems)
  197. {
  198. return false;
  199. }
  200. var states = ["queued", "error", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"]
  201. return states.indexOf(printer.activePrintJob.state) !== -1
  202. }
  203. }
  204. MonitorContextMenu
  205. {
  206. id: contextMenu
  207. printJob: printer ? printer.activePrintJob : null
  208. target: contextMenuButton
  209. }
  210. // For cloud printing, add this mouse area over the disabled contextButton to indicate that it's not available
  211. MouseArea
  212. {
  213. id: contextMenuDisabledButtonArea
  214. anchors.fill: contextMenuButton
  215. hoverEnabled: contextMenuButton.visible && !contextMenuButton.enabled
  216. onEntered: contextMenuDisabledInfo.open()
  217. onExited: contextMenuDisabledInfo.close()
  218. enabled: !contextMenuButton.enabled
  219. }
  220. MonitorInfoBlurb
  221. {
  222. id: contextMenuDisabledInfo
  223. text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
  224. target: contextMenuButton
  225. }
  226. CameraButton
  227. {
  228. id: cameraButton
  229. anchors
  230. {
  231. right: parent.right
  232. rightMargin: 20 * screenScaleFactor // TODO: Theme!
  233. bottom: parent.bottom
  234. bottomMargin: 20 * screenScaleFactor // TODO: Theme!
  235. }
  236. iconSource: Qt.resolvedUrl("../svg/icons/CameraPhoto.svg")
  237. enabled: !cloudConnection
  238. visible: printer
  239. }
  240. // For cloud printing, add this mouse area over the disabled cameraButton to indicate that it's not available
  241. // Fix CURA-7637 to allow camera connections via cloud.
  242. MouseArea
  243. {
  244. id: cameraDisabledButtonArea
  245. anchors.fill: cameraButton
  246. hoverEnabled: cameraButton.visible && !cameraButton.enabled
  247. onEntered: cameraDisabledInfo.open()
  248. onExited: cameraDisabledInfo.close()
  249. enabled: !cameraButton.enabled
  250. }
  251. MonitorInfoBlurb
  252. {
  253. id: cameraDisabledInfo
  254. text: catalog.i18nc("@info", "Webcam feeds for cloud printers cannot be viewed from Ultimaker Cura." +
  255. " Click \"Manage printer\" to visit Ultimaker Digital Factory and view this webcam.")
  256. target: cameraButton
  257. }
  258. }
  259. // Divider
  260. Rectangle
  261. {
  262. anchors
  263. {
  264. top: printJobInfo.top
  265. left: printJobInfo.left
  266. right: printJobInfo.right
  267. }
  268. height: borderSize // Remove once themed
  269. color: background.border.color
  270. }
  271. // Print job portion
  272. Rectangle
  273. {
  274. id: printJobInfo
  275. anchors
  276. {
  277. top: printerInfo.bottom
  278. topMargin: -borderSize * screenScaleFactor // TODO: Theme!
  279. }
  280. border
  281. {
  282. color: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? UM.Theme.getColor("warning") : "transparent" // TODO: Theme!
  283. width: borderSize // TODO: Remove once themed
  284. }
  285. color: "transparent" // TODO: Theme!
  286. height: 84 * screenScaleFactor + borderSize // TODO: Remove once themed
  287. width: parent.width
  288. Row
  289. {
  290. anchors
  291. {
  292. fill: parent
  293. topMargin: 12 * screenScaleFactor + borderSize // TODO: Theme!
  294. bottomMargin: 12 * screenScaleFactor // TODO: Theme!
  295. leftMargin: 36 * screenScaleFactor // TODO: Theme!
  296. }
  297. height: childrenRect.height
  298. spacing: UM.Theme.getSize("default_margin").width
  299. UM.Label
  300. {
  301. id: printerStatus
  302. anchors
  303. {
  304. verticalCenter: parent.verticalCenter
  305. }
  306. color: printer ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  307. font: UM.Theme.getFont("large_bold") // 16pt, bold
  308. text: {
  309. if (!printer) {
  310. return catalog.i18nc("@label:status", "Loading...")
  311. }
  312. if (printer.state == "disabled")
  313. {
  314. return catalog.i18nc("@label:status", "Unavailable")
  315. }
  316. if (printer.state == "unreachable")
  317. {
  318. return catalog.i18nc("@label:status", "Unreachable")
  319. }
  320. if (!printer.activePrintJob && printer.state == "idle")
  321. {
  322. return catalog.i18nc("@label:status", "Idle")
  323. }
  324. if (!printer.activePrintJob && printer.state == "pre_print")
  325. {
  326. return catalog.i18nc("@label:status", "Preparing...")
  327. }
  328. if (!printer.activePrintJob && printer.state == "printing")
  329. {
  330. // The print job isn't quite updated yet.
  331. return catalog.i18nc("@label:status", "Printing")
  332. }
  333. return ""
  334. }
  335. visible: text !== ""
  336. }
  337. Item
  338. {
  339. anchors
  340. {
  341. verticalCenter: parent.verticalCenter
  342. }
  343. width: printerImage.width
  344. height: 60 * screenScaleFactor // TODO: Theme!
  345. MonitorPrintJobPreview
  346. {
  347. anchors.centerIn: parent
  348. printJob: printer ? printer.activePrintJob : null
  349. size: parent.height
  350. }
  351. visible: printer && printer.activePrintJob && !printerStatus.visible
  352. }
  353. Item
  354. {
  355. anchors
  356. {
  357. verticalCenter: parent.verticalCenter
  358. }
  359. width: 180 * screenScaleFactor // TODO: Theme!
  360. height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme!
  361. visible: printer && printer.activePrintJob && !printerStatus.visible
  362. UM.Label
  363. {
  364. id: printerJobNameLabel
  365. color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  366. elide: Text.ElideRight
  367. wrapMode: Text.NoWrap
  368. font: UM.Theme.getFont("large") // 16pt, bold
  369. text: printer && printer.activePrintJob ? printer.activePrintJob.name : catalog.i18nc("@label", "Untitled")
  370. width: parent.width
  371. }
  372. UM.Label
  373. {
  374. id: printerJobOwnerLabel
  375. anchors
  376. {
  377. top: printerJobNameLabel.bottom
  378. topMargin: UM.Theme.getSize("narrow_margin").height
  379. left: printerJobNameLabel.left
  380. }
  381. color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_text_disabled")
  382. elide: Text.ElideRight
  383. text: printer && printer.activePrintJob ? printer.activePrintJob.owner : catalog.i18nc("@label", "Anonymous")
  384. width: parent.width
  385. }
  386. }
  387. MonitorPrintJobProgressBar
  388. {
  389. anchors
  390. {
  391. verticalCenter: parent.verticalCenter
  392. }
  393. printJob: printer && printer.activePrintJob
  394. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0 && !printerStatus.visible
  395. }
  396. UM.Label
  397. {
  398. anchors
  399. {
  400. verticalCenter: parent.verticalCenter
  401. }
  402. text: catalog.i18nc("@label:status", "Requires configuration changes")
  403. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible
  404. }
  405. }
  406. Cura.SecondaryButton
  407. {
  408. id: detailsButton
  409. anchors
  410. {
  411. verticalCenter: parent.verticalCenter
  412. right: parent.right
  413. rightMargin: UM.Theme.getSize("default_margin").width
  414. }
  415. text: catalog.i18nc("@action:button", "Details")
  416. visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible
  417. onClicked: base.enabled ? overrideConfirmationDialog.open() : {}
  418. enabled: OutputDevice.supportsPrintJobActions
  419. }
  420. // For cloud printing, add this mouse area over the disabled details button to indicate that it's not available
  421. MouseArea
  422. {
  423. id: detailsButtonDisabledButtonArea
  424. anchors.fill: detailsButton
  425. hoverEnabled: detailsButton.visible && !detailsButton.enabled
  426. onEntered: overrideButtonDisabledInfo.open()
  427. onExited: overrideButtonDisabledInfo.close()
  428. enabled: !detailsButton.enabled
  429. }
  430. MonitorInfoBlurb
  431. {
  432. id: overrideButtonDisabledInfo
  433. text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.")
  434. target: detailsButton
  435. }
  436. }
  437. MonitorConfigOverrideDialog
  438. {
  439. id: overrideConfirmationDialog
  440. printer: base.printer
  441. }
  442. }