MaterialBrandMenu.qml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // Copyright (c) 2022 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.7
  4. import QtQuick.Controls 2.4
  5. import QtQuick.Layouts 2.7
  6. import UM 1.5 as UM
  7. import Cura 1.7 as Cura
  8. /* This element is a workaround for MacOS, where it can crash in Qt6 when nested menus are closed.
  9. Instead we'll use a pop-up which doesn't seem to have that problem. */
  10. Cura.MenuItem
  11. {
  12. id: materialBrandMenu
  13. overrideShowArrow: true
  14. property var materialTypesModel
  15. text: materialTypesModel.name
  16. contentItem: MouseArea
  17. {
  18. hoverEnabled: true
  19. RowLayout
  20. {
  21. spacing: 0
  22. opacity: materialBrandMenu.enabled ? 1 : 0.5
  23. Item
  24. {
  25. // Spacer
  26. width: UM.Theme.getSize("default_margin").width
  27. }
  28. UM.Label
  29. {
  30. text: replaceText(materialBrandMenu.text)
  31. Layout.fillWidth: true
  32. Layout.fillHeight:true
  33. elide: Label.ElideRight
  34. wrapMode: Text.NoWrap
  35. }
  36. Item
  37. {
  38. Layout.fillWidth: true
  39. }
  40. Item
  41. {
  42. // Right side margin
  43. width: UM.Theme.getSize("default_margin").width
  44. }
  45. }
  46. onEntered: showTimer.restartTimer()
  47. onExited: hideTimer.restartTimer()
  48. }
  49. Timer
  50. {
  51. id: showTimer
  52. interval: 250
  53. function restartTimer()
  54. {
  55. restart();
  56. running = Qt.binding(function() { return materialBrandMenu.enabled && materialBrandMenu.contentItem.containsMouse; });
  57. hideTimer.running = false;
  58. }
  59. onTriggered: menuPopup.open()
  60. }
  61. Timer
  62. {
  63. id: hideTimer
  64. interval: 250
  65. function restartTimer() //Restart but re-evaluate the running property then.
  66. {
  67. restart();
  68. running = Qt.binding(function() { return materialBrandMenu.enabled && !materialBrandMenu.contentItem.containsMouse && !menuPopup.itemHovered > 0; });
  69. showTimer.running = false;
  70. }
  71. onTriggered: menuPopup.close()
  72. }
  73. Popup
  74. {
  75. id: menuPopup
  76. width: materialTypesList.width + padding * 2
  77. height: materialTypesList.height + padding * 2
  78. property var flipped: false
  79. x: parent.width - UM.Theme.getSize("default_lining").width
  80. y: {
  81. // Checks if popup is more than halfway down the screen AND further than 400 down (this avoids popup going off the top of screen)
  82. // If it is then the popup will push up instead of down
  83. // This fixes the popups appearing bellow the bottom of the screen.
  84. if (materialBrandMenu.parent.height / 2 < parent.y && parent.y > 400) {
  85. flipped = true
  86. return -UM.Theme.getSize("default_lining").width - height + UM.Theme.getSize("menu").height
  87. }
  88. flipped = false
  89. return -UM.Theme.getSize("default_lining").width
  90. }
  91. padding: background.border.width
  92. // Nasty hack to ensure that we can keep track if the popup contains the mouse.
  93. // Since we also want a hover for the sub items (and these events are sent async)
  94. // We have to keep a count of itemHovered (instead of just a bool)
  95. property int itemHovered: 0
  96. MouseArea
  97. {
  98. id: submenuArea
  99. anchors.fill: parent
  100. hoverEnabled: true
  101. onEntered: hideTimer.restartTimer()
  102. }
  103. background: Rectangle
  104. {
  105. color: UM.Theme.getColor("main_background")
  106. border.color: UM.Theme.getColor("lining")
  107. border.width: UM.Theme.getSize("default_lining").width
  108. }
  109. Column
  110. {
  111. id: materialTypesList
  112. spacing: 0
  113. property var brandMaterials: materialTypesModel.material_types
  114. Repeater
  115. {
  116. model: parent.brandMaterials
  117. //Use a MouseArea and Rectangle, not a button, because the button grabs mouse events which makes the parent pop-up think it's no longer being hovered.
  118. //With a custom MouseArea, we can prevent the events from being accepted.
  119. delegate: Rectangle
  120. {
  121. id: brandMaterialBase
  122. height: UM.Theme.getSize("menu").height
  123. width: UM.Theme.getSize("menu").width
  124. color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1")
  125. property var isFlipped: menuPopup.flipped
  126. RowLayout
  127. {
  128. spacing: 0
  129. opacity: materialBrandMenu.enabled ? 1 : 0.5
  130. height: parent.height
  131. width: parent.width
  132. Item
  133. {
  134. // Spacer
  135. width: UM.Theme.getSize("default_margin").width
  136. }
  137. UM.Label
  138. {
  139. text: model.name
  140. Layout.fillWidth: true
  141. Layout.fillHeight: true
  142. elide: Label.ElideRight
  143. wrapMode: Text.NoWrap
  144. }
  145. Item
  146. {
  147. Layout.fillWidth: true
  148. }
  149. UM.ColorImage
  150. {
  151. height: UM.Theme.getSize("default_arrow").height
  152. width: UM.Theme.getSize("default_arrow").width
  153. color: UM.Theme.getColor("setting_control_text")
  154. source: UM.Theme.getIcon("ChevronSingleRight")
  155. }
  156. Item
  157. {
  158. // Right side margin
  159. width: UM.Theme.getSize("default_margin").width
  160. }
  161. }
  162. MouseArea
  163. {
  164. id: materialTypeButton
  165. anchors.fill: parent
  166. hoverEnabled: true
  167. acceptedButtons: Qt.NoButton
  168. onEntered:
  169. {
  170. menuPopup.itemHovered += 1;
  171. showSubTimer.restartTimer();
  172. }
  173. onExited:
  174. {
  175. menuPopup.itemHovered -= 1;
  176. hideSubTimer.restartTimer();
  177. }
  178. }
  179. Timer
  180. {
  181. id: showSubTimer
  182. interval: 250
  183. function restartTimer()
  184. {
  185. restart();
  186. running = Qt.binding(function() { return materialTypeButton.containsMouse; });
  187. hideSubTimer.running = false;
  188. }
  189. onTriggered: colorPopup.open()
  190. }
  191. Timer
  192. {
  193. id: hideSubTimer
  194. interval: 250
  195. function restartTimer() //Restart but re-evaluate the running property then.
  196. {
  197. restart();
  198. running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; });
  199. showSubTimer.running = false;
  200. }
  201. onTriggered: colorPopup.close()
  202. }
  203. Popup
  204. {
  205. id: colorPopup
  206. width: materialColorsList.width + padding * 2
  207. height: materialColorsList.height + padding * 2
  208. x: parent.width
  209. y: {
  210. // If flipped the popup should push up rather than down from the parent
  211. if (brandMaterialBase.isFlipped) {
  212. return -height + UM.Theme.getSize("menu").height + UM.Theme.getSize("default_lining").width
  213. }
  214. return -UM.Theme.getSize("default_lining").width
  215. }
  216. property int itemHovered: 0
  217. padding: background.border.width
  218. background: Rectangle
  219. {
  220. color: UM.Theme.getColor("main_background")
  221. border.color: UM.Theme.getColor("lining")
  222. border.width: UM.Theme.getSize("default_lining").width
  223. }
  224. Column
  225. {
  226. id: materialColorsList
  227. property var brandColors: model.colors
  228. spacing: 0
  229. Repeater
  230. {
  231. model: parent.brandColors
  232. delegate: Rectangle
  233. {
  234. height: UM.Theme.getSize("menu").height
  235. width: UM.Theme.getSize("menu").width
  236. color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1")
  237. Item
  238. {
  239. opacity: materialBrandMenu.enabled ? 1 : 0.5
  240. anchors.fill: parent
  241. //Checkmark, if the material is selected.
  242. UM.ColorImage
  243. {
  244. id: checkmark
  245. visible: model.id === materialMenu.activeMaterialId
  246. height: UM.Theme.getSize("default_arrow").height
  247. width: height
  248. anchors.left: parent.left
  249. anchors.leftMargin: UM.Theme.getSize("default_margin").width
  250. anchors.verticalCenter: parent.verticalCenter
  251. source: UM.Theme.getIcon("Check", "low")
  252. color: UM.Theme.getColor("setting_control_text")
  253. }
  254. UM.Label
  255. {
  256. text: model.name
  257. anchors.left: parent.left
  258. anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_arrow").height
  259. anchors.verticalCenter: parent.verticalCenter
  260. anchors.right: parent.right
  261. anchors.rightMargin: UM.Theme.getSize("default_margin").width
  262. elide: Label.ElideRight
  263. wrapMode: Text.NoWrap
  264. }
  265. }
  266. MouseArea
  267. {
  268. id: materialColorButton
  269. anchors.fill: parent
  270. hoverEnabled: true
  271. onClicked:
  272. {
  273. Cura.MachineManager.setMaterial(extruderIndex, model.container_node);
  274. menuPopup.close();
  275. colorPopup.close();
  276. materialMenu.close();
  277. }
  278. onEntered:
  279. {
  280. menuPopup.itemHovered += 1;
  281. colorPopup.itemHovered += 1;
  282. }
  283. onExited:
  284. {
  285. menuPopup.itemHovered -= 1;
  286. colorPopup.itemHovered -= 1;
  287. }
  288. }
  289. }
  290. }
  291. }
  292. }
  293. }
  294. }
  295. }
  296. }
  297. }