SettingView.qml 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. // Copyright (c) 2022 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick
  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. topPadding: height / 4
  38. leftPadding: searchIcon.width + UM.Theme.getSize("default_margin").width * 2
  39. placeholderText: catalog.i18nc("@label:textbox", "Search settings")
  40. font: UM.Theme.getFont("default_italic")
  41. property var expandedCategories
  42. property bool lastFindingSettings: false
  43. UM.ColorImage
  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("Magnifier")
  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. onCollapseAllCategories:
  113. {
  114. settingsSearchTimer.stop()
  115. filter.text = "" // clear search field
  116. filter.editingFinished()
  117. definitionsModel.collapseAllCategories()
  118. }
  119. }
  120. UM.BurgerButton
  121. {
  122. id: settingVisibilityMenu
  123. anchors
  124. {
  125. verticalCenter: filterContainer.verticalCenter
  126. right: parent.right
  127. }
  128. onClicked:
  129. {
  130. settingVisibilityPresetsMenu.popup(
  131. popupContainer,
  132. -settingVisibilityPresetsMenu.width + UM.Theme.getSize("default_margin").width,
  133. settingVisibilityMenu.height
  134. )
  135. }
  136. }
  137. Item
  138. {
  139. // Work around to prevent the buttom from being rescaled if a popup is attached
  140. id: popupContainer
  141. anchors.bottom: settingVisibilityMenu.bottom
  142. anchors.right: settingVisibilityMenu.right
  143. }
  144. // Mouse area that gathers the scroll events to not propagate it to the main view.
  145. MouseArea
  146. {
  147. anchors.fill: contents
  148. acceptedButtons: Qt.AllButtons
  149. onWheel: (wheel) => { wheel.accepted = true }
  150. }
  151. ListView
  152. {
  153. id: contents
  154. maximumFlickVelocity: 2000 * screenScaleFactor
  155. flickDeceleration: 4000 * screenScaleFactor
  156. // Timer
  157. // {
  158. // id: flickTimer
  159. // interval: 50
  160. // onTriggered:
  161. // {
  162. // print("triggered")
  163. // contents.cancelFlick()
  164. // }
  165. // }
  166. //
  167. // onFlickStarted: {
  168. // print("Start Flick")
  169. // flickTimer.start()
  170. // }
  171. anchors
  172. {
  173. top: filterContainer.bottom
  174. topMargin: UM.Theme.getSize("default_margin").height
  175. bottom: parent.bottom
  176. right: parent.right
  177. left: parent.left
  178. }
  179. clip: true
  180. cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
  181. ScrollBar.vertical: UM.ScrollBar
  182. {
  183. id: scrollBar
  184. onPositionChanged: {
  185. // This removes focus from items when scrolling.
  186. // This fixes comboboxes staying open and scrolling container
  187. if (!activeFocus && !filter.activeFocus) {
  188. forceActiveFocus();
  189. }
  190. }
  191. // snapMode: ScrollBar.SnapAlways
  192. // stepSize: 10
  193. }
  194. model: UM.SettingDefinitionsModel
  195. {
  196. id: definitionsModel
  197. containerId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.definition.id: ""
  198. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  199. 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.
  200. expanded: CuraApplication.expandedCategories
  201. onExpandedChanged:
  202. {
  203. if (!findingSettings)
  204. {
  205. // Do not change expandedCategories preference while filtering settings
  206. // because all categories are expanded while filtering
  207. CuraApplication.setExpandedCategories(expanded)
  208. }
  209. }
  210. onVisibilityChanged: Cura.SettingInheritanceManager.scheduleUpdate()
  211. }
  212. property int indexWithFocus: -1
  213. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  214. delegate: Loader
  215. {
  216. id: delegate
  217. width: contents.width - (scrollBar.width + UM.Theme.getSize("narrow_margin").width)
  218. opacity: enabled ? 1 : 0
  219. enabled: provider.properties.enabled === "True"
  220. property var definition: model
  221. property var settingDefinitionsModel: definitionsModel
  222. property var propertyProvider: provider
  223. property var globalPropertyProvider: inheritStackProvider
  224. property bool externalResetHandler: false
  225. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  226. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  227. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  228. asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder"
  229. active: model.type !== undefined
  230. source:
  231. {
  232. switch(model.type)
  233. {
  234. case "int":
  235. return "SettingTextField.qml"
  236. case "[int]":
  237. return "SettingTextField.qml"
  238. case "float":
  239. return "SettingTextField.qml"
  240. case "enum":
  241. return "SettingComboBox.qml"
  242. case "extruder":
  243. return "SettingExtruder.qml"
  244. case "bool":
  245. return "SettingCheckBox.qml"
  246. case "str":
  247. return "SettingTextField.qml"
  248. case "category":
  249. return "SettingCategory.qml"
  250. case "optional_extruder":
  251. return "SettingOptionalExtruder.qml"
  252. default:
  253. return "SettingUnknown.qml"
  254. }
  255. }
  256. // Binding to ensure that the right containerstack ID is set for the provider.
  257. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  258. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  259. Binding
  260. {
  261. target: provider
  262. property: "containerStackId"
  263. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0);
  264. value:
  265. {
  266. // Associate this binding with Cura.MachineManager.activeMachine.id in the beginning so this
  267. // binding will be triggered when activeMachineId is changed too.
  268. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  269. // machine gets changed.
  270. if (!model.settable_per_extruder)
  271. {
  272. //Not settable per extruder or there only is global, so we must pick global.
  273. return contents.activeMachineId
  274. }
  275. if (inheritStackProvider.properties.limit_to_extruder !== undefined && inheritStackProvider.properties.limit_to_extruder >= 0)
  276. {
  277. //We have limit_to_extruder, so pick that stack.
  278. return Cura.ExtruderManager.extruderIds[inheritStackProvider.properties.limit_to_extruder]
  279. }
  280. if (Cura.ExtruderManager.activeExtruderStackId)
  281. {
  282. //We're on an extruder tab. Pick the current extruder.
  283. return Cura.ExtruderManager.activeExtruderStackId
  284. }
  285. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  286. return contents.activeMachineId
  287. }
  288. }
  289. // Specialty provider that only watches global_inherits (we can't filter on what property changed we get events
  290. // so we bypass that to make a dedicated provider).
  291. UM.SettingPropertyProvider
  292. {
  293. id: inheritStackProvider
  294. containerStackId: contents.activeMachineId
  295. key: model.key
  296. watchedProperties: [ "limit_to_extruder" ]
  297. }
  298. UM.SettingPropertyProvider
  299. {
  300. id: provider
  301. containerStackId: contents.activeMachineId
  302. key: model.key
  303. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  304. storeIndex: 0
  305. removeUnusedValue: model.resolve === undefined
  306. }
  307. Connections
  308. {
  309. target: item
  310. function onContextMenuRequested()
  311. {
  312. contextMenu.key = model.key
  313. contextMenu.settingVisible = model.visible
  314. contextMenu.provider = provider
  315. contextMenu.popup() //iconName: model.icon_name
  316. }
  317. function onShowTooltip(text) { base.showTooltip(delegate, Qt.point(-settingsView.x - UM.Theme.getSize("default_margin").width, 0), text) }
  318. function onHideTooltip() { base.hideTooltip() }
  319. function onShowAllHiddenInheritedSettings()
  320. {
  321. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  322. for(var i = 0; i < children_with_override.length; i++)
  323. {
  324. definitionsModel.setVisible(children_with_override[i], true)
  325. }
  326. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  327. }
  328. function onFocusReceived()
  329. {
  330. contents.indexWithFocus = index
  331. contents.positionViewAtIndex(index, ListView.Contain)
  332. }
  333. function onSetActiveFocusToNextSetting(forward)
  334. {
  335. if (forward == undefined || forward)
  336. {
  337. contents.currentIndex = contents.indexWithFocus + 1
  338. while(contents.currentItem && contents.currentItem.height <= 0)
  339. {
  340. contents.currentIndex++
  341. }
  342. if (contents.currentItem)
  343. {
  344. contents.currentItem.item.focusItem.forceActiveFocus()
  345. }
  346. }
  347. else
  348. {
  349. contents.currentIndex = contents.indexWithFocus - 1
  350. while(contents.currentItem && contents.currentItem.height <= 0)
  351. {
  352. contents.currentIndex--
  353. }
  354. if (contents.currentItem)
  355. {
  356. contents.currentItem.item.focusItem.forceActiveFocus()
  357. }
  358. }
  359. }
  360. }
  361. }
  362. Cura.Menu
  363. {
  364. id: contextMenu
  365. property string key
  366. property var provider
  367. property bool settingVisible
  368. Cura.MenuItem
  369. {
  370. //: Settings context menu action
  371. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  372. visible: machineExtruderCount.properties.value > 1
  373. enabled: contextMenu.provider !== undefined && contextMenu.provider.properties.settable_per_extruder !== "False"
  374. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  375. }
  376. Cura.MenuItem
  377. {
  378. //: Settings context menu action
  379. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  380. visible: machineExtruderCount.properties.value > 1
  381. enabled: contextMenu.provider !== undefined
  382. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  383. }
  384. Cura.MenuSeparator
  385. {
  386. visible: machineExtruderCount.properties.value > 1
  387. }
  388. Instantiator
  389. {
  390. id: customMenuItems
  391. model: Cura.SidebarCustomMenuItemsModel { }
  392. Cura.MenuItem
  393. {
  394. text: model.name
  395. onTriggered:
  396. {
  397. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  398. }
  399. }
  400. onObjectAdded: contextMenu.insertItem(index, object)
  401. onObjectRemoved: contextMenu.removeItem(object)
  402. }
  403. Cura.MenuSeparator
  404. {
  405. visible: customMenuItems.count > 0
  406. }
  407. Cura.MenuItem
  408. {
  409. //: Settings context menu action
  410. visible: !findingSettings
  411. text: catalog.i18nc("@action:menu", "Hide this setting")
  412. onTriggered:
  413. {
  414. definitionsModel.hide(contextMenu.key)
  415. }
  416. }
  417. Cura.MenuItem
  418. {
  419. //: Settings context menu action
  420. text:
  421. {
  422. if (contextMenu.settingVisible)
  423. {
  424. return catalog.i18nc("@action:menu", "Don't show this setting")
  425. }
  426. else
  427. {
  428. return catalog.i18nc("@action:menu", "Keep this setting visible")
  429. }
  430. }
  431. visible: findingSettings
  432. onTriggered:
  433. {
  434. if (contextMenu.settingVisible)
  435. {
  436. definitionsModel.hide(contextMenu.key)
  437. }
  438. else
  439. {
  440. definitionsModel.show(contextMenu.key)
  441. }
  442. }
  443. }
  444. Cura.MenuItem
  445. {
  446. //: Settings context menu action
  447. text: catalog.i18nc("@action:menu", "Configure setting visibility...")
  448. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu)
  449. }
  450. }
  451. UM.SettingPropertyProvider
  452. {
  453. id: machineExtruderCount
  454. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  455. key: "machine_extruder_count"
  456. watchedProperties: [ "value" ]
  457. storeIndex: 0
  458. }
  459. }
  460. }