SettingView.qml 19 KB

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