SettingView.qml 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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|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. 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 double delegateHeight: UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height
  217. property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  218. delegate: Loader
  219. {
  220. id: delegate
  221. width: scrollView.width
  222. height: enabled ? contents.delegateHeight: 0
  223. Behavior on height { NumberAnimation { duration: 100 } }
  224. opacity: enabled ? 1 : 0
  225. Behavior on opacity { NumberAnimation { duration: 100 } }
  226. enabled: provider.properties.enabled === "True"
  227. property var definition: model
  228. property var settingDefinitionsModel: definitionsModel
  229. property var propertyProvider: provider
  230. property var globalPropertyProvider: inheritStackProvider
  231. property bool externalResetHandler: false
  232. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  233. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  234. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  235. asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder"
  236. active: model.type !== undefined
  237. source:
  238. {
  239. switch(model.type)
  240. {
  241. case "int":
  242. return "SettingTextField.qml"
  243. case "[int]":
  244. return "SettingTextField.qml"
  245. case "float":
  246. return "SettingTextField.qml"
  247. case "enum":
  248. return "SettingComboBox.qml"
  249. case "extruder":
  250. return "SettingExtruder.qml"
  251. case "bool":
  252. return "SettingCheckBox.qml"
  253. case "str":
  254. return "SettingTextField.qml"
  255. case "category":
  256. return "SettingCategory.qml"
  257. case "optional_extruder":
  258. return "SettingOptionalExtruder.qml"
  259. default:
  260. return "SettingUnknown.qml"
  261. }
  262. }
  263. // Binding to ensure that the right containerstack ID is set for the provider.
  264. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  265. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  266. Binding
  267. {
  268. target: provider
  269. property: "containerStackId"
  270. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder !== null && inheritStackProvider.properties.limit_to_extruder >= 0);
  271. value:
  272. {
  273. // Associate this binding with Cura.MachineManager.activeMachine.id in the beginning so this
  274. // binding will be triggered when activeMachineId is changed too.
  275. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  276. // machine gets changed.
  277. if (!model.settable_per_extruder)
  278. {
  279. //Not settable per extruder or there only is global, so we must pick global.
  280. return contents.activeMachineId
  281. }
  282. if (inheritStackProvider.properties.limit_to_extruder !== null && inheritStackProvider.properties.limit_to_extruder >= 0)
  283. {
  284. //We have limit_to_extruder, so pick that stack.
  285. return Cura.ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)];
  286. }
  287. if (Cura.ExtruderManager.activeExtruderStackId)
  288. {
  289. //We're on an extruder tab. Pick the current extruder.
  290. return Cura.ExtruderManager.activeExtruderStackId;
  291. }
  292. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  293. return contents.activeMachineId
  294. }
  295. }
  296. // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events
  297. // so we bypass that to make a dedicated provider).
  298. UM.SettingPropertyProvider
  299. {
  300. id: inheritStackProvider
  301. containerStackId: contents.activeMachineId
  302. key: model.key
  303. watchedProperties: [ "limit_to_extruder" ]
  304. }
  305. UM.SettingPropertyProvider
  306. {
  307. id: provider
  308. containerStackId: contents.activeMachineId
  309. key: model.key
  310. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  311. storeIndex: 0
  312. removeUnusedValue: model.resolve === undefined
  313. }
  314. Connections
  315. {
  316. target: item
  317. onContextMenuRequested:
  318. {
  319. contextMenu.key = model.key;
  320. contextMenu.settingVisible = model.visible;
  321. contextMenu.provider = provider
  322. contextMenu.popup();
  323. }
  324. onShowTooltip: base.showTooltip(delegate, Qt.point(-settingsView.x - UM.Theme.getSize("default_margin").width, 0), text)
  325. onHideTooltip: base.hideTooltip()
  326. onShowAllHiddenInheritedSettings:
  327. {
  328. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  329. for(var i = 0; i < children_with_override.length; i++)
  330. {
  331. definitionsModel.setVisible(children_with_override[i], true)
  332. }
  333. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  334. }
  335. onFocusReceived:
  336. {
  337. contents.indexWithFocus = index;
  338. animateContentY.from = contents.contentY;
  339. contents.positionViewAtIndex(index, ListView.Contain);
  340. animateContentY.to = contents.contentY;
  341. animateContentY.running = true;
  342. }
  343. onSetActiveFocusToNextSetting:
  344. {
  345. if (forward == undefined || forward)
  346. {
  347. contents.currentIndex = contents.indexWithFocus + 1;
  348. while(contents.currentItem && contents.currentItem.height <= 0)
  349. {
  350. contents.currentIndex++;
  351. }
  352. if (contents.currentItem)
  353. {
  354. contents.currentItem.item.focusItem.forceActiveFocus();
  355. }
  356. }
  357. else
  358. {
  359. contents.currentIndex = contents.indexWithFocus - 1;
  360. while(contents.currentItem && contents.currentItem.height <= 0)
  361. {
  362. contents.currentIndex--;
  363. }
  364. if (contents.currentItem)
  365. {
  366. contents.currentItem.item.focusItem.forceActiveFocus();
  367. }
  368. }
  369. }
  370. }
  371. }
  372. NumberAnimation {
  373. id: animateContentY
  374. target: contents
  375. property: "contentY"
  376. duration: 50
  377. }
  378. add: Transition {
  379. SequentialAnimation {
  380. NumberAnimation { properties: "height"; from: 0; duration: 100 }
  381. NumberAnimation { properties: "opacity"; from: 0; duration: 100 }
  382. }
  383. }
  384. remove: Transition {
  385. SequentialAnimation {
  386. NumberAnimation { properties: "opacity"; to: 0; duration: 100 }
  387. NumberAnimation { properties: "height"; to: 0; duration: 100 }
  388. }
  389. }
  390. addDisplaced: Transition {
  391. NumberAnimation { properties: "x,y"; duration: 100 }
  392. }
  393. removeDisplaced: Transition {
  394. SequentialAnimation {
  395. PauseAnimation { duration: 100; }
  396. NumberAnimation { properties: "x,y"; duration: 100 }
  397. }
  398. }
  399. Menu
  400. {
  401. id: contextMenu
  402. property string key
  403. property var provider
  404. property bool settingVisible
  405. MenuItem
  406. {
  407. //: Settings context menu action
  408. text: catalog.i18nc("@action:menu", "Copy value to all extruders")
  409. visible: machineExtruderCount.properties.value > 1
  410. enabled: contextMenu.provider !== undefined && contextMenu.provider.properties.settable_per_extruder !== "False"
  411. onTriggered: Cura.MachineManager.copyValueToExtruders(contextMenu.key)
  412. }
  413. MenuItem
  414. {
  415. //: Settings context menu action
  416. text: catalog.i18nc("@action:menu", "Copy all changed values to all extruders")
  417. visible: machineExtruderCount.properties.value > 1
  418. enabled: contextMenu.provider !== undefined
  419. onTriggered: Cura.MachineManager.copyAllValuesToExtruders()
  420. }
  421. MenuSeparator
  422. {
  423. visible: machineExtruderCount.properties.value > 1
  424. }
  425. Instantiator
  426. {
  427. id: customMenuItems
  428. model: Cura.SidebarCustomMenuItemsModel { }
  429. MenuItem
  430. {
  431. text: model.name
  432. iconName: model.icon_name
  433. onTriggered:
  434. {
  435. customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
  436. }
  437. }
  438. onObjectAdded: contextMenu.insertItem(index, object)
  439. onObjectRemoved: contextMenu.removeItem(object)
  440. }
  441. MenuSeparator
  442. {
  443. visible: customMenuItems.count > 0
  444. }
  445. MenuItem
  446. {
  447. //: Settings context menu action
  448. visible: !findingSettings
  449. text: catalog.i18nc("@action:menu", "Hide this setting");
  450. onTriggered:
  451. {
  452. definitionsModel.hide(contextMenu.key)
  453. }
  454. }
  455. MenuItem
  456. {
  457. //: Settings context menu action
  458. text:
  459. {
  460. if (contextMenu.settingVisible)
  461. {
  462. return catalog.i18nc("@action:menu", "Don't show this setting");
  463. }
  464. else
  465. {
  466. return catalog.i18nc("@action:menu", "Keep this setting visible");
  467. }
  468. }
  469. visible: findingSettings
  470. onTriggered:
  471. {
  472. if (contextMenu.settingVisible)
  473. {
  474. definitionsModel.hide(contextMenu.key);
  475. }
  476. else
  477. {
  478. definitionsModel.show(contextMenu.key);
  479. }
  480. }
  481. }
  482. MenuItem
  483. {
  484. //: Settings context menu action
  485. text: catalog.i18nc("@action:menu", "Configure setting visibility...");
  486. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu);
  487. }
  488. }
  489. UM.SettingPropertyProvider
  490. {
  491. id: machineExtruderCount
  492. containerStackId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : ""
  493. key: "machine_extruder_count"
  494. watchedProperties: [ "value" ]
  495. storeIndex: 0
  496. }
  497. }
  498. }
  499. }