SettingView.qml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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 { id: scrollBar }
  168. onContentYChanged: {
  169. // This removes focus from SettingItems when scrolling.
  170. // This fixes comboboxes staying open and scrolling out of the settingView.
  171. if (!scrollBar.activeFocus) {
  172. scrollBar.forceActiveFocus();
  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. }