diff --git a/plugins/Marketplace/PackageModel.py b/plugins/Marketplace/PackageModel.py index 7c2a5d9ae1..29c8abe653 100644 --- a/plugins/Marketplace/PackageModel.py +++ b/plugins/Marketplace/PackageModel.py @@ -39,6 +39,7 @@ class PackageModel(QObject): self._package_type = package_data.get("package_type", "") self._is_bundled = package_data.get("is_bundled", False) self._icon_url = package_data.get("icon_url", "") + self._marketplace_url = package_data.get("marketplace_url", "") self._display_name = package_data.get("display_name", catalog.i18nc("@label:property", "Unknown Package")) tags = package_data.get("tags", []) self._is_checked_by_ultimaker = (self._package_type == "plugin" and "verified" in tags) or ( @@ -210,6 +211,10 @@ class PackageModel(QObject): def packageId(self) -> str: return self._package_id + @pyqtProperty(str, constant=True) + def marketplaceURL(self)-> str: + return self._marketplace_url + @pyqtProperty(str, constant = True) def packageType(self) -> str: return self._package_type diff --git a/plugins/Marketplace/resources/images/Plugin.svg b/plugins/Marketplace/resources/images/Plugin.svg new file mode 100644 index 0000000000..51356d842c --- /dev/null +++ b/plugins/Marketplace/resources/images/Plugin.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/Marketplace/resources/images/Spool.svg b/plugins/Marketplace/resources/images/Spool.svg new file mode 100644 index 0000000000..dae9b43030 --- /dev/null +++ b/plugins/Marketplace/resources/images/Spool.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/Marketplace/resources/qml/ManagedPackages.qml b/plugins/Marketplace/resources/qml/ManagedPackages.qml index 8ccaacea46..3eb77d8485 100644 --- a/plugins/Marketplace/resources/qml/ManagedPackages.qml +++ b/plugins/Marketplace/resources/qml/ManagedPackages.qml @@ -13,7 +13,7 @@ Packages bannerVisible: UM.Preferences.getValue("cura/market_place_show_manage_packages_banner"); bannerIcon: UM.Theme.getIcon("ArrowDoubleCircleRight") bannerText: catalog.i18nc("@text", "Manage your Ultimaker Cura plugins and material profiles here. Make sure to keep your plugins up to date and backup your setup regularly.") - bannerReadMoreUrl: "" // TODO add when support page is ready + bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/4411125921426/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-manage" onRemoveBanner: function() { UM.Preferences.setValue("cura/market_place_show_manage_packages_banner", false); bannerVisible = false; diff --git a/plugins/Marketplace/resources/qml/Marketplace.qml b/plugins/Marketplace/resources/qml/Marketplace.qml index fc6d3cd755..5a30141b32 100644 --- a/plugins/Marketplace/resources/qml/Marketplace.qml +++ b/plugins/Marketplace/resources/qml/Marketplace.qml @@ -46,198 +46,182 @@ Window { anchors.fill: parent color: UM.Theme.getColor("main_background") + } + //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility. + StackView + { + id: contextStack + anchors.fill: parent - //The Marketplace can have a page in front of everything with package details. The stack view controls its visibility. - StackView + initialItem: packageBrowse + + ColumnLayout { - id: contextStack - anchors.fill: parent + id: packageBrowse - initialItem: packageBrowse + spacing: UM.Theme.getSize("narrow_margin").height - ColumnLayout + // Page title. + Item { - id: packageBrowse + implicitWidth: parent.width + implicitHeight: childrenRect.height + UM.Theme.getSize("default_margin").height - spacing: UM.Theme.getSize("default_margin").height - - // Page title. - Item + Label { - Layout.preferredWidth: parent.width - Layout.preferredHeight: childrenRect.height + UM.Theme.getSize("default_margin").height - - Label + id: pageTitle + anchors { - id: pageTitle - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - bottom: parent.bottom - } - - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - text: content.item ? content.item.pageTitle: catalog.i18nc("@title", "Loading...") + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + bottom: parent.bottom } - } - OnboardBanner - { - visible: content.item && content.item.bannerVisible - text: content.item && content.item.bannerText - icon: content.item && content.item.bannerIcon - onRemove: content.item && content.item.onRemoveBanner - readMoreUrl: content.item && content.item.bannerReadMoreUrl + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + text: content.item ? content.item.pageTitle: catalog.i18nc("@title", "Loading...") } + } - // Search & Top-Level Tabs - Item + OnboardBanner + { + visible: content.item && content.item.bannerVisible + text: content.item && content.item.bannerText + icon: content.item && content.item.bannerIcon + onRemove: content.item && content.item.onRemoveBanner + readMoreUrl: content.item && content.item.bannerReadMoreUrl + + Layout.fillWidth: true + Layout.leftMargin: UM.Theme.getSize("default_margin").width + Layout.rightMargin: UM.Theme.getSize("default_margin").width + } + + // Search & Top-Level Tabs + Item + { + implicitHeight: childrenRect.height + implicitWidth: parent.width - 2 * UM.Theme.getSize("default_margin").width + Layout.alignment: Qt.AlignHCenter + RowLayout { - Layout.preferredHeight: childrenRect.height - Layout.preferredWidth: parent.width - 2 * UM.Theme.getSize("thin_margin").width - RowLayout + width: parent.width + height: UM.Theme.getSize("button_icon").height + UM.Theme.getSize("default_margin").height + spacing: UM.Theme.getSize("thin_margin").width + + Cura.SearchBar { - width: parent.width - height: UM.Theme.getSize("button_icon").height + UM.Theme.getSize("default_margin").height - spacing: UM.Theme.getSize("thin_margin").width + id: searchBar + implicitHeight: UM.Theme.getSize("button_icon").height + Layout.fillWidth: true + onTextEdited: searchStringChanged(text) + } - Item + // Page selection. + TabBar + { + id: pageSelectionTabBar + Layout.alignment: Qt.AlignRight + height: UM.Theme.getSize("button_icon").height + spacing: 0 + background: Rectangle { color: "transparent" } + currentIndex: manager.tabShown + + onCurrentIndexChanged: { - Layout.preferredHeight: parent.height - Layout.preferredWidth: searchBar.visible ? UM.Theme.getSize("thin_margin").width : 0 - Layout.fillWidth: ! searchBar.visible + manager.tabShown = currentIndex + searchBar.text = ""; + searchBar.visible = currentItem.hasSearch; + content.source = currentItem.sourcePage; } - Cura.SearchBar + PackageTypeTab { - id: searchBar - Layout.preferredHeight: UM.Theme.getSize("button_icon").height - Layout.fillWidth: true - onTextEdited: searchStringChanged(text) + id: pluginTabText + width: implicitWidth + text: catalog.i18nc("@button", "Plugins") + property string sourcePage: "Plugins.qml" + property bool hasSearch: true } - - // Page selection. - TabBar + PackageTypeTab { - id: pageSelectionTabBar - Layout.alignment: Qt.AlignRight - height: UM.Theme.getSize("button_icon").height - spacing: 0 - background: Rectangle { color: "transparent" } - currentIndex: manager.tabShown + id: materialsTabText + width: implicitWidth + text: catalog.i18nc("@button", "Materials") + property string sourcePage: "Materials.qml" + property bool hasSearch: true + } + ManagePackagesButton + { + property string sourcePage: "ManagedPackages.qml" + property bool hasSearch: false - onCurrentIndexChanged: + Cura.NotificationIcon { - manager.tabShown = currentIndex - searchBar.text = ""; - searchBar.visible = currentItem.hasSearch; - content.source = currentItem.sourcePage; - } - - PackageTypeTab - { - id: pluginTabText - width: implicitWidth - text: catalog.i18nc("@button", "Plugins") - property string sourcePage: "Plugins.qml" - property bool hasSearch: true - } - PackageTypeTab - { - id: materialsTabText - width: implicitWidth - text: catalog.i18nc("@button", "Materials") - property string sourcePage: "Materials.qml" - property bool hasSearch: true - } - ManagePackagesButton - { - property string sourcePage: "ManagedPackages.qml" - property bool hasSearch: false - - Cura.NotificationIcon + anchors { - anchors - { - horizontalCenter: parent.right - verticalCenter: parent.top - } - visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 + horizontalCenter: parent.right + verticalCenter: parent.top + } + visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 - labelText: - { - const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length - return itemCount > 9 ? "9+" : itemCount - } + labelText: + { + const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length + return itemCount > 9 ? "9+" : itemCount } } } - - TextMetrics - { - id: pluginTabTextMetrics - text: pluginTabText.text - font: pluginTabText.font - } - TextMetrics - { - id: materialsTabTextMetrics - text: materialsTabText.text - font: materialsTabText.font - } } } + } - FontMetrics - { - id: fontMetrics - font: UM.Theme.getFont("default") - } + FontMetrics + { + id: fontMetrics + font: UM.Theme.getFont("default") + } - Cura.TertiaryButton - { - text: catalog.i18nc("@info", "Search in the browser") - iconSource: UM.Theme.getIcon("LinkExternal") - visible: pageSelectionTabBar.currentItem.hasSearch - isIconOnRightSide: true - height: fontMetrics.height - textFont: fontMetrics.font - textColor: UM.Theme.getColor("text") + Cura.TertiaryButton + { + text: catalog.i18nc("@info", "Search in the browser") + iconSource: UM.Theme.getIcon("LinkExternal") + visible: pageSelectionTabBar.currentItem.hasSearch + isIconOnRightSide: true + height: fontMetrics.height + textFont: fontMetrics.font + textColor: UM.Theme.getColor("text") - onClicked: content.item && Qt.openUrlExternally(content.item.searchInBrowserUrl) - } + onClicked: content.item && Qt.openUrlExternally(content.item.searchInBrowserUrl) + } + + // Page contents. + Rectangle + { + Layout.preferredWidth: parent.width + Layout.fillHeight: true + color: UM.Theme.getColor("detail_background") // Page contents. - Rectangle + Loader { - Layout.preferredWidth: parent.width - Layout.fillHeight: true - color: UM.Theme.getColor("detail_background") + id: content + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + source: "Plugins.qml" - // Page contents. - Loader + Connections { - id: content - anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_margin").width - source: "Plugins.qml" - - Connections + target: content + function onLoaded() { - target: content - function onLoaded() - { - pageTitle.text = content.item.pageTitle - searchStringChanged.connect(handleSearchStringChanged) - } - function handleSearchStringChanged(new_search) - { - content.item.model.searchString = new_search - } + pageTitle.text = content.item.pageTitle + searchStringChanged.connect(handleSearchStringChanged) + } + function handleSearchStringChanged(new_search) + { + content.item.model.searchString = new_search } } } diff --git a/plugins/Marketplace/resources/qml/Materials.qml b/plugins/Marketplace/resources/qml/Materials.qml index 39fae7042a..ddac8b0bbe 100644 --- a/plugins/Marketplace/resources/qml/Materials.qml +++ b/plugins/Marketplace/resources/qml/Materials.qml @@ -10,7 +10,7 @@ Packages bannerVisible: UM.Preferences.getValue("cura/market_place_show_material_banner") bannerIcon: UM.Theme.getIcon("Spool") bannerText: catalog.i18nc("@text", "Select and install material profiles optimised for your Ultimaker 3D printers.") - bannerReadMoreUrl: "" // TODO add when support page is ready + bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/360011968360/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-materials" onRemoveBanner: function() { UM.Preferences.setValue("cura/market_place_show_material_banner", false); bannerVisible = false; diff --git a/plugins/Marketplace/resources/qml/OnboardBanner.qml b/plugins/Marketplace/resources/qml/OnboardBanner.qml index 25e4b53241..7d973cb74a 100644 --- a/plugins/Marketplace/resources/qml/OnboardBanner.qml +++ b/plugins/Marketplace/resources/qml/OnboardBanner.qml @@ -16,10 +16,7 @@ Rectangle property var onRemove property string readMoreUrl - Layout.preferredHeight: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height - Layout.fillWidth: true - Layout.margins: UM.Theme.getSize("default_margin").width - + implicitHeight: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height color: UM.Theme.getColor("action_panel_secondary") // Icon diff --git a/plugins/Marketplace/resources/qml/PackageCard.qml b/plugins/Marketplace/resources/qml/PackageCard.qml index 633d2b25b9..9a5f9beab6 100644 --- a/plugins/Marketplace/resources/qml/PackageCard.qml +++ b/plugins/Marketplace/resources/qml/PackageCard.qml @@ -31,64 +31,14 @@ Rectangle { id: descriptionLabel width: parent.width - property real lastLineWidth: 0; //Store the width of the last line, to properly position the elision. text: packageData.description - textFormat: Text.PlainText //Must be plain text, or we won't get onLineLaidOut signals. Don't auto-detect! font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") maximumLineCount: 2 wrapMode: Text.Wrap elide: Text.ElideRight visible: text !== "" - - onLineLaidOut: - { - if(truncated && line.isLast) - { - let max_line_width = parent.width - readMoreButton.width - fontMetrics.advanceWidth("… ") - 2 * UM.Theme.getSize("default_margin").width; - if(line.implicitWidth > max_line_width) - { - line.width = max_line_width; - } - else - { - line.width = line.implicitWidth - fontMetrics.advanceWidth("…"); //Truncate the ellipsis. We're adding this ourselves. - } - descriptionLabel.lastLineWidth = line.implicitWidth; - } - } - } - Label - { - id: tripleDotLabel - anchors.left: parent.left - anchors.leftMargin: descriptionLabel.lastLineWidth - anchors.bottom: descriptionLabel.bottom - - text: "… " - font: descriptionLabel.font - color: descriptionLabel.color - visible: descriptionLabel.truncated && descriptionLabel.text !== "" - } - Cura.TertiaryButton - { - id: readMoreButton - anchors.right: parent.right - anchors.bottom: descriptionLabel.bottom - height: fontMetrics.height //Height of a single line. - - text: catalog.i18nc("@info", "Read more") - iconSource: UM.Theme.getIcon("LinkExternal") - - visible: descriptionLabel.truncated && descriptionLabel.text !== "" - enabled: visible - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("wide_margin").width - textFont: descriptionLabel.font - isIconOnRightSide: true - - onClicked: Qt.openUrlExternally(packageData.packageInfoUrl) } } } diff --git a/plugins/Marketplace/resources/qml/PackageCardHeader.qml b/plugins/Marketplace/resources/qml/PackageCardHeader.qml index 3a76f7a959..9e3e673ea2 100644 --- a/plugins/Marketplace/resources/qml/PackageCardHeader.qml +++ b/plugins/Marketplace/resources/qml/PackageCardHeader.qml @@ -12,7 +12,7 @@ import Cura 1.6 as Cura // are combined into the reusable "PackageCardHeader" component Item { - default property alias contents: contentItem.children; + default property alias contents: contentItem.children property var packageData property bool showManageButtons: false @@ -32,8 +32,21 @@ Item } width: UM.Theme.getSize("card_icon").width height: width - - source: packageData.iconUrl != "" ? packageData.iconUrl : "../images/placeholder.svg" + sourceSize.height: height + sourceSize.width: width + source: + { + if (packageData.iconUrl != "") + { + return packageData.iconUrl + } + switch (packageData.packageType) + { + case "plugin": return "../images/Plugin.svg"; + case "material": return "../images/Spool.svg"; + default: return "../images/placeholder.svg"; + } + } } ColumnLayout @@ -103,7 +116,7 @@ Item color: externalLinkButton.hovered ? UM.Theme.getColor("action_button_hovered"): "transparent" radius: externalLinkButton.width / 2 } - onClicked: Qt.openUrlExternally(packageData.authorInfoUrl) + onClicked: Qt.openUrlExternally(packageData.marketplaceURL) } } diff --git a/plugins/Marketplace/resources/qml/PackagePage.qml b/plugins/Marketplace/resources/qml/PackagePage.qml index 21c400fff2..4384d2843f 100644 --- a/plugins/Marketplace/resources/qml/PackagePage.qml +++ b/plugins/Marketplace/resources/qml/PackagePage.qml @@ -40,7 +40,10 @@ Rectangle id: downloadCount Layout.preferredWidth: parent.width Layout.fillHeight: true - + // It's not the perfect way to handle this, since a package really can have 0 downloads + // But we re-use the package page for the manage plugins as well. The one user that doesn't see + // the num downloads is an acceptable "sacrifice" to make this easy to fix. + visible: packageData.downloadCount != "0" UM.RecolorImage { id: downloadsIcon @@ -53,6 +56,7 @@ Rectangle Label { + anchors.verticalCenter: downloadsIcon.verticalCenter color: UM.Theme.getColor("text") diff --git a/plugins/Marketplace/resources/qml/Packages.qml b/plugins/Marketplace/resources/qml/Packages.qml index 194c90c248..5cd52e5628 100644 --- a/plugins/Marketplace/resources/qml/Packages.qml +++ b/plugins/Marketplace/resources/qml/Packages.qml @@ -53,8 +53,8 @@ ListView // Vertical ScrollBar, styled similarly to the scrollBar in the settings panel id: verticalScrollBar visible: packages.contentHeight > packages.height - - background: Item{} + anchors.right: parent.right + background: Item {} contentItem: Rectangle { @@ -83,7 +83,16 @@ ListView { manageableInListView: packages.packagesManageableInListView packageData: model.package - width: parent.width - UM.Theme.getSize("default_margin").width - UM.Theme.getSize("narrow_margin").width + width: { + if (verticalScrollBar.visible) + { + return parent.width - UM.Theme.getSize("default_margin").width - UM.Theme.getSize("default_margin").width + } + else + { + return parent.width - UM.Theme.getSize("default_margin").width + } + } color: cardMouseArea.containsMouse ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("main_background") } } diff --git a/plugins/Marketplace/resources/qml/Plugins.qml b/plugins/Marketplace/resources/qml/Plugins.qml index 9983a827d8..f1a524c029 100644 --- a/plugins/Marketplace/resources/qml/Plugins.qml +++ b/plugins/Marketplace/resources/qml/Plugins.qml @@ -10,7 +10,7 @@ Packages bannerVisible: UM.Preferences.getValue("cura/market_place_show_plugin_banner") bannerIcon: UM.Theme.getIcon("Shop") bannerText: catalog.i18nc("@text", "Streamline your workflow and customize your Ultimaker Cura experience with plugins contributed by our amazing community of users.") - bannerReadMoreUrl: "" // TODO add when support page is ready + bannerReadMoreUrl: "https://support.ultimaker.com/hc/en-us/articles/360011968360/?utm_source=cura&utm_medium=software&utm_campaign=marketplace-learn-plugins" onRemoveBanner: function() { UM.Preferences.setValue("cura/market_place_show_plugin_banner", false) bannerVisible = false;