diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1bc55d76f9..d5ab2bd706 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -114,6 +114,7 @@ from cura.UI.ObjectsModel import ObjectsModel from cura.UI.TextManager import TextManager from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel from cura.UI.WelcomePagesModel import WelcomePagesModel +from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel from .SingleInstance import SingleInstance from .AutoSave import AutoSave @@ -219,6 +220,7 @@ class CuraApplication(QtApplication): self._first_start_machine_actions_model = FirstStartMachineActionsModel(self) self._welcome_pages_model = WelcomePagesModel(self) self._add_printer_pages_model = AddPrinterPagesModel(self) + self._whats_new_pages_model = WhatsNewPagesModel(self) self._text_manager = TextManager(self) self._quality_profile_drop_down_menu_model = None @@ -765,6 +767,7 @@ class CuraApplication(QtApplication): self._output_device_manager.start() self._welcome_pages_model.initialize() self._add_printer_pages_model.initialize() + self._whats_new_pages_model.initialize() # Detect in which mode to run and execute that mode if self._is_headless: @@ -880,6 +883,10 @@ class CuraApplication(QtApplication): def getAddPrinterPagesModel(self, *args) -> "AddPrinterPagesModel": return self._add_printer_pages_model + @pyqtSlot(result = QObject) + def getWhatsNewPagesModel(self, *args) -> "WhatsNewPagesModel": + return self._whats_new_pages_model + @pyqtSlot(result = QObject) def getMachineSettingsManager(self, *args) -> "MachineSettingsManager": return self._machine_settings_manager @@ -1021,6 +1028,7 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) qmlRegisterType(WelcomePagesModel, "Cura", 1, 0, "WelcomePagesModel") + qmlRegisterType(WhatsNewPagesModel, "Cura", 1, 0, "WhatsNewPagesModel") qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel") qmlRegisterType(TextManager, "Cura", 1, 0, "TextManager") @@ -1765,3 +1773,21 @@ class CuraApplication(QtApplication): def getSidebarCustomMenuItems(self) -> list: return self._sidebar_custom_menu_items + @pyqtSlot(result = bool) + def shouldShowWelcomeDialog(self) -> bool: + has_active_machine = self._machine_manager.activeMachine is not None + + # Only show the complete flow if there is not printer yet. + show_complete_flow = not has_active_machine + return show_complete_flow + + @pyqtSlot(result = bool) + def shouldShowWhatsNewDialog(self) -> bool: + has_active_machine = self._machine_manager.activeMachine is not None + has_app_just_upgraded = self.hasJustUpdatedFromOldVersion() + + print("!!!!!!!!!!!!! has_active_machine = ", has_active_machine) + + # Only show the what's new dialog if there's no machine and we have just upgraded + show_whatsnew_only = has_active_machine and has_app_just_upgraded + return show_whatsnew_only diff --git a/cura/UI/WelcomePagesModel.py b/cura/UI/WelcomePagesModel.py index be29ed5619..f75082e20d 100644 --- a/cura/UI/WelcomePagesModel.py +++ b/cura/UI/WelcomePagesModel.py @@ -59,10 +59,6 @@ class WelcomePagesModel(ListModel): # Store all the previous page indices so it can go back. self._previous_page_indices_stack = deque() # type: deque - # If the welcome flow should be shown. It can show the complete flow or just the changelog depending on the - # specific case. See initialize() for how this variable is set. - self._should_show_welcome_flow = False - allFinished = pyqtSignal() # emitted when all steps have been finished currentPageIndexChanged = pyqtSignal() @@ -178,12 +174,6 @@ class WelcomePagesModel(ListModel): self.currentPageIndexChanged.emit() - shouldShowWelcomeFlowChanged = pyqtSignal() - - @pyqtProperty(bool, notify = shouldShowWelcomeFlowChanged) - def shouldShowWelcomeFlow(self) -> bool: - return self._should_show_welcome_flow - # 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 @@ -199,33 +189,7 @@ class WelcomePagesModel(ListModel): return QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, os.path.join("WelcomePages", page_filename))) - # FIXME: HACKs for optimization that we don't update the model every time the active machine gets changed. - def _onActiveMachineChanged(self) -> None: - self._application.getMachineManager().globalContainerChanged.disconnect(self._onActiveMachineChanged) - self._initialize() - def initialize(self) -> None: - self._application.getMachineManager().globalContainerChanged.connect(self._onActiveMachineChanged) - self._initialize() - - def _initialize(self) -> None: - has_active_machine = self._application.getMachineManager().activeMachine is not None - has_app_just_upgraded = self._application.hasJustUpdatedFromOldVersion() - - # Only show the what's new dialog if there's no machine and we have just upgraded - show_complete_flow = not has_active_machine - show_whatsnew_only = has_active_machine and has_app_just_upgraded - - # FIXME: This is a hack. Because of the circular dependency between MachineManager, ExtruderManager, and - # possibly some others, setting the initial active machine is not done when the MachineManager gets initialized. - # So at this point, we don't know if there will be an active machine or not. It could be that the active machine - # files are corrupted so we cannot rely on Preferences either. This makes sure that once the active machine - # gets changed, this model updates the flags, so it can decide whether to show the welcome flow or not. - should_show_welcome_flow = show_complete_flow or show_whatsnew_only - if should_show_welcome_flow != self._should_show_welcome_flow: - self._should_show_welcome_flow = should_show_welcome_flow - self.shouldShowWelcomeFlowChanged.emit() - # All pages all_pages_list = [{"id": "welcome", "page_url": self._getBuiltinWelcomePagePath("WelcomeContent.qml"), @@ -257,11 +221,7 @@ class WelcomePagesModel(ListModel): }, ] - pages_to_show = all_pages_list - if show_whatsnew_only: - pages_to_show = list(filter(lambda x: x["id"] == "whats_new", all_pages_list)) - - self._pages = pages_to_show + self._pages = all_pages_list self.setItems(self._pages) # For convenience, inject the default "next" button text to each item if it's not present. diff --git a/cura/UI/WhatsNewPagesModel.py b/cura/UI/WhatsNewPagesModel.py new file mode 100644 index 0000000000..5b968ae574 --- /dev/null +++ b/cura/UI/WhatsNewPagesModel.py @@ -0,0 +1,22 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from .WelcomePagesModel import WelcomePagesModel + + +# +# This Qt ListModel is more or less the same the WelcomePagesModel, except that this model is only for showing the +# "what's new" page. This is also used in the "Help" menu to show the changes log. +# +class WhatsNewPagesModel(WelcomePagesModel): + + def initialize(self) -> None: + self._pages = [] + self._pages.append({"id": "whats_new", + "page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"), + "next_page_button_text": self._catalog.i18nc("@action:button", "Close"), + }) + self.setItems(self._pages) + + +__all__ = ["WhatsNewPagesModel"] diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 81c14aa01a..ed89eb553e 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -61,6 +61,7 @@ Item property alias documentation: documentationAction; property alias showTroubleshooting: showTroubleShootingAction property alias reportBug: reportBugAction; + property alias whatsNew: whatsNewAction property alias about: aboutAction; property alias toggleFullScreen: toggleFullScreenAction; @@ -229,6 +230,13 @@ Item onTriggered: CuraActions.openBugReportPage(); } + Action + { + id: whatsNewAction; + text: catalog.i18nc("@action:inmenu menubar:help", "What's New"); + iconName: "help-whats-new"; + } + Action { id: aboutAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bdd016d24c..08fbc95b0e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -68,32 +68,54 @@ UM.MainWindow z: greyOutBackground.z + 1 } - Component.onCompleted: + Connections { - CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) - // Workaround silly issues with QML Action's shortcut property. - // - // Currently, there is no way to define shortcuts as "Application Shortcut". - // This means that all Actions are "Window Shortcuts". The code for this - // implements a rather naive check that just checks if any of the action's parents - // are a window. Since the "Actions" object is a singleton it has no parent by - // default. If we set its parent to something contained in this window, the - // shortcut will activate properly because one of its parents is a window. - // - // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property. - Cura.Actions.parent = backgroundItem - CuraApplication.purgeWindows() + target: CuraApplication + onInitializationFinished: + { + CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) + // Workaround silly issues with QML Action's shortcut property. + // + // Currently, there is no way to define shortcuts as "Application Shortcut". + // This means that all Actions are "Window Shortcuts". The code for this + // implements a rather naive check that just checks if any of the action's parents + // are a window. Since the "Actions" object is a singleton it has no parent by + // default. If we set its parent to something contained in this window, the + // shortcut will activate properly because one of its parents is a window. + // + // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property. + Cura.Actions.parent = backgroundItem + CuraApplication.purgeWindows() - if (CuraApplication.getWelcomePagesModel().shouldShowWelcomeFlow) - { - welcomeDialogItem.visible = true - } - else - { - welcomeDialogItem.visible = false + if (CuraApplication.shouldShowWelcomeDialog()) + { + welcomeDialogItem.visible = true + } + else + { + welcomeDialogItem.visible = false + } + + if (CuraApplication.shouldShowWhatsNewDialog()) + { + showWhatsNewDialogTimer.start() + } } } + // HACK: Use a timer here because if we call "Cura.Actions.whatsNew.trigger()" or "whatsNewDialog.show()" when + // the component gets completed or when the application finishes its initialization, the main window has not been + // fully initialized yet. If we should the dialog before the main window is fully initialized, you will see the + // dialog first but when the main windows is fully initialized, the dialog will disappear. Adding a timer here is + // to bypass this problem. + Timer + { + id: showWhatsNewDialogTimer + repeat: false + interval: 1000 + onTriggered: Cura.Actions.whatsNew.trigger() + } + Item { id: backgroundItem @@ -780,6 +802,20 @@ UM.MainWindow progressBarVisible: false } + Cura.WizardDialog + { + id: whatsNewDialog + title: catalog.i18nc("@title:window", "What's New") + model: CuraApplication.getWhatsNewPagesModel() + progressBarVisible: false + } + + Connections + { + target: Cura.Actions.whatsNew + onTriggered: whatsNewDialog.show() + } + Connections { target: Cura.Actions.addMachine diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index 2f18df8914..7f343eb8f4 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -101,6 +101,7 @@ Item MenuItem { action: Cura.Actions.documentation } MenuItem { action: Cura.Actions.reportBug } MenuSeparator { } + MenuItem { action: Cura.Actions.whatsNew } MenuItem { action: Cura.Actions.about } } } diff --git a/resources/qml/WelcomePages/WhatsNewContent.qml b/resources/qml/WelcomePages/WhatsNewContent.qml index 415acae05c..51a347779a 100644 --- a/resources/qml/WelcomePages/WhatsNewContent.qml +++ b/resources/qml/WelcomePages/WhatsNewContent.qml @@ -51,7 +51,7 @@ Item id: getStartedButton anchors.right: parent.right anchors.bottom: parent.bottom - text: catalog.i18nc("@button", "Next") + text: base.currentItem.next_page_button_text onClicked: base.showNextPage() } }