diff --git a/cura/API/Account.py b/cura/API/Account.py index 1250d8b81a..e190fe9b42 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -103,6 +103,11 @@ class Account(QObject): self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged) self._authorization_service.loadAuthDataFromPreferences() + + @pyqtProperty(int, notify=syncStateChanged) + def syncState(self): + return self._sync_state + def setSyncState(self, service_name: str, state: int) -> None: """ Can be used to register sync services and update account sync states diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 19f8174a95..dfb5c6cac1 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -262,6 +262,10 @@ class CuraApplication(QtApplication): def ultimakerCloudAccountRootUrl(self) -> str: return UltimakerCloudConstants.CuraCloudAccountAPIRoot + @pyqtProperty(str, constant=True) + def ultimakerDigitalFactoryUrl(self) -> str: + return UltimakerCloudConstants.CuraDigitalFactoryURL + def addCommandLineOptions(self): """Adds command line options to the command line parser. diff --git a/cura/UltimakerCloud/UltimakerCloudConstants.py b/cura/UltimakerCloud/UltimakerCloudConstants.py index 624d2e7b8f..0c8ea0c9c7 100644 --- a/cura/UltimakerCloud/UltimakerCloudConstants.py +++ b/cura/UltimakerCloud/UltimakerCloudConstants.py @@ -7,6 +7,7 @@ DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str DEFAULT_CLOUD_API_VERSION = "1" # type: str DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str +DEFAULT_DIGITAL_FACTORY_URL = "https://digitalfactory.ultimaker.com" # type: str # Container Metadata keys META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account" @@ -32,3 +33,10 @@ try: CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT except ImportError: CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT + +try: + from cura.CuraVersion import CuraDigitalFactoryURL # type: ignore + if CuraDigitalFactoryURL == "": + CuraDigitalFactoryURL = DEFAULT_DIGITAL_FACTORY_URL +except ImportError: + CuraDigitalFactoryURL = DEFAULT_DIGITAL_FACTORY_URL diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e7f8094089..323e9e16a7 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5702,7 +5702,7 @@ "minimum_value": "0", "maximum_value": "min(0.5 * machine_width, 0.5 * machine_depth)", "minimum_value_warning": "max(extruderValues('prime_tower_line_width')) * 2", - "maximum_value_warning": "20", + "maximum_value_warning": "42", "settable_per_mesh": false, "settable_per_extruder": false }, diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 265842e2b4..031e376a21 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2020 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 @@ -7,19 +7,16 @@ import QtQuick.Controls 2.3 import UM 1.4 as UM import Cura 1.1 as Cura -Column +Item { - property var profile: null - property var loggedIn: false - property var profileImage: "" - - padding: UM.Theme.getSize("wide_margin").height - spacing: UM.Theme.getSize("wide_margin").height + property var profile: Cura.API.account.userProfile + property bool loggedIn: Cura.API.account.isLoggedIn + property var profileImage: Cura.API.account.profileImageUrl Loader { id: accountOperations - anchors.horizontalCenter: parent.horizontalCenter + anchors.centerIn: parent sourceComponent: loggedIn ? userOperations : generalOperations } diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index eed711e745..48c05f8a11 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -131,14 +131,9 @@ Item opacity: opened ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - + padding: 0 contentItem: AccountDetails - { - id: panel - profile: Cura.API.account.userProfile - loggedIn: Cura.API.account.isLoggedIn - profileImage: Cura.API.account.profileImageUrl - } + {} background: UM.PointingRectangle { diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index a4f922a10d..120173366f 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -54,6 +54,6 @@ Item visible: hasAvatar source: UM.Theme.getIcon("circle_outline") sourceSize: Qt.size(parent.width, parent.height) - color: UM.Theme.getColor("account_widget_ouline_active") + color: UM.Theme.getColor("account_widget_outline_active") } } diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index bea90c1d67..9562f940a4 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -10,7 +10,7 @@ import Cura 1.1 as Cura Column { spacing: UM.Theme.getSize("default_margin").width - + padding: UM.Theme.getSize("default_margin").width Image { id: machinesImage diff --git a/resources/qml/Account/SyncState.qml b/resources/qml/Account/SyncState.qml index 98e5991b5a..4e5543f751 100644 --- a/resources/qml/Account/SyncState.qml +++ b/resources/qml/Account/SyncState.qml @@ -4,15 +4,45 @@ import QtQuick.Controls 2.3 import UM 1.4 as UM import Cura 1.1 as Cura -Row // sync state icon + message +Row // Sync state icon + message { + property var syncState: Cura.API.account.syncState id: syncRow width: childrenRect.width height: childrenRect.height - anchors.horizontalCenter: parent.horizontalCenter spacing: UM.Theme.getSize("narrow_margin").height + states: [ + State + { + name: "idle" + when: syncState == Cura.AccountSyncState.IDLE + PropertyChanges { target: icon; source: UM.Theme.getIcon("update")} + }, + State + { + name: "syncing" + when: syncState == Cura.AccountSyncState.SYNCING + PropertyChanges { target: icon; source: UM.Theme.getIcon("update") } + PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Checking...")} + }, + State + { + name: "up_to_date" + when: syncState == Cura.AccountSyncState.SUCCESS + PropertyChanges { target: icon; source: UM.Theme.getIcon("checked") } + PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Account synced")} + }, + State + { + name: "error" + when: syncState == Cura.AccountSyncState.ERROR + PropertyChanges { target: icon; source: UM.Theme.getIcon("warning_light") } + PropertyChanges { target: stateLabel; text: catalog.i18nc("@label", "Something went wrong...")} + } + ] + UM.RecolorImage { id: icon @@ -20,7 +50,7 @@ Row // sync state icon + message height: width source: Cura.API.account.manualSyncEnabled ? UM.Theme.getIcon("update") : UM.Theme.getIcon("checked") - color: palette.text + color: UM.Theme.getColor("account_sync_state_icon") RotationAnimator { @@ -30,7 +60,7 @@ Row // sync state icon + message to: 360 duration: 1000 loops: Animation.Infinite - running: true + running: syncState == Cura.AccountSyncState.SYNCING // reset rotation when stopped onRunningChanged: { @@ -50,10 +80,13 @@ Row // sync state icon + message Label { id: stateLabel - text: catalog.i18nc("@state", catalog.i18nc("@label", "You are in sync with your account")) + text: catalog.i18nc("@state", catalog.i18nc("@label", "Account synced")) color: UM.Theme.getColor("text") font: UM.Theme.getFont("medium") renderType: Text.NativeRendering + width: contentWidth + UM.Theme.getSize("default_margin").height + height: contentHeight + verticalAlignment: Text.AlignVCenter visible: !Cura.API.account.manualSyncEnabled } @@ -64,8 +97,10 @@ Row // sync state icon + message color: UM.Theme.getColor("secondary_button_text") font: UM.Theme.getFont("medium") renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + height: contentHeight + width: contentWidth + UM.Theme.getSize("default_margin").height visible: Cura.API.account.manualSyncEnabled - height: visible ? accountSyncButton.intrinsicHeight : 0 MouseArea { @@ -77,33 +112,4 @@ Row // sync state icon + message } } } - - signal syncStateChanged(string newState) - - onSyncStateChanged: { - if(newState == Cura.AccountSyncState.IDLE){ - icon.source = UM.Theme.getIcon("update") - } else if(newState == Cura.AccountSyncState.SYNCING){ - icon.source = UM.Theme.getIcon("update") - stateLabel.text = catalog.i18nc("@label", "Checking...") - } else if (newState == Cura.AccountSyncState.SUCCESS) { - icon.source = UM.Theme.getIcon("checked") - stateLabel.text = catalog.i18nc("@label", "You are in sync with your account") - } else if (newState == Cura.AccountSyncState.ERROR) { - icon.source = UM.Theme.getIcon("warning_light") - stateLabel.text = catalog.i18nc("@label", "Something went wrong...") - } else { - print("Error: unexpected sync state: " + newState) - } - - if(newState == Cura.AccountSyncState.SYNCING){ - updateAnimator.running = true - } else { - updateAnimator.running = false - } - } - - Component.onCompleted: Cura.API.account.syncStateChanged.connect(syncStateChanged) - - -} \ No newline at end of file +} diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index c0f33c74cd..e996e41b20 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -9,72 +9,119 @@ import Cura 1.1 as Cura Column { - width: Math.max( - Math.max(title.width, accountButton.width) + 2 * UM.Theme.getSize("default_margin").width, - syncRow.width - ) + spacing: UM.Theme.getSize("narrow_margin").height + topPadding: UM.Theme.getSize("default_margin").height + bottomPadding: UM.Theme.getSize("default_margin").height + width: childrenRect.width - spacing: UM.Theme.getSize("default_margin").height - - SystemPalette + Item { - id: palette + id: accountInfo + width: childrenRect.width + height: childrenRect.height + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + AvatarImage + { + id: avatar + anchors.verticalCenter: parent.verticalCenter + + width: UM.Theme.getSize("main_window_header").height + height: UM.Theme.getSize("main_window_header").height + + source: profile["profile_image_url"] ? profile["profile_image_url"] : "" + outlineColor: UM.Theme.getColor("main_background") + } + Rectangle + { + id: initialCircle + width: avatar.width + height: avatar.height + radius: width + anchors.verticalCenter: parent.verticalCenter + color: UM.Theme.getColor("action_button_disabled") + visible: !avatar.hasAvatar + Label + { + id: initialLabel + anchors.centerIn: parent + text: profile["username"].charAt(0).toUpperCase() + font: UM.Theme.getFont("large_bold") + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering + } + } + + Column + { + anchors.left: avatar.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("narrow_margin").height + width: childrenRect.width + height: childrenRect.height + Label + { + id: username + renderType: Text.NativeRendering + text: profile.username + font: UM.Theme.getFont("large_bold") + color: UM.Theme.getColor("text") + } + + SyncState + { + id: syncRow + } + Label + { + id: lastSyncLabel + renderType: Text.NativeRendering + text: catalog.i18nc("@label The argument is a timestamp", "Last update: %1").arg(Cura.API.account.lastSyncDateTime) + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_medium") + } + } } - Label + Rectangle { - id: title - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - renderType: Text.NativeRendering - text: catalog.i18nc("@label The argument is a username.", "Hi %1").arg(profile.username) - font: UM.Theme.getFont("large_bold") - color: UM.Theme.getColor("text") + width: parent.width + color: UM.Theme.getColor("lining") + height: UM.Theme.getSize("default_lining").height } - - SyncState { - id: syncRow - } - - Label + Cura.TertiaryButton { - id: lastSyncLabel - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - renderType: Text.NativeRendering - text: catalog.i18nc("@label The argument is a timestamp", "Last update: %1").arg(Cura.API.account.lastSyncDateTime) - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text_medium") - } - - Cura.SecondaryButton - { - id: accountButton - anchors.horizontalCenter: parent.horizontalCenter + id: cloudButton width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height - text: catalog.i18nc("@button", "Ultimaker account") + text: catalog.i18nc("@button", "Ultimaker Digital Factory") + onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl) + fixedWidthMode: false + } + + Cura.TertiaryButton + { + id: accountButton + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + text: catalog.i18nc("@button", "Ultimaker Account") onClicked: Qt.openUrlExternally(CuraApplication.ultimakerCloudAccountRootUrl) fixedWidthMode: false } - Label + Rectangle { - id: signOutButton - anchors.horizontalCenter: parent.horizontalCenter - text: catalog.i18nc("@button", "Sign out") - color: UM.Theme.getColor("secondary_button_text") - font: UM.Theme.getFont("medium") - renderType: Text.NativeRendering - - MouseArea - { - anchors.fill: parent - onClicked: Cura.API.account.logout() - hoverEnabled: true - onEntered: signOutButton.font.underline = true - onExited: signOutButton.font.underline = false - } + width: parent.width + color: UM.Theme.getColor("lining") + height: UM.Theme.getSize("default_lining").height } + Cura.TertiaryButton + { + id: signOutButton + onClicked: Cura.API.account.logout() + text: catalog.i18nc("@button", "Sign Out") + } } diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index a38b47df8f..0c1be007b5 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -33,6 +33,8 @@ Button property alias shadowEnabled: shadow.visible property alias busy: busyIndicator.visible + property bool underlineTextOnHover: false + property alias toolTipContentAlignment: tooltip.contentAlignment // This property is used to indicate whether the button has a fixed width or the width would depend on the contents @@ -49,6 +51,14 @@ Button height: UM.Theme.getSize("action_button").height hoverEnabled: true + onHoveredChanged: + { + if(underlineTextOnHover) + { + buttonText.font.underline = hovered + } + } + contentItem: Row { spacing: UM.Theme.getSize("narrow_margin").width diff --git a/resources/qml/TertiaryButton.qml b/resources/qml/TertiaryButton.qml new file mode 100644 index 0000000000..e3840dbdd3 --- /dev/null +++ b/resources/qml/TertiaryButton.qml @@ -0,0 +1,21 @@ +// Copyright (c) 2020 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 + +import UM 1.4 as UM +import Cura 1.1 as Cura + + +Cura.ActionButton +{ + shadowEnabled: true + shadowColor: enabled ? UM.Theme.getColor("secondary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") + color: "transparent" + textColor: UM.Theme.getColor("secondary_button_text") + outlineColor: "transparent" + disabledColor: UM.Theme.getColor("action_button_disabled") + textDisabledColor: UM.Theme.getColor("action_button_disabled_text") + hoverColor: "transparent" + underlineTextOnHover: true +} diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 37c2462633..69bd14765a 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -24,6 +24,8 @@ "main_window_header_button_text_inactive": [128, 128, 128, 255], + "account_sync_state_icon": [255, 255, 255, 204], + "machine_selector_bar": [39, 44, 48, 255], "machine_selector_active": [39, 44, 48, 255], "machine_selector_printer_icon": [204, 204, 204, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 0bc09701b2..cab2dfe89c 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -183,6 +183,7 @@ "main_window_header_button_background_hovered": [117, 114, 159, 255], "account_widget_outline_active": [70, 66, 126, 255], + "account_sync_state_icon": [25, 25, 25, 255], "machine_selector_bar": [31, 36, 39, 255], "machine_selector_active": [68, 72, 75, 255],