MonitorPrinterCard.qml 18 KB

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