SettingView.qml 21 KB

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