diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 60fe095537..20c72a580e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -10,7 +10,7 @@ import Cura 1.1 as Cura Column { property bool installed: toolbox.isInstalled(model.id) - property bool canUpdate: toolbox.canUpdate(model.id) + property bool canUpdate: CuraApplication.getPackageManager().packagesWithUpdate.indexOf(model.id) != -1 property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn property var packageData @@ -112,11 +112,9 @@ Column { target: toolbox onInstallChanged: installed = toolbox.isInstalled(model.id) - onMetadataChanged: canUpdate = toolbox.canUpdate(model.id) onFilterChanged: { installed = toolbox.isInstalled(model.id) - canUpdate = toolbox.canUpdate(model.id) } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index 087402d564..491567eb5f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -1,9 +1,11 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.10 import QtQuick.Controls 1.4 -import UM 1.1 as UM + +import UM 1.4 as UM +import Cura 1.0 as Cura Item { @@ -50,6 +52,7 @@ Item } } } + ToolboxTabButton { id: installedTabButton @@ -62,7 +65,25 @@ Item rightMargin: UM.Theme.getSize("default_margin").width } onClicked: toolbox.viewCategory = "installed" + width: UM.Theme.getSize("toolbox_header_tab").width + marketplaceNotificationIcon.width - UM.Theme.getSize("default_margin").width } + + Cura.NotificationIcon + { + id: marketplaceNotificationIcon + + visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 + + anchors.right: installedTabButton.right + anchors.verticalCenter: installedTabButton.verticalCenter + + labelText: + { + const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length + return itemCount > 9 ? "9+" : itemCount + } + } + ToolboxShadow { anchors.top: bar.bottom diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 61af84fbe5..db30b1caf5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -10,7 +10,7 @@ import Cura 1.1 as Cura Column { - property bool canUpdate: false + property bool canUpdate: CuraApplication.getPackageManager().packagesWithUpdate.indexOf(model.id) != -1 property bool canDowngrade: false property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn width: UM.Theme.getSize("toolbox_action_button").width @@ -83,7 +83,6 @@ Column target: toolbox onMetadataChanged: { - canUpdate = toolbox.canUpdate(model.id) canDowngrade = toolbox.canDowngrade(model.id) } } diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml index 5e1aeaa636..cde87c5bc4 100644 --- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml @@ -9,14 +9,17 @@ Button { id: control property bool active: false - hoverEnabled: true + + implicitWidth: UM.Theme.getSize("toolbox_header_tab").width + implicitHeight: UM.Theme.getSize("toolbox_header_tab").height background: Item { - implicitWidth: UM.Theme.getSize("toolbox_header_tab").width - implicitHeight: UM.Theme.getSize("toolbox_header_tab").height + id: backgroundItem Rectangle { + id: highlight + visible: control.active color: UM.Theme.getColor("primary") anchors.bottom: parent.bottom @@ -24,28 +27,42 @@ Button height: UM.Theme.getSize("toolbox_header_highlight").height } } + contentItem: Label { id: label text: control.text - color: - { - if(control.hovered) - { - return UM.Theme.getColor("toolbox_header_button_text_hovered"); - } - if(control.active) - { - return UM.Theme.getColor("toolbox_header_button_text_active"); - } - else - { - return UM.Theme.getColor("toolbox_header_button_text_inactive"); - } - } - font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") + color: UM.Theme.getColor("toolbox_header_button_text_inactive") + font: UM.Theme.getFont("medium") + verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering } + + states: + [ + State + { + name: "disabled" + when: !control.enabled + PropertyChanges + { + target: label + font: UM.Theme.getFont("default_italic") + } + }, + State + { + name: "active" + when: control.active + PropertyChanges + { + target: label + font: UM.Theme.getFont("medium_bold") + color: UM.Theme.getColor("toolbox_header_button_text_active") + } + } + ] } \ No newline at end of file diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 7d8d359831..12a033c358 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -190,8 +190,10 @@ class Toolbox(QObject, Extension): "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)) } - @pyqtSlot() - def browsePackages(self) -> None: + # Request the latest and greatest! + self._fetchPackageData() + + def _fetchPackageData(self): # Create the network manager: # This was formerly its own function but really had no reason to be as # it was never called more than once ever. @@ -209,6 +211,10 @@ class Toolbox(QObject, Extension): # Gather installed packages: self._updateInstalledModels() + @pyqtSlot() + def browsePackages(self) -> None: + self._fetchPackageData() + if not self._dialog: self._dialog = self._createDialog("Toolbox.qml") @@ -455,36 +461,6 @@ class Toolbox(QObject, Extension): break return remote_package - # Checks - # -------------------------------------------------------------------------- - @pyqtSlot(str, result = bool) - def canUpdate(self, package_id: str) -> bool: - local_package = self._package_manager.getInstalledPackageInfo(package_id) - if local_package is None: - local_package = self.getOldPluginPackageMetadata(package_id) - if local_package is None: - return False - - remote_package = self.getRemotePackage(package_id) - if remote_package is None: - return False - - local_version = Version(local_package["package_version"]) - remote_version = Version(remote_package["package_version"]) - can_upgrade = False - if remote_version > local_version: - can_upgrade = True - # A package with the same version can be built to have different SDK versions. So, for a package with the same - # version, we also need to check if the current one has a lower SDK version. If so, this package should also - # be upgradable. - elif remote_version == local_version: - # First read sdk_version_semver. If that doesn't exist, read just sdk_version (old version system). - remote_sdk_version = Version(remote_package.get("sdk_version_semver", remote_package.get("sdk_version", 0))) - local_sdk_version = Version(local_package.get("sdk_version_semver", local_package.get("sdk_version", 0))) - can_upgrade = local_sdk_version < remote_sdk_version - - return can_upgrade - @pyqtSlot(str, result = bool) def canDowngrade(self, package_id: str) -> bool: # If the currently installed version is higher than the bundled version (if present), the we can downgrade @@ -636,6 +612,7 @@ class Toolbox(QObject, Extension): self._models[response_type].setFilter({"type": "plugin"}) self.reBuildMaterialsModels() self.reBuildPluginsModels() + self._notifyPackageManager() elif response_type is "authors": self._models[response_type].setFilter({"package_types": "material"}) self._models[response_type].setFilter({"tags": "generic"}) @@ -656,6 +633,11 @@ class Toolbox(QObject, Extension): # Ignore any operation that is not a get operation pass + # This function goes through all known remote versions of a package and notifies the package manager of this change + def _notifyPackageManager(self): + for package in self._server_response_data["packages"]: + self._package_manager.addAvailablePackageVersion(package["package_id"], Version(package["package_version"])) + def _onDownloadProgress(self, bytes_sent: int, bytes_total: int) -> None: if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index fab8010dd7..43ec03d947 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -117,6 +117,25 @@ Item rightMargin: UM.Theme.getSize("default_margin").width verticalCenter: parent.verticalCenter } + + Cura.NotificationIcon + { + id: marketplaceNotificationIcon + anchors + { + top: parent.top + right: parent.right + rightMargin: (-0.5 * width) | 0 + topMargin: (-0.5 * height) | 0 + } + visible: CuraApplication.getPackageManager().packagesWithUpdate.length > 0 + + labelText: + { + const itemCount = CuraApplication.getPackageManager().packagesWithUpdate.length + return itemCount > 9 ? "9+" : itemCount + } + } } AccountWidget diff --git a/resources/qml/Widgets/NotificationIcon.qml b/resources/qml/Widgets/NotificationIcon.qml new file mode 100644 index 0000000000..7e4b0878dc --- /dev/null +++ b/resources/qml/Widgets/NotificationIcon.qml @@ -0,0 +1,35 @@ +// Copyright (c) 2019 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +import UM 1.4 as UM + + +// +// A notification icon which is a circle with a number at the center, that can be used to indicate, for example, how +// many new messages that are available. +// +Rectangle +{ + id: notificationIcon + color: UM.Theme.getColor("notification_icon") + width: UM.Theme.getSize("notification_icon").width + height: UM.Theme.getSize("notification_icon").height + radius: (0.5 * width) | 0 + + property alias labelText: notificationLabel.text + property alias labelFont: notificationLabel.font + + Label + { + id: notificationLabel + anchors.centerIn: parent + anchors.fill: parent + color: UM.Theme.getColor("primary_text") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("small") + } +} diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 62997cc27a..56eda5217d 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -17,3 +17,5 @@ SettingView 1.0 SettingView.qml ProfileMenu 1.0 ProfileMenu.qml CheckBoxWithTooltip 1.0 CheckBoxWithTooltip.qml ToolTip 1.0 ToolTip.qml + +NotificationIcon 1.0 NotificationIcon.qml diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index aed45e8a71..a5e35e1c02 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -214,7 +214,6 @@ "toolbox_header_button_text_active": [255, 255, 255, 255], "toolbox_header_button_text_inactive": [128, 128, 128, 255], - "toolbox_header_button_text_hovered": [255, 255, 255, 255], "monitor_printer_family_tag": [86, 86, 106, 255], "monitor_text_primary": [229, 229, 229, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index c870936723..8097ee498d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -189,6 +189,8 @@ "toolbar_background": [255, 255, 255, 255], + "notification_icon": [255, 0, 0, 255], + "printer_type_label_background": [228, 228, 242, 255], "text": [25, 25, 25, 255], @@ -384,7 +386,6 @@ "toolbox_header_button_text_active": [0, 0, 0, 255], "toolbox_header_button_text_inactive": [0, 0, 0, 255], - "toolbox_header_button_text_hovered": [0, 0, 0, 255], "favorites_header_bar": [245, 245, 245, 255], "favorites_header_hover": [245, 245, 245, 255], @@ -595,6 +596,8 @@ "toolbox_action_button": [8.0, 2.5], "toolbox_loader": [2.0, 2.0], + "notification_icon": [1.4, 1.4], + "avatar_image": [6.8, 6.8], "monitor_config_override_box": [1.0, 14.0],