PackageCard.qml 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  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.1
  6. import UM 1.6 as UM
  7. import Cura 1.6 as Cura
  8. Rectangle
  9. {
  10. property var packageData
  11. property bool expanded: false
  12. height: childrenRect.height
  13. color: UM.Theme.getColor("main_background")
  14. radius: UM.Theme.getSize("default_radius").width
  15. states:
  16. [
  17. State
  18. {
  19. name: "Folded"
  20. when: !expanded
  21. PropertyChanges
  22. {
  23. target: shortDescription
  24. visible: true
  25. }
  26. PropertyChanges
  27. {
  28. target: downloadCount
  29. visible: false
  30. }
  31. PropertyChanges
  32. {
  33. target: extendedDescription
  34. visible: false
  35. }
  36. },
  37. State
  38. {
  39. name: "Expanded"
  40. when: expanded
  41. PropertyChanges
  42. {
  43. target: shortDescription
  44. visible: false
  45. }
  46. PropertyChanges
  47. {
  48. target: downloadCount
  49. visible: true
  50. }
  51. PropertyChanges
  52. {
  53. target: extendedDescription
  54. visible: true
  55. }
  56. }
  57. ]
  58. Column
  59. {
  60. width: parent.width
  61. spacing: 0
  62. Item
  63. {
  64. width: parent.width
  65. height: UM.Theme.getSize("card").height
  66. Image
  67. {
  68. id: packageItem
  69. anchors
  70. {
  71. top: parent.top
  72. left: parent.left
  73. margins: UM.Theme.getSize("default_margin").width
  74. }
  75. width: UM.Theme.getSize("card_icon").width
  76. height: width
  77. source: packageData.iconUrl != "" ? packageData.iconUrl : "../images/placeholder.svg"
  78. }
  79. ColumnLayout
  80. {
  81. anchors
  82. {
  83. left: packageItem.right
  84. leftMargin: UM.Theme.getSize("default_margin").width
  85. right: parent.right
  86. rightMargin: UM.Theme.getSize("thick_margin").width
  87. top: parent.top
  88. topMargin: UM.Theme.getSize("narrow_margin").height
  89. }
  90. height: packageItem.height + packageItem.anchors.margins * 2
  91. // Title row.
  92. RowLayout
  93. {
  94. id: titleBar
  95. Layout.preferredWidth: parent.width
  96. Layout.preferredHeight: childrenRect.height
  97. Label
  98. {
  99. text: packageData.displayName
  100. font: UM.Theme.getFont("medium_bold")
  101. color: UM.Theme.getColor("text")
  102. verticalAlignment: Text.AlignTop
  103. }
  104. Control
  105. {
  106. Layout.preferredWidth: UM.Theme.getSize("card_tiny_icon").width
  107. Layout.preferredHeight: UM.Theme.getSize("card_tiny_icon").height
  108. enabled: packageData.isCheckedByUltimaker
  109. visible: packageData.isCheckedByUltimaker
  110. Cura.ToolTip
  111. {
  112. tooltipText:
  113. {
  114. switch(packageData.packageType)
  115. {
  116. case "plugin": return catalog.i18nc("@info", "Ultimaker Verified Plug-in");
  117. case "material": return catalog.i18nc("@info", "Ultimaker Certified Material");
  118. default: return catalog.i18nc("@info", "Ultimaker Verified Package");
  119. }
  120. }
  121. visible: parent.hovered
  122. targetPoint: Qt.point(0, Math.round(parent.y + parent.height / 4))
  123. }
  124. Rectangle
  125. {
  126. anchors.fill: parent
  127. color: UM.Theme.getColor("action_button_hovered")
  128. radius: width
  129. UM.RecolorImage
  130. {
  131. anchors.fill: parent
  132. color: UM.Theme.getColor("primary")
  133. source: packageData.packageType == "plugin" ? UM.Theme.getIcon("CheckCircle") : UM.Theme.getIcon("Certified")
  134. }
  135. }
  136. //NOTE: Can we link to something here? (Probably a static link explaining what verified is):
  137. // onClicked: Qt.openUrlExternally( XXXXXX )
  138. }
  139. Control
  140. {
  141. Layout.preferredWidth: UM.Theme.getSize("card_tiny_icon").width
  142. Layout.preferredHeight: UM.Theme.getSize("card_tiny_icon").height
  143. Layout.alignment: Qt.AlignCenter
  144. enabled: false // remove!
  145. visible: false // replace packageInfo.XXXXXX
  146. // TODO: waiting for materials card implementation
  147. Cura.ToolTip
  148. {
  149. tooltipText: "" // TODO
  150. visible: parent.hovered
  151. }
  152. UM.RecolorImage
  153. {
  154. anchors.fill: parent
  155. color: UM.Theme.getColor("primary")
  156. source: UM.Theme.getIcon("CheckCircle") // TODO
  157. }
  158. // onClicked: Qt.openUrlExternally( XXXXXX ) // TODO
  159. }
  160. Label
  161. {
  162. id: packageVersionLabel
  163. text: packageData.packageVersion
  164. font: UM.Theme.getFont("default")
  165. color: UM.Theme.getColor("text")
  166. Layout.fillWidth: true
  167. }
  168. Button
  169. {
  170. id: externalLinkButton
  171. // For some reason if i set padding, they don't match up. If i set all of them explicitly, it does work?
  172. leftPadding: UM.Theme.getSize("narrow_margin").width
  173. rightPadding: UM.Theme.getSize("narrow_margin").width
  174. topPadding: UM.Theme.getSize("narrow_margin").width
  175. bottomPadding: UM.Theme.getSize("narrow_margin").width
  176. Layout.preferredWidth: UM.Theme.getSize("card_tiny_icon").width + 2 * padding
  177. Layout.preferredHeight: UM.Theme.getSize("card_tiny_icon").width + 2 * padding
  178. contentItem: UM.RecolorImage
  179. {
  180. source: UM.Theme.getIcon("LinkExternal")
  181. color: UM.Theme.getColor("icon")
  182. implicitWidth: UM.Theme.getSize("card_tiny_icon").width
  183. implicitHeight: UM.Theme.getSize("card_tiny_icon").height
  184. }
  185. background: Rectangle
  186. {
  187. color: externalLinkButton.hovered ? UM.Theme.getColor("action_button_hovered"): "transparent"
  188. radius: externalLinkButton.width / 2
  189. }
  190. onClicked: Qt.openUrlExternally(packageData.authorInfoUrl)
  191. }
  192. }
  193. Item
  194. {
  195. id: shortDescription
  196. Layout.preferredWidth: parent.width
  197. Layout.fillHeight: true
  198. Label
  199. {
  200. id: descriptionLabel
  201. width: parent.width
  202. property real lastLineWidth: 0; //Store the width of the last line, to properly position the elision.
  203. text: packageData.description
  204. textFormat: Text.PlainText //Must be plain text, or we won't get onLineLaidOut signals. Don't auto-detect!
  205. font: UM.Theme.getFont("default")
  206. color: UM.Theme.getColor("text")
  207. maximumLineCount: 2
  208. wrapMode: Text.Wrap
  209. elide: Text.ElideRight
  210. visible: text !== ""
  211. onLineLaidOut:
  212. {
  213. if(truncated && line.isLast)
  214. {
  215. let max_line_width = parent.width - readMoreButton.width - fontMetrics.advanceWidth("… ") - 2 * UM.Theme.getSize("default_margin").width;
  216. if(line.implicitWidth > max_line_width)
  217. {
  218. line.width = max_line_width;
  219. }
  220. else
  221. {
  222. line.width = line.implicitWidth - fontMetrics.advanceWidth("…"); //Truncate the ellipsis. We're adding this ourselves.
  223. }
  224. descriptionLabel.lastLineWidth = line.implicitWidth;
  225. }
  226. }
  227. }
  228. Label
  229. {
  230. id: tripleDotLabel
  231. anchors.left: parent.left
  232. anchors.leftMargin: descriptionLabel.lastLineWidth
  233. anchors.bottom: descriptionLabel.bottom
  234. text: "… "
  235. font: descriptionLabel.font
  236. color: descriptionLabel.color
  237. visible: descriptionLabel.truncated && descriptionLabel.text !== ""
  238. }
  239. Cura.TertiaryButton
  240. {
  241. id: readMoreButton
  242. anchors.right: parent.right
  243. anchors.bottom: parent.bottom
  244. height: fontMetrics.height //Height of a single line.
  245. text: catalog.i18nc("@info", "Read more")
  246. iconSource: UM.Theme.getIcon("LinkExternal")
  247. visible: descriptionLabel.truncated && descriptionLabel.text !== ""
  248. enabled: visible
  249. leftPadding: UM.Theme.getSize("default_margin").width
  250. rightPadding: UM.Theme.getSize("wide_margin").width
  251. textFont: descriptionLabel.font
  252. isIconOnRightSide: true
  253. onClicked: Qt.openUrlExternally(packageData.packageInfoUrl)
  254. }
  255. }
  256. Row
  257. {
  258. id: downloadCount
  259. Layout.preferredWidth: parent.width
  260. Layout.fillHeight: true
  261. UM.RecolorImage
  262. {
  263. id: downloadsIcon
  264. width: UM.Theme.getSize("card_tiny_icon").width
  265. height: UM.Theme.getSize("card_tiny_icon").height
  266. source: UM.Theme.getIcon("Download")
  267. color: UM.Theme.getColor("text")
  268. }
  269. Label
  270. {
  271. anchors.verticalCenter: downloadsIcon.verticalCenter
  272. color: UM.Theme.getColor("text")
  273. font: UM.Theme.getFont("default")
  274. text: packageData.downloadCount
  275. }
  276. }
  277. // Author and action buttons.
  278. RowLayout
  279. {
  280. id: authorAndActionButton
  281. Layout.preferredWidth: parent.width
  282. Layout.preferredHeight: childrenRect.height
  283. spacing: UM.Theme.getSize("narrow_margin").width
  284. Label
  285. {
  286. id: authorBy
  287. Layout.alignment: Qt.AlignCenter
  288. text: catalog.i18nc("@label", "By")
  289. font: UM.Theme.getFont("default")
  290. color: UM.Theme.getColor("text")
  291. }
  292. Cura.TertiaryButton
  293. {
  294. Layout.fillWidth: true
  295. Layout.preferredHeight: authorBy.height
  296. Layout.alignment: Qt.AlignCenter
  297. text: packageData.authorName
  298. textFont: UM.Theme.getFont("default_bold")
  299. textColor: UM.Theme.getColor("text") // override normal link color
  300. leftPadding: 0
  301. rightPadding: 0
  302. iconSource: UM.Theme.getIcon("LinkExternal")
  303. isIconOnRightSide: true
  304. onClicked: Qt.openUrlExternally(packageData.authorInfoUrl)
  305. }
  306. ManageButton
  307. {
  308. id: enableManageButton
  309. state: packageData.stateManageEnableButton
  310. Layout.alignment: Qt.AlignTop
  311. primaryText: catalog.i18nc("@button", "Enable")
  312. busyPrimaryText: catalog.i18nc("@button", "enabling...")
  313. secondaryText: catalog.i18nc("@button", "Disable")
  314. busySecondaryText: catalog.i18nc("@button", "disabling...")
  315. enabled: !(installManageButton.busy || updateManageButton.busy)
  316. }
  317. Connections
  318. {
  319. target: enableManageButton
  320. function onClicked(primary_action)
  321. {
  322. if (primary_action)
  323. {
  324. packageData.enablePackageTriggered(packageData.packageId)
  325. }
  326. else
  327. {
  328. packageData.disablePackageTriggered(packageData.packageId)
  329. }
  330. }
  331. }
  332. ManageButton
  333. {
  334. id: installManageButton
  335. state: packageData.stateManageInstallButton
  336. Layout.alignment: Qt.AlignTop
  337. primaryText: catalog.i18nc("@button", "Install")
  338. busyPrimaryText: catalog.i18nc("@button", "installing...")
  339. secondaryText: catalog.i18nc("@button", "Uninstall")
  340. busySecondaryText: catalog.i18nc("@button", "uninstalling...")
  341. enabled: !(enableManageButton.busy || updateManageButton.busy)
  342. }
  343. Connections
  344. {
  345. target: installManageButton
  346. function onClicked(primary_action)
  347. {
  348. if (primary_action)
  349. {
  350. packageData.installPackageTriggered(packageData.packageId)
  351. }
  352. else
  353. {
  354. packageData.uninstallPackageTriggered(packageData.packageId)
  355. }
  356. }
  357. }
  358. ManageButton
  359. {
  360. id: updateManageButton
  361. state: packageData.stateManageUpdateButton
  362. Layout.alignment: Qt.AlignTop
  363. primaryText: catalog.i18nc("@button", "Update")
  364. busyPrimaryText: catalog.i18nc("@button", "updating...")
  365. enabled: !(installManageButton.busy || enableManageButton.busy)
  366. }
  367. Connections
  368. {
  369. target: updateManageButton
  370. function onClicked(primary_action)
  371. {
  372. packageData.updatePackageTriggered(packageData.packageId)
  373. }
  374. }
  375. }
  376. }
  377. }
  378. Column
  379. {
  380. id: extendedDescription
  381. width: parent.width
  382. padding: UM.Theme.getSize("default_margin").width
  383. topPadding: 0
  384. spacing: UM.Theme.getSize("default_margin").height
  385. Label
  386. {
  387. width: parent.width - parent.padding * 2
  388. text: catalog.i18nc("@header", "Description")
  389. font: UM.Theme.getFont("medium_bold")
  390. color: UM.Theme.getColor("text")
  391. elide: Text.ElideRight
  392. }
  393. Label
  394. {
  395. width: parent.width - parent.padding * 2
  396. text: packageData.formattedDescription
  397. font: UM.Theme.getFont("medium")
  398. color: UM.Theme.getColor("text")
  399. linkColor: UM.Theme.getColor("text_link")
  400. wrapMode: Text.Wrap
  401. textFormat: Text.RichText
  402. onLinkActivated: UM.UrlUtil.openUrl(link, ["http", "https"])
  403. }
  404. Column //Separate column to have no spacing between compatible printers.
  405. {
  406. id: compatiblePrinterColumn
  407. width: parent.width - parent.padding * 2
  408. visible: packageData.packageType === "material"
  409. spacing: 0
  410. Label
  411. {
  412. width: parent.width
  413. text: catalog.i18nc("@header", "Compatible printers")
  414. font: UM.Theme.getFont("medium_bold")
  415. color: UM.Theme.getColor("text")
  416. elide: Text.ElideRight
  417. }
  418. Repeater
  419. {
  420. model: packageData.compatiblePrinters
  421. Label
  422. {
  423. width: compatiblePrinterColumn.width
  424. text: modelData
  425. font: UM.Theme.getFont("medium")
  426. color: UM.Theme.getColor("text")
  427. elide: Text.ElideRight
  428. }
  429. }
  430. Label
  431. {
  432. width: parent.width
  433. visible: packageData.compatiblePrinters.length == 0
  434. text: "(" + catalog.i18nc("@info", "No compatibility information") + ")"
  435. font: UM.Theme.getFont("medium")
  436. color: UM.Theme.getColor("text")
  437. elide: Text.ElideRight
  438. }
  439. }
  440. Column
  441. {
  442. id: compatibleSupportMaterialColumn
  443. width: parent.width - parent.padding * 2
  444. visible: packageData.packageType === "material"
  445. spacing: 0
  446. Label
  447. {
  448. width: parent.width
  449. text: catalog.i18nc("@header", "Compatible support materials")
  450. font: UM.Theme.getFont("medium_bold")
  451. color: UM.Theme.getColor("text")
  452. elide: Text.ElideRight
  453. }
  454. Repeater
  455. {
  456. model: packageData.compatibleSupportMaterials
  457. Label
  458. {
  459. width: compatibleSupportMaterialColumn.width
  460. text: modelData
  461. font: UM.Theme.getFont("medium")
  462. color: UM.Theme.getColor("text")
  463. elide: Text.ElideRight
  464. }
  465. }
  466. Label
  467. {
  468. width: parent.width
  469. visible: packageData.compatibleSupportMaterials.length == 0
  470. text: "(" + catalog.i18nc("@info No materials", "None") + ")"
  471. font: UM.Theme.getFont("medium")
  472. color: UM.Theme.getColor("text")
  473. elide: Text.ElideRight
  474. }
  475. }
  476. Column
  477. {
  478. width: parent.width - parent.padding * 2
  479. visible: packageData.packageType === "material"
  480. spacing: 0
  481. Label
  482. {
  483. width: parent.width
  484. text: catalog.i18nc("@header", "Compatible with Material Station")
  485. font: UM.Theme.getFont("medium_bold")
  486. color: UM.Theme.getColor("text")
  487. elide: Text.ElideRight
  488. }
  489. Label
  490. {
  491. width: parent.width
  492. text: packageData.isCompatibleMaterialStation ? catalog.i18nc("@info", "Yes") : catalog.i18nc("@info", "No")
  493. font: UM.Theme.getFont("medium")
  494. color: UM.Theme.getColor("text")
  495. elide: Text.ElideRight
  496. }
  497. }
  498. Column
  499. {
  500. width: parent.width - parent.padding * 2
  501. visible: packageData.packageType === "material"
  502. spacing: 0
  503. Label
  504. {
  505. width: parent.width
  506. text: catalog.i18nc("@header", "Optimized for Air Manager")
  507. font: UM.Theme.getFont("medium_bold")
  508. color: UM.Theme.getColor("text")
  509. elide: Text.ElideRight
  510. }
  511. Label
  512. {
  513. width: parent.width
  514. text: packageData.isCompatibleAirManager ? catalog.i18nc("@info", "Yes") : catalog.i18nc("@info", "No")
  515. font: UM.Theme.getFont("medium")
  516. color: UM.Theme.getColor("text")
  517. elide: Text.ElideRight
  518. }
  519. }
  520. Row
  521. {
  522. id: externalButtonRow
  523. anchors.horizontalCenter: parent.horizontalCenter
  524. spacing: UM.Theme.getSize("narrow_margin").width
  525. Cura.SecondaryButton
  526. {
  527. text: packageData.packageType === "plugin" ? catalog.i18nc("@button", "Visit plug-in website") : catalog.i18nc("@button", "Website")
  528. iconSource: UM.Theme.getIcon("Globe")
  529. outlineColor: "transparent"
  530. onClicked: Qt.openUrlExternally(packageData.packageInfoUrl)
  531. }
  532. Cura.SecondaryButton
  533. {
  534. visible: packageData.packageType === "material"
  535. text: catalog.i18nc("@button", "Buy spool")
  536. iconSource: UM.Theme.getIcon("ShoppingCart")
  537. outlineColor: "transparent"
  538. onClicked: Qt.openUrlExternally(packageData.whereToBuy)
  539. }
  540. Cura.SecondaryButton
  541. {
  542. visible: packageData.packageType === "material"
  543. text: catalog.i18nc("@button", "Safety datasheet")
  544. iconSource: UM.Theme.getIcon("Warning")
  545. outlineColor: "transparent"
  546. onClicked: Qt.openUrlExternally(packageData.safetyDataSheet)
  547. }
  548. Cura.SecondaryButton
  549. {
  550. visible: packageData.packageType === "material"
  551. text: catalog.i18nc("@button", "Technical datasheet")
  552. iconSource: UM.Theme.getIcon("DocumentFilled")
  553. outlineColor: "transparent"
  554. onClicked: Qt.openUrlExternally(packageData.technicalDataSheet)
  555. }
  556. }
  557. }
  558. }
  559. FontMetrics
  560. {
  561. id: fontMetrics
  562. font: UM.Theme.getFont("default")
  563. }
  564. }