SettingView.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. // Copyright (c) 2019 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": "*" + 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. ToolButton
  134. {
  135. id: settingVisibilityMenu
  136. anchors
  137. {
  138. top: filterContainer.top
  139. bottom: filterContainer.bottom
  140. right: parent.right
  141. rightMargin: UM.Theme.getSize("wide_margin").width
  142. }
  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("standard_arrow").width
  152. height: UM.Theme.getSize("standard_arrow").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("menu")
  157. }
  158. }
  159. label: Label {}
  160. }
  161. menu: SettingVisibilityPresetsMenu
  162. {
  163. onCollapseAllCategories:
  164. {
  165. settingsSearchTimer.stop()
  166. filter.text = "" // clear search field
  167. filter.editingFinished()
  168. definitionsModel.collapseAllCategories()
  169. }
  170. }
  171. }
  172. // Mouse area that gathers the scroll events to not propagate it to the main view.
  173. MouseArea
  174. {
  175. anchors.fill: scrollView
  176. acceptedButtons: Qt.AllButtons
  177. onWheel: wheel.accepted = true
  178. }
  179. ScrollView
  180. {
  181. id: scrollView
  182. anchors
  183. {
  184. top: filterContainer.bottom
  185. topMargin: UM.Theme.getSize("default_margin").height
  186. bottom: parent.bottom
  187. right: parent.right
  188. left: parent.left
  189. }
  190. style: UM.Theme.styles.scrollview
  191. flickableItem.flickableDirection: Flickable.VerticalFlick
  192. __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event
  193. ListView
  194. {
  195. id: contents
  196. cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item.
  197. model: UM.SettingDefinitionsModel
  198. {
  199. id: definitionsModel
  200. containerId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.definition.id: ""
  201. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  202. 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.
  203. expanded: CuraApplication.expandedCategories
  204. onExpandedChanged:
  205. {
  206. if (!findingSettings)
  207. {
  208. // Do not change expandedCategories preference while filtering settings
  209. // because all categories are expanded while filtering
  210. CuraApplication.setExpandedCategories(expanded)
  211. }
  212. }
  213. onVisibilityChanged: Cura.SettingInheritanceManager.scheduleUpdate()
  214. }
  215. property int indexWithFocus: -1
  216. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  217. delegate: Loader
  218. {
  219. id: delegate
  220. width: scrollView.width
  221. height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height : 0
  222. Behavior on height { NumberAnimation { duration: 100 } }
  223. opacity: provider.properties.enabled === "True" ? 1 : 0
  224. Behavior on opacity { NumberAnimation { duration: 100 } }
  225. enabled:
  226. {
  227. if (!Cura.ExtruderManager.activeExtruderStackId && machineExtruderCount.properties.value > 1)
  228. {
  229. // disable all controls on the global tab, except categories
  230. return model.type === "category"
  231. }
  232. return provider.properties.enabled === "True"
  233. }
  234. property var definition: model
  235. property var settingDefinitionsModel: definitionsModel
  236. property var propertyProvider: provider
  237. property var globalPropertyProvider: inheritStackProvider
  238. property bool externalResetHandler: false
  239. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  240. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  241. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  242. asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder"
  243. active: model.type !== undefined
  244. source:
  245. {
  246. switch(model.type)
  247. {
  248. case "int":
  249. return "SettingTextField.qml"
  250. case "[int]":
  251. return "SettingTextField.qml"
  252. case "float":
  253. return "SettingTextField.qml"
  254. case "enum":
  255. return "SettingComboBox.qml"
  256. case "extruder":
  257. return "SettingExtruder.qml"
  258. case "bool":
  259. return "SettingCheckBox.qml"
  260. case "str":
  261. return "SettingTextField.qml"
  262. case "category":
  263. return "SettingCategory.qml"
  264. case "optional_extruder":
  265. return "SettingOptionalExtruder.qml"
  266. default:
  267. return "SettingUnknown.qml"
  268. }
  269. }
  270. // Binding to ensure that the right containerstack ID is set for the provider.
  271. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  272. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  273. Binding
  274. {
  275. target: provider
  276. property: "containerStackId"
  277. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder !== null && inheritStackProvider.properties.limit_to_extruder >= 0);
  278. value:
  279. {
  280. // Associate this binding with Cura.MachineManager.activeMachine.id in the beginning so this
  281. // binding will be triggered when activeMachineId is changed too.
  282. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  283. // machine gets changed.
  284. if (!model.settable_per_extruder)
  285. {
  286. //Not settable per extruder or there only is global, so we must pick global.
  287. return contents.activeMachineId
  288. }
  289. if (inheritStackProvider.properties.limit_to_extruder !== null && inheritStackProvider.properties.limit_to_extruder >= 0)
  290. {
  291. //We have limit_to_extruder, so pick that stack.
  292. return Cura.ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)];
  293. }
  294. if (Cura.ExtruderManager.activeExtruderStackId)
  295. {
  296. //We're on an extruder tab. Pick the current extruder.
  297. return Cura.ExtruderManager.activeExtruderStackId;
  298. }
  299. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  300. return contents.activeMachineId
  301. }
  302. }
  303. // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events
  304. // so we bypass that to make a dedicated provider).
  305. UM.SettingPropertyProvider
  306. {
  307. id: inheritStackProvider
  308. containerStackId: contents.activeMachineId
  309. key: model.key
  310. watchedProperties: [ "limit_to_extruder" ]
  311. }
  312. UM.SettingPropertyProvider
  313. {
  314. id: provider
  315. containerStackId: contents.activeMachineId
  316. key: model.key ? model.key : ""
  317. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  318. storeIndex: 0
  319. removeUnusedValue: model.resolve === undefined
  320. }
  321. Connections
  322. {
  323. target: item
  324. onContextMenuRequested:
  325. {
  326. contextMenu.key = model.key;
  327. contextMenu.settingVisible = model.visible;
  328. contextMenu.provider = provider
  329. contextMenu.popup();
  330. }
  331. onShowTooltip: base.showTooltip(delegate, Qt.point(-settingsView.x - UM.Theme.getSize("default_margin").width, 0), text)
  332. onHideTooltip: base.hideTooltip()
  333. onShowAllHiddenInheritedSettings:
  334. {
  335. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  336. for(var i = 0; i < children_with_override.length; i++)
  337. {
  338. definitionsModel.setVisible(children_with_override[i], true)
  339. }
  340. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  341. }
  342. onFocusReceived:
  343. {
  344. contents.indexWithFocus = index;
  345. animateContentY.from = contents.contentY;
  346. contents.positionViewAtIndex(index, ListView.Contain);
  347. animateContentY.to = contents.contentY;
  348. animateContentY.running = true;
  349. }
  350. onSetActiveFocusToNextSetting:
  351. {
  352. if (forward == undefined || forward)
  353. {
  354. contents.currentIndex = contents.indexWithFocus + 1;
  355. while(contents.currentItem && contents.currentItem.height <= 0)
  356. {
  357. contents.currentIndex++;
  358. }
  359. if (contents.currentItem)
  360. {
  361. contents.currentItem.item.focusItem.forceActiveFocus();
  362. }
  363. }
  364. else
  365. {
  366. contents.currentIndex = contents.indexWithFocus - 1;
  367. while(contents.currentItem && contents.currentItem.height <= 0)
  368. {
  369. contents.currentIndex--;
  370. }
  371. if (contents.currentItem)
  372. {
  373. contents.currentItem.item.focusItem.forceActiveFocus();
  374. }
  375. }
  376. }
  377. }
  378. }
  379. NumberAnimation {
  380. id: animateContentY
  381. target: contents
  382. property: "contentY"
  383. duration: 50
  384. }
  385. add: Transition {
  386. SequentialAnimation {
  387. NumberAnimation { properties: "height"; from: 0; duration: 100 }
  388. NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
  389. }
  390. }
  391. remove: Transition {
  392. SequentialAnimation {
  393. NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
  394. NumberAnimation { properties: "height"; to: 0; duration: 100 }
  395. }
  396. }
  397. addDisplaced: Transition {
  398. NumberAnimation { properties: "x,y"; duration: 100 }
  399. }
  400. removeDisplaced: Transition {
  401. SequentialAnimation {
  402. PauseAnimation { duration: 100; }
  403. NumberAnimation { properties: "x,y"; duration: 100 }
  404. }
  405. }
  406. Menu
  407. {
  408. id: contextMenu
  409. property string key
  410. property var provider
  411. property bool settingVisible
  412. MenuItem
  413. {
  414. //: Settings context menu action
  415. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  416. visible: machineExtruderCount.properties.value > 1
  417. enabled: contextMenu.provider !== undefined && contextMenu.provider.properties.settable_per_extruder !== "False"
  418. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  419. }
  420. MenuItem
  421. {
  422. //: Settings context menu action
  423. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  424. visible: machineExtruderCount.properties.value > 1
  425. enabled: contextMenu.provider !== undefined
  426. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  427. }
  428. MenuSeparator
  429. {
  430. visible: machineExtruderCount.properties.value > 1
  431. }
  432. Instantiator
  433. {
  434. id: customMenuItems
  435. model: Cura.SidebarCustomMenuItemsModel { }
  436. MenuItem
  437. {
  438. text: model.name
  439. iconName: model.icon_name
  440. onTriggered:
  441. {
  442. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  443. }
  444. }
  445. onObjectAdded: contextMenu.insertItem(index, object)
  446. onObjectRemoved: contextMenu.removeItem(object)
  447. }
  448. MenuSeparator
  449. {
  450. visible: customMenuItems.count > 0
  451. }
  452. MenuItem
  453. {
  454. //: Settings context menu action
  455. visible: !findingSettings
  456. text: catalog.i18nc("@action:menu", "Hide this setting");
  457. onTriggered:
  458. {
  459. definitionsModel.hide(contextMenu.key)
  460. }
  461. }
  462. MenuItem
  463. {
  464. //: Settings context menu action
  465. text:
  466. {
  467. if (contextMenu.settingVisible)
  468. {
  469. return catalog.i18nc("@action:menu", "Don't show this setting");
  470. }
  471. else
  472. {
  473. return catalog.i18nc("@action:menu", "Keep this setting visible");
  474. }
  475. }
  476. visible: findingSettings
  477. onTriggered:
  478. {
  479. if (contextMenu.settingVisible)
  480. {
  481. definitionsModel.hide(contextMenu.key);
  482. }
  483. else
  484. {
  485. definitionsModel.show(contextMenu.key);
  486. }
  487. }
  488. }
  489. MenuItem
  490. {
  491. //: Settings context menu action
  492. text: catalog.i18nc("@action:menu", "Configure setting visibility...");
  493. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu);
  494. }
  495. }
  496. UM.SettingPropertyProvider
  497. {
  498. id: machineExtruderCount
  499. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  500. key: "machine_extruder_count"
  501. watchedProperties: [ "value" ]
  502. storeIndex: 0
  503. }
  504. }
  505. }
  506. }