Sidebar.qml 23 KB

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