From 013f020a4a9c807f26431b2e54cac445f6126e99 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 17 Jan 2018 17:02:02 +0100 Subject: [PATCH 1/8] Initial styling changes - Dual-button layout - Blue buttons in line with Cura styles - Contact info --- plugins/PluginBrowser/PluginBrowser.qml | 315 ++++++++++++++++++------ 1 file changed, 235 insertions(+), 80 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 13000d23ad..b30fba7fc4 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -3,13 +3,14 @@ import QtQuick 2.2 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.4 UM.Dialog { id: base title: catalog.i18nc("@title:window", "Find & Update plugins") - width: 600 * screenScaleFactor + width: 800 * screenScaleFactor height: 450 * screenScaleFactor minimumWidth: 350 * screenScaleFactor minimumHeight: 350 * screenScaleFactor @@ -62,17 +63,7 @@ UM.Dialog height: closeButton.height anchors.bottom: parent.bottom anchors.left: parent.left - ProgressBar - { - id: progressbar - anchors.bottom: parent.bottom - minimumValue: 0; - maximumValue: 100 - anchors.left:parent.left - anchors.right: closeButton.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - value: manager.isDownloading ? manager.downloadProgress : 0 - } + Button { @@ -92,91 +83,255 @@ UM.Dialog } } - Item - { - SystemPalette { id: palette } - Component - { + Item { + Component { id: pluginDelegate - Rectangle - { + + Rectangle { width: pluginList.width; - height: texts.height; - color: index % 2 ? palette.base : palette.alternateBase - Column - { - id: texts - width: parent.width - height: childrenRect.height + height: 96 + color: index % 2 ? UM.Theme.getColor("secondary") : "white" + + // Plugin info + Column { + id: pluginInfo + height: parent.height anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.right: downloadButton.left + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.right: authorInfo.left anchors.rightMargin: UM.Theme.getSize("default_margin").width - Label - { - text: "" + model.name + "" + ((model.author !== "") ? (" - " + model.author) : "") - width: contentWidth - height: contentHeight + UM.Theme.getSize("default_margin").height - verticalAlignment: Text.AlignVCenter - } - Label - { - text: model.short_description + Label { + text: model.name width: parent.width - height: contentHeight + UM.Theme.getSize("default_margin").height + height: 24 wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter + font { + pixelSize: 15 + bold: true + } + } + + Label { + text: model.short_description + width: parent.width + height: 72 + wrapMode: Text.WordWrap } } - Button - { - id: downloadButton - text: - { - if (manager.isDownloading && pluginList.activePlugin == model) - { - return catalog.i18nc("@action:button", "Cancel"); - } - else if (model.already_installed) - { - if (model.can_upgrade) - { - return catalog.i18nc("@action:button", "Upgrade"); - } - return catalog.i18nc("@action:button", "Installed"); - } - return catalog.i18nc("@action:button", "Download"); - } - onClicked: - { - if(!manager.isDownloading) - { - pluginList.activePlugin = model; - manager.downloadAndInstallPlugin(model.file_location); - } - else - { - manager.cancelDownload(); - } - } - anchors.right: parent.right + + // Author info + Column { + id: authorInfo + width: 192 + height: parent.height + + + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + + anchors.right: pluginActions.left anchors.rightMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: parent.verticalCenter - enabled: - { - if (manager.isDownloading) - { - return (pluginList.activePlugin == model); + + Label { + text: model.author + width: parent.width + height: 24 + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignRight + } + + Label { + text: "author@ultimaker.com" + width: parent.width + height: 72 + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignRight + } + } + + // Plugin actions + Row { + id: pluginActions + width: 180 + UM.Theme.getSize("default_margin").width + height: parent.height + anchors { + top: parent.top + right: parent.right + topMargin: UM.Theme.getSize("default_margin").height + rightMargin: UM.Theme.getSize("default_margin").width + } + layoutDirection: Qt.RightToLeft + spacing: UM.Theme.getSize("default_margin").width + + Rectangle { + visible: model.already_installed + width: 108 + height: 30 + color: "transparent" + Button { + id: removeButton + text: "Remove" + enabled: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return false; + } else { + return true; + } + } + style: ButtonStyle { + background: Rectangle { + color: white + implicitWidth: 108 + implicitHeight: 30 + radius: 4 + border { + width: 1 + color: "grey" + } + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignLeft + } + } } - else - { - return (!model.already_installed || model.can_upgrade); + Button { + id: removeDropDown + UM.RecolorImage { + anchors.centerIn: parent + height: 10 + width: 10 + source: UM.Theme.getIcon("arrow_bottom") + color: "grey" + } + enabled: { + if ( model.required || ( manager.isDownloading && pluginList.activePlugin == model )) { + return false; + } else { + return true; + } + } + anchors.right: parent.right + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 30 + implicitHeight: 30 + radius: 4 + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + + // For the disable option: + onClicked: pluginList.model.setEnabled(model.id, checked) + } + Rectangle { + id: divider + width: 1 + height: parent.height + anchors.right: removeDropDown.left + color: "grey" } } + + + + Button { + id: updateButton + // visible: model.already_installed && model.can_upgrade + visible: model.already_installed + text: { + // If currently downloading: + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return catalog.i18nc( "@action:button", "Cancel" ); + } else { + return catalog.i18nc("@action:button", "Update"); + } + } + style: ButtonStyle { + background: Rectangle { + color: UM.Theme.getColor("primary") + implicitWidth: 72 + implicitHeight: 30 + radius: 4 + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "white" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } + Button { + id: installButton + visible: !model.already_installed + text: { + // If currently downloading: + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return catalog.i18nc( "@action:button", "Cancel" ); + } else { + return catalog.i18nc("@action:button", "Install"); + } + } + onClicked: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + manager.cancelDownload(); + } else { + pluginList.activePlugin = model; + manager.downloadAndInstallPlugin( model.file_location ); + } + } + style: ButtonStyle { + background: Rectangle { + color: UM.Theme.getColor("primary") + implicitWidth: 72 + implicitHeight: 30 + radius: 4 + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "white" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } + ProgressBar { + id: progressbar + minimumValue: 0; + maximumValue: 100 + anchors.left: installButton.left + anchors.right: installButton.right + anchors.top: installButton.bottom + anchors.topMargin: 4 + value: manager.isDownloading ? manager.downloadProgress : 0 + visible: manager.isDownloading && pluginList.activePlugin == model + style: ProgressBarStyle { + background: Rectangle { + color: "lightgray" + implicitHeight: 6 + } + progress: Rectangle { + color: UM.Theme.getColor("primary") + } + } + } + } } - } } UM.I18nCatalog { id: catalog; name: "cura" } From 81b2f0f7cd36f040257f9460e2c0ec0b25507c20 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 19 Jan 2018 09:56:19 +0100 Subject: [PATCH 2/8] Added drop-down buttons --- plugins/PluginBrowser/PluginBrowser.qml | 54 ++++++++++++++++++++----- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index b30fba7fc4..7c58f96b03 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -5,11 +5,10 @@ import QtQuick.Window 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.4 -UM.Dialog -{ +UM.Dialog { id: base - title: catalog.i18nc("@title:window", "Find & Update plugins") + title: catalog.i18nc("@title:tab", "Plugins"); width: 800 * screenScaleFactor height: 450 * screenScaleFactor minimumWidth: 350 * screenScaleFactor @@ -46,8 +45,7 @@ UM.Dialog anchors.bottom: bottomBar.top anchors.bottomMargin: UM.Theme.getSize("default_margin").height frameVisible: true - ListView - { + ListView { id: pluginList model: manager.pluginsModel anchors.fill: parent @@ -113,6 +111,7 @@ UM.Dialog pixelSize: 15 bold: true } + color: model.enabled ? UM.Theme.getColor("text") : "grey" } Label { @@ -169,6 +168,7 @@ UM.Dialog spacing: UM.Theme.getSize("default_margin").width Rectangle { + id: removeControls visible: model.already_installed width: 108 height: 30 @@ -188,7 +188,7 @@ UM.Dialog color: white implicitWidth: 108 implicitHeight: 30 - radius: 4 + // radius: 4 border { width: 1 color: "grey" @@ -204,6 +204,7 @@ UM.Dialog } Button { id: removeDropDown + property bool open: false UM.RecolorImage { anchors.centerIn: parent height: 10 @@ -224,7 +225,7 @@ UM.Dialog color: "transparent" implicitWidth: 30 implicitHeight: 30 - radius: 4 + // radius: 4 } label: Text { verticalAlignment: Text.AlignVCenter @@ -234,8 +235,21 @@ UM.Dialog } } + + // For the disable option: - onClicked: pluginList.model.setEnabled(model.id, checked) + // onClicked: pluginList.model.setEnabled(model.id, checked) + + onClicked: { + if ( !removeDropDown.open ) { + removeDropDown.open = true + } + else { + removeDropDown.open = false + } + + } + } Rectangle { id: divider @@ -244,6 +258,26 @@ UM.Dialog anchors.right: removeDropDown.left color: "grey" } + Column { + id: options + anchors { + top: removeButton.bottom + left: parent.left + right: parent.right + } + height: childrenRect.height + visible: removeDropDown.open + + Button { + text: "Disable" + height: 30 + width: parent.width + onClicked: { + removeDropDown.open = false + model.setEnabled(model.id, checked) + } + } + } } @@ -265,7 +299,7 @@ UM.Dialog color: UM.Theme.getColor("primary") implicitWidth: 72 implicitHeight: 30 - radius: 4 + // radius: 4 } label: Text { verticalAlignment: Text.AlignVCenter @@ -299,7 +333,7 @@ UM.Dialog color: UM.Theme.getColor("primary") implicitWidth: 72 implicitHeight: 30 - radius: 4 + // radius: 4 } label: Text { verticalAlignment: Text.AlignVCenter From 628314af9e9667561ec46c6d2f9be657bd2f2198 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 22 Jan 2018 13:00:22 +0100 Subject: [PATCH 3/8] Basic style ready for PR - Temporarily removed remove/disable button as it's non-functional for now --- plugins/PluginBrowser/PluginBrowser.py | 10 ++-- plugins/PluginBrowser/PluginBrowser.qml | 61 ++++++++++++------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index 35b88b3465..c766a35dbe 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -229,9 +229,11 @@ class PluginBrowser(QObject, Extension): self._plugins_model.addRoleName(Qt.UserRole + 2, "version") self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") self._plugins_model.addRoleName(Qt.UserRole + 4, "author") - self._plugins_model.addRoleName(Qt.UserRole + 5, "already_installed") - self._plugins_model.addRoleName(Qt.UserRole + 6, "file_location") - self._plugins_model.addRoleName(Qt.UserRole + 7, "can_upgrade") + self._plugins_model.addRoleName(Qt.UserRole + 5, "author_email") + self._plugins_model.addRoleName(Qt.UserRole + 6, "already_installed") + self._plugins_model.addRoleName(Qt.UserRole + 7, "file_location") + self._plugins_model.addRoleName(Qt.UserRole + 8, "enabled") + self._plugins_model.addRoleName(Qt.UserRole + 9, "can_upgrade") else: self._plugins_model.clear() items = [] @@ -241,8 +243,10 @@ class PluginBrowser(QObject, Extension): "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], + "author_email": "author@gmail.com", "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], + "enabled": True, "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) self._plugins_model.setItems(items) diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 7c58f96b03..5c148b07d1 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -2,35 +2,36 @@ import UM 1.1 as UM import QtQuick 2.2 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 -import QtQuick.Controls 1.1 +import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 +// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles + UM.Dialog { id: base title: catalog.i18nc("@title:tab", "Plugins"); width: 800 * screenScaleFactor - height: 450 * screenScaleFactor + height: 600 * screenScaleFactor minimumWidth: 350 * screenScaleFactor minimumHeight: 350 * screenScaleFactor - Item - { + + Item { anchors.fill: parent - Item - { + + Item { id: topBar height: childrenRect.height; width: parent.width - Label - { + + Label { id: introText text: catalog.i18nc("@label", "Here you can find a list of Third Party plugins.") width: parent.width height: 30 } - Button - { + Button { id: refresh text: catalog.i18nc("@action:button", "Refresh") onClicked: manager.requestPluginList() @@ -38,40 +39,37 @@ UM.Dialog { enabled: !manager.isDownloading } } - ScrollView - { + + // Scroll view breaks in QtQuick.Controls 2.x + ScrollView { width: parent.width anchors.top: topBar.bottom anchors.bottom: bottomBar.top anchors.bottomMargin: UM.Theme.getSize("default_margin").height frameVisible: true + ListView { id: pluginList model: manager.pluginsModel anchors.fill: parent - property var activePlugin delegate: pluginDelegate } } - Item - { + + Item { id: bottomBar width: parent.width height: closeButton.height anchors.bottom: parent.bottom anchors.left: parent.left - - Button - { + Button { id: closeButton text: catalog.i18nc("@action:button", "Close") iconName: "dialog-close" - onClicked: - { - if (manager.isDownloading) - { + onClicked: { + if ( manager.isDownloading ) { manager.cancelDownload() } base.close(); @@ -87,7 +85,7 @@ UM.Dialog { Rectangle { width: pluginList.width; - height: 96 + height: 102 color: index % 2 ? UM.Theme.getColor("secondary") : "white" // Plugin info @@ -108,10 +106,10 @@ UM.Dialog { wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter font { - pixelSize: 15 + pixelSize: 13 bold: true } - color: model.enabled ? UM.Theme.getColor("text") : "grey" + color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("secondary") } Label { @@ -145,7 +143,7 @@ UM.Dialog { } Label { - text: "author@ultimaker.com" + text: model.author_email width: parent.width height: 72 wrapMode: Text.WordWrap @@ -156,7 +154,7 @@ UM.Dialog { // Plugin actions Row { id: pluginActions - width: 180 + UM.Theme.getSize("default_margin").width + width: 72 + UM.Theme.getSize("default_margin").width height: parent.height anchors { top: parent.top @@ -167,6 +165,7 @@ UM.Dialog { layoutDirection: Qt.RightToLeft spacing: UM.Theme.getSize("default_margin").width + /* Rectangle { id: removeControls visible: model.already_installed @@ -279,13 +278,11 @@ UM.Dialog { } } } - - - + */ Button { id: updateButton - // visible: model.already_installed && model.can_upgrade - visible: model.already_installed + visible: model.already_installed && model.can_upgrade + // visible: model.already_installed text: { // If currently downloading: if ( manager.isDownloading && pluginList.activePlugin == model ) { From f27dc4473e96d759fc11c54458f4e037aa99deb0 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 22 Jan 2018 14:25:34 +0100 Subject: [PATCH 4/8] Possible to remove plugins Note that this required adding an `uninstallPlugin()` method to the `PluginRegistry` class of Uranium. --- plugins/PluginBrowser/PluginBrowser.py | 44 ++++++++++++++++++++----- plugins/PluginBrowser/PluginBrowser.qml | 14 ++++---- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index c766a35dbe..d9464dd67b 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -17,6 +17,7 @@ import os import tempfile import platform import zipfile +import shutil i18n_catalog = i18nCatalog("cura") @@ -179,10 +180,12 @@ class PluginBrowser(QObject, Extension): @pyqtSlot(str) def installPlugin(self, file_path): + # Ensure that it starts with a /, as otherwise it doesn't work on windows. if not file_path.startswith("/"): - location = "/" + file_path # Ensure that it starts with a /, as otherwise it doesn't work on windows. + location = "/" + file_path else: location = file_path + result = PluginRegistry.getInstance().installPlugin("file://" + location) self._newly_installed_plugin_ids.append(result["id"]) @@ -190,6 +193,14 @@ class PluginBrowser(QObject, Extension): Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) + @pyqtSlot(str) + def removePlugin(self, plugin_id): + result = PluginRegistry.getInstance().uninstallPlugin(plugin_id) + + self.pluginsMetadataChanged.emit() + + Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) + @pyqtProperty(int, notify = onDownloadProgressChanged) def downloadProgress(self): return self._download_progress @@ -226,26 +237,29 @@ class PluginBrowser(QObject, Extension): if self._plugins_model is None: self._plugins_model = ListModel() self._plugins_model.addRoleName(Qt.UserRole + 1, "name") - self._plugins_model.addRoleName(Qt.UserRole + 2, "version") - self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") - self._plugins_model.addRoleName(Qt.UserRole + 4, "author") - self._plugins_model.addRoleName(Qt.UserRole + 5, "author_email") - self._plugins_model.addRoleName(Qt.UserRole + 6, "already_installed") - self._plugins_model.addRoleName(Qt.UserRole + 7, "file_location") - self._plugins_model.addRoleName(Qt.UserRole + 8, "enabled") - self._plugins_model.addRoleName(Qt.UserRole + 9, "can_upgrade") + self._plugins_model.addRoleName(Qt.UserRole + 2, "id") + self._plugins_model.addRoleName(Qt.UserRole + 3, "version") + self._plugins_model.addRoleName(Qt.UserRole + 4, "short_description") + self._plugins_model.addRoleName(Qt.UserRole + 5, "author") + self._plugins_model.addRoleName(Qt.UserRole + 6, "author_email") + self._plugins_model.addRoleName(Qt.UserRole + 7, "already_installed") + self._plugins_model.addRoleName(Qt.UserRole + 8, "file_location") + self._plugins_model.addRoleName(Qt.UserRole + 9, "enabled") + self._plugins_model.addRoleName(Qt.UserRole + 10, "can_upgrade") else: self._plugins_model.clear() items = [] for metadata in self._plugins_metadata: items.append({ "name": metadata["label"], + "id": metadata["id"], "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], "author_email": "author@gmail.com", "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], + # "enabled": self._checkEnabled(metadata["id"]), "enabled": True, "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) @@ -274,6 +288,18 @@ class PluginBrowser(QObject, Extension): return True # We already installed this plugin, but the registry just doesn't know it yet. return False + def _checkEnabled(self, id): + plugin_registry = PluginRegistry.getInstance() + metadata = plugin_registry.getMetaData(id) + # if metadata != {}: + # if id in self._newly_installed_plugin_ids: + # return False # We already updated this plugin. + # current_version = Version(metadata["plugin"]["version"]) + # new_version = Version(version) + # if new_version > current_version: + # return True + return False + def _onRequestFinished(self, reply): reply_url = reply.url().toString() if reply.error() == QNetworkReply.TimeoutError: diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 5c148b07d1..728f3139f7 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -154,7 +154,7 @@ UM.Dialog { // Plugin actions Row { id: pluginActions - width: 72 + UM.Theme.getSize("default_margin").width + width: 180 + UM.Theme.getSize("default_margin").width height: parent.height anchors { top: parent.top @@ -165,7 +165,6 @@ UM.Dialog { layoutDirection: Qt.RightToLeft spacing: UM.Theme.getSize("default_margin").width - /* Rectangle { id: removeControls visible: model.already_installed @@ -182,12 +181,14 @@ UM.Dialog { return true; } } + onClicked: { + manager.removePlugin(model.id); + } style: ButtonStyle { background: Rectangle { color: white implicitWidth: 108 implicitHeight: 30 - // radius: 4 border { width: 1 color: "grey" @@ -224,7 +225,6 @@ UM.Dialog { color: "transparent" implicitWidth: 30 implicitHeight: 30 - // radius: 4 } label: Text { verticalAlignment: Text.AlignVCenter @@ -246,10 +246,9 @@ UM.Dialog { else { removeDropDown.open = false } - } - } + Rectangle { id: divider width: 1 @@ -257,6 +256,7 @@ UM.Dialog { anchors.right: removeDropDown.left color: "grey" } + Column { id: options anchors { @@ -278,7 +278,7 @@ UM.Dialog { } } } - */ + Button { id: updateButton visible: model.already_installed && model.can_upgrade From dcb1ac5deb1a58ae68a135c092e258439872469e Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 23 Jan 2018 16:20:19 +0100 Subject: [PATCH 5/8] Plugin browswer updates - Partial refactor - Preparation for merge with plugins in preferences --- plugins/PluginBrowser/PluginBrowser.py | 120 +++++++++++++++++----- plugins/PluginBrowser/PluginBrowser.qml | 130 ++++++++++++++++++++---- 2 files changed, 205 insertions(+), 45 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index d9464dd67b..e1862dc06e 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -1,17 +1,20 @@ # Copyright (c) 2017 Ultimaker B.V. # PluginBrowser is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import QUrl, QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply + +from UM.Application import Application +from UM.Qt.ListModel import ListModel +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry +from UM.Qt.Bindings.PluginsModel import PluginsModel from UM.Extension import Extension from UM.i18n import i18nCatalog -from UM.Logger import Logger -from UM.Qt.ListModel import ListModel -from UM.PluginRegistry import PluginRegistry -from UM.Application import Application + from UM.Version import Version from UM.Message import Message -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply -from PyQt5.QtCore import QUrl, QObject, Qt, pyqtProperty, pyqtSignal, pyqtSlot - import json import os import tempfile @@ -21,6 +24,39 @@ import shutil i18n_catalog = i18nCatalog("cura") +# Architecture thoughts: +# ------------------------------------------------------------------------------ +# The plugin manager has 2 parts: the browser and the installer. +# +# UNINSTALLING: +# ------------------------------------------------------------------------------ +# Uninstalling is done via the PluginRegistry's uninstallPlugin() method, +# supplied with a plugin_id. Uninstalling is currently done instantly so there's +# no need to use an list of "newly uninstalled plugins" but this will be needed +# in the future with more complex plugins, as well as when merging the built-in +# plugins into the plugin browser. +# +# STATUS: +# ------------------------------------------------------------------------------ +# 'status' is used to keep track of installations and uninstallations. It is +# intended to replace "newly installed" and "newly uninstalled" lists. These +# lists introduce a lot of complexity if a plugin finds itself on both lists. +# +# 'status' can have several values: +# - "uninstalled" +# - "will_install" +# - "installed" +# - "will_uninstall" +# +# This is more civilized than checking if a plugin's metadata exists. It also +# means uninstalling doesn't require erasing the metadata. +# +# ACTIVE: +# ------------------------------------------------------------------------------ +# Although 'active' could have been lumped with 'status', it is essentially a +# sub-status within 'installed' and 'will_uninstall'. Rather than create more +# statuses such as 'active_installed' and 'active_will_uninstall', the 'active' +# property is just a boolean used in conjunction with 'status'. class PluginBrowser(QObject, Extension): def __init__(self, parent=None): @@ -35,11 +71,13 @@ class PluginBrowser(QObject, Extension): self._download_plugin_reply = None self._network_manager = None + self._plugin_registry = Application.getInstance().getPluginRegistry() self._plugins_metadata = [] self._plugins_model = None self._dialog = None + self._restartDialog = None self._download_progress = 0 self._is_downloading = False @@ -53,16 +91,27 @@ class PluginBrowser(QObject, Extension): ) ] - # Installed plugins are really installed after reboot. In order to prevent the user from downloading the - # same file over and over again, we keep track of the upgraded plugins. + # Installed plugins are really installed after reboot. In order to + # prevent the user from downloading the same file over and over again, + # we keep track of the upgraded plugins. + + # NOTE: This will be depreciated in favor of the 'status' system. self._newly_installed_plugin_ids = [] + self._newly_uninstalled_plugin_ids = [] + + self._plugin_statuses = {} # type: Dict[str, str] # variables for the license agreement dialog self._license_dialog_plugin_name = "" self._license_dialog_license_content = "" self._license_dialog_plugin_file_location = "" + self._restart_dialog_message = "" showLicenseDialog = pyqtSignal() + showRestartDialog = pyqtSignal() + pluginsMetadataChanged = pyqtSignal() + onDownloadProgressChanged = pyqtSignal() + onIsDownloadingChanged = pyqtSignal() @pyqtSlot(result = str) def getLicenseDialogPluginName(self): @@ -76,15 +125,19 @@ class PluginBrowser(QObject, Extension): def getLicenseDialogLicenseContent(self): return self._license_dialog_license_content + @pyqtSlot(result = str) + def getRestartDialogMessage(self): + return self._restart_dialog_message + def openLicenseDialog(self, plugin_name, license_content, plugin_file_location): self._license_dialog_plugin_name = plugin_name self._license_dialog_license_content = license_content self._license_dialog_plugin_file_location = plugin_file_location self.showLicenseDialog.emit() - pluginsMetadataChanged = pyqtSignal() - onDownloadProgressChanged = pyqtSignal() - onIsDownloadingChanged = pyqtSignal() + def openRestartDialog(self, message): + self._restart_dialog_message = message + self.showRestartDialog.emit() @pyqtProperty(bool, notify = onIsDownloadingChanged) def isDownloading(self): @@ -191,12 +244,14 @@ class PluginBrowser(QObject, Extension): self._newly_installed_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() - Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) + self.openRestartDialog(result["message"]) + # Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) @pyqtSlot(str) def removePlugin(self, plugin_id): result = PluginRegistry.getInstance().uninstallPlugin(plugin_id) + self._newly_uninstalled_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) @@ -233,19 +288,12 @@ class PluginBrowser(QObject, Extension): self.setIsDownloading(False) @pyqtProperty(QObject, notify=pluginsMetadataChanged) + def pluginsModel(self): + self._plugins_model = PluginsModel() + """ if self._plugins_model is None: - self._plugins_model = ListModel() - self._plugins_model.addRoleName(Qt.UserRole + 1, "name") - self._plugins_model.addRoleName(Qt.UserRole + 2, "id") - self._plugins_model.addRoleName(Qt.UserRole + 3, "version") - self._plugins_model.addRoleName(Qt.UserRole + 4, "short_description") - self._plugins_model.addRoleName(Qt.UserRole + 5, "author") - self._plugins_model.addRoleName(Qt.UserRole + 6, "author_email") - self._plugins_model.addRoleName(Qt.UserRole + 7, "already_installed") - self._plugins_model.addRoleName(Qt.UserRole + 8, "file_location") - self._plugins_model.addRoleName(Qt.UserRole + 9, "enabled") - self._plugins_model.addRoleName(Qt.UserRole + 10, "can_upgrade") + self._plugins_model = PluginsModel() else: self._plugins_model.clear() items = [] @@ -257,16 +305,19 @@ class PluginBrowser(QObject, Extension): "short_description": metadata["short_description"], "author": metadata["author"], "author_email": "author@gmail.com", + "status": self._checkInstallStatus(metadata["id"]), "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], - # "enabled": self._checkEnabled(metadata["id"]), + # "active": self._checkActive(metadata["id"]), "enabled": True, "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) self._plugins_model.setItems(items) + """ return self._plugins_model def _checkCanUpgrade(self, id, version): + plugin_registry = Application.getInstance().getPluginRegistry() plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: @@ -281,13 +332,26 @@ class PluginBrowser(QObject, Extension): def _checkAlreadyInstalled(self, id): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) - if metadata != {}: + # We already installed this plugin, but the registry just doesn't know it yet. + if id in self._newly_installed_plugin_ids: + return True + # We already uninstalled this plugin, but the registry just doesn't know it yet: + elif id in self._newly_uninstalled_plugin_ids: + return False + elif metadata != {}: return True else: - if id in self._newly_installed_plugin_ids: - return True # We already installed this plugin, but the registry just doesn't know it yet. return False + def _checkInstallStatus(self, plugin_id): + plugin_registry = PluginRegistry.getInstance() + + # If plugin is registered, it's installed: + if plugin_id in plugin_registry._plugins: + return "installed" + else: + return "uninstalled" + def _checkEnabled(self, id): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 728f3139f7..1a606e114b 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -1,4 +1,6 @@ -import UM 1.1 as UM +// Copyright (c) 2017 Ultimaker B.V. +// PluginBrowser is released under the terms of the LGPLv3 or higher. + import QtQuick 2.2 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 @@ -7,18 +9,34 @@ import QtQuick.Controls.Styles 1.4 // TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles +import UM 1.1 as UM + UM.Dialog { id: base title: catalog.i18nc("@title:tab", "Plugins"); width: 800 * screenScaleFactor - height: 600 * screenScaleFactor + height: 640 * screenScaleFactor minimumWidth: 350 * screenScaleFactor minimumHeight: 350 * screenScaleFactor - Item { - anchors.fill: parent + Column { + // anchors.fill: parent + height: parent.height + width: parent.width + spacing: UM.Theme.getSize("default_margin").height + Rectangle { + id: topBar + width: parent.width + color: "red" + height: 30 + Text { + text: "Search" + } + } + + /* Item { id: topBar height: childrenRect.height; @@ -39,15 +57,24 @@ UM.Dialog { enabled: !manager.isDownloading } } + */ // Scroll view breaks in QtQuick.Controls 2.x + Label { + text: "Installed Plugins" + } ScrollView { + id: installedPluginList width: parent.width + height: 280 + /* anchors.top: topBar.bottom - anchors.bottom: bottomBar.top + anchors.bottom: availiblePluginList.top anchors.bottomMargin: UM.Theme.getSize("default_margin").height + */ frameVisible: true + ListView { id: pluginList model: manager.pluginsModel @@ -56,13 +83,45 @@ UM.Dialog { delegate: pluginDelegate } } + Label { + text: "Availible plugins..." + } + /* + Rectangle { + width: parent.width + color: "red" + height: 200 + Text { + text: "Plugins not installed yet" + } + } + */ - Item { + // Scroll view breaks in QtQuick.Controls 2.x + ScrollView { + id: availiblePluginList + width: parent.width + /* + anchors.top: installedPluginList.bottom + anchors.bottom: bottomBar.top + anchors.bottomMargin: UM.Theme.getSize("default_margin").height + */ + frameVisible: true + height: 180 + + Rectangle { + width: parent.width + color: "red" + height: 1000 + } + } + + Rectangle { id: bottomBar width: parent.width - height: closeButton.height - anchors.bottom: parent.bottom - anchors.left: parent.left + height: childrenRect.height; + // anchors.bottom: parent.bottom + // anchors.left: parent.left Button { id: closeButton @@ -79,7 +138,7 @@ UM.Dialog { } } - Item { + Rectangle { Component { id: pluginDelegate @@ -109,7 +168,8 @@ UM.Dialog { pixelSize: 13 bold: true } - color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("secondary") + // color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("secondary") + color: UM.Theme.getColor("text") } Label { @@ -367,11 +427,9 @@ UM.Dialog { } UM.I18nCatalog { id: catalog; name: "cura" } - Connections - { + Connections { target: manager - onShowLicenseDialog: - { + onShowLicenseDialog: { licenseDialog.pluginName = manager.getLicenseDialogPluginName(); licenseDialog.licenseContent = manager.getLicenseDialogLicenseContent(); licenseDialog.pluginFileLocation = manager.getLicenseDialogPluginFileLocation(); @@ -379,8 +437,7 @@ UM.Dialog { } } - UM.Dialog - { + UM.Dialog { id: licenseDialog title: catalog.i18nc("@title:window", "Plugin License Agreement") @@ -444,5 +501,44 @@ UM.Dialog { } ] } + + Connections { + target: manager + onShowRestartDialog: { + restartDialog.message = manager.getRestartDialogMessage(); + restartDialog.show(); + } + } + + UM.Dialog { + id: restartDialog + // title: catalog.i18nc("@title:tab", "Plugins"); + width: 360 * screenScaleFactor + height: 180 * screenScaleFactor + minimumWidth: 360 * screenScaleFactor + minimumHeight: 180 * screenScaleFactor + property var message; + + Text { + id: message + text: restartDialog.message != null ? restartDialog.message : "" + } + Button { + id: laterButton + text: "Later" + onClicked: restartDialog.close(); + anchors.left: parent.left + anchors.bottom: parent.bottom + } + + Button { + id: restartButton + text: "Restart now" + onClicked: restartDialog.close(); + anchors.right: parent.right + anchors.bottom: parent.bottom + } + } + } } From 211f87bf098b575183670ceef7bbf200b66343d0 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 26 Jan 2018 14:59:47 +0100 Subject: [PATCH 6/8] New format and functionality - Browser is combined with plugin settings - Two tabs, one for viewing 3rd party plugins, one for installed - Enable/disable for bundled plugins - Install/Remove/Update for 3rd party plugins --- plugins/PluginBrowser/PluginBrowser.py | 132 ++++--- plugins/PluginBrowser/PluginBrowser.qml | 502 +++++++----------------- plugins/PluginBrowser/PluginEntry.qml | 473 ++++++++++++++++++++++ 3 files changed, 693 insertions(+), 414 deletions(-) create mode 100644 plugins/PluginBrowser/PluginEntry.qml diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index e1862dc06e..f08eb294e8 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -22,6 +22,8 @@ import platform import zipfile import shutil +from cura.CuraApplication import CuraApplication + i18n_catalog = i18nCatalog("cura") # Architecture thoughts: @@ -76,6 +78,11 @@ class PluginBrowser(QObject, Extension): self._plugins_metadata = [] self._plugins_model = None + # Can be 'installed' or 'availble' + self._view = "available" + + self._restart_required = False + self._dialog = None self._restartDialog = None self._download_progress = 0 @@ -112,6 +119,8 @@ class PluginBrowser(QObject, Extension): pluginsMetadataChanged = pyqtSignal() onDownloadProgressChanged = pyqtSignal() onIsDownloadingChanged = pyqtSignal() + restartRequiredChanged = pyqtSignal() + viewChanged = pyqtSignal() @pyqtSlot(result = str) def getLicenseDialogPluginName(self): @@ -245,6 +254,8 @@ class PluginBrowser(QObject, Extension): self.pluginsMetadataChanged.emit() self.openRestartDialog(result["message"]) + self._restart_required = True + self.restartRequiredChanged.emit() # Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) @pyqtSlot(str) @@ -254,8 +265,23 @@ class PluginBrowser(QObject, Extension): self._newly_uninstalled_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() + self._restart_required = True + self.restartRequiredChanged.emit() + Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) + @pyqtSlot(str) + def enablePlugin(self, plugin_id): + self._plugin_registry.enablePlugin(plugin_id) + self.pluginsMetadataChanged.emit() + Logger.log("i", "%s was set as 'active'", id) + + @pyqtSlot(str) + def disablePlugin(self, plugin_id): + self._plugin_registry.disablePlugin(plugin_id) + self.pluginsMetadataChanged.emit() + Logger.log("i", "%s was set as 'deactive'", id) + @pyqtProperty(int, notify = onDownloadProgressChanged) def downloadProgress(self): return self._download_progress @@ -287,51 +313,51 @@ class PluginBrowser(QObject, Extension): self.setDownloadProgress(0) self.setIsDownloading(False) - @pyqtProperty(QObject, notify=pluginsMetadataChanged) + @pyqtSlot(str) + def setView(self, view): + self._view = view + self.viewChanged.emit() + self.pluginsMetadataChanged.emit() + @pyqtProperty(QObject, notify=pluginsMetadataChanged) def pluginsModel(self): - self._plugins_model = PluginsModel() - """ - if self._plugins_model is None: - self._plugins_model = PluginsModel() - else: - self._plugins_model.clear() - items = [] - for metadata in self._plugins_metadata: - items.append({ - "name": metadata["label"], - "id": metadata["id"], - "version": metadata["version"], - "short_description": metadata["short_description"], - "author": metadata["author"], - "author_email": "author@gmail.com", - "status": self._checkInstallStatus(metadata["id"]), - "already_installed": self._checkAlreadyInstalled(metadata["id"]), - "file_location": metadata["file_location"], - # "active": self._checkActive(metadata["id"]), - "enabled": True, - "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) - }) - self._plugins_model.setItems(items) - """ + print("Updating plugins model...", self._view) + self._plugins_model = PluginsModel(self._view) + # self._plugins_model.update() + + # Check each plugin the registry for matching plugin from server + # metadata, and if found, compare the versions. Higher version sets + # 'can_upgrade' to 'True': + for plugin in self._plugins_model.items: + if self._checkCanUpgrade(plugin["id"], plugin["version"]): + plugin["can_upgrade"] = True + print(self._plugins_metadata) + + for item in self._plugins_metadata: + if item["id"] == plugin["id"]: + plugin["update_url"] = item["file_location"] + print("Updating from", item["file_location"]) + return self._plugins_model + + def _checkCanUpgrade(self, id, version): - plugin_registry = Application.getInstance().getPluginRegistry() - plugin_registry = PluginRegistry.getInstance() - metadata = plugin_registry.getMetaData(id) - if metadata != {}: - if id in self._newly_installed_plugin_ids: - return False # We already updated this plugin. - current_version = Version(metadata["plugin"]["version"]) - new_version = Version(version) - if new_version > current_version: - return True + + # TODO: This could maybe be done more efficiently using a dictionary... + + # Scan plugin server data for plugin with the given id: + for plugin in self._plugins_metadata: + if id == plugin["id"]: + reg_version = Version(version) + new_version = Version(plugin["version"]) + if new_version > reg_version: + Logger.log("i", "%s has an update availible: %s", plugin["id"], plugin["version"]) + return True return False def _checkAlreadyInstalled(self, id): - plugin_registry = PluginRegistry.getInstance() - metadata = plugin_registry.getMetaData(id) + metadata = self._plugin_registry.getMetaData(id) # We already installed this plugin, but the registry just doesn't know it yet. if id in self._newly_installed_plugin_ids: return True @@ -344,24 +370,14 @@ class PluginBrowser(QObject, Extension): return False def _checkInstallStatus(self, plugin_id): - plugin_registry = PluginRegistry.getInstance() - - # If plugin is registered, it's installed: - if plugin_id in plugin_registry._plugins: + if plugin_id in self._plugin_registry.getInstalledPlugins(): return "installed" else: return "uninstalled" def _checkEnabled(self, id): - plugin_registry = PluginRegistry.getInstance() - metadata = plugin_registry.getMetaData(id) - # if metadata != {}: - # if id in self._newly_installed_plugin_ids: - # return False # We already updated this plugin. - # current_version = Version(metadata["plugin"]["version"]) - # new_version = Version(version) - # if new_version > current_version: - # return True + if id in self._plugin_registry.getActivePlugins(): + return True return False def _onRequestFinished(self, reply): @@ -384,7 +400,11 @@ class PluginBrowser(QObject, Extension): if reply_url == self._api_url + "plugins": try: json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) + + # Add metadata to the manager: self._plugins_metadata = json_data + print(self._plugins_metadata) + self._plugin_registry.addExternalPlugins(self._plugins_metadata) self.pluginsMetadataChanged.emit() except json.decoder.JSONDecodeError: Logger.log("w", "Received an invalid print job state message: Not valid JSON.") @@ -410,3 +430,15 @@ class PluginBrowser(QObject, Extension): self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged) + + @pyqtProperty(bool, notify=restartRequiredChanged) + def restartRequired(self): + return self._restart_required + + @pyqtProperty(str, notify=viewChanged) + def viewing(self): + return self._view + + @pyqtSlot() + def restart(self): + CuraApplication.getInstance().quit() diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 1a606e114b..229d5c1755 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -11,7 +11,7 @@ import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM -UM.Dialog { +Window { id: base title: catalog.i18nc("@title:tab", "Plugins"); @@ -19,109 +19,154 @@ UM.Dialog { height: 640 * screenScaleFactor minimumWidth: 350 * screenScaleFactor minimumHeight: 350 * screenScaleFactor + color: "white" - Column { - // anchors.fill: parent - height: parent.height - width: parent.width - spacing: UM.Theme.getSize("default_margin").height + Item { + id: view + anchors { + fill: parent + leftMargin: UM.Theme.getSize("default_margin").width + rightMargin: UM.Theme.getSize("default_margin").width + topMargin: UM.Theme.getSize("default_margin").height + bottomMargin: UM.Theme.getSize("default_margin").height + } Rectangle { id: topBar width: parent.width - color: "red" - height: 30 - Text { - text: "Search" + color: "transparent" + height: childrenRect.height + + Row { + spacing: 12 + height: childrenRect.height + width: childrenRect.width + anchors.horizontalCenter: parent.horizontalCenter + + Button { + text: "Install" + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 48 + Rectangle { + visible: manager.viewing == "available" ? true : false + color: UM.Theme.getColor("primary") + anchors.bottom: parent.bottom + width: parent.width + height: 3 + } + } + label: Text { + text: control.text + color: UM.Theme.getColor("text") + font { + pixelSize: 15 + } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: manager.setView("available") + } + + Button { + text: "Manage" + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 48 + Rectangle { + visible: manager.viewing == "installed" ? true : false + color: UM.Theme.getColor("primary") + anchors.bottom: parent.bottom + width: parent.width + height: 3 + } + } + label: Text { + text: control.text + color: UM.Theme.getColor("text") + font { + pixelSize: 15 + } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: manager.setView("installed") + } } } - /* - Item { - id: topBar - height: childrenRect.height; - width: parent.width - - Label { - id: introText - text: catalog.i18nc("@label", "Here you can find a list of Third Party plugins.") - width: parent.width - height: 30 - } - - Button { - id: refresh - text: catalog.i18nc("@action:button", "Refresh") - onClicked: manager.requestPluginList() - anchors.right: parent.right - enabled: !manager.isDownloading - } - } - */ - // Scroll view breaks in QtQuick.Controls 2.x - Label { - text: "Installed Plugins" - } ScrollView { id: installedPluginList width: parent.width - height: 280 - /* - anchors.top: topBar.bottom - anchors.bottom: availiblePluginList.top - anchors.bottomMargin: UM.Theme.getSize("default_margin").height - */ - frameVisible: true + height: 400 + anchors { + top: topBar.bottom + topMargin: UM.Theme.getSize("default_margin").height + bottom: bottomBar.top + bottomMargin: UM.Theme.getSize("default_margin").height + } + + frameVisible: true ListView { id: pluginList - model: manager.pluginsModel - anchors.fill: parent property var activePlugin - delegate: pluginDelegate - } - } - Label { - text: "Availible plugins..." - } - /* - Rectangle { - width: parent.width - color: "red" - height: 200 - Text { - text: "Plugins not installed yet" - } - } - */ + property var filter: "installed" - // Scroll view breaks in QtQuick.Controls 2.x - ScrollView { - id: availiblePluginList - width: parent.width - /* - anchors.top: installedPluginList.bottom - anchors.bottom: bottomBar.top - anchors.bottomMargin: UM.Theme.getSize("default_margin").height - */ - frameVisible: true - height: 180 + anchors.fill: parent - Rectangle { - width: parent.width - color: "red" - height: 1000 + model: manager.pluginsModel + delegate: PluginEntry {} } } Rectangle { id: bottomBar width: parent.width - height: childrenRect.height; - // anchors.bottom: parent.bottom - // anchors.left: parent.left + height: childrenRect.height + color: "transparent" + anchors.bottom: parent.bottom + + Label { + visible: manager.restartRequired + text: "You will need to restart Cura before changes in plugins have effect." + height: 30 + verticalAlignment: Text.AlignVCenter + } + Button { + id: restartChangedButton + text: "Quit Cura" + anchors.right: closeButton.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + visible: manager.restartRequired + iconName: "dialog-restart" + onClicked: manager.restart() + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: UM.Theme.getColor("primary") + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "white" + font { + pixelSize: 13 + bold: true + } + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } Button { id: closeButton @@ -133,298 +178,27 @@ UM.Dialog { } base.close(); } - anchors.bottom: parent.bottom anchors.right: parent.right - } - } - - Rectangle { - Component { - id: pluginDelegate - - Rectangle { - width: pluginList.width; - height: 102 - color: index % 2 ? UM.Theme.getColor("secondary") : "white" - - // Plugin info - Column { - id: pluginInfo - height: parent.height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.right: authorInfo.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - Label { - text: model.name - width: parent.width - height: 24 - wrapMode: Text.WordWrap - verticalAlignment: Text.AlignVCenter - font { - pixelSize: 13 - bold: true - } - // color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("secondary") - color: UM.Theme.getColor("text") - } - - Label { - text: model.short_description - width: parent.width - height: 72 - wrapMode: Text.WordWrap + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 30 + border { + width: 1 + color: UM.Theme.getColor("lining") } } - - // Author info - Column { - id: authorInfo - width: 192 - height: parent.height - - - anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - - anchors.right: pluginActions.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - Label { - text: model.author - width: parent.width - height: 24 - wrapMode: Text.WordWrap - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignRight - } - - Label { - text: model.author_email - width: parent.width - height: 72 - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignRight - } - } - - // Plugin actions - Row { - id: pluginActions - width: 180 + UM.Theme.getSize("default_margin").width - height: parent.height - anchors { - top: parent.top - right: parent.right - topMargin: UM.Theme.getSize("default_margin").height - rightMargin: UM.Theme.getSize("default_margin").width - } - layoutDirection: Qt.RightToLeft - spacing: UM.Theme.getSize("default_margin").width - - Rectangle { - id: removeControls - visible: model.already_installed - width: 108 - height: 30 - color: "transparent" - Button { - id: removeButton - text: "Remove" - enabled: { - if ( manager.isDownloading && pluginList.activePlugin == model ) { - return false; - } else { - return true; - } - } - onClicked: { - manager.removePlugin(model.id); - } - style: ButtonStyle { - background: Rectangle { - color: white - implicitWidth: 108 - implicitHeight: 30 - border { - width: 1 - color: "grey" - } - } - label: Text { - verticalAlignment: Text.AlignVCenter - color: "grey" - text: control.text - horizontalAlignment: Text.AlignLeft - } - } - } - Button { - id: removeDropDown - property bool open: false - UM.RecolorImage { - anchors.centerIn: parent - height: 10 - width: 10 - source: UM.Theme.getIcon("arrow_bottom") - color: "grey" - } - enabled: { - if ( model.required || ( manager.isDownloading && pluginList.activePlugin == model )) { - return false; - } else { - return true; - } - } - anchors.right: parent.right - style: ButtonStyle { - background: Rectangle { - color: "transparent" - implicitWidth: 30 - implicitHeight: 30 - } - label: Text { - verticalAlignment: Text.AlignVCenter - color: "grey" - text: control.text - horizontalAlignment: Text.AlignHCenter - } - } - - - - // For the disable option: - // onClicked: pluginList.model.setEnabled(model.id, checked) - - onClicked: { - if ( !removeDropDown.open ) { - removeDropDown.open = true - } - else { - removeDropDown.open = false - } - } - } - - Rectangle { - id: divider - width: 1 - height: parent.height - anchors.right: removeDropDown.left - color: "grey" - } - - Column { - id: options - anchors { - top: removeButton.bottom - left: parent.left - right: parent.right - } - height: childrenRect.height - visible: removeDropDown.open - - Button { - text: "Disable" - height: 30 - width: parent.width - onClicked: { - removeDropDown.open = false - model.setEnabled(model.id, checked) - } - } - } - } - - Button { - id: updateButton - visible: model.already_installed && model.can_upgrade - // visible: model.already_installed - text: { - // If currently downloading: - if ( manager.isDownloading && pluginList.activePlugin == model ) { - return catalog.i18nc( "@action:button", "Cancel" ); - } else { - return catalog.i18nc("@action:button", "Update"); - } - } - style: ButtonStyle { - background: Rectangle { - color: UM.Theme.getColor("primary") - implicitWidth: 72 - implicitHeight: 30 - // radius: 4 - } - label: Text { - verticalAlignment: Text.AlignVCenter - color: "white" - text: control.text - horizontalAlignment: Text.AlignHCenter - } - } - } - Button { - id: installButton - visible: !model.already_installed - text: { - // If currently downloading: - if ( manager.isDownloading && pluginList.activePlugin == model ) { - return catalog.i18nc( "@action:button", "Cancel" ); - } else { - return catalog.i18nc("@action:button", "Install"); - } - } - onClicked: { - if ( manager.isDownloading && pluginList.activePlugin == model ) { - manager.cancelDownload(); - } else { - pluginList.activePlugin = model; - manager.downloadAndInstallPlugin( model.file_location ); - } - } - style: ButtonStyle { - background: Rectangle { - color: UM.Theme.getColor("primary") - implicitWidth: 72 - implicitHeight: 30 - // radius: 4 - } - label: Text { - verticalAlignment: Text.AlignVCenter - color: "white" - text: control.text - horizontalAlignment: Text.AlignHCenter - } - } - } - ProgressBar { - id: progressbar - minimumValue: 0; - maximumValue: 100 - anchors.left: installButton.left - anchors.right: installButton.right - anchors.top: installButton.bottom - anchors.topMargin: 4 - value: manager.isDownloading ? manager.downloadProgress : 0 - visible: manager.isDownloading && pluginList.activePlugin == model - style: ProgressBarStyle { - background: Rectangle { - color: "lightgray" - implicitHeight: 6 - } - progress: Rectangle { - color: UM.Theme.getColor("primary") - } - } - } - + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignHCenter } } } } + UM.I18nCatalog { id: catalog; name: "cura" } Connections { diff --git a/plugins/PluginBrowser/PluginEntry.qml b/plugins/PluginBrowser/PluginEntry.qml new file mode 100644 index 0000000000..d53e839094 --- /dev/null +++ b/plugins/PluginBrowser/PluginEntry.qml @@ -0,0 +1,473 @@ +// Copyright (c) 2017 Ultimaker B.V. +// PluginBrowser is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Dialogs 1.1 +import QtQuick.Window 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles + +import UM 1.1 as UM + +Component { + id: pluginDelegate + + Rectangle { + + // Don't show required plugins as they can't be managed anyway: + height: !model.required ? 84 : 0 + visible: !model.required ? true : false + color: "transparent" + anchors { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + + // Bottom border: + Rectangle { + color: UM.Theme.getColor("lining") + width: parent.width + height: 1 + anchors.bottom: parent.bottom + } + + // Plugin info + Column { + id: pluginInfo + + property var color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") + + // Styling: + height: parent.height + anchors { + left: parent.left + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + right: authorInfo.left + rightMargin: UM.Theme.getSize("default_margin").width + } + + + Label { + text: model.name + width: parent.width + height: 24 + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + font { + pixelSize: 13 + bold: true + } + color: pluginInfo.color + } + + Text { + text: model.description + width: parent.width + height: 36 + clip: true + wrapMode: Text.WordWrap + color: pluginInfo.color + elide: Text.ElideRight + } + } + + // Author info + Column { + id: authorInfo + width: 192 + height: parent.height + anchors { + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + right: pluginActions.left + rightMargin: UM.Theme.getSize("default_margin").width + } + + Label { + text: ""+model.author+"" + width: parent.width + height: 24 + wrapMode: Text.WordWrap + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + onLinkActivated: Qt.openUrlExternally("mailto:"+model.author_email+"?Subject=Cura: "+model.name+" Plugin") + color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") + } + } + + // Plugin actions + Row { + id: pluginActions + + width: 96 + height: parent.height + anchors { + top: parent.top + right: parent.right + topMargin: UM.Theme.getSize("default_margin").height + } + layoutDirection: Qt.RightToLeft + spacing: UM.Theme.getSize("default_margin").width + + // For 3rd-Party Plugins: + Button { + id: installButton + text: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return catalog.i18nc( "@action:button", "Cancel" ); + } else { + if (model.can_upgrade) { + return catalog.i18nc("@action:button", "Update"); + } + return catalog.i18nc("@action:button", "Install"); + } + } + visible: model.external && ((model.status !== "installed") || model.can_upgrade) + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: "transparent" + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Label { + text: control.text + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + manager.cancelDownload(); + } else { + pluginList.activePlugin = model; + if ( model.can_upgrade ) { + manager.downloadAndInstallPlugin( model.update_url ); + } else { + manager.downloadAndInstallPlugin( model.update_url ); + } + + } + } + } + Button { + id: removeButton + text: "Uninstall" + visible: model.external && model.status == "installed" + enabled: !manager.isDownloading + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: "transparent" + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + text: control.text + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: manager.removePlugin( model.id ) + } + + // For Ultimaker Plugins: + Button { + id: enableButton + text: "Enable" + visible: !model.external && model.enabled == false + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: "transparent" + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + text: control.text + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: { + manager.enablePlugin(model.id); + } + } + Button { + id: disableButton + text: "Disable" + visible: !model.external && model.enabled == true + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: "transparent" + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + text: control.text + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + onClicked: { + manager.disablePlugin(model.id); + } + } + /* + Rectangle { + id: removeControls + visible: model.status == "installed" && model.enabled + width: 96 + height: 30 + color: "transparent" + Button { + id: removeButton + text: "Disable" + enabled: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return false; + } else if ( model.required ) { + return false; + } else { + return true; + } + } + onClicked: { + manager.disablePlugin(model.id); + } + style: ButtonStyle { + background: Rectangle { + color: "white" + implicitWidth: 96 + implicitHeight: 30 + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignLeft + } + } + } + Button { + id: removeDropDown + property bool open: false + UM.RecolorImage { + anchors.centerIn: parent + height: 10 + width: 10 + source: UM.Theme.getIcon("arrow_bottom") + color: "grey" + } + enabled: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return false; + } else if ( model.required ) { + return false; + } else { + return true; + } + } + anchors.right: parent.right + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 30 + implicitHeight: 30 + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + + + + // For the disable option: + // onClicked: pluginList.model.setEnabled(model.id, checked) + + onClicked: { + if ( !removeDropDown.open ) { + removeDropDown.open = true + } + else { + removeDropDown.open = false + } + } + } + + Rectangle { + id: divider + width: 1 + height: parent.height + anchors.right: removeDropDown.left + color: UM.Theme.getColor("lining") + } + + Column { + id: options + anchors { + top: removeButton.bottom + left: parent.left + right: parent.right + } + height: childrenRect.height + visible: removeDropDown.open + + Button { + id: disableButton + text: "Remove" + height: 30 + width: parent.width + onClicked: { + removeDropDown.open = false; + manager.removePlugin( model.id ); + } + } + } + } + */ + /* + Button { + id: enableButton + visible: !model.enabled && model.status == "installed" + onClicked: manager.enablePlugin( model.id ); + + text: "Enable" + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 30 + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } + + Button { + id: updateButton + visible: model.status == "installed" && model.can_upgrade && model.enabled + // visible: model.already_installed + text: { + // If currently downloading: + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return catalog.i18nc( "@action:button", "Cancel" ); + } else { + return catalog.i18nc("@action:button", "Update"); + } + } + style: ButtonStyle { + background: Rectangle { + color: UM.Theme.getColor("primary") + implicitWidth: 96 + implicitHeight: 30 + // radius: 4 + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "white" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } + Button { + id: externalControls + visible: model.status == "available" ? true : false + text: { + // If currently downloading: + if ( manager.isDownloading && pluginList.activePlugin == model ) { + return catalog.i18nc( "@action:button", "Cancel" ); + } else { + return catalog.i18nc("@action:button", "Install"); + } + } + onClicked: { + if ( manager.isDownloading && pluginList.activePlugin == model ) { + manager.cancelDownload(); + } else { + pluginList.activePlugin = model; + manager.downloadAndInstallPlugin( model.file_location ); + } + } + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 30 + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: "grey" + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } + } + */ + ProgressBar { + id: progressbar + minimumValue: 0; + maximumValue: 100 + anchors.left: installButton.left + anchors.right: installButton.right + anchors.top: installButton.bottom + anchors.topMargin: 4 + value: manager.isDownloading ? manager.downloadProgress : 0 + visible: manager.isDownloading && pluginList.activePlugin == model + style: ProgressBarStyle { + background: Rectangle { + color: "lightgray" + implicitHeight: 6 + } + progress: Rectangle { + color: UM.Theme.getColor("primary") + } + } + } + + } + } +} From 757f83fad2b81977ba13abe89a308b653f6fb88f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 26 Jan 2018 17:06:36 +0100 Subject: [PATCH 7/8] Change download source `update_url` is used only for updaitng plugins (when `file_location` refers to a place on the hard drive). --- plugins/PluginBrowser/PluginBrowser.py | 1 - plugins/PluginBrowser/PluginEntry.qml | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index f08eb294e8..23c1bafe61 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -336,7 +336,6 @@ class PluginBrowser(QObject, Extension): for item in self._plugins_metadata: if item["id"] == plugin["id"]: plugin["update_url"] = item["file_location"] - print("Updating from", item["file_location"]) return self._plugins_model diff --git a/plugins/PluginBrowser/PluginEntry.qml b/plugins/PluginBrowser/PluginEntry.qml index d53e839094..85fc0b91b8 100644 --- a/plugins/PluginBrowser/PluginEntry.qml +++ b/plugins/PluginBrowser/PluginEntry.qml @@ -64,6 +64,7 @@ Component { bold: true } color: pluginInfo.color + } Text { @@ -154,7 +155,7 @@ Component { if ( model.can_upgrade ) { manager.downloadAndInstallPlugin( model.update_url ); } else { - manager.downloadAndInstallPlugin( model.update_url ); + manager.downloadAndInstallPlugin( model.file_location ); } } From d3b224161694d2d5af5832ef30d56a9a10231d25 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 1 Feb 2018 11:37:23 +0100 Subject: [PATCH 8/8] Fixed "Quit" button & others - Quit button in the dialog post-install now quits the application instead of simply closing the window - Improved styling: - All colors now pulled from `UM.Theme.getColor()` - Dialog has white background - Buttons look like Cura buttons - Removed inaccurate comments regarding the structure that were created while working on it --- plugins/PluginBrowser/PluginBrowser.py | 34 ------------ plugins/PluginBrowser/PluginBrowser.qml | 74 +++++++++++++++++++++---- 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/plugins/PluginBrowser/PluginBrowser.py b/plugins/PluginBrowser/PluginBrowser.py index 23c1bafe61..19721559ab 100644 --- a/plugins/PluginBrowser/PluginBrowser.py +++ b/plugins/PluginBrowser/PluginBrowser.py @@ -26,40 +26,6 @@ from cura.CuraApplication import CuraApplication i18n_catalog = i18nCatalog("cura") -# Architecture thoughts: -# ------------------------------------------------------------------------------ -# The plugin manager has 2 parts: the browser and the installer. -# -# UNINSTALLING: -# ------------------------------------------------------------------------------ -# Uninstalling is done via the PluginRegistry's uninstallPlugin() method, -# supplied with a plugin_id. Uninstalling is currently done instantly so there's -# no need to use an list of "newly uninstalled plugins" but this will be needed -# in the future with more complex plugins, as well as when merging the built-in -# plugins into the plugin browser. -# -# STATUS: -# ------------------------------------------------------------------------------ -# 'status' is used to keep track of installations and uninstallations. It is -# intended to replace "newly installed" and "newly uninstalled" lists. These -# lists introduce a lot of complexity if a plugin finds itself on both lists. -# -# 'status' can have several values: -# - "uninstalled" -# - "will_install" -# - "installed" -# - "will_uninstall" -# -# This is more civilized than checking if a plugin's metadata exists. It also -# means uninstalling doesn't require erasing the metadata. -# -# ACTIVE: -# ------------------------------------------------------------------------------ -# Although 'active' could have been lumped with 'status', it is essentially a -# sub-status within 'installed' and 'will_uninstall'. Rather than create more -# statuses such as 'active_installed' and 'active_will_uninstall', the 'active' -# property is just a boolean used in conjunction with 'status'. - class PluginBrowser(QObject, Extension): def __init__(self, parent=None): super().__init__(parent) diff --git a/plugins/PluginBrowser/PluginBrowser.qml b/plugins/PluginBrowser/PluginBrowser.qml index 229d5c1755..ec4c2a9135 100644 --- a/plugins/PluginBrowser/PluginBrowser.qml +++ b/plugins/PluginBrowser/PluginBrowser.qml @@ -19,7 +19,7 @@ Window { height: 640 * screenScaleFactor minimumWidth: 350 * screenScaleFactor minimumHeight: 350 * screenScaleFactor - color: "white" + color: UM.Theme.getColor("sidebar") Item { id: view @@ -157,7 +157,7 @@ Window { } label: Text { verticalAlignment: Text.AlignVCenter - color: "white" + color: UM.Theme.getColor("button_text") font { pixelSize: 13 bold: true @@ -191,7 +191,7 @@ Window { } label: Text { verticalAlignment: Text.AlignVCenter - color: "grey" + color: UM.Theme.getColor("text") text: control.text horizontalAlignment: Text.AlignHCenter } @@ -284,33 +284,83 @@ Window { } } - UM.Dialog { + Window { id: restartDialog // title: catalog.i18nc("@title:tab", "Plugins"); width: 360 * screenScaleFactor - height: 180 * screenScaleFactor + height: 120 * screenScaleFactor minimumWidth: 360 * screenScaleFactor - minimumHeight: 180 * screenScaleFactor + minimumHeight: 120 * screenScaleFactor + color: UM.Theme.getColor("sidebar") property var message; Text { id: message + anchors { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + } text: restartDialog.message != null ? restartDialog.message : "" } Button { id: laterButton text: "Later" onClicked: restartDialog.close(); - anchors.left: parent.left - anchors.bottom: parent.bottom + anchors { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("default_margin").height + } + style: ButtonStyle { + background: Rectangle { + color: "transparent" + implicitWidth: 96 + implicitHeight: 30 + border { + width: 1 + color: UM.Theme.getColor("lining") + } + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } } + Button { id: restartButton - text: "Restart now" - onClicked: restartDialog.close(); - anchors.right: parent.right - anchors.bottom: parent.bottom + text: "Quit Cura" + anchors { + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("default_margin").height + } + onClicked: manager.restart() + style: ButtonStyle { + background: Rectangle { + implicitWidth: 96 + implicitHeight: 30 + color: UM.Theme.getColor("primary") + } + label: Text { + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("button_text") + font { + pixelSize: 13 + bold: true + } + text: control.text + horizontalAlignment: Text.AlignHCenter + } + } } }