Merge pull request #3618 from Ultimaker/feature_cura_package_api

Backend of the Toolbox
This commit is contained in:
Ian Paschal 2018-04-04 11:01:05 +02:00 committed by GitHub
commit 0cfcc8cd5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 246 additions and 148 deletions

View File

@ -261,7 +261,7 @@ class CuraApplication(QtApplication):
"TranslateTool", "TranslateTool",
"FileLogger", "FileLogger",
"XmlMaterialProfile", "XmlMaterialProfile",
"PluginBrowser", "Toolbox",
"PrepareStage", "PrepareStage",
"MonitorStage" "MonitorStage"
]) ])

View File

@ -1,12 +0,0 @@
# Copyright (c) 2017 Ultimaker B.V.
# PluginBrowser is released under the terms of the LGPLv3 or higher.
from . import PluginBrowser
def getMetaData():
return {}
def register(app):
return {"extension": PluginBrowser.PluginBrowser()}

View File

@ -1,7 +0,0 @@
{
"name": "Plugin Browser",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"api": 4,
"description": "Find, manage and install new plugins."
}

View File

@ -0,0 +1,100 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import re
from typing import Dict
from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal
from UM.Qt.ListModel import ListModel
## Model that holds cura packages. By setting the filter property the instances held by this model can be changed.
class CuraPackageModel(ListModel):
IdRole = Qt.UserRole + 1
TypeRole = Qt.UserRole + 2
NameRole = Qt.UserRole + 3
VersionRole = Qt.UserRole + 4
AuthorRole = Qt.UserRole + 5
DescriptionRole = Qt.UserRole + 6
IconURLRole = Qt.UserRole + 7
ImageURLsRole = Qt.UserRole + 8
def __init__(self, parent = None):
super().__init__(parent)
self._packages_metadata = None
self.addRoleName(CuraPackageModel.IdRole, "id")
self.addRoleName(CuraPackageModel.TypeRole, "type")
self.addRoleName(CuraPackageModel.NameRole, "name")
self.addRoleName(CuraPackageModel.VersionRole, "version")
self.addRoleName(CuraPackageModel.AuthorRole, "author")
self.addRoleName(CuraPackageModel.DescriptionRole, "description")
self.addRoleName(CuraPackageModel.IconURLRole, "icon_url")
self.addRoleName(CuraPackageModel.ImageURLsRole, "image_urls")
# List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str,str]
def setPackagesMetaData(self, data):
self._packages_metadata = data
self._update()
def _update(self):
items = []
for package in self._packages_metadata:
items.append({
"id": package["package_id"],
"type": package["package_type"],
"name": package["display_name"],
"version": package["package_version"],
"author": package["author"],
"description": package["description"],
"icon_url": package["icon_url"] if "icon_url" in package else None,
"image_urls": package["image_urls"]
})
# Filter on all the key-word arguments.
for key, value in self._filter.items():
if "*" in value:
key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
else:
key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
items = filter(key_filter, items)
# Execute all filters.
filtered_items = list(items)
filtered_items.sort(key = lambda k: k["name"])
self.setItems(filtered_items)
## Set the filter of this model based on a string.
# \param filter_dict \type{Dict} Dictionary to do the filtering by.
def setFilter(self, filter_dict: Dict[str, str]) -> None:
if filter_dict != self._filter:
self._filter = filter_dict
self._update()
@pyqtProperty("QVariantMap", fset = setFilter, constant = True)
def filter(self) -> Dict[str, str]:
return self._filter
# Check to see if a container matches with a regular expression
def _matchRegExp(self, metadata, property_name, value):
if property_name not in metadata:
return False
value = re.escape(value) #Escape for regex patterns.
value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match.
if self._ignore_case:
value_pattern = re.compile(value, re.IGNORECASE)
else:
value_pattern = re.compile(value)
return value_pattern.match(str(metadata[property_name]))
# Check to see if a container matches with a string
def _matchString(self, metadata, property_name, value):
if property_name not in metadata:
return False
return value.lower() == str(metadata[property_name]).lower()

View File

@ -1,5 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# PluginBrowser is released under the terms of the LGPLv3 or higher. # Toolbox is released under the terms of the LGPLv3 or higher.
from typing import Dict
from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot from PyQt5.QtCore import QUrl, QObject, pyqtProperty, pyqtSignal, pyqtSlot
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
@ -21,30 +22,33 @@ import platform
import zipfile import zipfile
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from .CuraPackageModel import CuraPackageModel
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
class PluginBrowser(QObject, Extension): ## The Toolbox class is responsible of communicating with the server through the API
class Toolbox(QObject, Extension):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self._api_version = 4 self._api_version = 1
self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version self._api_url = "https://api-staging.ultimaker.com/cura-packages/v%s/" % self._api_version
self._plugin_list_request = None self._package_list_request = None
self._download_plugin_request = None self._download_plugin_request = None
self._download_plugin_reply = None self._download_plugin_reply = None
self._network_manager = None self._network_manager = None
self._plugin_registry = Application.getInstance().getPluginRegistry() self._plugin_registry = Application.getInstance().getPluginRegistry()
self._packages_version_number = self._plugin_registry.APIVersion
self._plugins_metadata = [] self._packages_metadata = [] # Stores the remote information of the packages
self._plugins_model = None self._packages_model = None # Model that list the remote available packages
# Can be 'installed' or 'available' # Nowadays can be 'plugins', 'materials' or 'installed'
self._view = "available" self._current_view = "plugins"
self._detail_view = "" self._detail_view = False
self._restart_required = False self._restart_required = False
@ -81,12 +85,13 @@ class PluginBrowser(QObject, Extension):
showLicenseDialog = pyqtSignal() showLicenseDialog = pyqtSignal()
showRestartDialog = pyqtSignal() showRestartDialog = pyqtSignal()
pluginsMetadataChanged = pyqtSignal() packagesMetadataChanged = pyqtSignal()
onDownloadProgressChanged = pyqtSignal() onDownloadProgressChanged = pyqtSignal()
onIsDownloadingChanged = pyqtSignal() onIsDownloadingChanged = pyqtSignal()
restartRequiredChanged = pyqtSignal() restartRequiredChanged = pyqtSignal()
viewChanged = pyqtSignal() viewChanged = pyqtSignal()
detailViewChanged = pyqtSignal() detailViewChanged = pyqtSignal()
filterChanged = pyqtSignal()
@pyqtSlot(result = str) @pyqtSlot(result = str)
def getLicenseDialogPluginName(self): def getLicenseDialogPluginName(self):
@ -119,21 +124,20 @@ class PluginBrowser(QObject, Extension):
return self._is_downloading return self._is_downloading
@pyqtSlot() @pyqtSlot()
def browsePlugins(self): def browsePackages(self):
self._createNetworkManager() self._createNetworkManager()
self.requestPluginList() self.requestPackageList()
if not self._dialog: if not self._dialog:
self._dialog = self._createDialog("PluginBrowser.qml") self._dialog = self._createDialog("PluginBrowser.qml")
self._dialog.show() self._dialog.show()
@pyqtSlot() def requestPackageList(self):
def requestPluginList(self): Logger.log("i", "Requesting package list")
Logger.log("i", "Requesting plugin list") url = QUrl("{base_url}packages?cura_version={version}".format(base_url = self._api_url, version = self._packages_version_number))
url = QUrl(self._api_url + "plugins") self._package_list_request = QNetworkRequest(url)
self._plugin_list_request = QNetworkRequest(url) self._package_list_request.setRawHeader(*self._request_header)
self._plugin_list_request.setRawHeader(*self._request_header) self._network_manager.get(self._package_list_request)
self._network_manager.get(self._plugin_list_request)
def _createDialog(self, qml_name): def _createDialog(self, qml_name):
Logger.log("d", "Creating dialog [%s]", qml_name) Logger.log("d", "Creating dialog [%s]", qml_name)
@ -217,19 +221,18 @@ class PluginBrowser(QObject, Extension):
result = PluginRegistry.getInstance().installPlugin("file://" + location) result = PluginRegistry.getInstance().installPlugin("file://" + location)
self._newly_installed_plugin_ids.append(result["id"]) self._newly_installed_plugin_ids.append(result["id"])
self.pluginsMetadataChanged.emit() self.packagesMetadataChanged.emit()
self.openRestartDialog(result["message"]) self.openRestartDialog(result["message"])
self._restart_required = True self._restart_required = True
self.restartRequiredChanged.emit() self.restartRequiredChanged.emit()
# Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"])
@pyqtSlot(str) @pyqtSlot(str)
def removePlugin(self, plugin_id): def removePlugin(self, plugin_id):
result = PluginRegistry.getInstance().uninstallPlugin(plugin_id) result = PluginRegistry.getInstance().uninstallPlugin(plugin_id)
self._newly_uninstalled_plugin_ids.append(result["id"]) self._newly_uninstalled_plugin_ids.append(result["id"])
self.pluginsMetadataChanged.emit() self.packagesMetadataChanged.emit()
self._restart_required = True self._restart_required = True
self.restartRequiredChanged.emit() self.restartRequiredChanged.emit()
@ -239,13 +242,13 @@ class PluginBrowser(QObject, Extension):
@pyqtSlot(str) @pyqtSlot(str)
def enablePlugin(self, plugin_id): def enablePlugin(self, plugin_id):
self._plugin_registry.enablePlugin(plugin_id) self._plugin_registry.enablePlugin(plugin_id)
self.pluginsMetadataChanged.emit() self.packagesMetadataChanged.emit()
Logger.log("i", "%s was set as 'active'", id) Logger.log("i", "%s was set as 'active'", id)
@pyqtSlot(str) @pyqtSlot(str)
def disablePlugin(self, plugin_id): def disablePlugin(self, plugin_id):
self._plugin_registry.disablePlugin(plugin_id) self._plugin_registry.disablePlugin(plugin_id)
self.pluginsMetadataChanged.emit() self.packagesMetadataChanged.emit()
Logger.log("i", "%s was set as 'deactive'", id) Logger.log("i", "%s was set as 'deactive'", id)
@pyqtProperty(int, notify = onDownloadProgressChanged) @pyqtProperty(int, notify = onDownloadProgressChanged)
@ -280,28 +283,32 @@ class PluginBrowser(QObject, Extension):
self.setIsDownloading(False) self.setIsDownloading(False)
@pyqtSlot(str) @pyqtSlot(str)
def setView(self, view = "available"): def filterPackagesByType(self, type):
self._view = view if not self._packages_model:
return
self._packages_model.setFilter({"type": type})
self.filterChanged.emit()
def setCurrentView(self, view = "plugins"):
self._current_view = view
self._detail_view = False
self.viewChanged.emit() self.viewChanged.emit()
self.pluginsMetadataChanged.emit()
@pyqtProperty(str, notify = viewChanged) @pyqtProperty(str, fset = setCurrentView, notify = viewChanged)
def viewing(self): def currentView(self):
return self._view return self._current_view
@pyqtSlot(str) def setDetailView(self, item = False):
def setDetailView(self, item = ""):
self._detail_view = item self._detail_view = item
self.detailViewChanged.emit() self.detailViewChanged.emit()
self.pluginsMetadataChanged.emit()
@pyqtProperty(str, notify = detailViewChanged) @pyqtProperty(bool, fset = setDetailView, notify = detailViewChanged)
def detailView(self): def detailView(self):
return self._detail_view return self._detail_view
@pyqtProperty(QObject, notify = pluginsMetadataChanged) @pyqtProperty(QObject, notify = packagesMetadataChanged)
def pluginsModel(self): def pluginsModel(self):
self._plugins_model = PluginsModel(None, self._view) self._plugins_model = PluginsModel(None, self._current_view)
# self._plugins_model.update() # self._plugins_model.update()
# Check each plugin the registry for matching plugin from server # Check each plugin the registry for matching plugin from server
@ -311,20 +318,22 @@ class PluginBrowser(QObject, Extension):
if self._checkCanUpgrade(plugin["id"], plugin["version"]): if self._checkCanUpgrade(plugin["id"], plugin["version"]):
plugin["can_upgrade"] = True plugin["can_upgrade"] = True
for item in self._plugins_metadata: for item in self._packages_metadata:
if item["id"] == plugin["id"]: if item["id"] == plugin["id"]:
plugin["update_url"] = item["file_location"] plugin["update_url"] = item["file_location"]
return self._plugins_model return self._plugins_model
@pyqtProperty(QObject, notify = packagesMetadataChanged)
def packagesModel(self):
return self._packages_model
def _checkCanUpgrade(self, id, version): def _checkCanUpgrade(self, id, version):
# TODO: This could maybe be done more efficiently using a dictionary... # TODO: This could maybe be done more efficiently using a dictionary...
# Scan plugin server data for plugin with the given id: # Scan plugin server data for plugin with the given id:
for plugin in self._plugins_metadata: for plugin in self._packages_metadata:
if id == plugin["id"]: if id == plugin["id"]:
reg_version = Version(version) reg_version = Version(version)
new_version = Version(plugin["version"]) new_version = Version(plugin["version"])
@ -374,14 +383,17 @@ class PluginBrowser(QObject, Extension):
return return
if reply.operation() == QNetworkAccessManager.GetOperation: if reply.operation() == QNetworkAccessManager.GetOperation:
if reply_url == self._api_url + "plugins": if reply_url == "{base_url}packages?cura_version={version}".format(base_url = self._api_url, version = self._packages_version_number):
try: try:
json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
# Add metadata to the manager: # Add metadata to the manager:
self._plugins_metadata = json_data self._packages_metadata = json_data["data"]
self._plugin_registry.addExternalPlugins(self._plugins_metadata) if not self._packages_model:
self.pluginsMetadataChanged.emit() self._packages_model = CuraPackageModel()
self._packages_model.setPackagesMetaData(self._packages_metadata)
# self._plugin_registry.addExternalPlugins(self._packages_metadata)
self.packagesMetadataChanged.emit()
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
Logger.log("w", "Received an invalid print job state message: Not valid JSON.") Logger.log("w", "Received an invalid print job state message: Not valid JSON.")
return return

View File

View File

@ -0,0 +1,12 @@
# Copyright (c) 2018 Ultimaker B.V.
# Toolbox is released under the terms of the LGPLv3 or higher.
from .Toolbox import Toolbox
def getMetaData():
return {}
def register(app):
return {"extension": Toolbox.Toolbox()}

View File

@ -0,0 +1,7 @@
{
"name": "Toolbox",
"author": "Ultimaker B.V.",
"version": "1.0.0",
"api": 4,
"description": "Find, manage and install new cura packages."
}

View File

@ -12,9 +12,10 @@ import QtQuick.Controls.Styles 1.4
import UM 1.1 as UM import UM 1.1 as UM
Window Window
{ {
id: base id: base
title: catalog.i18nc("@title:tab", "Plugins"); title: catalog.i18nc("@title:tab", "Toolbox");
modality: Qt.ApplicationModal
width: 800 * screenScaleFactor width: 800 * screenScaleFactor
height: 640 * screenScaleFactor height: 640 * screenScaleFactor
minimumWidth: 800 * screenScaleFactor minimumWidth: 800 * screenScaleFactor
@ -42,18 +43,17 @@ Window
ToolboxViewDownloads ToolboxViewDownloads
{ {
id: viewDownloads id: viewDownloads
visible: manager.viewing == "available" && manager.detailView == "" ? true : false visible: manager.currentView != "installed" && !manager.detailView
} }
ToolboxViewDetail ToolboxViewDetail
{ {
id: viewDetail id: viewDetail
visible: manager.viewing == "available" && manager.detailView != "" ? true : false visible: manager.currentView != "installed" && manager.detailView
} }
ToolboxViewInstalled ToolboxViewInstalled
{ {
id: installedPluginList id: installedPluginList
visible: manager.viewing == "installed" ? true : false visible: manager.currentView == "installed"
} }
} }
SectionShadow SectionShadow
@ -75,9 +75,6 @@ Window
} }
} }
UM.I18nCatalog { id: catalog; name: "cura" } UM.I18nCatalog { id: catalog; name: "cura" }
Connections Connections

View File

@ -11,41 +11,34 @@ import UM 1.1 as UM
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles // TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
Rectangle Column
{ {
id: base id: base
width: parent.width height: childrenRect.height
height: childrenRect.height + UM.Theme.getSize("double_margin").height * 8 spacing: UM.Theme.getSize("base_unit").height
color: "transparent"
Label Label
{ {
id: heading id: heading
text: "Community Plugins" text: "Community Plugins"
width: parent.width width: parent.width
height: UM.Theme.getSize("base_unit").width * 4
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
} }
GridLayout GridLayout
{ {
id: grid id: grid
width: base.width width: parent.width
anchors
{
top: heading.bottom
}
columns: 3 columns: 3
columnSpacing: UM.Theme.getSize("base_unit").width columnSpacing: UM.Theme.getSize("base_unit").width
rowSpacing: UM.Theme.getSize("base_unit").height rowSpacing: UM.Theme.getSize("base_unit").height
ToolboxGridTile {} Repeater
ToolboxGridTile {} {
ToolboxGridTile {} model: manager.packagesModel
ToolboxGridTile {} delegate: ToolboxGridTile
ToolboxGridTile {} {
ToolboxGridTile {} Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
ToolboxGridTile {} }
ToolboxGridTile {} }
} }
} }

View File

@ -13,7 +13,7 @@ Item
{ {
id: base id: base
height: childrenRect.height height: childrenRect.height
Layout.fillWidth: true Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Row Row
{ {
width: parent.width width: parent.width
@ -29,22 +29,28 @@ Item
} }
Column Column
{ {
width: UM.Theme.getSize("base_unit").width * 12 width: parent.width - thumbnail.width - parent.spacing
spacing: Math.floor(UM.Theme.getSize("base_unit").width / 2)
Label Label
{ {
id: name id: name
text: "Auto Orientation" text: model.name
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
height: UM.Theme.getSize("base_unit").height * 2
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
font: UM.Theme.getFont("default_bold") font: UM.Theme.getFont("default_bold")
} }
Label Label
{ {
id: info id: info
text: "Automatically orientate your model." text:
{
if (model.description.length > 50)
{
return model.description.substring(0, 50) + "..."
}
return model.description
}
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
@ -55,8 +61,6 @@ Item
MouseArea MouseArea
{ {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: manager.detailView = true
manager.setDetailView("thingy")
}
} }
} }

View File

@ -32,7 +32,7 @@ Rectangle {
implicitWidth: 96 implicitWidth: 96
implicitHeight: 48 implicitHeight: 48
Rectangle { Rectangle {
visible: manager.viewing == "available" ? true : false visible: manager.currentView == "plugins"
color: UM.Theme.getColor("primary") color: UM.Theme.getColor("primary")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
@ -49,7 +49,11 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
} }
onClicked: manager.setView("available") onClicked:
{
manager.filterPackagesByType("plugin")
manager.currentView = "plugins"
}
} }
Button { Button {
@ -60,7 +64,7 @@ Rectangle {
implicitWidth: 96 implicitWidth: 96
implicitHeight: 48 implicitHeight: 48
Rectangle { Rectangle {
visible: manager.viewing == "available" ? true : false visible: manager.currentView == "materials"
color: UM.Theme.getColor("primary") color: UM.Theme.getColor("primary")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
@ -77,7 +81,11 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
} }
onClicked: manager.setView("available") onClicked:
{
manager.filterPackagesByType("material")
manager.currentView = "materials"
}
} }
} }
@ -91,7 +99,7 @@ Rectangle {
implicitWidth: 96 implicitWidth: 96
implicitHeight: 48 implicitHeight: 48
Rectangle { Rectangle {
visible: manager.viewing == "installed" ? true : false visible: manager.currentView == "installed"
color: UM.Theme.getColor("primary") color: UM.Theme.getColor("primary")
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
width: parent.width width: parent.width
@ -108,6 +116,6 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
} }
onClicked: manager.setView("installed") onClicked: manager.currentView = "installed"
} }
} }

View File

@ -10,45 +10,30 @@ import UM 1.1 as UM
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles // TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
Rectangle Column
{ {
id: base id: base
width: parent.width
height: childrenRect.height height: childrenRect.height
color: "transparent" spacing: UM.Theme.getSize("base_unit").height
Label Label
{ {
id: heading id: heading
text: "Top Downloads" text: "Top Downloads"
width: parent.width width: parent.width
height: UM.Theme.getSize("base_unit").width * 4
verticalAlignment: Text.AlignVCenter
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("medium") font: UM.Theme.getFont("medium")
} }
Row Row
{ {
height: childrenRect.height height: childrenRect.height
width: childrenRect.width
spacing: UM.Theme.getSize("base_unit").width * 2 spacing: UM.Theme.getSize("base_unit").width * 2
anchors anchors
{ {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
top: heading.bottom
} }
ToolboxShowcaseTile {} ToolboxShowcaseTile {}
ToolboxShowcaseTile {} ToolboxShowcaseTile {}
ToolboxShowcaseTile {} ToolboxShowcaseTile {}
} }
Rectangle
{
color: UM.Theme.getColor("text_medium")
width: parent.width
height: UM.Theme.getSize("base_unit").height / 6
anchors
{
bottom: parent.bottom
}
}
} }

View File

@ -44,10 +44,7 @@ Item
} }
width: UM.Theme.getSize("base_unit").width * 4 width: UM.Theme.getSize("base_unit").width * 4
height: UM.Theme.getSize("base_unit").height * 2 height: UM.Theme.getSize("base_unit").height * 2
onClicked: onClicked: manager.detailView = false
{
manager.setDetailView("")
}
style: ButtonStyle style: ButtonStyle
{ {
background: Rectangle background: Rectangle

View File

@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V. // Copyright (c) 2018 Ultimaker B.V.
// PluginBrowser is released under the terms of the LGPLv3 or higher. // PluginBrowser is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2 import QtQuick 2.7
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4 import QtQuick.Controls.Styles 1.4
@ -12,29 +12,31 @@ import UM 1.1 as UM
ScrollView ScrollView
{ {
id: base id: base
frameVisible: false frameVisible: true
anchors.fill: parent width: parent.width
height: parent.height
style: UM.Theme.styles.scrollview style: UM.Theme.styles.scrollview
Column Column
{ {
width: base.width width: base.width
spacing: UM.Theme.getSize("base_unit").height spacing: UM.Theme.getSize("base_unit").height
height: childrenRect.height padding: UM.Theme.getSize("base_unit").height * 2
anchors height: childrenRect.height + 2 * padding
{
fill: parent
topMargin: UM.Theme.getSize("base_unit").height
bottomMargin: UM.Theme.getSize("base_unit").height
leftMargin: UM.Theme.getSize("base_unit").width * 2
rightMargin: UM.Theme.getSize("base_unit").width * 2
}
ToolboxShowcase ToolboxShowcase
{ {
id: showcase id: showcase
width: parent.width - 2 * parent.padding
}
Rectangle
{
color: UM.Theme.getColor("text_medium")
width: parent.width - 2 * parent.padding
height: UM.Theme.getSize("base_unit").height / 6
} }
ToolboxGrid ToolboxGrid
{ {
id: allPlugins id: allPlugins
width: parent.width - 2 * parent.padding
} }
} }
} }

View File

@ -68,7 +68,7 @@ Item
property alias configureSettingVisibility: configureSettingVisibilityAction property alias configureSettingVisibility: configureSettingVisibilityAction
property alias browsePlugins: browsePluginsAction property alias browsePackages: browsePackagesAction
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name:"cura"}
@ -429,8 +429,8 @@ Item
Action Action
{ {
id: browsePluginsAction id: browsePackagesAction
text: catalog.i18nc("@action:menu", "Browse plugins...") text: catalog.i18nc("@action:menu", "Browse packages...")
iconName: "plugins_browse" iconName: "plugins_browse"
} }

View File

@ -270,9 +270,9 @@ UM.MainWindow
Menu Menu
{ {
id: plugin_menu id: plugin_menu
title: catalog.i18nc("@title:menu menubar:toplevel", "P&lugins") title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox")
MenuItem { action: Cura.Actions.browsePlugins } MenuItem { action: Cura.Actions.browsePackages }
} }
Menu Menu
@ -668,9 +668,9 @@ UM.MainWindow
// show the plugin browser dialog // show the plugin browser dialog
Connections Connections
{ {
target: Cura.Actions.browsePlugins target: Cura.Actions.browsePackages
onTriggered: { onTriggered: {
curaExtensions.callExtensionMethod("Plugin Browser", "browsePlugins") curaExtensions.callExtensionMethod("Toolbox", "browsePackages")
} }
} }