Marketplace.qml 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright (c) 2021 Ultimaker B.V.
  2. // Cura is released under the terms of the LGPLv3 or higher.
  3. import QtQuick 2.15
  4. import QtQuick.Controls 2.15
  5. import QtQuick.Layouts 1.15
  6. import QtQuick.Window 2.2
  7. import UM 1.5 as UM
  8. import Cura 1.6 as Cura
  9. Window
  10. {
  11. id: marketplaceDialog
  12. property variant catalog: UM.I18nCatalog { name: "cura" }
  13. signal searchStringChanged(string new_search)
  14. property alias showOnboadBanner: onBoardBanner.visible
  15. property alias showSearchHeader: searchHeader.visible
  16. property alias pageContentsSource: content.source
  17. minimumWidth: UM.Theme.getSize("modal_window_minimum").width
  18. minimumHeight: UM.Theme.getSize("modal_window_minimum").height
  19. width: minimumWidth
  20. height: minimumHeight
  21. onVisibleChanged:
  22. {
  23. while(contextStack.depth > 1)
  24. {
  25. contextStack.pop(); //Do NOT use the StackView.Immediate transition here, since it causes the window to stay empty. Seemingly a Qt bug: https://bugreports.qt.io/browse/QTBUG-60670?
  26. }
  27. }
  28. Connections
  29. {
  30. target: Cura.API.account
  31. function onLoginStateChanged()
  32. {
  33. close();
  34. }
  35. }
  36. title: "Marketplace" //Seen by Ultimaker as a brand name, so this doesn't get translated.
  37. modality: Qt.NonModal
  38. // Background color
  39. Rectangle
  40. {
  41. anchors.fill: parent
  42. color: UM.Theme.getColor("main_background")
  43. }
  44. //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility.
  45. StackView
  46. {
  47. id: contextStack
  48. anchors.fill: parent
  49. initialItem: packageBrowse
  50. ColumnLayout
  51. {
  52. id: packageBrowse
  53. spacing: UM.Theme.getSize("narrow_margin").height
  54. // Page title.
  55. Item
  56. {
  57. Layout.preferredWidth: parent.width
  58. Layout.preferredHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
  59. UM.Label
  60. {
  61. id: pageTitle
  62. anchors
  63. {
  64. left: parent.left
  65. leftMargin: UM.Theme.getSize("default_margin").width
  66. right: parent.right
  67. rightMargin: UM.Theme.getSize("default_margin").width
  68. bottom: parent.bottom
  69. }
  70. font: UM.Theme.getFont("large")
  71. text: content.item ? content.item.pageTitle: catalog.i18nc("@title", "Loading...")
  72. }
  73. }
  74. OnboardBanner
  75. {
  76. id: onBoardBanner
  77. visible: content.item && content.item.bannerVisible
  78. text: content.item && content.item.bannerText
  79. icon: content.item && content.item.bannerIcon
  80. onRemove: content.item && content.item.onRemoveBanner
  81. readMoreUrl: content.item && content.item.bannerReadMoreUrl
  82. Layout.fillWidth: true
  83. Layout.leftMargin: UM.Theme.getSize("default_margin").width
  84. Layout.rightMargin: UM.Theme.getSize("default_margin").width
  85. }
  86. // Search & Top-Level Tabs
  87. Item
  88. {
  89. id: searchHeader
  90. implicitHeight: childrenRect.height
  91. implicitWidth: parent.width - 2 * UM.Theme.getSize("default_margin").width
  92. Layout.alignment: Qt.AlignHCenter
  93. RowLayout
  94. {
  95. width: parent.width
  96. height: UM.Theme.getSize("button_icon").height + UM.Theme.getSize("default_margin").height
  97. spacing: UM.Theme.getSize("thin_margin").width
  98. Cura.SearchBar
  99. {
  100. id: searchBar
  101. implicitHeight: UM.Theme.getSize("button_icon").height
  102. Layout.fillWidth: true
  103. onTextEdited: searchStringChanged(text)
  104. }
  105. // Page selection.
  106. TabBar
  107. {
  108. id: pageSelectionTabBar
  109. Layout.alignment: Qt.AlignRight
  110. height: UM.Theme.getSize("button_icon").height
  111. spacing: 0
  112. background: Rectangle { color: "transparent" }
  113. currentIndex: manager.tabShown
  114. onCurrentIndexChanged:
  115. {
  116. manager.tabShown = currentIndex
  117. searchBar.text = "";
  118. searchBar.visible = currentItem.hasSearch;
  119. content.source = currentItem.sourcePage;
  120. }
  121. PackageTypeTab
  122. {
  123. id: pluginTabText
  124. width: implicitWidth
  125. text: catalog.i18nc("@button", "Plugins")
  126. property string sourcePage: "Plugins.qml"
  127. property bool hasSearch: true
  128. }
  129. PackageTypeTab
  130. {
  131. id: materialsTabText
  132. width: implicitWidth
  133. text: catalog.i18nc("@button", "Materials")
  134. property string sourcePage: "Materials.qml"
  135. property bool hasSearch: true
  136. }
  137. ManagePackagesButton
  138. {
  139. property string sourcePage: "ManagedPackages.qml"
  140. property bool hasSearch: false
  141. Cura.NotificationIcon
  142. {
  143. anchors
  144. {
  145. horizontalCenter: parent.right
  146. verticalCenter: parent.top
  147. }
  148. visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0
  149. labelText:
  150. {
  151. const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length
  152. return itemCount > 9 ? "9+" : itemCount
  153. }
  154. }
  155. }
  156. }
  157. }
  158. }
  159. FontMetrics
  160. {
  161. id: fontMetrics
  162. font: UM.Theme.getFont("default")
  163. }
  164. Cura.TertiaryButton
  165. {
  166. text: catalog.i18nc("@info", "Search in the browser")
  167. iconSource: UM.Theme.getIcon("LinkExternal")
  168. visible: pageSelectionTabBar.currentItem.hasSearch && searchHeader.visible
  169. isIconOnRightSide: true
  170. height: fontMetrics.height
  171. textFont: fontMetrics.font
  172. textColor: UM.Theme.getColor("text")
  173. onClicked: content.item && Qt.openUrlExternally(content.item.searchInBrowserUrl)
  174. }
  175. // Page contents.
  176. Rectangle
  177. {
  178. Layout.preferredWidth: parent.width
  179. Layout.fillHeight: true
  180. color: UM.Theme.getColor("detail_background")
  181. // Page contents.
  182. Loader
  183. {
  184. id: content
  185. anchors.fill: parent
  186. anchors.margins: UM.Theme.getSize("default_margin").width
  187. source: "Plugins.qml"
  188. Connections
  189. {
  190. target: content
  191. function onLoaded()
  192. {
  193. pageTitle.text = content.item.pageTitle
  194. searchStringChanged.connect(handleSearchStringChanged)
  195. }
  196. function handleSearchStringChanged(new_search)
  197. {
  198. content.item.model.searchString = new_search
  199. }
  200. }
  201. }
  202. }
  203. }
  204. }
  205. Rectangle
  206. {
  207. height: quitButton.height + 2 * UM.Theme.getSize("default_margin").width
  208. color: UM.Theme.getColor("primary")
  209. visible: manager.showRestartNotification
  210. anchors
  211. {
  212. left: parent.left
  213. right: parent.right
  214. bottom: parent.bottom
  215. }
  216. RowLayout
  217. {
  218. anchors
  219. {
  220. left: parent.left
  221. right: parent.right
  222. verticalCenter: parent.verticalCenter
  223. margins: UM.Theme.getSize("default_margin").width
  224. }
  225. spacing: UM.Theme.getSize("default_margin").width
  226. UM.ColorImage
  227. {
  228. id: bannerIcon
  229. source: UM.Theme.getIcon("Plugin")
  230. color: UM.Theme.getColor("primary_button_text")
  231. implicitWidth: UM.Theme.getSize("banner_icon_size").width
  232. implicitHeight: UM.Theme.getSize("banner_icon_size").height
  233. }
  234. Text
  235. {
  236. color: UM.Theme.getColor("primary_button_text")
  237. text: catalog.i18nc("@button", "In order to use the package you will need to restart Cura")
  238. font: UM.Theme.getFont("default")
  239. renderType: Text.NativeRendering
  240. Layout.fillWidth: true
  241. }
  242. Cura.SecondaryButton
  243. {
  244. id: quitButton
  245. text: catalog.i18nc("@info:button, %1 is the application name", "Quit %1").arg(CuraApplication.applicationDisplayName)
  246. onClicked:
  247. {
  248. marketplaceDialog.hide();
  249. CuraApplication.checkAndExitApplication();
  250. }
  251. }
  252. }
  253. }
  254. Rectangle
  255. {
  256. color: UM.Theme.getColor("main_background")
  257. anchors.fill: parent
  258. visible: !Cura.API.account.isLoggedIn && CuraApplication.isEnterprise
  259. UM.Label
  260. {
  261. id: signInLabel
  262. anchors.centerIn: parent
  263. width: Math.round(UM.Theme.getSize("modal_window_minimum").width / 2.5)
  264. text: catalog.i18nc("@description","Please sign in to get verified plugins and materials for UltiMaker Cura Enterprise")
  265. horizontalAlignment: Text.AlignHCenter
  266. }
  267. Cura.PrimaryButton
  268. {
  269. id: loginButton
  270. width: UM.Theme.getSize("account_button").width
  271. height: UM.Theme.getSize("account_button").height
  272. anchors.horizontalCenter: parent.horizontalCenter
  273. anchors.top: signInLabel.bottom
  274. anchors.topMargin: UM.Theme.getSize("default_margin").height * 2
  275. text: catalog.i18nc("@button", "Sign in")
  276. fixedWidthMode: true
  277. onClicked: Cura.API.account.login()
  278. }
  279. }
  280. }