SettingView.qml 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. anchors
  157. {
  158. top: filterContainer.bottom
  159. topMargin: UM.Theme.getSize("default_margin").height
  160. bottom: parent.bottom
  161. right: parent.right
  162. left: parent.left
  163. }
  164. clip: true
  165. cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
  166. ScrollBar.vertical: UM.ScrollBar {}
  167. model: UM.SettingDefinitionsModel
  168. {
  169. id: definitionsModel
  170. containerId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.definition.id: ""
  171. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  172. 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.
  173. expanded: CuraApplication.expandedCategories
  174. onExpandedChanged:
  175. {
  176. if (!findingSettings)
  177. {
  178. // Do not change expandedCategories preference while filtering settings
  179. // because all categories are expanded while filtering
  180. CuraApplication.setExpandedCategories(expanded)
  181. }
  182. }
  183. onVisibilityChanged: Cura.SettingInheritanceManager.scheduleUpdate()
  184. }
  185. property int indexWithFocus: -1
  186. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  187. delegate: Loader
  188. {
  189. id: delegate
  190. width: contents.width
  191. Behavior on height { NumberAnimation { duration: 100 } }
  192. opacity: enabled ? 1 : 0
  193. Behavior on opacity { NumberAnimation { duration: 100 } }
  194. enabled: provider.properties.enabled === "True"
  195. property var definition: model
  196. property var settingDefinitionsModel: definitionsModel
  197. property var propertyProvider: provider
  198. property var globalPropertyProvider: inheritStackProvider
  199. property bool externalResetHandler: false
  200. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  201. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  202. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  203. asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder"
  204. active: model.type !== undefined
  205. source:
  206. {
  207. switch(model.type)
  208. {
  209. case "int":
  210. return "SettingTextField.qml"
  211. case "[int]":
  212. return "SettingTextField.qml"
  213. case "float":
  214. return "SettingTextField.qml"
  215. case "enum":
  216. return "SettingComboBox.qml"
  217. case "extruder":
  218. return "SettingExtruder.qml"
  219. case "bool":
  220. return "SettingCheckBox.qml"
  221. case "str":
  222. return "SettingTextField.qml"
  223. case "category":
  224. return "SettingCategory.qml"
  225. case "optional_extruder":
  226. return "SettingOptionalExtruder.qml"
  227. default:
  228. return "SettingUnknown.qml"
  229. }
  230. }
  231. // Binding to ensure that the right containerstack ID is set for the provider.
  232. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  233. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  234. Binding
  235. {
  236. target: provider
  237. property: "containerStackId"
  238. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0);
  239. value:
  240. {
  241. // Associate this binding with Cura.MachineManager.activeMachine.id in the beginning so this
  242. // binding will be triggered when activeMachineId is changed too.
  243. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  244. // machine gets changed.
  245. if (!model.settable_per_extruder)
  246. {
  247. //Not settable per extruder or there only is global, so we must pick global.
  248. return contents.activeMachineId
  249. }
  250. if (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0)
  251. {
  252. //We have limit_to_extruder, so pick that stack.
  253. return Cura.ExtruderManager.extruderIds[inheritStackProvider.properties.limit_to_extruder]
  254. }
  255. if (Cura.ExtruderManager.activeExtruderStackId)
  256. {
  257. //We're on an extruder tab. Pick the current extruder.
  258. return Cura.ExtruderManager.activeExtruderStackId
  259. }
  260. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  261. return contents.activeMachineId
  262. }
  263. }
  264. // Specialty provider that only watches global_inherits (we can't filter on what property changed we get events
  265. // so we bypass that to make a dedicated provider).
  266. UM.SettingPropertyProvider
  267. {
  268. id: inheritStackProvider
  269. containerStackId: contents.activeMachineId
  270. key: model.key
  271. watchedProperties: [ "limit_to_extruder" ]
  272. }
  273. UM.SettingPropertyProvider
  274. {
  275. id: provider
  276. containerStackId: contents.activeMachineId
  277. key: model.key
  278. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  279. storeIndex: 0
  280. removeUnusedValue: model.resolve === undefined
  281. }
  282. Connections
  283. {
  284. target: item
  285. function onContextMenuRequested()
  286. {
  287. contextMenu.key = model.key
  288. contextMenu.settingVisible = model.visible
  289. contextMenu.provider = provider
  290. contextMenu.popup() //iconName: model.icon_name
  291. }
  292. function onShowTooltip(text) { base.showTooltip(delegate, Qt.point(-settingsView.x - UM.Theme.getSize("default_margin").width, 0), text) }
  293. function onHideTooltip() { base.hideTooltip() }
  294. function onShowAllHiddenInheritedSettings()
  295. {
  296. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  297. for(var i = 0; i < children_with_override.length; i++)
  298. {
  299. definitionsModel.setVisible(children_with_override[i], true)
  300. }
  301. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  302. }
  303. function onFocusReceived()
  304. {
  305. contents.indexWithFocus = index
  306. animateContentY.from = contents.contentY
  307. contents.positionViewAtIndex(index, ListView.Contain)
  308. animateContentY.to = contents.contentY
  309. animateContentY.running = true
  310. }
  311. function onSetActiveFocusToNextSetting(forward)
  312. {
  313. if (forward == undefined || forward)
  314. {
  315. contents.currentIndex = contents.indexWithFocus + 1
  316. while(contents.currentItem && contents.currentItem.height <= 0)
  317. {
  318. contents.currentIndex++
  319. }
  320. if (contents.currentItem)
  321. {
  322. contents.currentItem.item.focusItem.forceActiveFocus()
  323. }
  324. }
  325. else
  326. {
  327. contents.currentIndex = contents.indexWithFocus - 1
  328. while(contents.currentItem && contents.currentItem.height <= 0)
  329. {
  330. contents.currentIndex--
  331. }
  332. if (contents.currentItem)
  333. {
  334. contents.currentItem.item.focusItem.forceActiveFocus()
  335. }
  336. }
  337. }
  338. }
  339. }
  340. NumberAnimation {
  341. id: animateContentY
  342. target: contents
  343. property: "contentY"
  344. duration: 50
  345. }
  346. add: Transition {
  347. SequentialAnimation {
  348. NumberAnimation { properties: "height"; from: 0; duration: 100 }
  349. NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
  350. }
  351. }
  352. remove: Transition {
  353. SequentialAnimation {
  354. NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
  355. NumberAnimation { properties: "height"; to: 0; duration: 100 }
  356. }
  357. }
  358. addDisplaced: Transition {
  359. NumberAnimation { properties: "x,y"; duration: 100 }
  360. }
  361. removeDisplaced: Transition {
  362. SequentialAnimation {
  363. PauseAnimation { duration: 100; }
  364. NumberAnimation { properties: "x,y"; duration: 100 }
  365. }
  366. }
  367. Cura.Menu
  368. {
  369. id: contextMenu
  370. property string key
  371. property var provider
  372. property bool settingVisible
  373. Cura.MenuItem
  374. {
  375. //: Settings context menu action
  376. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  377. visible: machineExtruderCount.properties.value > 1
  378. enabled: contextMenu.provider !== undefined && contextMenu.provider.properties.settable_per_extruder !== "False"
  379. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  380. }
  381. Cura.MenuItem
  382. {
  383. //: Settings context menu action
  384. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  385. visible: machineExtruderCount.properties.value > 1
  386. enabled: contextMenu.provider !== undefined
  387. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  388. }
  389. Cura.MenuSeparator
  390. {
  391. visible: machineExtruderCount.properties.value > 1
  392. }
  393. Instantiator
  394. {
  395. id: customMenuItems
  396. model: Cura.SidebarCustomMenuItemsModel { }
  397. Cura.MenuItem
  398. {
  399. text: model.name
  400. onTriggered:
  401. {
  402. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  403. }
  404. }
  405. onObjectAdded: contextMenu.insertItem(index, object)
  406. onObjectRemoved: contextMenu.removeItem(object)
  407. }
  408. Cura.MenuSeparator
  409. {
  410. visible: customMenuItems.count > 0
  411. }
  412. Cura.MenuItem
  413. {
  414. //: Settings context menu action
  415. visible: !findingSettings
  416. text: catalog.i18nc("@action:menu", "Hide this setting")
  417. onTriggered:
  418. {
  419. definitionsModel.hide(contextMenu.key)
  420. }
  421. }
  422. Cura.MenuItem
  423. {
  424. //: Settings context menu action
  425. text:
  426. {
  427. if (contextMenu.settingVisible)
  428. {
  429. return catalog.i18nc("@action:menu", "Don't show this setting")
  430. }
  431. else
  432. {
  433. return catalog.i18nc("@action:menu", "Keep this setting visible")
  434. }
  435. }
  436. visible: findingSettings
  437. onTriggered:
  438. {
  439. if (contextMenu.settingVisible)
  440. {
  441. definitionsModel.hide(contextMenu.key)
  442. }
  443. else
  444. {
  445. definitionsModel.show(contextMenu.key)
  446. }
  447. }
  448. }
  449. Cura.MenuItem
  450. {
  451. //: Settings context menu action
  452. text: catalog.i18nc("@action:menu", "Configure setting visibility...")
  453. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu)
  454. }
  455. }
  456. UM.SettingPropertyProvider
  457. {
  458. id: machineExtruderCount
  459. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  460. key: "machine_extruder_count"
  461. watchedProperties: [ "value" ]
  462. storeIndex: 0
  463. }
  464. }
  465. }