SettingView.qml 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. // Copyright (c) 2021 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 1.1
  5. import QtQuick.Controls.Styles 1.1
  6. import QtQuick.Layouts 1.2
  7. import UM 1.2 as UM
  8. import Cura 1.0 as Cura
  9. import "../Menus"
  10. Item
  11. {
  12. id: settingsView
  13. property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel()
  14. property Action configureSettings
  15. property bool findingSettings
  16. Item
  17. {
  18. id: filterContainer
  19. anchors
  20. {
  21. top: parent.top
  22. left: parent.left
  23. right: settingVisibilityMenu.left
  24. rightMargin: UM.Theme.getSize("default_margin").width
  25. }
  26. height: UM.Theme.getSize("print_setup_big_item").height
  27. Timer
  28. {
  29. id: settingsSearchTimer
  30. onTriggered: filter.editingFinished()
  31. interval: 500
  32. running: false
  33. repeat: false
  34. }
  35. Cura.SearchBar
  36. {
  37. id: filter
  38. height: parent.height
  39. anchors.left: parent.left
  40. anchors.right: parent.right
  41. placeholderText: catalog.i18nc("@label:textbox", "Search settings") // Overwrite
  42. property var expandedCategories
  43. property bool lastFindingSettings: false
  44. onTextChanged: settingsSearchTimer.restart()
  45. onEditingFinished:
  46. {
  47. definitionsModel.filter = {"i18n_label|i18n_description" : "*" + text}
  48. findingSettings = (text.length > 0)
  49. if (findingSettings != lastFindingSettings)
  50. {
  51. updateDefinitionModel()
  52. lastFindingSettings = findingSettings
  53. }
  54. }
  55. Keys.onEscapePressed: filter.text = ""
  56. function updateDefinitionModel()
  57. {
  58. if (findingSettings)
  59. {
  60. expandedCategories = definitionsModel.expanded.slice()
  61. definitionsModel.expanded = [""] // keep categories closed while to prevent render while making settings visible one by one
  62. definitionsModel.showAncestors = true
  63. definitionsModel.showAll = true
  64. definitionsModel.expanded = ["*"]
  65. }
  66. else
  67. {
  68. if (expandedCategories)
  69. {
  70. definitionsModel.expanded = expandedCategories
  71. }
  72. definitionsModel.showAncestors = false
  73. definitionsModel.showAll = false
  74. }
  75. }
  76. }
  77. UM.SimpleButton
  78. {
  79. id: clearFilterButton
  80. iconSource: UM.Theme.getIcon("Cancel")
  81. visible: findingSettings
  82. height: Math.round(parent.height * 0.4)
  83. width: visible ? height : 0
  84. anchors.verticalCenter: parent.verticalCenter
  85. anchors.right: parent.right
  86. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  87. color: UM.Theme.getColor("setting_control_button")
  88. hoverColor: UM.Theme.getColor("setting_control_button_hover")
  89. onClicked:
  90. {
  91. filter.text = ""
  92. filter.forceActiveFocus()
  93. }
  94. }
  95. }
  96. SettingVisibilityPresetsMenu
  97. {
  98. id: settingVisibilityPresetsMenu
  99. x: settingVisibilityMenu.x
  100. y: settingVisibilityMenu.y
  101. onCollapseAllCategories:
  102. {
  103. settingsSearchTimer.stop()
  104. filter.text = "" // clear search field
  105. filter.editingFinished()
  106. definitionsModel.collapseAllCategories()
  107. }
  108. }
  109. ToolButton
  110. {
  111. id: settingVisibilityMenu
  112. anchors
  113. {
  114. top: filterContainer.top
  115. bottom: filterContainer.bottom
  116. right: parent.right
  117. rightMargin: UM.Theme.getSize("wide_margin").width
  118. }
  119. width: UM.Theme.getSize("medium_button_icon").width
  120. height: UM.Theme.getSize("medium_button_icon").height
  121. style: ButtonStyle
  122. {
  123. background: Item
  124. {
  125. UM.RecolorImage
  126. {
  127. anchors.verticalCenter: parent.verticalCenter
  128. anchors.horizontalCenter: parent.horizontalCenter
  129. width: UM.Theme.getSize("medium_button_icon").width
  130. height: UM.Theme.getSize("medium_button_icon").height
  131. sourceSize.width: width
  132. sourceSize.height: height
  133. color: control.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text")
  134. source: UM.Theme.getIcon("Hamburger")
  135. }
  136. }
  137. label: Label {}
  138. }
  139. onClicked:
  140. {
  141. settingVisibilityPresetsMenu.popup(
  142. settingVisibilityMenu,
  143. -settingVisibilityPresetsMenu.width + UM.Theme.getSize("default_margin").width,
  144. settingVisibilityMenu.height
  145. )
  146. }
  147. }
  148. // Mouse area that gathers the scroll events to not propagate it to the main view.
  149. MouseArea
  150. {
  151. anchors.fill: scrollView
  152. acceptedButtons: Qt.AllButtons
  153. onWheel: wheel.accepted = true
  154. }
  155. ScrollView
  156. {
  157. id: scrollView
  158. anchors
  159. {
  160. top: filterContainer.bottom
  161. topMargin: UM.Theme.getSize("default_margin").height
  162. bottom: parent.bottom
  163. right: parent.right
  164. left: parent.left
  165. }
  166. style: UM.Theme.styles.scrollview
  167. flickableItem.flickableDirection: Flickable.VerticalFlick
  168. __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event
  169. ListView
  170. {
  171. id: contents
  172. cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
  173. model: UM.SettingDefinitionsModel
  174. {
  175. id: definitionsModel
  176. containerId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.definition.id: ""
  177. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  178. 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.
  179. expanded: CuraApplication.expandedCategories
  180. onExpandedChanged:
  181. {
  182. if (!findingSettings)
  183. {
  184. // Do not change expandedCategories preference while filtering settings
  185. // because all categories are expanded while filtering
  186. CuraApplication.setExpandedCategories(expanded)
  187. }
  188. }
  189. onVisibilityChanged: Cura.SettingInheritanceManager.scheduleUpdate()
  190. }
  191. property int indexWithFocus: -1
  192. property double delegateHeight: UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height
  193. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  194. delegate: Loader
  195. {
  196. id: delegate
  197. width: scrollView.width
  198. height: enabled ? contents.delegateHeight: 0
  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();
  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(category_id)
  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. Menu
  376. {
  377. id: contextMenu
  378. property string key
  379. property var provider
  380. property bool settingVisible
  381. 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. 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. MenuSeparator
  398. {
  399. visible: machineExtruderCount.properties.value > 1
  400. }
  401. Instantiator
  402. {
  403. id: customMenuItems
  404. model: Cura.SidebarCustomMenuItemsModel { }
  405. MenuItem
  406. {
  407. text: model.name
  408. iconName: model.icon_name
  409. onTriggered:
  410. {
  411. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  412. }
  413. }
  414. onObjectAdded: contextMenu.insertItem(index, object)
  415. onObjectRemoved: contextMenu.removeItem(object)
  416. }
  417. MenuSeparator
  418. {
  419. visible: customMenuItems.count > 0
  420. }
  421. MenuItem
  422. {
  423. //: Settings context menu action
  424. visible: !findingSettings
  425. text: catalog.i18nc("@action:menu", "Hide this setting");
  426. onTriggered:
  427. {
  428. definitionsModel.hide(contextMenu.key)
  429. }
  430. }
  431. MenuItem
  432. {
  433. //: Settings context menu action
  434. text:
  435. {
  436. if (contextMenu.settingVisible)
  437. {
  438. return catalog.i18nc("@action:menu", "Don't show this setting");
  439. }
  440. else
  441. {
  442. return catalog.i18nc("@action:menu", "Keep this setting visible");
  443. }
  444. }
  445. visible: findingSettings
  446. onTriggered:
  447. {
  448. if (contextMenu.settingVisible)
  449. {
  450. definitionsModel.hide(contextMenu.key);
  451. }
  452. else
  453. {
  454. definitionsModel.show(contextMenu.key);
  455. }
  456. }
  457. }
  458. MenuItem
  459. {
  460. //: Settings context menu action
  461. text: catalog.i18nc("@action:menu", "Configure setting visibility...");
  462. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu);
  463. }
  464. }
  465. UM.SettingPropertyProvider
  466. {
  467. id: machineExtruderCount
  468. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  469. key: "machine_extruder_count"
  470. watchedProperties: [ "value" ]
  471. storeIndex: 0
  472. }
  473. }
  474. }
  475. }