SettingView.qml 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // Copyright (c) 2022 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.1
  5. import UM 1.5 as UM
  6. import Cura 1.0 as Cura
  7. import "../Menus"
  8. Item
  9. {
  10. id: settingsView
  11. property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel()
  12. property bool findingSettings
  13. Item
  14. {
  15. id: filterContainer
  16. anchors
  17. {
  18. top: parent.top
  19. left: parent.left
  20. right: settingVisibilityMenu.left
  21. }
  22. height: UM.Theme.getSize("print_setup_big_item").height
  23. Timer
  24. {
  25. id: settingsSearchTimer
  26. onTriggered: filter.editingFinished()
  27. interval: 500
  28. running: false
  29. repeat: false
  30. }
  31. Cura.TextField
  32. {
  33. id: filter
  34. height: parent.height
  35. anchors.left: parent.left
  36. anchors.right: parent.right
  37. leftPadding: searchIcon.width + UM.Theme.getSize("default_margin").width * 2
  38. placeholderText: catalog.i18nc("@label:textbox", "Search settings")
  39. font.italic: true
  40. property var expandedCategories
  41. property bool lastFindingSettings: false
  42. UM.RecolorImage
  43. {
  44. id: searchIcon
  45. anchors
  46. {
  47. verticalCenter: parent.verticalCenter
  48. left: parent.left
  49. leftMargin: UM.Theme.getSize("default_margin").width
  50. }
  51. source: UM.Theme.getIcon("search")
  52. height: UM.Theme.getSize("small_button_icon").height
  53. width: height
  54. color: UM.Theme.getColor("text")
  55. }
  56. onTextChanged: settingsSearchTimer.restart()
  57. onEditingFinished:
  58. {
  59. definitionsModel.filter = {"i18n_label|i18n_description" : "*" + text}
  60. findingSettings = (text.length > 0)
  61. if (findingSettings != lastFindingSettings)
  62. {
  63. updateDefinitionModel()
  64. lastFindingSettings = findingSettings
  65. }
  66. }
  67. Keys.onEscapePressed: filter.text = ""
  68. function updateDefinitionModel()
  69. {
  70. if (findingSettings)
  71. {
  72. expandedCategories = definitionsModel.expanded.slice()
  73. definitionsModel.expanded = [""] // keep categories closed while to prevent render while making settings visible one by one
  74. definitionsModel.showAncestors = true
  75. definitionsModel.showAll = true
  76. definitionsModel.expanded = ["*"]
  77. }
  78. else
  79. {
  80. if (expandedCategories)
  81. {
  82. definitionsModel.expanded = expandedCategories
  83. }
  84. definitionsModel.showAncestors = false
  85. definitionsModel.showAll = false
  86. }
  87. }
  88. }
  89. UM.SimpleButton
  90. {
  91. id: clearFilterButton
  92. iconSource: UM.Theme.getIcon("Cancel")
  93. visible: findingSettings
  94. height: Math.round(parent.height * 0.4)
  95. width: visible ? height : 0
  96. anchors.verticalCenter: parent.verticalCenter
  97. anchors.right: parent.right
  98. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  99. color: UM.Theme.getColor("setting_control_button")
  100. hoverColor: UM.Theme.getColor("setting_control_button_hover")
  101. onClicked:
  102. {
  103. filter.text = ""
  104. filter.forceActiveFocus()
  105. }
  106. }
  107. }
  108. SettingVisibilityPresetsMenu
  109. {
  110. id: settingVisibilityPresetsMenu
  111. onCollapseAllCategories:
  112. {
  113. settingsSearchTimer.stop()
  114. filter.text = "" // clear search field
  115. filter.editingFinished()
  116. definitionsModel.collapseAllCategories()
  117. }
  118. }
  119. UM.BurgerButton
  120. {
  121. id: settingVisibilityMenu
  122. anchors
  123. {
  124. verticalCenter: filterContainer.verticalCenter
  125. right: parent.right
  126. }
  127. onClicked:
  128. {
  129. settingVisibilityPresetsMenu.popup(
  130. popupContainer,
  131. -settingVisibilityPresetsMenu.width + UM.Theme.getSize("default_margin").width,
  132. settingVisibilityMenu.height
  133. )
  134. }
  135. }
  136. Item
  137. {
  138. // Work around to prevent the buttom from being rescaled if a popup is attached
  139. id: popupContainer
  140. anchors.bottom: settingVisibilityMenu.bottom
  141. anchors.right: settingVisibilityMenu.right
  142. }
  143. // Mouse area that gathers the scroll events to not propagate it to the main view.
  144. MouseArea
  145. {
  146. anchors.fill: contents
  147. acceptedButtons: Qt.AllButtons
  148. onWheel: wheel.accepted = true
  149. }
  150. ListView
  151. {
  152. id: contents
  153. maximumFlickVelocity: 1000
  154. anchors
  155. {
  156. top: filterContainer.bottom
  157. topMargin: UM.Theme.getSize("default_margin").height
  158. bottom: parent.bottom
  159. right: parent.right
  160. left: parent.left
  161. }
  162. clip: true
  163. cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
  164. ScrollBar.vertical: UM.ScrollBar
  165. {
  166. id: scrollBar
  167. onPositionChanged: {
  168. // This removes focus from items when scrolling.
  169. // This fixes comboboxes staying open and scrolling container
  170. if (!activeFocus) {
  171. forceActiveFocus();
  172. }
  173. }
  174. }
  175. model: UM.SettingDefinitionsModel
  176. {
  177. id: definitionsModel
  178. containerId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.definition.id: ""
  179. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  180. exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "cutting_mesh", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settings are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false.
  181. expanded: CuraApplication.expandedCategories
  182. onExpandedChanged:
  183. {
  184. if (!findingSettings)
  185. {
  186. // Do not change expandedCategories preference while filtering settings
  187. // because all categories are expanded while filtering
  188. CuraApplication.setExpandedCategories(expanded)
  189. }
  190. }
  191. onVisibilityChanged: Cura.SettingInheritanceManager.scheduleUpdate()
  192. }
  193. property int indexWithFocus: -1
  194. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  195. delegate: Loader
  196. {
  197. id: delegate
  198. width: contents.width - (scrollBar.width + UM.Theme.getSize("narrow_margin").width)
  199. Behavior on height { NumberAnimation { duration: 100 } }
  200. opacity: enabled ? 1 : 0
  201. Behavior on opacity { NumberAnimation { duration: 100 } }
  202. enabled: provider.properties.enabled === "True"
  203. property var definition: model
  204. property var settingDefinitionsModel: definitionsModel
  205. property var propertyProvider: provider
  206. property var globalPropertyProvider: inheritStackProvider
  207. property bool externalResetHandler: false
  208. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  209. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  210. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  211. asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder"
  212. active: model.type !== undefined
  213. source:
  214. {
  215. switch(model.type)
  216. {
  217. case "int":
  218. return "SettingTextField.qml"
  219. case "[int]":
  220. return "SettingTextField.qml"
  221. case "float":
  222. return "SettingTextField.qml"
  223. case "enum":
  224. return "SettingComboBox.qml"
  225. case "extruder":
  226. return "SettingExtruder.qml"
  227. case "bool":
  228. return "SettingCheckBox.qml"
  229. case "str":
  230. return "SettingTextField.qml"
  231. case "category":
  232. return "SettingCategory.qml"
  233. case "optional_extruder":
  234. return "SettingOptionalExtruder.qml"
  235. default:
  236. return "SettingUnknown.qml"
  237. }
  238. }
  239. // Binding to ensure that the right containerstack ID is set for the provider.
  240. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  241. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  242. Binding
  243. {
  244. target: provider
  245. property: "containerStackId"
  246. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0);
  247. value:
  248. {
  249. // Associate this binding with Cura.MachineManager.activeMachine.id in the beginning so this
  250. // binding will be triggered when activeMachineId is changed too.
  251. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  252. // machine gets changed.
  253. if (!model.settable_per_extruder)
  254. {
  255. //Not settable per extruder or there only is global, so we must pick global.
  256. return contents.activeMachineId
  257. }
  258. if (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0)
  259. {
  260. //We have limit_to_extruder, so pick that stack.
  261. return Cura.ExtruderManager.extruderIds[inheritStackProvider.properties.limit_to_extruder]
  262. }
  263. if (Cura.ExtruderManager.activeExtruderStackId)
  264. {
  265. //We're on an extruder tab. Pick the current extruder.
  266. return Cura.ExtruderManager.activeExtruderStackId
  267. }
  268. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  269. return contents.activeMachineId
  270. }
  271. }
  272. // Specialty provider that only watches global_inherits (we can't filter on what property changed we get events
  273. // so we bypass that to make a dedicated provider).
  274. UM.SettingPropertyProvider
  275. {
  276. id: inheritStackProvider
  277. containerStackId: contents.activeMachineId
  278. key: model.key
  279. watchedProperties: [ "limit_to_extruder" ]
  280. }
  281. UM.SettingPropertyProvider
  282. {
  283. id: provider
  284. containerStackId: contents.activeMachineId
  285. key: model.key
  286. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  287. storeIndex: 0
  288. removeUnusedValue: model.resolve === undefined
  289. }
  290. Connections
  291. {
  292. target: item
  293. function onContextMenuRequested()
  294. {
  295. contextMenu.key = model.key
  296. contextMenu.settingVisible = model.visible
  297. contextMenu.provider = provider
  298. contextMenu.popup() //iconName: model.icon_name
  299. }
  300. function onShowTooltip(text) { base.showTooltip(delegate, Qt.point(-settingsView.x - UM.Theme.getSize("default_margin").width, 0), text) }
  301. function onHideTooltip() { base.hideTooltip() }
  302. function onShowAllHiddenInheritedSettings()
  303. {
  304. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  305. for(var i = 0; i < children_with_override.length; i++)
  306. {
  307. definitionsModel.setVisible(children_with_override[i], true)
  308. }
  309. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  310. }
  311. function onFocusReceived()
  312. {
  313. contents.indexWithFocus = index
  314. animateContentY.from = contents.contentY
  315. contents.positionViewAtIndex(index, ListView.Contain)
  316. animateContentY.to = contents.contentY
  317. animateContentY.running = true
  318. }
  319. function onSetActiveFocusToNextSetting(forward)
  320. {
  321. if (forward == undefined || forward)
  322. {
  323. contents.currentIndex = contents.indexWithFocus + 1
  324. while(contents.currentItem && contents.currentItem.height <= 0)
  325. {
  326. contents.currentIndex++
  327. }
  328. if (contents.currentItem)
  329. {
  330. contents.currentItem.item.focusItem.forceActiveFocus()
  331. }
  332. }
  333. else
  334. {
  335. contents.currentIndex = contents.indexWithFocus - 1
  336. while(contents.currentItem && contents.currentItem.height <= 0)
  337. {
  338. contents.currentIndex--
  339. }
  340. if (contents.currentItem)
  341. {
  342. contents.currentItem.item.focusItem.forceActiveFocus()
  343. }
  344. }
  345. }
  346. }
  347. }
  348. NumberAnimation {
  349. id: animateContentY
  350. target: contents
  351. property: "contentY"
  352. duration: 50
  353. }
  354. add: Transition {
  355. SequentialAnimation {
  356. NumberAnimation { properties: "height"; from: 0; duration: 100 }
  357. NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
  358. }
  359. }
  360. remove: Transition {
  361. SequentialAnimation {
  362. NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
  363. NumberAnimation { properties: "height"; to: 0; duration: 100 }
  364. }
  365. }
  366. addDisplaced: Transition {
  367. NumberAnimation { properties: "x,y"; duration: 100 }
  368. }
  369. removeDisplaced: Transition {
  370. SequentialAnimation {
  371. PauseAnimation { duration: 100; }
  372. NumberAnimation { properties: "x,y"; duration: 100 }
  373. }
  374. }
  375. Cura.Menu
  376. {
  377. id: contextMenu
  378. property string key
  379. property var provider
  380. property bool settingVisible
  381. Cura.MenuItem
  382. {
  383. //: Settings context menu action
  384. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  385. visible: machineExtruderCount.properties.value > 1
  386. enabled: contextMenu.provider !== undefined && contextMenu.provider.properties.settable_per_extruder !== "False"
  387. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  388. }
  389. Cura.MenuItem
  390. {
  391. //: Settings context menu action
  392. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  393. visible: machineExtruderCount.properties.value > 1
  394. enabled: contextMenu.provider !== undefined
  395. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  396. }
  397. Cura.MenuSeparator
  398. {
  399. visible: machineExtruderCount.properties.value > 1
  400. }
  401. Instantiator
  402. {
  403. id: customMenuItems
  404. model: Cura.SidebarCustomMenuItemsModel { }
  405. Cura.MenuItem
  406. {
  407. text: model.name
  408. onTriggered:
  409. {
  410. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  411. }
  412. }
  413. onObjectAdded: contextMenu.insertItem(index, object)
  414. onObjectRemoved: contextMenu.removeItem(object)
  415. }
  416. Cura.MenuSeparator
  417. {
  418. visible: customMenuItems.count > 0
  419. }
  420. Cura.MenuItem
  421. {
  422. //: Settings context menu action
  423. visible: !findingSettings
  424. text: catalog.i18nc("@action:menu", "Hide this setting")
  425. onTriggered:
  426. {
  427. definitionsModel.hide(contextMenu.key)
  428. }
  429. }
  430. Cura.MenuItem
  431. {
  432. //: Settings context menu action
  433. text:
  434. {
  435. if (contextMenu.settingVisible)
  436. {
  437. return catalog.i18nc("@action:menu", "Don't show this setting")
  438. }
  439. else
  440. {
  441. return catalog.i18nc("@action:menu", "Keep this setting visible")
  442. }
  443. }
  444. visible: findingSettings
  445. onTriggered:
  446. {
  447. if (contextMenu.settingVisible)
  448. {
  449. definitionsModel.hide(contextMenu.key)
  450. }
  451. else
  452. {
  453. definitionsModel.show(contextMenu.key)
  454. }
  455. }
  456. }
  457. Cura.MenuItem
  458. {
  459. //: Settings context menu action
  460. text: catalog.i18nc("@action:menu", "Configure setting visibility...")
  461. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu)
  462. }
  463. }
  464. UM.SettingPropertyProvider
  465. {
  466. id: machineExtruderCount
  467. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  468. key: "machine_extruder_count"
  469. watchedProperties: [ "value" ]
  470. storeIndex: 0
  471. }
  472. }
  473. }