SettingView.qml 22 KB

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