ClusterControlItem.qml 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. import QtQuick 2.3
  2. import QtQuick.Controls 1.4
  3. import QtQuick.Controls.Styles 1.3
  4. import QtGraphicalEffects 1.0
  5. import QtQuick.Controls 2.0 as Controls2
  6. import UM 1.3 as UM
  7. import Cura 1.0 as Cura
  8. Component
  9. {
  10. Rectangle
  11. {
  12. id: base
  13. property var lineColor: "#DCDCDC" // TODO: Should be linked to theme.
  14. property var shadowRadius: 5 * screenScaleFactor
  15. property var cornerRadius: 4 * screenScaleFactor // TODO: Should be linked to theme.
  16. visible: OutputDevice != null
  17. anchors.fill: parent
  18. color: "white"
  19. UM.I18nCatalog
  20. {
  21. id: catalog
  22. name: "cura"
  23. }
  24. Label
  25. {
  26. id: printingLabel
  27. font: UM.Theme.getFont("large")
  28. anchors
  29. {
  30. margins: 2 * UM.Theme.getSize("default_margin").width
  31. leftMargin: 4 * UM.Theme.getSize("default_margin").width
  32. top: parent.top
  33. left: parent.left
  34. right: parent.right
  35. }
  36. text: catalog.i18nc("@label", "Printing")
  37. elide: Text.ElideRight
  38. }
  39. Label
  40. {
  41. id: managePrintersLabel
  42. anchors.rightMargin: 4 * UM.Theme.getSize("default_margin").width
  43. anchors.right: printerScrollView.right
  44. anchors.bottom: printingLabel.bottom
  45. text: catalog.i18nc("@label link to connect manager", "Manage printers")
  46. font: UM.Theme.getFont("default")
  47. color: UM.Theme.getColor("primary")
  48. linkColor: UM.Theme.getColor("primary")
  49. }
  50. MouseArea
  51. {
  52. anchors.fill: managePrintersLabel
  53. hoverEnabled: true
  54. onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
  55. onEntered: managePrintersLabel.font.underline = true
  56. onExited: managePrintersLabel.font.underline = false
  57. }
  58. ScrollView
  59. {
  60. id: printerScrollView
  61. anchors
  62. {
  63. top: printingLabel.bottom
  64. left: parent.left
  65. right: parent.right
  66. topMargin: UM.Theme.getSize("default_margin").height
  67. bottom: parent.bottom
  68. bottomMargin: UM.Theme.getSize("default_margin").height
  69. }
  70. style: UM.Theme.styles.scrollview
  71. ListView
  72. {
  73. id: printer_list
  74. property var current_index: -1
  75. anchors
  76. {
  77. top: parent.top
  78. bottom: parent.bottom
  79. left: parent.left
  80. right: parent.right
  81. leftMargin: 2 * UM.Theme.getSize("default_margin").width
  82. rightMargin: 2 * UM.Theme.getSize("default_margin").width
  83. }
  84. spacing: UM.Theme.getSize("default_margin").height -10
  85. model: OutputDevice.printers
  86. delegate: Item
  87. {
  88. width: parent.width
  89. height: base.height + 2 * base.shadowRadius // To ensure that the shadow doesn't get cut off.
  90. Rectangle
  91. {
  92. width: parent.width - 2 * shadowRadius
  93. height: childrenRect.height + UM.Theme.getSize("default_margin").height
  94. anchors.horizontalCenter: parent.horizontalCenter
  95. anchors.verticalCenter: parent.verticalCenter
  96. color:
  97. {
  98. if(modelData.state == "disabled")
  99. {
  100. return UM.Theme.getColor("monitor_background_inactive")
  101. }
  102. else
  103. {
  104. return UM.Theme.getColor("monitor_background_active")
  105. }
  106. }
  107. id: base
  108. property var shadowRadius: 5 * screenScaleFactor
  109. property var collapsed: true
  110. layer.enabled: true
  111. layer.effect: DropShadow
  112. {
  113. radius: 5 * screenScaleFactor
  114. verticalOffset: 2
  115. color: "#3F000000" // 25% shadow
  116. }
  117. Connections
  118. {
  119. target: printer_list
  120. onCurrent_indexChanged: { base.collapsed = printer_list.current_index != model.index }
  121. }
  122. Item
  123. {
  124. id: printerInfo
  125. height: machineIcon.height
  126. anchors
  127. {
  128. top: parent.top
  129. left: parent.left
  130. right: parent.right
  131. margins: UM.Theme.getSize("default_margin").width
  132. }
  133. MouseArea
  134. {
  135. anchors.fill: parent
  136. onClicked:
  137. {
  138. if (base.collapsed) {
  139. printer_list.current_index = model.index
  140. }
  141. else
  142. {
  143. printer_list.current_index = -1
  144. }
  145. }
  146. }
  147. Item
  148. {
  149. id: machineIcon
  150. // Yeah, this is hardcoded now, but I can't think of a good way to fix this.
  151. // The UI is going to get another update soon, so it's probably not worth the effort...
  152. width: 58
  153. height: 58
  154. anchors.top: parent.top
  155. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  156. anchors.left: parent.left
  157. UM.RecolorImage
  158. {
  159. anchors.centerIn: parent
  160. source:
  161. {
  162. switch(modelData.type)
  163. {
  164. case "Ultimaker 3":
  165. return "../svg/UM3-icon.svg"
  166. case "Ultimaker 3 Extended":
  167. return "../svg/UM3x-icon.svg"
  168. case "Ultimaker S5":
  169. return "../svg/UMs5-icon.svg"
  170. }
  171. }
  172. width: sourceSize.width
  173. height: sourceSize.height
  174. color:
  175. {
  176. if(modelData.state == "disabled")
  177. {
  178. return UM.Theme.getColor("monitor_text_inactive")
  179. }
  180. if(modelData.activePrintJob != undefined)
  181. {
  182. return UM.Theme.getColor("primary")
  183. }
  184. return UM.Theme.getColor("monitor_text_inactive")
  185. }
  186. }
  187. }
  188. Item
  189. {
  190. height: childrenRect.height
  191. anchors
  192. {
  193. right: collapseIcon.left
  194. rightMargin: UM.Theme.getSize("default_margin").width
  195. left: machineIcon.right
  196. leftMargin: UM.Theme.getSize("default_margin").width
  197. verticalCenter: machineIcon.verticalCenter
  198. }
  199. Label
  200. {
  201. id: machineNameLabel
  202. text: modelData.name
  203. width: parent.width
  204. elide: Text.ElideRight
  205. font: UM.Theme.getFont("default_bold")
  206. }
  207. Label
  208. {
  209. id: activeJobLabel
  210. text:
  211. {
  212. if (modelData.state == "disabled")
  213. {
  214. return catalog.i18nc("@label", "Not available")
  215. } else if (modelData.state == "unreachable")
  216. {
  217. return catalog.i18nc("@label", "Unreachable")
  218. }
  219. if (modelData.activePrintJob != null)
  220. {
  221. return modelData.activePrintJob.name
  222. }
  223. return catalog.i18nc("@label", "Available")
  224. }
  225. anchors.top: machineNameLabel.bottom
  226. width: parent.width
  227. elide: Text.ElideRight
  228. font: UM.Theme.getFont("default")
  229. color: UM.Theme.getColor("monitor_text_inactive")
  230. }
  231. }
  232. UM.RecolorImage
  233. {
  234. id: collapseIcon
  235. width: 15
  236. height: 15
  237. sourceSize.width: width
  238. sourceSize.height: height
  239. source: base.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom")
  240. anchors.verticalCenter: parent.verticalCenter
  241. anchors.right: parent.right
  242. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  243. color: "black"
  244. }
  245. }
  246. Item
  247. {
  248. id: detailedInfo
  249. property var printJob: modelData.activePrintJob
  250. visible: height == childrenRect.height
  251. anchors.top: printerInfo.bottom
  252. width: parent.width
  253. height: !base.collapsed ? childrenRect.height : 0
  254. opacity: visible ? 1 : 0
  255. Behavior on height { NumberAnimation { duration: 100 } }
  256. Behavior on opacity { NumberAnimation { duration: 100 } }
  257. Rectangle
  258. {
  259. id: topSpacer
  260. color:
  261. {
  262. if(modelData.state == "disabled")
  263. {
  264. return UM.Theme.getColor("monitor_lining_inactive")
  265. }
  266. return UM.Theme.getColor("viewport_background")
  267. }
  268. // UM.Theme.getColor("viewport_background")
  269. height: 1
  270. anchors
  271. {
  272. left: parent.left
  273. right: parent.right
  274. margins: UM.Theme.getSize("default_margin").width
  275. top: parent.top
  276. topMargin: UM.Theme.getSize("default_margin").width
  277. }
  278. }
  279. PrinterFamilyPill
  280. {
  281. id: printerFamilyPill
  282. color:
  283. {
  284. if(modelData.state == "disabled")
  285. {
  286. return "transparent"
  287. }
  288. return UM.Theme.getColor("viewport_background")
  289. }
  290. anchors.top: topSpacer.bottom
  291. anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
  292. text: modelData.type
  293. anchors.left: parent.left
  294. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  295. padding: 3
  296. }
  297. Row
  298. {
  299. id: extrudersInfo
  300. anchors.top: printerFamilyPill.bottom
  301. anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
  302. anchors.left: parent.left
  303. anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
  304. anchors.right: parent.right
  305. anchors.rightMargin: 2 * UM.Theme.getSize("default_margin").width
  306. height: childrenRect.height
  307. spacing: UM.Theme.getSize("default_margin").width
  308. PrintCoreConfiguration
  309. {
  310. id: leftExtruderInfo
  311. width: Math.round(parent.width / 2)
  312. printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[0]
  313. }
  314. PrintCoreConfiguration
  315. {
  316. id: rightExtruderInfo
  317. width: Math.round(parent.width / 2)
  318. printCoreConfiguration: modelData.printerConfiguration.extruderConfigurations[1]
  319. }
  320. }
  321. Rectangle
  322. {
  323. id: jobSpacer
  324. color: UM.Theme.getColor("viewport_background")
  325. height: 2
  326. anchors
  327. {
  328. left: parent.left
  329. right: parent.right
  330. margins: UM.Theme.getSize("default_margin").width
  331. top: extrudersInfo.bottom
  332. topMargin: 2 * UM.Theme.getSize("default_margin").height
  333. }
  334. }
  335. Item
  336. {
  337. id: jobInfo
  338. property var showJobInfo: modelData.activePrintJob != null && modelData.activePrintJob.state != "queued"
  339. anchors.top: jobSpacer.bottom
  340. anchors.topMargin: 2 * UM.Theme.getSize("default_margin").height
  341. anchors.left: parent.left
  342. anchors.right: parent.right
  343. anchors.margins: UM.Theme.getSize("default_margin").width
  344. anchors.leftMargin: 2 * UM.Theme.getSize("default_margin").width
  345. height: showJobInfo ? childrenRect.height + 2 * UM.Theme.getSize("default_margin").height: 0
  346. visible: showJobInfo
  347. Label
  348. {
  349. id: printJobName
  350. text: modelData.activePrintJob != null ? modelData.activePrintJob.name : ""
  351. font: UM.Theme.getFont("default_bold")
  352. anchors.left: parent.left
  353. anchors.right: contextButton.left
  354. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  355. elide: Text.ElideRight
  356. }
  357. Label
  358. {
  359. id: ownerName
  360. anchors.top: printJobName.bottom
  361. text: modelData.activePrintJob != null ? modelData.activePrintJob.owner : ""
  362. font: UM.Theme.getFont("default")
  363. opacity: 0.6
  364. width: parent.width
  365. elide: Text.ElideRight
  366. }
  367. function switchPopupState()
  368. {
  369. if (popup.visible)
  370. {
  371. popup.close()
  372. }
  373. else
  374. {
  375. popup.open()
  376. }
  377. }
  378. Controls2.Button
  379. {
  380. id: contextButton
  381. text: "\u22EE" //Unicode; Three stacked points.
  382. font.pixelSize: 25
  383. width: 35
  384. height: width
  385. anchors
  386. {
  387. right: parent.right
  388. top: parent.top
  389. }
  390. hoverEnabled: true
  391. background: Rectangle
  392. {
  393. opacity: contextButton.down || contextButton.hovered ? 1 : 0
  394. width: contextButton.width
  395. height: contextButton.height
  396. radius: 0.5 * width
  397. color: UM.Theme.getColor("viewport_background")
  398. }
  399. onClicked: parent.switchPopupState()
  400. }
  401. Controls2.Popup
  402. {
  403. // TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property
  404. id: popup
  405. clip: true
  406. closePolicy: Controls2.Popup.CloseOnPressOutsideParent
  407. x: parent.width - width
  408. y: contextButton.height
  409. width: 160
  410. height: contentItem.height + 2 * padding
  411. visible: false
  412. transformOrigin: Controls2.Popup.Top
  413. contentItem: Item
  414. {
  415. width: popup.width - 2 * popup.padding
  416. height: childrenRect.height + 15
  417. Controls2.Button
  418. {
  419. id: pauseButton
  420. text: modelData.activePrintJob != null && modelData.activePrintJob.state == "paused" ? catalog.i18nc("@label", "Resume") : catalog.i18nc("@label", "Pause")
  421. onClicked:
  422. {
  423. if(modelData.activePrintJob.state == "paused")
  424. {
  425. modelData.activePrintJob.setState("print")
  426. }
  427. else if(modelData.activePrintJob.state == "printing")
  428. {
  429. modelData.activePrintJob.setState("pause")
  430. }
  431. popup.close()
  432. }
  433. width: parent.width
  434. enabled: modelData.activePrintJob != null && ["paused", "printing"].indexOf(modelData.activePrintJob.state) >= 0
  435. anchors.top: parent.top
  436. anchors.topMargin: 10
  437. hoverEnabled: true
  438. background: Rectangle
  439. {
  440. opacity: pauseButton.down || pauseButton.hovered ? 1 : 0
  441. color: UM.Theme.getColor("viewport_background")
  442. }
  443. }
  444. Controls2.Button
  445. {
  446. id: abortButton
  447. text: catalog.i18nc("@label", "Abort")
  448. onClicked:
  449. {
  450. modelData.activePrintJob.setState("abort")
  451. popup.close()
  452. }
  453. width: parent.width
  454. anchors.top: pauseButton.bottom
  455. hoverEnabled: true
  456. enabled: modelData.activePrintJob != null && ["paused", "printing", "pre_print"].indexOf(modelData.activePrintJob.state) >= 0
  457. background: Rectangle
  458. {
  459. opacity: abortButton.down || abortButton.hovered ? 1 : 0
  460. color: UM.Theme.getColor("viewport_background")
  461. }
  462. }
  463. }
  464. background: Item
  465. {
  466. width: popup.width
  467. height: popup.height
  468. DropShadow
  469. {
  470. anchors.fill: pointedRectangle
  471. radius: 5
  472. color: "#3F000000" // 25% shadow
  473. source: pointedRectangle
  474. transparentBorder: true
  475. verticalOffset: 2
  476. }
  477. Item
  478. {
  479. id: pointedRectangle
  480. width: parent.width -10
  481. height: parent.height -10
  482. anchors.horizontalCenter: parent.horizontalCenter
  483. anchors.verticalCenter: parent.verticalCenter
  484. Rectangle
  485. {
  486. id: point
  487. height: 13
  488. width: 13
  489. color: UM.Theme.getColor("setting_control")
  490. transform: Rotation { angle: 45}
  491. anchors.right: bloop.right
  492. y: 1
  493. }
  494. Rectangle
  495. {
  496. id: bloop
  497. color: UM.Theme.getColor("setting_control")
  498. width: parent.width
  499. anchors.top: parent.top
  500. anchors.topMargin: 10
  501. anchors.bottom: parent.bottom
  502. anchors.bottomMargin: 5
  503. }
  504. }
  505. }
  506. exit: Transition
  507. {
  508. // This applies a default NumberAnimation to any changes a state change makes to x or y properties
  509. NumberAnimation { property: "visible"; duration: 75; }
  510. }
  511. enter: Transition
  512. {
  513. // This applies a default NumberAnimation to any changes a state change makes to x or y properties
  514. NumberAnimation { property: "visible"; duration: 75; }
  515. }
  516. onClosed: visible = false
  517. onOpened: visible = true
  518. }
  519. Image
  520. {
  521. id: printJobPreview
  522. source: modelData.activePrintJob != null ? modelData.activePrintJob.previewImageUrl : ""
  523. anchors.top: ownerName.bottom
  524. anchors.horizontalCenter: parent.horizontalCenter
  525. width: parent.width / 2
  526. height: width
  527. opacity:
  528. {
  529. if(modelData.activePrintJob == null)
  530. {
  531. return 1.0
  532. }
  533. switch(modelData.activePrintJob.state)
  534. {
  535. case "wait_cleanup":
  536. case "wait_user_action":
  537. case "paused":
  538. return 0.5
  539. default:
  540. return 1.0
  541. }
  542. }
  543. }
  544. UM.RecolorImage
  545. {
  546. id: statusImage
  547. anchors.centerIn: printJobPreview
  548. source:
  549. {
  550. if(modelData.activePrintJob == null)
  551. {
  552. return ""
  553. }
  554. switch(modelData.activePrintJob.state)
  555. {
  556. case "paused":
  557. return "../svg/paused-icon.svg"
  558. case "wait_cleanup":
  559. if(modelData.activePrintJob.timeElapsed < modelData.activePrintJob.timeTotal)
  560. {
  561. return "../svg/aborted-icon.svg"
  562. }
  563. return "../svg/approved-icon.svg"
  564. case "wait_user_action":
  565. return "../svg/aborted-icon.svg"
  566. default:
  567. return ""
  568. }
  569. }
  570. visible: source != ""
  571. width: 0.5 * printJobPreview.width
  572. height: 0.5 * printJobPreview.height
  573. sourceSize.width: width
  574. sourceSize.height: height
  575. color: "black"
  576. }
  577. DotButton
  578. {
  579. id: showCameraButton
  580. iconSource: "../svg/camera-icon.svg"
  581. anchors
  582. {
  583. left: parent.left
  584. bottom: printJobPreview.bottom
  585. }
  586. }
  587. }
  588. }
  589. ProgressBar
  590. {
  591. property var progress:
  592. {
  593. if(modelData.activePrintJob == null)
  594. {
  595. return 0
  596. }
  597. var result = modelData.activePrintJob.timeElapsed / modelData.activePrintJob.timeTotal
  598. if(result > 1.0)
  599. {
  600. result = 1.0
  601. }
  602. return result
  603. }
  604. id: jobProgressBar
  605. width: parent.width
  606. value: progress
  607. anchors.top: detailedInfo.bottom
  608. anchors.topMargin: UM.Theme.getSize("default_margin").height
  609. visible: modelData.activePrintJob != null && modelData.activePrintJob != undefined
  610. style: ProgressBarStyle
  611. {
  612. property var remainingTime:
  613. {
  614. if(modelData.activePrintJob == null)
  615. {
  616. return 0
  617. }
  618. /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining
  619. time from ever being less than 0. Negative durations cause strange behavior such
  620. as displaying "-1h -1m". */
  621. var activeJob = modelData.activePrintJob
  622. return Math.max(activeJob.timeTotal - activeJob.timeElapsed, 0);
  623. }
  624. property var progressText:
  625. {
  626. if(modelData.activePrintJob == null)
  627. {
  628. return ""
  629. }
  630. switch(modelData.activePrintJob.state)
  631. {
  632. case "wait_cleanup":
  633. if(modelData.activePrintJob.timeTotal > modelData.activePrintJob.timeElapsed)
  634. {
  635. return catalog.i18nc("@label:status", "Aborted")
  636. }
  637. return catalog.i18nc("@label:status", "Finished")
  638. case "pre_print":
  639. case "sent_to_printer":
  640. return catalog.i18nc("@label:status", "Preparing")
  641. case "aborted":
  642. return catalog.i18nc("@label:status", "Aborted")
  643. case "wait_user_action":
  644. return catalog.i18nc("@label:status", "Aborted")
  645. case "pausing":
  646. return catalog.i18nc("@label:status", "Pausing")
  647. case "paused":
  648. return OutputDevice.formatDuration( remainingTime )
  649. case "resuming":
  650. return catalog.i18nc("@label:status", "Resuming")
  651. case "queued":
  652. return catalog.i18nc("@label:status", "Action required")
  653. default:
  654. return OutputDevice.formatDuration( remainingTime )
  655. }
  656. }
  657. background: Rectangle
  658. {
  659. implicitWidth: 100
  660. implicitHeight: visible ? 24 : 0
  661. color: UM.Theme.getColor("viewport_background")
  662. }
  663. progress: Rectangle
  664. {
  665. color:
  666. {
  667. var state = modelData.activePrintJob.state
  668. var inactiveStates = [
  669. "pausing",
  670. "paused",
  671. "resuming",
  672. "wait_cleanup"
  673. ]
  674. if(inactiveStates.indexOf(state) > -1 && remainingTime > 0)
  675. {
  676. return UM.Theme.getColor("monitor_text_inactive")
  677. }
  678. else
  679. {
  680. return UM.Theme.getColor("primary")
  681. }
  682. }
  683. id: progressItem
  684. function getTextOffset()
  685. {
  686. if(progressItem.width + progressLabel.width + 16 < control.width)
  687. {
  688. return progressItem.width + UM.Theme.getSize("default_margin").width
  689. }
  690. else
  691. {
  692. return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width
  693. }
  694. }
  695. Label
  696. {
  697. id: progressLabel
  698. anchors.left: parent.left
  699. anchors.leftMargin: getTextOffset()
  700. text: progressText
  701. anchors.verticalCenter: parent.verticalCenter
  702. color: progressItem.width + progressLabel.width < control.width ? "black" : "white"
  703. width: contentWidth
  704. font: UM.Theme.getFont("default")
  705. }
  706. }
  707. }
  708. }
  709. }
  710. }
  711. }
  712. }
  713. }
  714. }