SettingView.qml 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. // Copyright (c) 2017 Ultimaker B.V.
  2. // Uranium is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 1.1
  5. import QtQuick.Controls.Styles 1.1
  6. import QtQuick.Layouts 1.2
  7. import UM 1.2 as UM
  8. import Cura 1.0 as Cura
  9. import "../Menus"
  10. Item
  11. {
  12. id: base;
  13. property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel()
  14. property Action configureSettings
  15. property bool findingSettings
  16. signal showTooltip(Item item, point location, string text)
  17. signal hideTooltip()
  18. Item
  19. {
  20. id: globalProfileRow
  21. height: UM.Theme.getSize("sidebar_setup").height
  22. visible: !sidebar.hideSettings
  23. anchors
  24. {
  25. top: parent.top
  26. left: parent.left
  27. leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
  28. right: parent.right
  29. rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
  30. }
  31. Label
  32. {
  33. id: globalProfileLabel
  34. text: catalog.i18nc("@label","Profile:");
  35. width: Math.round(parent.width * 0.45 - UM.Theme.getSize("sidebar_margin").width - 2)
  36. font: UM.Theme.getFont("default");
  37. color: UM.Theme.getColor("text");
  38. verticalAlignment: Text.AlignVCenter
  39. anchors.top: parent.top
  40. anchors.bottom: parent.bottom
  41. }
  42. ToolButton
  43. {
  44. id: globalProfileSelection
  45. text: generateActiveQualityText()
  46. enabled: !header.currentExtruderVisible || header.currentExtruderIndex > -1
  47. width: Math.round(parent.width * 0.55)
  48. height: UM.Theme.getSize("setting_control").height
  49. anchors.left: globalProfileLabel.right
  50. anchors.right: parent.right
  51. tooltip: Cura.MachineManager.activeQualityOrQualityChangesName
  52. style: UM.Theme.styles.sidebar_header_button
  53. activeFocusOnPress: true
  54. menu: ProfileMenu { }
  55. function generateActiveQualityText () {
  56. var result = Cura.MachineManager.activeQualityOrQualityChangesName;
  57. if (Cura.MachineManager.isActiveQualitySupported) {
  58. if (Cura.MachineManager.activeQualityLayerHeight > 0) {
  59. result += " <font color=\"" + UM.Theme.getColor("text_detail") + "\">"
  60. result += " - "
  61. result += Cura.MachineManager.activeQualityLayerHeight + "mm"
  62. result += "</font>"
  63. }
  64. }
  65. return result
  66. }
  67. UM.SimpleButton
  68. {
  69. id: customisedSettings
  70. visible: Cura.MachineManager.hasUserSettings
  71. height: Math.round(parent.height * 0.6)
  72. width: Math.round(parent.height * 0.6)
  73. anchors.verticalCenter: parent.verticalCenter
  74. anchors.right: parent.right
  75. anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("sidebar_margin").width)
  76. color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button");
  77. iconSource: UM.Theme.getIcon("star");
  78. onClicked:
  79. {
  80. forceActiveFocus();
  81. Cura.Actions.manageProfiles.trigger()
  82. }
  83. onEntered:
  84. {
  85. var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.")
  86. base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), content)
  87. }
  88. onExited: base.hideTooltip()
  89. }
  90. }
  91. }
  92. ToolButton
  93. {
  94. id: settingVisibilityMenu
  95. width: height
  96. height: UM.Theme.getSize("setting_control").height
  97. anchors
  98. {
  99. top: globalProfileRow.bottom
  100. topMargin: UM.Theme.getSize("sidebar_margin").height
  101. right: parent.right
  102. rightMargin: UM.Theme.getSize("sidebar_margin").width
  103. }
  104. style: ButtonStyle
  105. {
  106. background: Item {
  107. UM.RecolorImage {
  108. anchors.verticalCenter: parent.verticalCenter
  109. anchors.horizontalCenter: parent.horizontalCenter
  110. width: UM.Theme.getSize("standard_arrow").width
  111. height: UM.Theme.getSize("standard_arrow").height
  112. sourceSize.width: width
  113. sourceSize.height: width
  114. color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text")
  115. source: UM.Theme.getIcon("menu")
  116. }
  117. }
  118. label: Label{}
  119. }
  120. menu: SettingVisibilityPresetsMenu
  121. {
  122. onShowAllSettings:
  123. {
  124. definitionsModel.setAllVisible(true);
  125. filter.updateDefinitionModel();
  126. }
  127. }
  128. }
  129. Rectangle
  130. {
  131. id: filterContainer
  132. visible: true
  133. border.width: Math.round(UM.Theme.getSize("default_lining").width)
  134. border.color:
  135. {
  136. if(hoverMouseArea.containsMouse || clearFilterButton.containsMouse)
  137. {
  138. return UM.Theme.getColor("setting_control_border_highlight");
  139. }
  140. else
  141. {
  142. return UM.Theme.getColor("setting_control_border");
  143. }
  144. }
  145. color: UM.Theme.getColor("setting_control")
  146. anchors
  147. {
  148. top: globalProfileRow.bottom
  149. topMargin: UM.Theme.getSize("sidebar_margin").height
  150. left: parent.left
  151. leftMargin: UM.Theme.getSize("sidebar_margin").width
  152. right: settingVisibilityMenu.left
  153. rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
  154. }
  155. height: visible ? UM.Theme.getSize("setting_control").height : 0
  156. Behavior on height { NumberAnimation { duration: 100 } }
  157. Timer
  158. {
  159. id: settingsSearchTimer
  160. onTriggered: filter.editingFinished()
  161. interval: 500
  162. running: false
  163. repeat: false
  164. }
  165. TextField
  166. {
  167. id: filter;
  168. height: parent.height
  169. anchors.left: parent.left
  170. anchors.right: clearFilterButton.left
  171. anchors.rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width)
  172. placeholderText: catalog.i18nc("@label:textbox", "Search...")
  173. style: TextFieldStyle
  174. {
  175. textColor: UM.Theme.getColor("setting_control_text");
  176. placeholderTextColor: UM.Theme.getColor("setting_control_text")
  177. font: UM.Theme.getFont("default");
  178. background: Item {}
  179. }
  180. property var expandedCategories
  181. property bool lastFindingSettings: false
  182. onTextChanged:
  183. {
  184. settingsSearchTimer.restart()
  185. }
  186. onEditingFinished:
  187. {
  188. definitionsModel.filter = {"i18n_label": "*" + text};
  189. findingSettings = (text.length > 0);
  190. if(findingSettings != lastFindingSettings)
  191. {
  192. updateDefinitionModel();
  193. lastFindingSettings = findingSettings;
  194. }
  195. }
  196. Keys.onEscapePressed:
  197. {
  198. filter.text = "";
  199. }
  200. function updateDefinitionModel()
  201. {
  202. if(findingSettings)
  203. {
  204. expandedCategories = definitionsModel.expanded.slice();
  205. definitionsModel.expanded = [""]; // keep categories closed while to prevent render while making settings visible one by one
  206. definitionsModel.showAncestors = true;
  207. definitionsModel.showAll = true;
  208. definitionsModel.expanded = ["*"];
  209. }
  210. else
  211. {
  212. if(expandedCategories)
  213. {
  214. definitionsModel.expanded = expandedCategories;
  215. }
  216. definitionsModel.showAncestors = false;
  217. definitionsModel.showAll = false;
  218. }
  219. }
  220. }
  221. MouseArea
  222. {
  223. id: hoverMouseArea
  224. anchors.fill: parent
  225. hoverEnabled: true
  226. acceptedButtons: Qt.NoButton
  227. cursorShape: Qt.IBeamCursor
  228. }
  229. UM.SimpleButton
  230. {
  231. id: clearFilterButton
  232. iconSource: UM.Theme.getIcon("cross1")
  233. visible: findingSettings
  234. height: Math.round(parent.height * 0.4)
  235. width: visible ? height : 0
  236. anchors.verticalCenter: parent.verticalCenter
  237. anchors.right: parent.right
  238. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  239. color: UM.Theme.getColor("setting_control_button")
  240. hoverColor: UM.Theme.getColor("setting_control_button_hover")
  241. onClicked:
  242. {
  243. filter.text = "";
  244. filter.forceActiveFocus();
  245. }
  246. }
  247. }
  248. ScrollView
  249. {
  250. anchors.top: filterContainer.bottom;
  251. anchors.bottom: parent.bottom;
  252. anchors.right: parent.right;
  253. anchors.left: parent.left;
  254. anchors.topMargin: filterContainer.visible ? UM.Theme.getSize("sidebar_margin").height : 0
  255. Behavior on anchors.topMargin { NumberAnimation { duration: 100 } }
  256. style: UM.Theme.styles.scrollview;
  257. flickableItem.flickableDirection: Flickable.VerticalFlick;
  258. __wheelAreaScrollSpeed: 75; // Scroll three lines in one scroll event
  259. ListView
  260. {
  261. id: contents
  262. spacing: Math.round(UM.Theme.getSize("default_lining").height);
  263. cacheBuffer: 1000000; // Set a large cache to effectively just cache every list item.
  264. model: UM.SettingDefinitionsModel
  265. {
  266. id: definitionsModel;
  267. containerId: Cura.MachineManager.activeDefinitionId
  268. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  269. exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "cutting_mesh", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false.
  270. expanded: CuraApplication.expandedCategories
  271. onExpandedChanged:
  272. {
  273. if(!findingSettings)
  274. {
  275. // Do not change expandedCategories preference while filtering settings
  276. // because all categories are expanded while filtering
  277. CuraApplication.setExpandedCategories(expanded)
  278. }
  279. }
  280. onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate()
  281. }
  282. property var indexWithFocus: -1
  283. delegate: Loader
  284. {
  285. id: delegate
  286. width: Math.round(UM.Theme.getSize("sidebar").width);
  287. height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : - contents.spacing
  288. Behavior on height { NumberAnimation { duration: 100 } }
  289. opacity: provider.properties.enabled == "True" ? 1 : 0
  290. Behavior on opacity { NumberAnimation { duration: 100 } }
  291. enabled:
  292. {
  293. if (!Cura.ExtruderManager.activeExtruderStackId && machineExtruderCount.properties.value > 1)
  294. {
  295. // disable all controls on the global tab, except categories
  296. return model.type == "category"
  297. }
  298. return provider.properties.enabled == "True"
  299. }
  300. property var definition: model
  301. property var settingDefinitionsModel: definitionsModel
  302. property var propertyProvider: provider
  303. property var globalPropertyProvider: inheritStackProvider
  304. property var externalResetHandler: false
  305. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  306. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  307. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  308. asynchronous: model.type != "enum" && model.type != "extruder" && model.type != "optional_extruder"
  309. active: model.type != undefined
  310. source:
  311. {
  312. switch(model.type)
  313. {
  314. case "int":
  315. return "SettingTextField.qml"
  316. case "[int]":
  317. return "SettingTextField.qml"
  318. case "float":
  319. return "SettingTextField.qml"
  320. case "enum":
  321. return "SettingComboBox.qml"
  322. case "extruder":
  323. return "SettingExtruder.qml"
  324. case "bool":
  325. return "SettingCheckBox.qml"
  326. case "str":
  327. return "SettingTextField.qml"
  328. case "category":
  329. return "SettingCategory.qml"
  330. case "optional_extruder":
  331. return "SettingOptionalExtruder.qml"
  332. default:
  333. return "SettingUnknown.qml"
  334. }
  335. }
  336. // Binding to ensure that the right containerstack ID is set for the provider.
  337. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  338. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  339. Binding
  340. {
  341. target: provider
  342. property: "containerStackId"
  343. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0);
  344. value:
  345. {
  346. // associate this binding with Cura.MachineManager.activeMachineId in the beginning so this
  347. // binding will be triggered when activeMachineId is changed too.
  348. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  349. // machine gets changed.
  350. var activeMachineId = Cura.MachineManager.activeMachineId;
  351. if(!model.settable_per_extruder)
  352. {
  353. //Not settable per extruder or there only is global, so we must pick global.
  354. return activeMachineId;
  355. }
  356. if(inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0)
  357. {
  358. //We have limit_to_extruder, so pick that stack.
  359. return Cura.ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)];
  360. }
  361. if(Cura.ExtruderManager.activeExtruderStackId)
  362. {
  363. //We're on an extruder tab. Pick the current extruder.
  364. return Cura.ExtruderManager.activeExtruderStackId;
  365. }
  366. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  367. return activeMachineId;
  368. }
  369. }
  370. // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events
  371. // so we bypass that to make a dedicated provider).
  372. UM.SettingPropertyProvider
  373. {
  374. id: inheritStackProvider
  375. containerStackId: Cura.MachineManager.activeMachineId
  376. key: model.key
  377. watchedProperties: [ "limit_to_extruder" ]
  378. }
  379. UM.SettingPropertyProvider
  380. {
  381. id: provider
  382. containerStackId: Cura.MachineManager.activeMachineId
  383. key: model.key ? model.key : ""
  384. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  385. storeIndex: 0
  386. removeUnusedValue: model.resolve == undefined
  387. }
  388. Connections
  389. {
  390. target: item
  391. onContextMenuRequested:
  392. {
  393. contextMenu.key = model.key;
  394. contextMenu.settingVisible = model.visible;
  395. contextMenu.provider = provider
  396. contextMenu.popup();
  397. }
  398. onShowTooltip: base.showTooltip(delegate, { x: -UM.Theme.getSize("default_arrow").width, y: Math.round(delegate.height / 2) }, text)
  399. onHideTooltip: base.hideTooltip()
  400. onShowAllHiddenInheritedSettings:
  401. {
  402. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  403. for(var i = 0; i < children_with_override.length; i++)
  404. {
  405. definitionsModel.setVisible(children_with_override[i], true)
  406. }
  407. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  408. }
  409. onFocusReceived:
  410. {
  411. contents.indexWithFocus = index;
  412. animateContentY.from = contents.contentY;
  413. contents.positionViewAtIndex(index, ListView.Contain);
  414. animateContentY.to = contents.contentY;
  415. animateContentY.running = true;
  416. }
  417. onSetActiveFocusToNextSetting:
  418. {
  419. if(forward == undefined || forward)
  420. {
  421. contents.currentIndex = contents.indexWithFocus + 1;
  422. while(contents.currentItem && contents.currentItem.height <= 0)
  423. {
  424. contents.currentIndex++;
  425. }
  426. if(contents.currentItem)
  427. {
  428. contents.currentItem.item.focusItem.forceActiveFocus();
  429. }
  430. }
  431. else
  432. {
  433. contents.currentIndex = contents.indexWithFocus - 1;
  434. while(contents.currentItem && contents.currentItem.height <= 0)
  435. {
  436. contents.currentIndex--;
  437. }
  438. if(contents.currentItem)
  439. {
  440. contents.currentItem.item.focusItem.forceActiveFocus();
  441. }
  442. }
  443. }
  444. }
  445. }
  446. UM.I18nCatalog { id: catalog; name: "cura"; }
  447. NumberAnimation {
  448. id: animateContentY
  449. target: contents
  450. property: "contentY"
  451. duration: 50
  452. }
  453. add: Transition {
  454. SequentialAnimation {
  455. NumberAnimation { properties: "height"; from: 0; duration: 100 }
  456. NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
  457. }
  458. }
  459. remove: Transition {
  460. SequentialAnimation {
  461. NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
  462. NumberAnimation { properties: "height"; to: 0; duration: 100 }
  463. }
  464. }
  465. addDisplaced: Transition {
  466. NumberAnimation { properties: "x,y"; duration: 100 }
  467. }
  468. removeDisplaced: Transition {
  469. SequentialAnimation {
  470. PauseAnimation { duration: 100; }
  471. NumberAnimation { properties: "x,y"; duration: 100 }
  472. }
  473. }
  474. Menu
  475. {
  476. id: contextMenu
  477. property string key
  478. property var provider
  479. property bool settingVisible
  480. MenuItem
  481. {
  482. //: Settings context menu action
  483. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  484. visible: machineExtruderCount.properties.value > 1
  485. enabled: contextMenu.provider != undefined && contextMenu.provider.properties.settable_per_extruder != "False"
  486. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  487. }
  488. MenuItem
  489. {
  490. //: Settings context menu action
  491. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  492. visible: machineExtruderCount.properties.value > 1
  493. enabled: contextMenu.provider != undefined
  494. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  495. }
  496. MenuSeparator
  497. {
  498. visible: machineExtruderCount.properties.value > 1
  499. }
  500. Instantiator
  501. {
  502. id: customMenuItems
  503. model: Cura.SidebarCustomMenuItemsModel { }
  504. MenuItem
  505. {
  506. text: model.name
  507. iconName: model.icon_name
  508. onTriggered:
  509. {
  510. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  511. }
  512. }
  513. onObjectAdded: contextMenu.insertItem(index, object)
  514. onObjectRemoved: contextMenu.removeItem(object)
  515. }
  516. MenuSeparator
  517. {
  518. visible: customMenuItems.count > 0
  519. }
  520. MenuItem
  521. {
  522. //: Settings context menu action
  523. visible: !findingSettings
  524. text: catalog.i18nc("@action:menu", "Hide this setting");
  525. onTriggered:
  526. {
  527. definitionsModel.hide(contextMenu.key);
  528. // visible settings have changed, so we're no longer showing a preset
  529. if (settingVisibilityPresetsModel.activePreset != "")
  530. {
  531. settingVisibilityPresetsModel.setActivePreset("custom");
  532. }
  533. }
  534. }
  535. MenuItem
  536. {
  537. //: Settings context menu action
  538. text:
  539. {
  540. if (contextMenu.settingVisible)
  541. {
  542. return catalog.i18nc("@action:menu", "Don't show this setting");
  543. }
  544. else
  545. {
  546. return catalog.i18nc("@action:menu", "Keep this setting visible");
  547. }
  548. }
  549. visible: findingSettings
  550. onTriggered:
  551. {
  552. if (contextMenu.settingVisible)
  553. {
  554. definitionsModel.hide(contextMenu.key);
  555. }
  556. else
  557. {
  558. definitionsModel.show(contextMenu.key);
  559. }
  560. // visible settings have changed, so we're no longer showing a preset
  561. if (settingVisibilityPresetsModel.activePreset != "")
  562. {
  563. settingVisibilityPresetsModel.setActivePreset("custom");
  564. }
  565. }
  566. }
  567. MenuItem
  568. {
  569. //: Settings context menu action
  570. text: catalog.i18nc("@action:menu", "Configure setting visibility...");
  571. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu);
  572. }
  573. MenuSeparator {}
  574. MenuItem
  575. {
  576. text: catalog.i18nc("@action:inmenu", "Collapse All")
  577. onTriggered: definitionsModel.collapseAll()
  578. }
  579. MenuItem
  580. {
  581. text: catalog.i18nc("@action:inmenu", "Expand All")
  582. onTriggered: definitionsModel.expandRecursive()
  583. }
  584. }
  585. UM.SettingPropertyProvider
  586. {
  587. id: machineExtruderCount
  588. containerStackId: Cura.MachineManager.activeMachineId
  589. key: "machine_extruder_count"
  590. watchedProperties: [ "value" ]
  591. storeIndex: 0
  592. }
  593. }
  594. }
  595. }