SettingView.qml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. // Copyright (c) 2017 Ultimaker B.V.
  2. // Uranium 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: base;
  13. property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel()
  14. property Action configureSettings
  15. property bool findingSettings
  16. signal showTooltip(Item item, point location, string text)
  17. signal hideTooltip()
  18. ToolButton
  19. {
  20. id: settingVisibilityMenu
  21. width: height
  22. height: UM.Theme.getSize("setting_control").height
  23. anchors
  24. {
  25. topMargin: UM.Theme.getSize("thick_margin").height
  26. right: parent.right
  27. rightMargin: UM.Theme.getSize("thick_margin").width
  28. }
  29. style: ButtonStyle
  30. {
  31. background: Item {
  32. UM.RecolorImage {
  33. anchors.verticalCenter: parent.verticalCenter
  34. anchors.horizontalCenter: parent.horizontalCenter
  35. width: UM.Theme.getSize("standard_arrow").width
  36. height: UM.Theme.getSize("standard_arrow").height
  37. sourceSize.width: width
  38. sourceSize.height: width
  39. color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text")
  40. source: UM.Theme.getIcon("menu")
  41. }
  42. }
  43. label: Label{}
  44. }
  45. menu: SettingVisibilityPresetsMenu
  46. {
  47. onShowAllSettings:
  48. {
  49. definitionsModel.setAllVisible(true);
  50. filter.updateDefinitionModel();
  51. }
  52. }
  53. }
  54. Rectangle
  55. {
  56. id: filterContainer
  57. visible: true
  58. border.width: Math.round(UM.Theme.getSize("default_lining").width)
  59. border.color:
  60. {
  61. if(hoverMouseArea.containsMouse || clearFilterButton.containsMouse)
  62. {
  63. return UM.Theme.getColor("setting_control_border_highlight");
  64. }
  65. else
  66. {
  67. return UM.Theme.getColor("setting_control_border");
  68. }
  69. }
  70. color: UM.Theme.getColor("setting_control")
  71. anchors
  72. {
  73. topMargin: UM.Theme.getSize("thick_margin").height
  74. left: parent.left
  75. leftMargin: UM.Theme.getSize("default_margin").width
  76. right: settingVisibilityMenu.left
  77. rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2)
  78. }
  79. height: UM.Theme.getSize("setting_control").height
  80. Timer
  81. {
  82. id: settingsSearchTimer
  83. onTriggered: filter.editingFinished()
  84. interval: 500
  85. running: false
  86. repeat: false
  87. }
  88. TextField
  89. {
  90. id: filter;
  91. height: parent.height
  92. anchors.left: parent.left
  93. anchors.right: clearFilterButton.left
  94. anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width)
  95. placeholderText: catalog.i18nc("@label:textbox", "Search...")
  96. style: TextFieldStyle
  97. {
  98. textColor: UM.Theme.getColor("setting_control_text");
  99. placeholderTextColor: UM.Theme.getColor("setting_control_text")
  100. font: UM.Theme.getFont("default");
  101. background: Item {}
  102. }
  103. property var expandedCategories
  104. property bool lastFindingSettings: false
  105. onTextChanged:
  106. {
  107. settingsSearchTimer.restart()
  108. }
  109. onEditingFinished:
  110. {
  111. definitionsModel.filter = {"i18n_label": "*" + text};
  112. findingSettings = (text.length > 0);
  113. if(findingSettings != lastFindingSettings)
  114. {
  115. updateDefinitionModel();
  116. lastFindingSettings = findingSettings;
  117. }
  118. }
  119. Keys.onEscapePressed:
  120. {
  121. filter.text = "";
  122. }
  123. function updateDefinitionModel()
  124. {
  125. if(findingSettings)
  126. {
  127. expandedCategories = definitionsModel.expanded.slice();
  128. definitionsModel.expanded = [""]; // keep categories closed while to prevent render while making settings visible one by one
  129. definitionsModel.showAncestors = true;
  130. definitionsModel.showAll = true;
  131. definitionsModel.expanded = ["*"];
  132. }
  133. else
  134. {
  135. if(expandedCategories)
  136. {
  137. definitionsModel.expanded = expandedCategories;
  138. }
  139. definitionsModel.showAncestors = false;
  140. definitionsModel.showAll = false;
  141. }
  142. }
  143. }
  144. MouseArea
  145. {
  146. id: hoverMouseArea
  147. anchors.fill: parent
  148. hoverEnabled: true
  149. acceptedButtons: Qt.NoButton
  150. cursorShape: Qt.IBeamCursor
  151. }
  152. UM.SimpleButton
  153. {
  154. id: clearFilterButton
  155. iconSource: UM.Theme.getIcon("cross1")
  156. visible: findingSettings
  157. height: Math.round(parent.height * 0.4)
  158. width: visible ? height : 0
  159. anchors.verticalCenter: parent.verticalCenter
  160. anchors.right: parent.right
  161. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  162. color: UM.Theme.getColor("setting_control_button")
  163. hoverColor: UM.Theme.getColor("setting_control_button_hover")
  164. onClicked:
  165. {
  166. filter.text = "";
  167. filter.forceActiveFocus();
  168. }
  169. }
  170. }
  171. ScrollView
  172. {
  173. anchors.top: filterContainer.bottom;
  174. anchors.bottom: parent.bottom;
  175. anchors.right: parent.right;
  176. anchors.left: parent.left;
  177. anchors.topMargin: UM.Theme.getSize("thick_margin").height
  178. anchors.rightMargin: UM.Theme.getSize("narrow_margin").height / 3
  179. style: UM.Theme.styles.scrollview;
  180. flickableItem.flickableDirection: Flickable.VerticalFlick;
  181. __wheelAreaScrollSpeed: 75; // Scroll three lines in one scroll event
  182. ListView
  183. {
  184. id: contents
  185. spacing: Math.round(UM.Theme.getSize("default_lining").height);
  186. cacheBuffer: 1000000; // Set a large cache to effectively just cache every list item.
  187. model: UM.SettingDefinitionsModel
  188. {
  189. id: definitionsModel;
  190. containerId: Cura.MachineManager.activeDefinitionId
  191. visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
  192. 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.
  193. expanded: CuraApplication.expandedCategories
  194. onExpandedChanged:
  195. {
  196. if(!findingSettings)
  197. {
  198. // Do not change expandedCategories preference while filtering settings
  199. // because all categories are expanded while filtering
  200. CuraApplication.setExpandedCategories(expanded)
  201. }
  202. }
  203. onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate()
  204. }
  205. property var indexWithFocus: -1
  206. delegate: Loader
  207. {
  208. id: delegate
  209. width: Math.round(UM.Theme.getSize("print_setup_widget").width);
  210. height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : - contents.spacing
  211. Behavior on height { NumberAnimation { duration: 100 } }
  212. opacity: provider.properties.enabled == "True" ? 1 : 0
  213. Behavior on opacity { NumberAnimation { duration: 100 } }
  214. enabled:
  215. {
  216. if (!Cura.ExtruderManager.activeExtruderStackId && machineExtruderCount.properties.value > 1)
  217. {
  218. // disable all controls on the global tab, except categories
  219. return model.type == "category"
  220. }
  221. return provider.properties.enabled == "True"
  222. }
  223. property var definition: model
  224. property var settingDefinitionsModel: definitionsModel
  225. property var propertyProvider: provider
  226. property var globalPropertyProvider: inheritStackProvider
  227. property var externalResetHandler: false
  228. //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
  229. //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
  230. //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
  231. asynchronous: model.type != "enum" && model.type != "extruder" && model.type != "optional_extruder"
  232. active: model.type != undefined
  233. source:
  234. {
  235. switch(model.type)
  236. {
  237. case "int":
  238. return "SettingTextField.qml"
  239. case "[int]":
  240. return "SettingTextField.qml"
  241. case "float":
  242. return "SettingTextField.qml"
  243. case "enum":
  244. return "SettingComboBox.qml"
  245. case "extruder":
  246. return "SettingExtruder.qml"
  247. case "bool":
  248. return "SettingCheckBox.qml"
  249. case "str":
  250. return "SettingTextField.qml"
  251. case "category":
  252. return "SettingCategory.qml"
  253. case "optional_extruder":
  254. return "SettingOptionalExtruder.qml"
  255. default:
  256. return "SettingUnknown.qml"
  257. }
  258. }
  259. // Binding to ensure that the right containerstack ID is set for the provider.
  260. // This ensures that if a setting has a limit_to_extruder id (for instance; Support speed points to the
  261. // extruder that actually prints the support, as that is the setting we need to use to calculate the value)
  262. Binding
  263. {
  264. target: provider
  265. property: "containerStackId"
  266. when: model.settable_per_extruder || (inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0);
  267. value:
  268. {
  269. // associate this binding with Cura.MachineManager.activeMachineId in the beginning so this
  270. // binding will be triggered when activeMachineId is changed too.
  271. // Otherwise, if this value only depends on the extruderIds, it won't get updated when the
  272. // machine gets changed.
  273. var activeMachineId = Cura.MachineManager.activeMachineId;
  274. if(!model.settable_per_extruder)
  275. {
  276. //Not settable per extruder or there only is global, so we must pick global.
  277. return activeMachineId;
  278. }
  279. if(inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0)
  280. {
  281. //We have limit_to_extruder, so pick that stack.
  282. return Cura.ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)];
  283. }
  284. if(Cura.ExtruderManager.activeExtruderStackId)
  285. {
  286. //We're on an extruder tab. Pick the current extruder.
  287. return Cura.ExtruderManager.activeExtruderStackId;
  288. }
  289. //No extruder tab is selected. Pick the global stack. Shouldn't happen any more since we removed the global tab.
  290. return activeMachineId;
  291. }
  292. }
  293. // Specialty provider that only watches global_inherits (we cant filter on what property changed we get events
  294. // so we bypass that to make a dedicated provider).
  295. UM.SettingPropertyProvider
  296. {
  297. id: inheritStackProvider
  298. containerStackId: Cura.MachineManager.activeMachineId
  299. key: model.key
  300. watchedProperties: [ "limit_to_extruder" ]
  301. }
  302. UM.SettingPropertyProvider
  303. {
  304. id: provider
  305. containerStackId: Cura.MachineManager.activeMachineId
  306. key: model.key ? model.key : ""
  307. watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ]
  308. storeIndex: 0
  309. removeUnusedValue: model.resolve == undefined
  310. }
  311. Connections
  312. {
  313. target: item
  314. onContextMenuRequested:
  315. {
  316. contextMenu.key = model.key;
  317. contextMenu.settingVisible = model.visible;
  318. contextMenu.provider = provider
  319. contextMenu.popup();
  320. }
  321. onShowTooltip: base.showTooltip(delegate, { x: -UM.Theme.getSize("default_arrow").width, y: Math.round(delegate.height / 2) }, text)
  322. onHideTooltip: base.hideTooltip()
  323. onShowAllHiddenInheritedSettings:
  324. {
  325. var children_with_override = Cura.SettingInheritanceManager.getChildrenKeysWithOverride(category_id)
  326. for(var i = 0; i < children_with_override.length; i++)
  327. {
  328. definitionsModel.setVisible(children_with_override[i], true)
  329. }
  330. Cura.SettingInheritanceManager.manualRemoveOverride(category_id)
  331. }
  332. onFocusReceived:
  333. {
  334. contents.indexWithFocus = index;
  335. animateContentY.from = contents.contentY;
  336. contents.positionViewAtIndex(index, ListView.Contain);
  337. animateContentY.to = contents.contentY;
  338. animateContentY.running = true;
  339. }
  340. onSetActiveFocusToNextSetting:
  341. {
  342. if(forward == undefined || forward)
  343. {
  344. contents.currentIndex = contents.indexWithFocus + 1;
  345. while(contents.currentItem && contents.currentItem.height <= 0)
  346. {
  347. contents.currentIndex++;
  348. }
  349. if(contents.currentItem)
  350. {
  351. contents.currentItem.item.focusItem.forceActiveFocus();
  352. }
  353. }
  354. else
  355. {
  356. contents.currentIndex = contents.indexWithFocus - 1;
  357. while(contents.currentItem && contents.currentItem.height <= 0)
  358. {
  359. contents.currentIndex--;
  360. }
  361. if(contents.currentItem)
  362. {
  363. contents.currentItem.item.focusItem.forceActiveFocus();
  364. }
  365. }
  366. }
  367. }
  368. }
  369. UM.I18nCatalog { id: catalog; name: "cura"; }
  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. // visible settings have changed, so we're no longer showing a preset
  452. if (settingVisibilityPresetsModel.activePreset != "")
  453. {
  454. settingVisibilityPresetsModel.setActivePreset("custom");
  455. }
  456. }
  457. }
  458. MenuItem
  459. {
  460. //: Settings context menu action
  461. text:
  462. {
  463. if (contextMenu.settingVisible)
  464. {
  465. return catalog.i18nc("@action:menu", "Don't show this setting");
  466. }
  467. else
  468. {
  469. return catalog.i18nc("@action:menu", "Keep this setting visible");
  470. }
  471. }
  472. visible: findingSettings
  473. onTriggered:
  474. {
  475. if (contextMenu.settingVisible)
  476. {
  477. definitionsModel.hide(contextMenu.key);
  478. }
  479. else
  480. {
  481. definitionsModel.show(contextMenu.key);
  482. }
  483. // visible settings have changed, so we're no longer showing a preset
  484. if (settingVisibilityPresetsModel.activePreset != "")
  485. {
  486. settingVisibilityPresetsModel.setActivePreset("custom");
  487. }
  488. }
  489. }
  490. MenuItem
  491. {
  492. //: Settings context menu action
  493. text: catalog.i18nc("@action:menu", "Configure setting visibility...");
  494. onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu);
  495. }
  496. MenuSeparator {}
  497. MenuItem
  498. {
  499. text: catalog.i18nc("@action:inmenu", "Collapse All")
  500. onTriggered: definitionsModel.collapseAll()
  501. }
  502. MenuItem
  503. {
  504. text: catalog.i18nc("@action:inmenu", "Expand All")
  505. onTriggered: definitionsModel.expandRecursive()
  506. }
  507. }
  508. UM.SettingPropertyProvider
  509. {
  510. id: machineExtruderCount
  511. containerStackId: Cura.MachineManager.activeMachineId
  512. key: "machine_extruder_count"
  513. watchedProperties: [ "value" ]
  514. storeIndex: 0
  515. }
  516. }
  517. }
  518. }