From 60be55802e6c78ced9d7ac5143753a329aee709b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 26 Mar 2019 11:04:17 +0100 Subject: [PATCH 1/4] Refactor StepPanel and WelcomeDialog --- resources/qml/WelcomePages/StepPanel.qml | 76 ++++++------------- resources/qml/WelcomePages/WelcomeContent.qml | 1 - resources/qml/WelcomePages/WelcomeDialog.qml | 17 +++++ 3 files changed, 41 insertions(+), 53 deletions(-) diff --git a/resources/qml/WelcomePages/StepPanel.qml b/resources/qml/WelcomePages/StepPanel.qml index 551a01687a..182e0e7972 100644 --- a/resources/qml/WelcomePages/StepPanel.qml +++ b/resources/qml/WelcomePages/StepPanel.qml @@ -3,7 +3,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import QtGraphicalEffects 1.0 // For the dropshadow import UM 1.3 as UM import Cura 1.1 as Cura @@ -15,14 +14,8 @@ Item { id: base - anchors.fill: parent clip: true - property int roundCornerRadius: 4 - property int shadowOffset: 1 - property int stepBarHeight: 12 - property int contentMargins: 1 - property int currentStep: 0 property int totalStepCount: (model == null) ? 0 : model.count property real progressValue: (totalStepCount == 0) ? 0 : (currentStep / totalStepCount) @@ -88,57 +81,36 @@ Item base.currentStep = 0 } - // Panel background - Rectangle + Rectangle // Panel background { id: panelBackground anchors.fill: parent - anchors.margins: 2 - color: "white" // TODO - radius: base.roundCornerRadius // TODO - } + radius: UM.Theme.getSize("default_radius").width - // Drop shadow around the panel - DropShadow - { - id: shadow - radius: UM.Theme.getSize("monitor_shadow_radius").width - anchors.fill: parent - source: parent - horizontalOffset: base.shadowOffset - verticalOffset: base.shadowOffset - color: UM.Theme.getColor("monitor_shadow") - transparentBorder: true - // Should always be drawn behind the background. - z: panelBackground.z - 1 - } - - CuraProgressBar - { - id: progressBar - - value: base.progressValue - - anchors + CuraProgressBar { - left: panelBackground.left - right: panelBackground.right - top: panelBackground.top - } - height: base.stepBarHeight - } + id: progressBar + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right - Loader - { - id: contentLoader - anchors - { - margins: base.contentMargins - top: progressBar.bottom - bottom: parent.bottom - left: parent.left - right: parent.right + height: UM.Theme.getSize("progressbar").height + + value: base.progressValue + } + + Loader + { + id: contentLoader + anchors + { + margins: base.contentMargins + top: progressBar.bottom + bottom: parent.bottom + left: parent.left + right: parent.right + } + source: base.currentItem.page_url } - source: base.currentItem.page_url } } diff --git a/resources/qml/WelcomePages/WelcomeContent.qml b/resources/qml/WelcomePages/WelcomeContent.qml index 2fde182c4c..770097d3a9 100644 --- a/resources/qml/WelcomePages/WelcomeContent.qml +++ b/resources/qml/WelcomePages/WelcomeContent.qml @@ -54,7 +54,6 @@ Item Cura.PrimaryButton { id: getStartedButton - anchors.top: contentArea.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.margins: UM.Theme.getSize("welcome_pages_default_margin").width text: catalog.i18nc("@button", "Get started") diff --git a/resources/qml/WelcomePages/WelcomeDialog.qml b/resources/qml/WelcomePages/WelcomeDialog.qml index 626b6b6877..00849ca037 100644 --- a/resources/qml/WelcomePages/WelcomeDialog.qml +++ b/resources/qml/WelcomePages/WelcomeDialog.qml @@ -4,6 +4,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 +import QtGraphicalEffects 1.0 // For the DropShadow import UM 1.3 as UM import Cura 1.1 as Cura @@ -21,15 +22,31 @@ Window height: 600 // TODO color: "transparent" + property int shadowOffset: 1 * screenScaleFactor + property alias currentStep: stepPanel.currentStep StepPanel { id: stepPanel + anchors.fill: parent currentStep: 0 model: CuraApplication.getWelcomePagesModel() } + // Drop shadow around the panel + DropShadow + { + id: shadow + radius: UM.Theme.getSize("monitor_shadow_radius").width + anchors.fill: stepPanel + source: stepPanel + horizontalOffset: shadowOffset + verticalOffset: shadowOffset + color: UM.Theme.getColor("monitor_shadow") + transparentBorder: true + } + // Close this dialog when there's no more page to show Connections { From fd0a60f8dc3205e357e69b34e247e6f06f67b76e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 26 Mar 2019 11:06:43 +0100 Subject: [PATCH 2/4] Rename StepPanel to WizardPanel --- resources/qml/WelcomePages/WelcomeDialog.qml | 2 +- resources/qml/WelcomePages/{StepPanel.qml => WizardPanel.qml} | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) rename resources/qml/WelcomePages/{StepPanel.qml => WizardPanel.qml} (95%) diff --git a/resources/qml/WelcomePages/WelcomeDialog.qml b/resources/qml/WelcomePages/WelcomeDialog.qml index 00849ca037..c5c1894764 100644 --- a/resources/qml/WelcomePages/WelcomeDialog.qml +++ b/resources/qml/WelcomePages/WelcomeDialog.qml @@ -26,7 +26,7 @@ Window property alias currentStep: stepPanel.currentStep - StepPanel + WizardPanel { id: stepPanel anchors.fill: parent diff --git a/resources/qml/WelcomePages/StepPanel.qml b/resources/qml/WelcomePages/WizardPanel.qml similarity index 95% rename from resources/qml/WelcomePages/StepPanel.qml rename to resources/qml/WelcomePages/WizardPanel.qml index 182e0e7972..61d9244b93 100644 --- a/resources/qml/WelcomePages/StepPanel.qml +++ b/resources/qml/WelcomePages/WizardPanel.qml @@ -10,6 +10,10 @@ import Cura 1.1 as Cura import "../Widgets" +// +// This item is a wizard panel that contains a progress bar at the top and a content area that's beneath the progress +// bar. +// Item { id: base From 20bd9ea501fcb2f4e06615265f8b18f8e82bebea Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 26 Mar 2019 09:02:28 +0100 Subject: [PATCH 3/4] Fix binding loop in CloudContent --- resources/qml/WelcomePages/CloudContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/WelcomePages/CloudContent.qml b/resources/qml/WelcomePages/CloudContent.qml index 84b8232a3f..60e07bd50c 100644 --- a/resources/qml/WelcomePages/CloudContent.qml +++ b/resources/qml/WelcomePages/CloudContent.qml @@ -43,7 +43,7 @@ Item Column { anchors.centerIn: parent - width: childrenRect.width + width: parent.width height: childrenRect.height spacing: 20 * screenScaleFactor From 418ad73a63a89fcd78b9e8d8eefe691ee1337991 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 26 Mar 2019 11:34:44 +0100 Subject: [PATCH 4/4] Move page index logic into WelcomePagesModel --- cura/UI/WelcomePagesModel.py | 103 +++++++++++++++++- resources/qml/Cura.qml | 5 +- .../WelcomePages/AddPrinterByIpContent.qml | 2 +- resources/qml/WelcomePages/WelcomeDialog.qml | 18 ++- resources/qml/WelcomePages/WizardPanel.qml | 71 ++---------- 5 files changed, 126 insertions(+), 73 deletions(-) diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index 2b58218cd6..25765bf777 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -1,10 +1,12 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from collections import deque import os -from typing import TYPE_CHECKING, Optional, List, Dict, Any +from typing import TYPE_CHECKING, Optional, List, Dict, Any, Deque -from PyQt5.QtCore import QUrl, Qt +from PyQt5.QtCore import QUrl, Qt, pyqtSlot, pyqtProperty, pyqtSignal +from UM.Logger import Logger from UM.Qt.ListModel import ListModel from UM.Resources import Resources @@ -18,7 +20,6 @@ class WelcomePagesModel(ListModel): PageUrlRole = Qt.UserRole + 2 # URL to the page's QML file NextPageIdRole = Qt.UserRole + 3 # The next page ID it should go to - def __init__(self, parent: Optional["QObject"] = None) -> None: super().__init__(parent) @@ -28,6 +29,99 @@ class WelcomePagesModel(ListModel): self._pages = [] # type: List[Dict[str, Any]] + self._current_page_index = 0 + # Store all the previous page indices so it can go back. + self._previous_page_indices_stack = deque() # type: Deque[int] + + allFinished = pyqtSignal() # emitted when all steps have been finished + currentPageIndexChanged = pyqtSignal() + + @pyqtProperty(int, notify = currentPageIndexChanged) + def currentPageIndex(self) -> int: + return self._current_page_index + + # Returns a float number in [0, 1] which indicates the current progress. + @pyqtProperty(float, notify = currentPageIndexChanged) + def currentProgress(self) -> float: + return self._current_page_index / len(self._items) + + # Indicates if the current page is the last page. + @pyqtProperty(bool, notify = currentPageIndexChanged) + def isCurrentPageLast(self) -> bool: + return self._current_page_index == len(self._items) - 1 + + def _setCurrentPageIndex(self, page_index: int) -> None: + if page_index != self._current_page_index: + self._previous_page_indices_stack.append(self._current_page_index) + self._current_page_index = page_index + self.currentPageIndexChanged.emit() + + # Goes to the next page. + @pyqtSlot() + def goToNextPage(self) -> None: + page_item = self._items[self._current_page_index] + # Check if there's a "next_page_id" assigned. If so, go to that page. Otherwise, go to the page with the + # current index + 1. + next_page_id = page_item.get("next_page_id") + next_page_index = self._current_page_index + 1 + if next_page_id: + idx = self.getPageIndexById(next_page_id) + if idx is None: + # FIXME: If we cannot find the next page, we cannot do anything here. + Logger.log("e", "Cannot find page with ID [%s]", next_page_id) + return + next_page_index = idx + + # If we have reached the last page, emit allFinished signal and reset. + if next_page_index == len(self._items): + self.allFinished.emit() + self.resetState() + + # Move to the next page + self._setCurrentPageIndex(next_page_index) + + # Goes to the previous page. If there's no previous page, do nothing. + @pyqtSlot() + def goToPreviousPage(self) -> None: + if len(self._previous_page_indices_stack) == 0: + Logger.log("i", "No previous page, do nothing") + return + + previous_page_index = self._previous_page_indices_stack.pop() + self._current_page_index = previous_page_index + self.currentPageIndexChanged.emit() + + # Sets the current page to the given page ID. If the page ID is not found, do nothing. + @pyqtSlot(str) + def goToPage(self, page_id: str) -> None: + page_index = self.getPageIndexById(page_id) + if page_index is None: + # FIXME: If we cannot find the next page, we cannot do anything here. + Logger.log("e", "Cannot find page with ID [%s]", page_index) + return + + # Move to that page + self._setCurrentPageIndex(page_index) + + # Resets the state of the WelcomePagesModel. This functions does the following: + # - Resets current_page_index to 0 + # - Clears the previous page indices stack + @pyqtSlot() + def resetState(self) -> None: + self._current_page_index = 0 + self._previous_page_indices_stack.clear() + + self.currentPageIndexChanged.emit() + + # Gets the page index with the given page ID. If the page ID doesn't exist, returns None. + def getPageIndexById(self, page_id: str) -> Optional[int]: + page_idx = None + for idx, page_item in enumerate(self._items): + if page_item["id"] == page_id: + page_idx = idx + break + return page_idx + # Convenience function to get QUrl path to pages that's located in "resources/qml/WelcomePages". def _getBuiltinWelcomePagePath(self, page_filename: str) -> "QUrl": from cura.CuraApplication import CuraApplication @@ -35,7 +129,6 @@ class WelcomePagesModel(ListModel): os.path.join("WelcomePages", page_filename))) def initialize(self) -> None: - # Add default welcome pages self._pages.append({"id": "welcome", "page_url": self._getBuiltinWelcomePagePath("WelcomeContent.qml"), @@ -51,9 +144,11 @@ class WelcomePagesModel(ListModel): }) self._pages.append({"id": "add_network_or_local_printer", "page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"), + "next_page_id": "cloud", }) self._pages.append({"id": "add_printer_by_ip", "page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"), + "next_page_id": "cloud", }) self._pages.append({"id": "cloud", "page_url": self._getBuiltinWelcomePagePath("CloudContent.qml"), diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 37a34ebb52..3572d299bb 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -76,12 +76,11 @@ UM.MainWindow if (CuraApplication.needToShowUserAgreement) { - welcomeDialog.visible = true; - welcomeDialog.currentStep = 0; + welcomeDialog.show() } else { - welcomeDialog.visible = false; + welcomeDialog.close() } // TODO: While the new onboarding process contains the user-agreement, // it should probably not entirely rely on 'needToShowUserAgreement' for show/hide. diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index 1ea0c1f19d..4fdffa5a79 100644 --- a/resources/qml/WelcomePages/AddPrinterByIpContent.qml +++ b/resources/qml/WelcomePages/AddPrinterByIpContent.qml @@ -247,7 +247,7 @@ Item text: catalog.i18nc("@button", "Cancel") width: UM.Theme.getSize("action_button").width fixedWidthMode: true - onClicked: base.goToPage("add_printer_by_selection") + onClicked: base.showPreviousPage() } Cura.PrimaryButton diff --git a/resources/qml/WelcomePages/WelcomeDialog.qml b/resources/qml/WelcomePages/WelcomeDialog.qml index c5c1894764..9704a318b6 100644 --- a/resources/qml/WelcomePages/WelcomeDialog.qml +++ b/resources/qml/WelcomePages/WelcomeDialog.qml @@ -14,6 +14,7 @@ Window { UM.I18nCatalog { id: catalog; name: "cura" } + id: dialog title: catalog.i18nc("@title", "Welcome to Ultimaker Cura") modality: Qt.ApplicationModal flags: Qt.Window | Qt.FramelessWindowHint @@ -24,14 +25,21 @@ Window property int shadowOffset: 1 * screenScaleFactor - property alias currentStep: stepPanel.currentStep + property var model: CuraApplication.getWelcomePagesModel() + + onVisibleChanged: + { + if (visible) + { + model.resetState() + } + } WizardPanel { id: stepPanel anchors.fill: parent - currentStep: 0 - model: CuraApplication.getWelcomePagesModel() + model: dialog.model } // Drop shadow around the panel @@ -50,7 +58,7 @@ Window // Close this dialog when there's no more page to show Connections { - target: stepPanel - onPassLastPage: close() + target: model + onAllFinished: close() } } diff --git a/resources/qml/WelcomePages/WizardPanel.qml b/resources/qml/WelcomePages/WizardPanel.qml index 61d9244b93..57f2873e80 100644 --- a/resources/qml/WelcomePages/WizardPanel.qml +++ b/resources/qml/WelcomePages/WizardPanel.qml @@ -20,70 +20,21 @@ Item clip: true - property int currentStep: 0 - property int totalStepCount: (model == null) ? 0 : model.count - property real progressValue: (totalStepCount == 0) ? 0 : (currentStep / totalStepCount) - - property var currentItem: (model == null) ? null : model.getItem(currentStep) + property var currentItem: (model == null) ? null : model.getItem(model.currentPageIndex) property var model: null + // Convenience properties + property var progressValue: model == null ? 0 : model.currentProgress + property string pageUrl: currentItem == null ? null : currentItem.page_url + signal showNextPage() signal showPreviousPage() - signal passLastPage() // Emitted when there is no more page to show signal goToPage(string page_id) // Go to a specific page by the given page_id. - onShowNextPage: - { - if (currentStep < totalStepCount - 1) - { - currentStep++ - } - else - { - passLastPage() - } - } - - onShowPreviousPage: - { - if (currentStep > 0) - { - currentStep-- - } - } - - onGoToPage: - { - // find the page index - var page_index = -1 - for (var i = 0; i < base.model.count; i++) - { - const item = base.model.getItem(i) - if (item.id == page_id) - { - page_index = i - break - } - } - if (page_index > 0) - { - currentStep = page_index - } - } - - onVisibleChanged: - { - if (visible) - { - base.currentStep = 0 - base.currentItem = base.model.getItem(base.currentStep) - } - } - - onModelChanged: - { - base.currentStep = 0 - } + // Call the corresponding functions in the model + onShowNextPage: model.goToNextPage() + onShowPreviousPage: model.goToPreviousPage() + onGoToPage: model.goToPage(page_id) Rectangle // Panel background { @@ -108,13 +59,13 @@ Item id: contentLoader anchors { - margins: base.contentMargins + margins: UM.Theme.getSize("default_margin").width top: progressBar.bottom bottom: parent.bottom left: parent.left right: parent.right } - source: base.currentItem.page_url + source: base.pageUrl } } }