diff --git a/plugins/Marketplace/PackageModel.py b/plugins/Marketplace/PackageModel.py index e57a402fd6..0e73cac3b7 100644 --- a/plugins/Marketplace/PackageModel.py +++ b/plugins/Marketplace/PackageModel.py @@ -4,10 +4,12 @@ from PyQt5.QtCore import pyqtProperty, QObject from typing import Any, Dict, Optional -from UM.i18n import i18nCatalog # To translate placeholder names if data is not present. +from UM.Util import parseBool +from UM.i18n import i18nCatalog # To translate placeholder names if data is not present. catalog = i18nCatalog("cura") + class PackageModel(QObject): """ Represents a package, containing all the relevant information to be displayed about a package. @@ -25,17 +27,65 @@ class PackageModel(QObject): """ super().__init__(parent) self._package_id = package_data.get("package_id", "UnknownPackageId") + + self._icon_url = package_data.get("icon_url", "") self._display_name = package_data.get("display_name", catalog.i18nc("@label:property", "Unknown Package")) + self._is_verified = "verified" in package_data.get("tags", []) + self._package_version = package_data.get("package_version", "") # Display purpose, no need for 'UM.Version'. + self._package_info_url = package_data.get("website", "") # Not to be confused with 'download_url'. + self._download_count = package_data.get("download_count", 0) + self._description = package_data.get("description", "") + + self._download_url = package_data.get("download_url", "") # Not used yet, will be. + self._release_notes = package_data.get("release_notes", "") # Not used yet, propose to add to description? + + author_data = package_data.get("author", {}) + self._author_name = author_data.get("display_name", catalog.i18nc("@label:property", "Unknown Author")) + self._author_info_url = author_data.get("website", "") + self._section_title = section_title + # Note that there's a lot more info in the package_data than just these specified here. @pyqtProperty(str, constant = True) def packageId(self) -> str: return self._package_id + @pyqtProperty(str, constant=True) + def iconUrl(self): + return self._icon_url + @pyqtProperty(str, constant = True) def displayName(self) -> str: return self._display_name + @pyqtProperty(bool, constant=True) + def isVerified(self): + return self._is_verified + + @pyqtProperty(str, constant=True) + def packageVersion(self): + return self._package_version + + @pyqtProperty(str, constant=True) + def packageInfoUrl(self): + return self._package_info_url + + @pyqtProperty(int, constant=True) + def downloadCount(self): + return self._download_count + + @pyqtProperty(str, constant=True) + def description(self): + return self._description + + @pyqtProperty(str, constant=True) + def authorName(self): + return self._author_name + + @pyqtProperty(str, constant=True) + def authorInfoUrl(self): + return self._author_info_url + @pyqtProperty(str, constant = True) def sectionTitle(self) -> Optional[str]: return self._section_title diff --git a/plugins/Marketplace/resources/qml/PackageCard.qml b/plugins/Marketplace/resources/qml/PackageCard.qml index 0a6c33a5b9..c200910077 100644 --- a/plugins/Marketplace/resources/qml/PackageCard.qml +++ b/plugins/Marketplace/resources/qml/PackageCard.qml @@ -3,7 +3,9 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 -import UM 1.4 as UM + +import UM 1.6 as UM +import Cura 1.6 as Cura Rectangle { @@ -15,14 +17,311 @@ Rectangle color: UM.Theme.getColor("main_background") radius: UM.Theme.getSize("default_radius").width - Label + Image { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: Math.round((parent.height - height) / 2) + id: packageIcon + anchors + { + top: parent.top + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + width: UM.Theme.getSize("section_icon").width * 3 + height: UM.Theme.getSize("section_icon").height * 3 - text: packageData.displayName - font: UM.Theme.getFont("medium_bold") - color: UM.Theme.getColor("text") + source: packageData.iconUrl != "" ? packageData.iconUrl : UM.Theme.getImage("CicleOutline") + } + + Item + { + anchors + { + top: parent.top + bottom: parent.bottom + right: parent.right + left: packageIcon.left + margins: UM.Theme.getSize("thin_margin").width + } + + Item + { + id: firstRowItems + anchors + { + top: parent.top + right: parent.right + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + + Label + { + id: titleLabel + anchors + { + top: parent.top + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + font: UM.Theme.getFont("medium_bold") + color: UM.Theme.getColor("text") + + text: packageData.displayName + } + + UM.RecolorImage + { + id: verifiedIcon + anchors + { + top: parent.top + left: titleLabel.right + margins: UM.Theme.getSize("thin_margin").width + } + width: UM.Theme.getSize("section_icon").height + height: UM.Theme.getSize("section_icon").height + color: UM.Theme.getColor("icon") + + visible: packageData.isVerified + source: UM.Theme.getIcon("CheckCircle") + + // TODO: on hover + } + + Rectangle + { // placeholder for 'certified material' icon+link whenever we implement the materials part of this card + id: certifiedIcon + anchors + { + top: parent.top + left: verifiedIcon.right + margins: UM.Theme.getSize("thin_margin").width + } + width: UM.Theme.getSize("section_icon").height + height: UM.Theme.getSize("section_icon").height + + // TODO: on hover + } + + Label + { + id: versionLabel + anchors + { + top: parent.top + left: certifiedIcon.right + margins: UM.Theme.getSize("thin_margin").width + } + + text: packageData.packageVersion + } + + UM.RecolorImage + { + id: packageInfoLink + anchors + { + top: parent.top + right: parent.right + margins: UM.Theme.getSize("thin_margin").width + } + width: UM.Theme.getSize("section_icon").height + height: UM.Theme.getSize("section_icon").height + color: UM.Theme.getColor("icon") + + source: UM.Theme.getIcon("Link") + + // TODO: on clicked url + } + } + + Item + { + id: secondRowItems + anchors + { + top: firstRowItems.bottom + right: parent.right + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + + UM.RecolorImage + { + id: downloadCountIcon + anchors + { + top: parent.top + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + width: UM.Theme.getSize("section_icon").height + height: UM.Theme.getSize("section_icon").height + color: UM.Theme.getColor("icon") + + source: UM.Theme.getIcon("Download") // TODO: The right icon. + } + + Label + { + id: downloadCountLobel + anchors + { + top: parent.top + left: downloadCountIcon.right + margins: UM.Theme.getSize("thin_margin").width + } + + text: packageData.downloadCount + } + } + + Item + { + id: mainRowItems + anchors + { + top: secondRowItems.bottom + bottom: footerRowItems.top + right: parent.right + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + + readonly property int charLimitSmall: 130 + + Label + { + id: descriptionLabel + anchors + { + top: parent.top + left: parent.left + right: parent.right + bottom: parent.bottom + margins: UM.Theme.getSize("thin_margin").width + } + + maximumLineCount: 2 + text: packageData.description.substring(0, parent.charLimitSmall) + } + + Cura.TertiaryButton + { + id: readMoreLabel + anchors + { + right: parent.right + bottom: parent.bottom + margins: UM.Theme.getSize("thin_margin").width + } + + visible: descriptionLabel.text.length > parent.charLimitSmall + text: catalog.i18nc("@info", "Read more") + + // TODO: overlaps elided text, is this ok? + } + + // TODO: _only_ limit to 130 or 2 rows (& all that that entails) when 'small' + } + + Item + { + id: footerRowItems + anchors + { + bottom: parent.bottom + right: parent.right + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + + Item + { + Label + { + id: preAuthorNameText + anchors + { + top: parent.top + left: parent.left + margins: UM.Theme.getSize("thin_margin").width + } + + text: catalog.i18nc("@label", "By") + } + + Cura.TertiaryButton + { + id: authorNameLabel + anchors + { + top: parent.top + left: preAuthorNameText.right + margins: UM.Theme.getSize("thin_margin").width + } + + text: packageData.authorName + + // TODO on clicked (is link) -> MouseArea? + } + } + + Item + { + id: packageActionButtons + anchors + { + bottom: parent.bottom + right: parent.right + margins: UM.Theme.getSize("thin_margin").width + } + + Cura.SecondaryButton + { + id: disableButton + anchors + { + right: uninstallButton.left + bottom: parent.bottom + margins: UM.Theme.getSize("thin_margin").width + } + height: parent.height + + text: catalog.i18nc("@button", "Disable") + // not functional right now + } + + Cura.SecondaryButton + { + id: uninstallButton + anchors + { + right: updateButton.left + bottom: parent.bottom + margins: UM.Theme.getSize("thin_margin").width + } + height: parent.height + + text: catalog.i18nc("@button", "Uninstall") + // not functional right now + } + + Cura.PrimaryButton + { + id: updateButton + anchors + { + right: parent.right + bottom: parent.bottom + margins: UM.Theme.getSize("thin_margin").width + } + height: parent.height + + text: catalog.i18nc("@button", "Update") + // not functional right now + } + } + } } } diff --git a/resources/themes/cura-light/icons/default/Download.svg b/resources/themes/cura-light/icons/default/Download.svg new file mode 100644 index 0000000000..cbe0da2a99 --- /dev/null +++ b/resources/themes/cura-light/icons/default/Download.svg @@ -0,0 +1,3 @@ + + +