mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-11 00:37:50 -06:00
Merge branch 'CURA-7129_license_window_design' of github.com:Ultimaker/Cura
This commit is contained in:
commit
61a3122a78
6 changed files with 112 additions and 41 deletions
|
@ -5,6 +5,7 @@ import QtQuick 2.10
|
||||||
import QtQuick.Dialogs 1.1
|
import QtQuick.Dialogs 1.1
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles
|
||||||
|
@ -20,31 +21,63 @@ UM.Dialog
|
||||||
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
||||||
width: minimumWidth
|
width: minimumWidth
|
||||||
height: minimumHeight
|
height: minimumHeight
|
||||||
|
backgroundColor: UM.Theme.getColor("main_background")
|
||||||
|
margin: screenScaleFactor * 10
|
||||||
|
|
||||||
Item
|
ColumnLayout
|
||||||
{
|
{
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
spacing: UM.Theme.getSize("thick_margin").height
|
||||||
|
|
||||||
UM.I18nCatalog{id: catalog; name: "cura"}
|
UM.I18nCatalog{id: catalog; name: "cura"}
|
||||||
|
|
||||||
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
id: licenseHeader
|
id: licenseHeader
|
||||||
anchors.top: parent.top
|
Layout.fillWidth: true
|
||||||
anchors.left: parent.left
|
text: catalog.i18nc("@label", "You need to accept the license to install the package")
|
||||||
anchors.right: parent.right
|
|
||||||
text: licenseModel.headerText
|
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: packageRow
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
leftPadding: UM.Theme.getSize("narrow_margin").width
|
||||||
|
|
||||||
|
Image
|
||||||
|
{
|
||||||
|
id: icon
|
||||||
|
width: 30 * screenScaleFactor
|
||||||
|
height: width
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: licenseModel.iconUrl || "../../images/logobot.svg"
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: packageName
|
||||||
|
text: licenseModel.packageName
|
||||||
|
font.bold: true
|
||||||
|
anchors.verticalCenter: icon.verticalCenter
|
||||||
|
height: contentHeight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TextArea
|
TextArea
|
||||||
{
|
{
|
||||||
id: licenseText
|
id: licenseText
|
||||||
anchors.top: licenseHeader.bottom
|
Layout.fillWidth: true
|
||||||
anchors.bottom: parent.bottom
|
Layout.fillHeight: true
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
readOnly: true
|
readOnly: true
|
||||||
text: licenseModel.licenseText
|
text: licenseModel.licenseText
|
||||||
|
@ -57,7 +90,7 @@ UM.Dialog
|
||||||
leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||||
rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||||
|
|
||||||
text: catalog.i18nc("@button", "Agree")
|
text: licenseModel.acceptButtonText
|
||||||
onClicked: { handler.onLicenseAccepted() }
|
onClicked: { handler.onLicenseAccepted() }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -67,7 +100,7 @@ UM.Dialog
|
||||||
Cura.SecondaryButton
|
Cura.SecondaryButton
|
||||||
{
|
{
|
||||||
id: declineButton
|
id: declineButton
|
||||||
text: catalog.i18nc("@button", "Decline and remove from account")
|
text: licenseModel.declineButtonText
|
||||||
onClicked: { handler.onLicenseDeclined() }
|
onClicked: { handler.onLicenseDeclined() }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -62,7 +62,8 @@ class DownloadPresenter:
|
||||||
"received": 0,
|
"received": 0,
|
||||||
"total": 1, # make sure this is not considered done yet. Also divByZero-safe
|
"total": 1, # make sure this is not considered done yet. Also divByZero-safe
|
||||||
"file_written": None,
|
"file_written": None,
|
||||||
"request_data": request_data
|
"request_data": request_data,
|
||||||
|
"package_model": item
|
||||||
}
|
}
|
||||||
|
|
||||||
self._started = True
|
self._started = True
|
||||||
|
@ -128,7 +129,14 @@ class DownloadPresenter:
|
||||||
if not item["file_written"]:
|
if not item["file_written"]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
success_items = {package_id : value["file_written"] for package_id, value in self._progress.items()}
|
success_items = {
|
||||||
|
package_id:
|
||||||
|
{
|
||||||
|
"package_path": value["file_written"],
|
||||||
|
"icon_url": value["package_model"]["icon_url"]
|
||||||
|
}
|
||||||
|
for package_id, value in self._progress.items()
|
||||||
|
}
|
||||||
error_items = [package_id for package_id in self._error]
|
error_items = [package_id for package_id in self._error]
|
||||||
|
|
||||||
self._progress_message.hide()
|
self._progress_message.hide()
|
||||||
|
|
|
@ -6,31 +6,52 @@ catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
# Model for the ToolboxLicenseDialog
|
# Model for the ToolboxLicenseDialog
|
||||||
class LicenseModel(QObject):
|
class LicenseModel(QObject):
|
||||||
dialogTitleChanged = pyqtSignal()
|
DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline")
|
||||||
headerChanged = pyqtSignal()
|
ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree")
|
||||||
licenseTextChanged = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
dialogTitleChanged = pyqtSignal()
|
||||||
|
packageNameChanged = pyqtSignal()
|
||||||
|
licenseTextChanged = pyqtSignal()
|
||||||
|
iconChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self._current_page_idx = 0
|
self._current_page_idx = 0
|
||||||
self._page_count = 1
|
self._page_count = 1
|
||||||
self._dialogTitle = ""
|
self._dialogTitle = ""
|
||||||
self._header_text = ""
|
|
||||||
self._license_text = ""
|
self._license_text = ""
|
||||||
self._package_name = ""
|
self._package_name = ""
|
||||||
|
self._icon_url = ""
|
||||||
|
self._decline_button_text = decline_button_text
|
||||||
|
|
||||||
|
@pyqtProperty(str, constant = True)
|
||||||
|
def acceptButtonText(self):
|
||||||
|
return self.ACCEPT_BUTTON_TEXT
|
||||||
|
|
||||||
|
@pyqtProperty(str, constant = True)
|
||||||
|
def declineButtonText(self):
|
||||||
|
return self._decline_button_text
|
||||||
|
|
||||||
@pyqtProperty(str, notify=dialogTitleChanged)
|
@pyqtProperty(str, notify=dialogTitleChanged)
|
||||||
def dialogTitle(self) -> str:
|
def dialogTitle(self) -> str:
|
||||||
return self._dialogTitle
|
return self._dialogTitle
|
||||||
|
|
||||||
@pyqtProperty(str, notify=headerChanged)
|
@pyqtProperty(str, notify=packageNameChanged)
|
||||||
def headerText(self) -> str:
|
def packageName(self) -> str:
|
||||||
return self._header_text
|
return self._package_name
|
||||||
|
|
||||||
def setPackageName(self, name: str) -> None:
|
def setPackageName(self, name: str) -> None:
|
||||||
self._header_text = name + ": " + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?")
|
self._package_name = name
|
||||||
self.headerChanged.emit()
|
self.packageNameChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=iconChanged)
|
||||||
|
def iconUrl(self) -> str:
|
||||||
|
return self._icon_url
|
||||||
|
|
||||||
|
def setIconUrl(self, url: str):
|
||||||
|
self._icon_url = url
|
||||||
|
self.iconChanged.emit()
|
||||||
|
|
||||||
@pyqtProperty(str, notify=licenseTextChanged)
|
@pyqtProperty(str, notify=licenseTextChanged)
|
||||||
def licenseText(self) -> str:
|
def licenseText(self) -> str:
|
||||||
|
|
|
@ -17,6 +17,7 @@ class LicensePresenter(QObject):
|
||||||
|
|
||||||
def __init__(self, app: CuraApplication) -> None:
|
def __init__(self, app: CuraApplication) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self._catalog = i18nCatalog("cura")
|
||||||
self._dialog = None # type: Optional[QObject]
|
self._dialog = None # type: Optional[QObject]
|
||||||
self._package_manager = app.getPackageManager() # type: PackageManager
|
self._package_manager = app.getPackageManager() # type: PackageManager
|
||||||
# Emits List[Dict[str, [Any]] containing for example
|
# Emits List[Dict[str, [Any]] containing for example
|
||||||
|
@ -25,7 +26,8 @@ class LicensePresenter(QObject):
|
||||||
|
|
||||||
self._current_package_idx = 0
|
self._current_package_idx = 0
|
||||||
self._package_models = [] # type: List[Dict]
|
self._package_models = [] # type: List[Dict]
|
||||||
self._license_model = LicenseModel() # type: LicenseModel
|
decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account")
|
||||||
|
self._license_model = LicenseModel(decline_button_text=decline_button_text) # type: LicenseModel
|
||||||
|
|
||||||
self._app = app
|
self._app = app
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ class LicensePresenter(QObject):
|
||||||
## Show a license dialog for multiple packages where users can read a license and accept or decline them
|
## Show a license dialog for multiple packages where users can read a license and accept or decline them
|
||||||
# \param plugin_path: Root directory of the Toolbox plugin
|
# \param plugin_path: Root directory of the Toolbox plugin
|
||||||
# \param packages: Dict[package id, file path]
|
# \param packages: Dict[package id, file path]
|
||||||
def present(self, plugin_path: str, packages: Dict[str, str]) -> None:
|
def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None:
|
||||||
path = os.path.join(plugin_path, self._compatibility_dialog_path)
|
path = os.path.join(plugin_path, self._compatibility_dialog_path)
|
||||||
|
|
||||||
self._initState(packages)
|
self._initState(packages)
|
||||||
|
@ -42,7 +44,7 @@ class LicensePresenter(QObject):
|
||||||
if self._dialog is None:
|
if self._dialog is None:
|
||||||
|
|
||||||
context_properties = {
|
context_properties = {
|
||||||
"catalog": i18nCatalog("cura"),
|
"catalog": self._catalog,
|
||||||
"licenseModel": self._license_model,
|
"licenseModel": self._license_model,
|
||||||
"handler": self
|
"handler": self
|
||||||
}
|
}
|
||||||
|
@ -60,18 +62,20 @@ class LicensePresenter(QObject):
|
||||||
self._package_models[self._current_package_idx]["accepted"] = False
|
self._package_models[self._current_package_idx]["accepted"] = False
|
||||||
self._checkNextPage()
|
self._checkNextPage()
|
||||||
|
|
||||||
def _initState(self, packages: Dict[str, str]) -> None:
|
def _initState(self, packages: Dict[str, Dict[str, str]]) -> None:
|
||||||
self._package_models = [
|
self._package_models = [
|
||||||
{
|
{
|
||||||
"package_id" : package_id,
|
"package_id" : package_id,
|
||||||
"package_path" : package_path,
|
"package_path" : item["package_path"],
|
||||||
|
"icon_url" : item["icon_url"],
|
||||||
"accepted" : None #: None: no answer yet
|
"accepted" : None #: None: no answer yet
|
||||||
}
|
}
|
||||||
for package_id, package_path in packages.items()
|
for package_id, item in packages.items()
|
||||||
]
|
]
|
||||||
|
|
||||||
def _presentCurrentPackage(self) -> None:
|
def _presentCurrentPackage(self) -> None:
|
||||||
package_model = self._package_models[self._current_package_idx]
|
package_model = self._package_models[self._current_package_idx]
|
||||||
|
package_info = self._package_manager.getPackageInfo(package_model["package_path"])
|
||||||
license_content = self._package_manager.getPackageLicense(package_model["package_path"])
|
license_content = self._package_manager.getPackageLicense(package_model["package_path"])
|
||||||
if license_content is None:
|
if license_content is None:
|
||||||
# Implicitly accept when there is no license
|
# Implicitly accept when there is no license
|
||||||
|
@ -79,7 +83,8 @@ class LicensePresenter(QObject):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._license_model.setCurrentPageIdx(self._current_package_idx)
|
self._license_model.setCurrentPageIdx(self._current_package_idx)
|
||||||
self._license_model.setPackageName(package_model["package_id"])
|
self._license_model.setPackageName(package_info["display_name"])
|
||||||
|
self._license_model.setIconUrl(package_model["icon_url"])
|
||||||
self._license_model.setLicenseText(license_content)
|
self._license_model.setLicenseText(license_content)
|
||||||
if self._dialog:
|
if self._dialog:
|
||||||
self._dialog.open() # Does nothing if already open
|
self._dialog.open() # Does nothing if already open
|
||||||
|
|
|
@ -63,9 +63,9 @@ class SyncOrchestrator(Extension):
|
||||||
self._download_presenter.download(mutations)
|
self._download_presenter.download(mutations)
|
||||||
|
|
||||||
## Called when a set of packages have finished downloading
|
## Called when a set of packages have finished downloading
|
||||||
# \param success_items: Dict[package_id, file_path]
|
# \param success_items: Dict[package_id, Dict[str, str]]
|
||||||
# \param error_items: List[package_id]
|
# \param error_items: List[package_id]
|
||||||
def _onDownloadFinished(self, success_items: Dict[str, str], error_items: List[str]) -> None:
|
def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None:
|
||||||
if error_items:
|
if error_items:
|
||||||
message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items)))
|
message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items)))
|
||||||
self._showErrorMessage(message)
|
self._showErrorMessage(message)
|
||||||
|
|
|
@ -154,10 +154,11 @@ class Toolbox(QObject, Extension):
|
||||||
def getLicenseDialogPluginFileLocation(self) -> str:
|
def getLicenseDialogPluginFileLocation(self) -> str:
|
||||||
return self._license_dialog_plugin_file_location
|
return self._license_dialog_plugin_file_location
|
||||||
|
|
||||||
def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str) -> None:
|
def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str, icon_url: str) -> None:
|
||||||
# Set page 1/1 when opening the dialog for a single package
|
# Set page 1/1 when opening the dialog for a single package
|
||||||
self._license_model.setCurrentPageIdx(0)
|
self._license_model.setCurrentPageIdx(0)
|
||||||
self._license_model.setPageCount(1)
|
self._license_model.setPageCount(1)
|
||||||
|
self._license_model.setIconUrl(icon_url)
|
||||||
|
|
||||||
self._license_model.setPackageName(plugin_name)
|
self._license_model.setPackageName(plugin_name)
|
||||||
self._license_model.setLicenseText(license_content)
|
self._license_model.setLicenseText(license_content)
|
||||||
|
@ -670,14 +671,17 @@ class Toolbox(QObject, Extension):
|
||||||
return
|
return
|
||||||
|
|
||||||
license_content = self._package_manager.getPackageLicense(file_path)
|
license_content = self._package_manager.getPackageLicense(file_path)
|
||||||
|
package_id = package_info["package_id"]
|
||||||
if license_content is not None:
|
if license_content is not None:
|
||||||
self.openLicenseDialog(package_info["package_id"], license_content, file_path)
|
# get the icon url for package_id, make sure the result is a string, never None
|
||||||
|
icon_url = next((x["icon_url"] for x in self.packagesModel.items if x["id"] == package_id), None) or ""
|
||||||
|
self.openLicenseDialog(package_info["display_name"], license_content, file_path, icon_url)
|
||||||
return
|
return
|
||||||
|
|
||||||
package_id = self.install(file_path)
|
installed_id = self.install(file_path)
|
||||||
if package_id != package_info["package_id"]:
|
if installed_id != package_id:
|
||||||
Logger.error("Installed package {} does not match {}".format(package_id, package_info["package_id"]))
|
Logger.error("Installed package {} does not match {}".format(installed_id, package_id))
|
||||||
self.subscribe(package_id)
|
self.subscribe(installed_id)
|
||||||
|
|
||||||
# Getter & Setters for Properties:
|
# Getter & Setters for Properties:
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
@ -699,14 +703,14 @@ class Toolbox(QObject, Extension):
|
||||||
def isDownloading(self) -> bool:
|
def isDownloading(self) -> bool:
|
||||||
return self._is_downloading
|
return self._is_downloading
|
||||||
|
|
||||||
def setActivePackage(self, package: Dict[str, Any]) -> None:
|
def setActivePackage(self, package: QObject) -> None:
|
||||||
if self._active_package != package:
|
if self._active_package != package:
|
||||||
self._active_package = package
|
self._active_package = package
|
||||||
self.activePackageChanged.emit()
|
self.activePackageChanged.emit()
|
||||||
|
|
||||||
## The active package is the package that is currently being downloaded
|
## The active package is the package that is currently being downloaded
|
||||||
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
|
@pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged)
|
||||||
def activePackage(self) -> Optional[Dict[str, Any]]:
|
def activePackage(self) -> Optional[QObject]:
|
||||||
return self._active_package
|
return self._active_package
|
||||||
|
|
||||||
def setViewCategory(self, category: str = "plugin") -> None:
|
def setViewCategory(self, category: str = "plugin") -> None:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue