Sidebar.qml 23 KB

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