diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index bf33ba7baa..5c3fd67f0b 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 @@ -27,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 @@ -49,9 +144,11 @@ class WelcomePagesModel(ListModel): }) self._pages.append({"id": "add_network_or_local_printer", "page_url": self._getBuiltinWelcomePagePath("AddNetworkOrLocalPrinterContent.qml"), + "next_page_id": "machine_actions", }) self._pages.append({"id": "add_printer_by_ip", "page_url": self._getBuiltinWelcomePagePath("AddPrinterByIpContent.qml"), + "next_page_id": "machine_actions", }) self._pages.append({"id": "machine_actions", "page_url": self._getBuiltinWelcomePagePath("FirstStartMachineActionsContent.qml"), diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 37a34ebb52..6ee0e090a1 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -77,7 +77,6 @@ UM.MainWindow if (CuraApplication.needToShowUserAgreement) { welcomeDialog.visible = true; - welcomeDialog.currentStep = 0; } else { diff --git a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml index 8117957d46..75e5c58724 100644 --- a/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml +++ b/resources/qml/WelcomePages/AddNetworkOrLocalPrinterContent.qml @@ -145,7 +145,7 @@ Item const localPrinterItem = addLocalPrinterDropDown.contentItem.currentItem Cura.MachineManager.addMachine(localPrinterItem.id) - base.goToPage("machine_actions") + base.showNextPage() } } } diff --git a/resources/qml/WelcomePages/AddPrinterByIpContent.qml b/resources/qml/WelcomePages/AddPrinterByIpContent.qml index a0b0e3c2cd..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_network_or_local_printer") + onClicked: base.showPreviousPage() } Cura.PrimaryButton diff --git a/resources/qml/WelcomePages/FirstStartMachineActionsContent.qml b/resources/qml/WelcomePages/FirstStartMachineActionsContent.qml index 4dbdd57916..6b4a79a24a 100644 --- a/resources/qml/WelcomePages/FirstStartMachineActionsContent.qml +++ b/resources/qml/WelcomePages/FirstStartMachineActionsContent.qml @@ -33,6 +33,7 @@ Item { if (visible) { + // Reset the action to start from the beginning when it is shown. currentActionIndex = 0 if (!hasActions) { @@ -48,7 +49,7 @@ Item anchors.topMargin: UM.Theme.getSize("welcome_pages_default_margin").height anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter - text: currentActionItem.title + text: currentActionItem == null ? "" : currentActionItem.title color: UM.Theme.getColor("primary_button") font: UM.Theme.getFont("large_bold") renderType: Text.NativeRendering diff --git a/resources/qml/WelcomePages/StepPanel.qml b/resources/qml/WelcomePages/StepPanel.qml index e3ae0cd17c..f1a4e5a4c8 100644 --- a/resources/qml/WelcomePages/StepPanel.qml +++ b/resources/qml/WelcomePages/StepPanel.qml @@ -21,70 +21,30 @@ Item 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) - - property var currentItem: (model == null) ? null : model.getItem(currentStep) + property var currentItem: (model == null) ? null : model.getItem(model.currentPageIndex) property var model: null + 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 - } - } + // Call the corresponding functions in the model + onShowNextPage: model.goToNextPage() + onShowPreviousPage: model.goToPreviousPage() + onGoToPage: model.goToPage(page_id) onVisibleChanged: { if (visible) { - base.currentStep = 0 - base.currentItem = base.model.getItem(base.currentStep) + model.resetState() } } - onModelChanged: - { - base.currentStep = 0 - } + onModelChanged: model.resetState() // Panel background Rectangle @@ -137,6 +97,6 @@ Item left: parent.left right: parent.right } - source: base.currentItem.page_url + source: base.pageUrl } } diff --git a/resources/qml/WelcomePages/WelcomeDialog.qml b/resources/qml/WelcomePages/WelcomeDialog.qml index 626b6b6877..17b983ed0e 100644 --- a/resources/qml/WelcomePages/WelcomeDialog.qml +++ b/resources/qml/WelcomePages/WelcomeDialog.qml @@ -9,8 +9,12 @@ import UM 1.3 as UM import Cura 1.1 as Cura +// +// This is a no-frame dialog that shows the welcome process. +// Window { + id: dialog UM.I18nCatalog { id: catalog; name: "cura" } title: catalog.i18nc("@title", "Welcome to Ultimaker Cura") @@ -21,19 +25,18 @@ Window height: 600 // TODO color: "transparent" - property alias currentStep: stepPanel.currentStep + property var model: CuraApplication.getWelcomePagesModel() StepPanel { id: stepPanel - currentStep: 0 - model: CuraApplication.getWelcomePagesModel() + model: dialog.model } // Close this dialog when there's no more page to show Connections { - target: stepPanel - onPassLastPage: close() + target: model + onAllFinished: close() } }