Sidebar.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. // Copyright (c) 2017 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 2.0
  5. import QtQuick.Layouts 1.3
  6. import UM 1.2 as UM
  7. import Cura 1.0 as Cura
  8. import "Menus"
  9. Rectangle
  10. {
  11. id: base
  12. property int currentModeIndex
  13. property bool hideSettings: PrintInformation.preSliced
  14. property bool hideView: Cura.MachineManager.activeMachineName == ""
  15. // Is there an output device for this printer?
  16. property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
  17. property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
  18. property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null
  19. property bool monitoringPrint: UM.Controller.activeStage.stageId == "MonitorStage"
  20. property variant printDuration: PrintInformation.currentPrintTime
  21. property variant printMaterialLengths: PrintInformation.materialLengths
  22. property variant printMaterialWeights: PrintInformation.materialWeights
  23. property variant printMaterialCosts: PrintInformation.materialCosts
  24. property variant printMaterialNames: PrintInformation.materialNames
  25. color: UM.Theme.getColor("sidebar")
  26. UM.I18nCatalog { id: catalog; name:"cura"}
  27. Timer {
  28. id: tooltipDelayTimer
  29. interval: 500
  30. repeat: false
  31. property var item
  32. property string text
  33. onTriggered:
  34. {
  35. base.showTooltip(base, {x: 0, y: item.y}, text);
  36. }
  37. }
  38. function showTooltip(item, position, text)
  39. {
  40. tooltip.text = text;
  41. position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y);
  42. tooltip.show(position);
  43. }
  44. function hideTooltip()
  45. {
  46. tooltip.hide();
  47. }
  48. function strPadLeft(string, pad, length) {
  49. return (new Array(length + 1).join(pad) + string).slice(-length);
  50. }
  51. function getPrettyTime(time)
  52. {
  53. var hours = Math.round(time / 3600)
  54. time -= hours * 3600
  55. var minutes = Math.round(time / 60);
  56. time -= minutes * 60
  57. var seconds = Math.round(time);
  58. var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2);
  59. return finalTime;
  60. }
  61. MouseArea
  62. {
  63. anchors.fill: parent
  64. acceptedButtons: Qt.AllButtons
  65. onWheel:
  66. {
  67. wheel.accepted = true;
  68. }
  69. }
  70. MachineSelection {
  71. id: machineSelection
  72. width: base.width
  73. height: UM.Theme.getSize("sidebar_header").height
  74. anchors.top: base.top
  75. anchors.right: parent.right
  76. }
  77. SidebarHeader {
  78. id: header
  79. width: parent.width
  80. visible: !hideSettings && (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && !monitoringPrint
  81. anchors.top: machineSelection.bottom
  82. onShowTooltip: base.showTooltip(item, location, text)
  83. onHideTooltip: base.hideTooltip()
  84. }
  85. Rectangle {
  86. id: headerSeparator
  87. width: parent.width
  88. visible: settingsModeSelection.visible && header.visible
  89. height: visible ? UM.Theme.getSize("sidebar_lining").height : 0
  90. color: UM.Theme.getColor("sidebar_lining")
  91. anchors.top: header.bottom
  92. anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0
  93. }
  94. onCurrentModeIndexChanged:
  95. {
  96. UM.Preferences.setValue("cura/active_mode", currentModeIndex);
  97. if(modesListModel.count > base.currentModeIndex)
  98. {
  99. sidebarContents.replace(modesListModel.get(base.currentModeIndex).item, { "replace": true })
  100. }
  101. }
  102. Label
  103. {
  104. id: settingsModeLabel
  105. text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified")
  106. renderType: Text.NativeRendering
  107. anchors.left: parent.left
  108. anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
  109. anchors.top: hideSettings ? machineSelection.bottom : headerSeparator.bottom
  110. anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
  111. width: Math.round(parent.width * 0.45)
  112. font: UM.Theme.getFont("large")
  113. color: UM.Theme.getColor("text")
  114. visible: !monitoringPrint && !hideView
  115. }
  116. // Settings mode selection toggle
  117. Rectangle
  118. {
  119. id: settingsModeSelection
  120. color: "transparent"
  121. width: Math.round(parent.width * 0.55)
  122. height: UM.Theme.getSize("sidebar_header_mode_toggle").height
  123. anchors.right: parent.right
  124. anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
  125. anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
  126. anchors.top:
  127. {
  128. if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2)
  129. {
  130. return settingsModeLabel.bottom;
  131. }
  132. else
  133. {
  134. return headerSeparator.bottom;
  135. }
  136. }
  137. visible: !monitoringPrint && !hideSettings && !hideView
  138. Component
  139. {
  140. id: wizardDelegate
  141. Button
  142. {
  143. id: control
  144. height: settingsModeSelection.height
  145. width: Math.round(parent.width / 2)
  146. anchors.left: parent.left
  147. anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2)
  148. anchors.verticalCenter: parent.verticalCenter
  149. ButtonGroup.group: modeMenuGroup
  150. checkable: true
  151. checked: base.currentModeIndex == index
  152. onClicked: base.currentModeIndex = index
  153. onHoveredChanged:
  154. {
  155. if (hovered)
  156. {
  157. tooltipDelayTimer.item = settingsModeSelection
  158. tooltipDelayTimer.text = model.tooltipText
  159. tooltipDelayTimer.start()
  160. }
  161. else
  162. {
  163. tooltipDelayTimer.stop()
  164. base.hideTooltip()
  165. }
  166. }
  167. background: Rectangle
  168. {
  169. border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width
  170. border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border"): UM.Theme.getColor("action_button_border")
  171. // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is
  172. color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button")
  173. }
  174. contentItem: Label
  175. {
  176. text: model.text
  177. font: UM.Theme.getFont("default")
  178. horizontalAlignment: Text.AlignHCenter
  179. verticalAlignment: Text.AlignVCenter
  180. renderType: Text.NativeRendering
  181. elide: Text.ElideRight
  182. color:
  183. {
  184. if(control.pressed)
  185. {
  186. return UM.Theme.getColor("action_button_active_text");
  187. }
  188. else if(control.hovered)
  189. {
  190. return UM.Theme.getColor("action_button_hovered_text");
  191. }
  192. return UM.Theme.getColor("action_button_text");
  193. }
  194. }
  195. }
  196. }
  197. ButtonGroup
  198. {
  199. id: modeMenuGroup
  200. }
  201. ListView
  202. {
  203. id: modesList
  204. property var index: 0
  205. model: modesListModel
  206. delegate: wizardDelegate
  207. anchors.top: parent.top
  208. anchors.left: parent.left
  209. width: parent.width
  210. }
  211. }
  212. StackView
  213. {
  214. id: sidebarContents
  215. anchors.bottom: footerSeparator.top
  216. anchors.top: settingsModeSelection.bottom
  217. anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
  218. anchors.left: base.left
  219. anchors.right: base.right
  220. visible: !monitoringPrint && !hideSettings
  221. replaceEnter: Transition {
  222. PropertyAnimation {
  223. property: "opacity"
  224. from: 0
  225. to:1
  226. duration: 100
  227. }
  228. }
  229. replaceExit: Transition {
  230. PropertyAnimation {
  231. property: "opacity"
  232. from: 1
  233. to:0
  234. duration: 100
  235. }
  236. }
  237. }
  238. Loader
  239. {
  240. id: controlItem
  241. anchors.bottom: footerSeparator.top
  242. anchors.top: monitoringPrint ? machineSelection.bottom : headerSeparator.bottom
  243. anchors.left: base.left
  244. anchors.right: base.right
  245. sourceComponent:
  246. {
  247. if(monitoringPrint && connectedPrinter != null)
  248. {
  249. if(connectedPrinter.controlItem != null)
  250. {
  251. return connectedPrinter.controlItem
  252. }
  253. }
  254. return null
  255. }
  256. }
  257. Loader
  258. {
  259. anchors.bottom: footerSeparator.top
  260. anchors.top: monitoringPrint ? machineSelection.bottom : headerSeparator.bottom
  261. anchors.left: base.left
  262. anchors.right: base.right
  263. source:
  264. {
  265. if(controlItem.sourceComponent == null)
  266. {
  267. if(monitoringPrint)
  268. {
  269. return "PrintMonitor.qml"
  270. } else
  271. {
  272. return "SidebarContents.qml"
  273. }
  274. }
  275. else
  276. {
  277. return ""
  278. }
  279. }
  280. }
  281. Rectangle
  282. {
  283. id: footerSeparator
  284. width: parent.width
  285. height: UM.Theme.getSize("sidebar_lining").height
  286. color: UM.Theme.getColor("sidebar_lining")
  287. anchors.bottom: printSpecs.top
  288. anchors.bottomMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize)
  289. }
  290. Item
  291. {
  292. id: printSpecs
  293. anchors.left: parent.left
  294. anchors.bottom: parent.bottom
  295. anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
  296. anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height
  297. height: timeDetails.height + costSpec.height
  298. width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width)
  299. visible: !monitoringPrint
  300. clip: true
  301. Label
  302. {
  303. id: timeDetails
  304. anchors.left: parent.left
  305. anchors.bottom: costSpec.top
  306. font: UM.Theme.getFont("large")
  307. color: UM.Theme.getColor("text_subtext")
  308. text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short)
  309. renderType: Text.NativeRendering
  310. MouseArea
  311. {
  312. id: timeDetailsMouseArea
  313. anchors.fill: parent
  314. hoverEnabled: true
  315. onEntered:
  316. {
  317. if(base.printDuration.valid && !base.printDuration.isTotalDurationZero)
  318. {
  319. // All the time information for the different features is achieved
  320. var print_time = PrintInformation.getFeaturePrintTimes();
  321. var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds))
  322. // A message is created and displayed when the user hover the time label
  323. var tooltip_html = "<b>%1</b><br/><table width=\"100%\">".arg(catalog.i18nc("@tooltip", "Time specification"));
  324. for(var feature in print_time)
  325. {
  326. if(!print_time[feature].isTotalDurationZero)
  327. {
  328. tooltip_html += "<tr><td>" + feature + ":</td>" +
  329. "<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1</td>".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) +
  330. "<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1%</td>".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) +
  331. "</td></tr>";
  332. }
  333. }
  334. tooltip_html += "</table>";
  335. base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html);
  336. }
  337. }
  338. onExited:
  339. {
  340. base.hideTooltip();
  341. }
  342. }
  343. }
  344. Label
  345. {
  346. function formatRow(items)
  347. {
  348. var row_html = "<tr>";
  349. for(var item = 0; item < items.length; item++)
  350. {
  351. if (item == 0)
  352. {
  353. row_html += "<td valign=\"bottom\">%1</td>".arg(items[item]);
  354. }
  355. else
  356. {
  357. row_html += "<td align=\"right\" valign=\"bottom\">&nbsp;&nbsp;%1</td>".arg(items[item]);
  358. }
  359. }
  360. row_html += "</tr>";
  361. return row_html;
  362. }
  363. function getSpecsData()
  364. {
  365. var lengths = [];
  366. var total_length = 0;
  367. var weights = [];
  368. var total_weight = 0;
  369. var costs = [];
  370. var total_cost = 0;
  371. var some_costs_known = false;
  372. var names = [];
  373. if(base.printMaterialLengths)
  374. {
  375. for(var index = 0; index < base.printMaterialLengths.length; index++)
  376. {
  377. if(base.printMaterialLengths[index] > 0)
  378. {
  379. names.push(base.printMaterialNames[index]);
  380. lengths.push(base.printMaterialLengths[index].toFixed(2));
  381. weights.push(String(Math.round(base.printMaterialWeights[index])));
  382. var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2);
  383. costs.push(cost);
  384. if(cost > 0)
  385. {
  386. some_costs_known = true;
  387. }
  388. total_length += base.printMaterialLengths[index];
  389. total_weight += base.printMaterialWeights[index];
  390. total_cost += base.printMaterialCosts[index];
  391. }
  392. }
  393. }
  394. if(lengths.length == 0)
  395. {
  396. lengths = ["0.00"];
  397. weights = ["0"];
  398. costs = ["0.00"];
  399. }
  400. var tooltip_html = "<b>%1</b><br/><table width=\"100%\">".arg(catalog.i18nc("@label", "Cost specification"));
  401. for(var index = 0; index < lengths.length; index++)
  402. {
  403. tooltip_html += formatRow([
  404. "%1:".arg(names[index]),
  405. catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]),
  406. catalog.i18nc("@label g for grams", "%1g").arg(weights[index]),
  407. "%1&nbsp;%2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]),
  408. ]);
  409. }
  410. if(lengths.length > 1)
  411. {
  412. tooltip_html += formatRow([
  413. catalog.i18nc("@label", "Total:"),
  414. catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)),
  415. catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)),
  416. "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)),
  417. ]);
  418. }
  419. tooltip_html += "</table>";
  420. tooltipText = tooltip_html;
  421. return tooltipText
  422. }
  423. id: costSpec
  424. anchors.left: parent.left
  425. anchors.bottom: parent.bottom
  426. font: UM.Theme.getFont("very_small")
  427. renderType: Text.NativeRendering
  428. color: UM.Theme.getColor("text_subtext")
  429. elide: Text.ElideMiddle
  430. width: parent.width
  431. property string tooltipText
  432. text:
  433. {
  434. var lengths = [];
  435. var weights = [];
  436. var costs = [];
  437. var someCostsKnown = false;
  438. if(base.printMaterialLengths) {
  439. for(var index = 0; index < base.printMaterialLengths.length; index++)
  440. {
  441. if(base.printMaterialLengths[index] > 0)
  442. {
  443. lengths.push(base.printMaterialLengths[index].toFixed(2));
  444. weights.push(String(Math.round(base.printMaterialWeights[index])));
  445. var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2);
  446. costs.push(cost);
  447. if(cost > 0)
  448. {
  449. someCostsKnown = true;
  450. }
  451. }
  452. }
  453. }
  454. if(lengths.length == 0)
  455. {
  456. lengths = ["0.00"];
  457. weights = ["0"];
  458. costs = ["0.00"];
  459. }
  460. if(someCostsKnown)
  461. {
  462. return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + "))
  463. .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency"));
  464. }
  465. else
  466. {
  467. return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + "));
  468. }
  469. }
  470. MouseArea
  471. {
  472. id: costSpecMouseArea
  473. anchors.fill: parent
  474. hoverEnabled: true
  475. onEntered:
  476. {
  477. if(base.printDuration.valid && !base.printDuration.isTotalDurationZero)
  478. {
  479. var show_data = costSpec.getSpecsData()
  480. base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data);
  481. }
  482. }
  483. onExited:
  484. {
  485. base.hideTooltip();
  486. }
  487. }
  488. }
  489. }
  490. // SaveButton and MonitorButton are actually the bottom footer panels.
  491. // "!monitoringPrint" currently means "show-settings-mode"
  492. SaveButton
  493. {
  494. id: saveButton
  495. implicitWidth: base.width
  496. anchors.top: footerSeparator.bottom
  497. anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
  498. anchors.bottom: parent.bottom
  499. visible: !monitoringPrint
  500. }
  501. MonitorButton
  502. {
  503. id: monitorButton
  504. implicitWidth: base.width
  505. anchors.top: footerSeparator.bottom
  506. anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
  507. anchors.bottom: parent.bottom
  508. visible: monitoringPrint
  509. }
  510. SidebarTooltip
  511. {
  512. id: tooltip
  513. }
  514. // Setting mode: Recommended or Custom
  515. ListModel
  516. {
  517. id: modesListModel
  518. }
  519. SidebarSimple
  520. {
  521. id: sidebarSimple
  522. visible: false
  523. onShowTooltip: base.showTooltip(item, location, text)
  524. onHideTooltip: base.hideTooltip()
  525. }
  526. SidebarAdvanced
  527. {
  528. id: sidebarAdvanced
  529. visible: false
  530. onShowTooltip: base.showTooltip(item, location, text)
  531. onHideTooltip: base.hideTooltip()
  532. }
  533. Component.onCompleted:
  534. {
  535. modesListModel.append({
  536. text: catalog.i18nc("@title:tab", "Recommended"),
  537. tooltipText: catalog.i18nc("@tooltip", "<b>Recommended Print Setup</b><br/><br/>Print with the recommended settings for the selected printer, material and quality."),
  538. item: sidebarSimple
  539. })
  540. modesListModel.append({
  541. text: catalog.i18nc("@title:tab", "Custom"),
  542. tooltipText: catalog.i18nc("@tooltip", "<b>Custom Print Setup</b><br/><br/>Print with finegrained control over every last bit of the slicing process."),
  543. item: sidebarAdvanced
  544. })
  545. sidebarContents.replace(modesListModel.get(base.currentModeIndex).item, { "immediate": true })
  546. var index = Math.round(UM.Preferences.getValue("cura/active_mode"))
  547. if(index)
  548. {
  549. currentModeIndex = index;
  550. }
  551. }
  552. UM.SettingPropertyProvider
  553. {
  554. id: machineExtruderCount
  555. containerStackId: Cura.MachineManager.activeMachineId
  556. key: "machine_extruder_count"
  557. watchedProperties: [ "value" ]
  558. storeIndex: 0
  559. }
  560. UM.SettingPropertyProvider
  561. {
  562. id: machineHeatedBed
  563. containerStackId: Cura.MachineManager.activeMachineId
  564. key: "machine_heated_bed"
  565. watchedProperties: [ "value" ]
  566. storeIndex: 0
  567. }
  568. }