SettingView.qml 26 KB

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