diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c83ed04c82..eec6c7f503 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -721,6 +721,7 @@ class CuraApplication(QtApplication): run_headless = self.getCommandLineOption("headless", False) if not run_headless: self.initializeEngine() + controller.setActiveStage("PrepareStage") if run_headless or self._engine.rootObjects: self.closeSplash() diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py deleted file mode 100644 index a40ee07157..0000000000 --- a/cura/Sidebar/SidebarController.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from UM.Logger import Logger -from UM.Preferences import Preferences -from UM.Signal import Signal -from UM.PluginRegistry import PluginRegistry - -# The sidebar controller manages available sidebar components and decides which one to display. -# The cura.qml file uses this controller to repeat over the sidebars and show the active index. -class SidebarController: - - def __init__(self, application): - self._application = application - self._sidebar_views = {"default": {}} # default is needed for the default settings sidebar - self._active_sidebar_view = None - - # Register the sidebar_view plugin type so plugins can expose custom sidebar views. - PluginRegistry.addType("sidebar_view", self.addSidebarView) - - ## Emitted when the list of views changes. - sidebarViewsChanged = Signal() - - ## Emitted when the active view changes. - activeSidebarViewChanged = Signal() - - ## Get the active application instance. - def getApplication(self): - return self._application - - ## Get all sidebar views registered in this controller. - def getAllSidebarViews(self): - return self._sidebar_views - - ## Add a sidebar view to the registry. - # It get's a unique name based on the plugin ID. - def addSidebarView(self, sidebar_view): - sidebar_view_id = sidebar_view.getPluginId() - if sidebar_view_id not in self._sidebar_views: - self._sidebar_views[sidebar_view_id] = sidebar_view - self.sidebarViewsChanged.emit() - - ## Get a registered sidebar view by name. - # The name is the ID of the plugin that registered the view. - def getSidebarView(self, name: str): - try: - return self._sidebar_views[name] - except KeyError: - Logger.log("e", "Unable to find %s in sidebar view list", name) - return None - - ## Get the active sidebar view. - def getActiveSidebarView(self): - return self._active_sidebar_view - - ## Get the ID of the active sidebar view. - def getActiveSidebarViewId(self): - if self._active_sidebar_view: - if hasattr(self._active_sidebar_view, "getPluginId"): - return self._active_sidebar_view.getPluginId() - return "default" - - ## Change the active sidebar view to one of the registered views. - def setActiveSidebarView(self, sidebar_view_id: str): - if sidebar_view_id in self._sidebar_views: - self._active_sidebar_view = self._sidebar_views[sidebar_view_id] - Preferences.getInstance().setValue("cura/active_sidebar_view", sidebar_view_id) - self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py deleted file mode 100644 index 00a898301a..0000000000 --- a/cura/Sidebar/SidebarControllerProxy.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtProperty, pyqtSignal -from UM.Application import Application - - -## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. -from UM.Logger import Logger - - -class SidebarControllerProxy(QObject): - - def __init__(self, parent = None): - super().__init__(parent) - self._controller = Application.getInstance().getSidebarController() - self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarViewChanged) - - activeSidebarViewChanged = pyqtSignal() - - @classmethod - def createSidebarControllerProxy(self, engine, script_engine): - return SidebarControllerProxy() - - @pyqtProperty(str, notify = activeSidebarViewChanged) - def activeSidebarId(self): - return self._controller.getActiveSidebarViewId() - - @pyqtSlot(str) - def setActiveSidebarView(self, sidebar_view_id): - Logger.log("d", "Setting active sidebar view to %s", sidebar_view_id) - self._controller.setActiveSidebarView(sidebar_view_id) - - @pyqtSlot(str, result = QObject) - def getSidebarComponent(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponent() - - @pyqtSlot(str, result = QUrl) - def getSidebarComponentPath(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponentPath() - - def _onActiveSidebarViewChanged(self): - self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py deleted file mode 100644 index d5c20e0858..0000000000 --- a/cura/Sidebar/SidebarView.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -import os.path - -from UM.Application import Application -from UM.Logger import Logger -from UM.PluginObject import PluginObject -from UM.PluginRegistry import PluginRegistry - -# Abstract class for sidebar view objects. -# By default the sidebar is Cura's settings, slicing and printing overview. -# The last plugin to claim the sidebar QML target will be displayed. -class SidebarView(PluginObject): - - def __init__(self): - super().__init__() - self._view = None - - def getComponent(self): - if not self._view: - self.createView() - return self._view - - def createView(self): - component_path = self.getComponentPath() - self._view = Application.getInstance().createQmlComponent(component_path, {"manager": self}) - - ## Get the path to the component QML file as QUrl - def getComponentPath(self): - try: - plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) - sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] - return os.path.join(plugin_path, sidebar_component_file_path) - except KeyError: - Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) - return "" diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py deleted file mode 100644 index 8000d87cc5..0000000000 --- a/cura/Sidebar/SidebarViewModel.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import Qt -from UM.Qt.ListModel import ListModel -from UM.Application import Application -from UM.PluginRegistry import PluginRegistry - -## The SidebarViewModel is the default sidebar view in Cura with all the print settings and print button. -class SidebarViewModel(ListModel): - IdRole = Qt.UserRole + 1 - NameRole = Qt.UserRole + 2 - ActiveRole = Qt.UserRole + 3 - - def __init__(self, parent = None): - super().__init__(parent) - - # connect views changed signals - self._controller = Application.getInstance().getSidebarController() - self._controller.sidebarViewsChanged.connect(self._onSidebarViewsChanged) - self._controller.activeSidebarViewChanged.connect(self._onSidebarViewsChanged) - - # register Qt list roles - self.addRoleName(self.IdRole, "id") - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.ActiveRole, "active") - - ## Update the model when new views are added or another view is made the active view. - def _onSidebarViewsChanged(self): - items = [] - current_view_id = "default" - - sidebar_views = self._controller.getAllSidebarViews() - current_view = self._controller.getActiveSidebarView() - if current_view and hasattr(current_view, "getPluginId"): - current_view_id = current_view.getPluginId() - - for sidebar_view_id, sidebar_view in sidebar_views.items(): - - # Override fields for default settings sidebar - if sidebar_view_id == "default": - items.append({ - "id": "default", - "name": "Print settings sidebar", - "active": sidebar_view_id == current_view_id, - "weight": 0 - }) - continue - - sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) - - # Skip view modes that are marked as not visible - if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: - continue - - name = sidebar_view_metadata.get("name", sidebar_view_id) - weight = sidebar_view_metadata.get("weight", 1) - - items.append({ - "id": sidebar_view_id, - "name": name, - "active": sidebar_view_id == current_view_id, - "weight": weight - }) - - # Sort the views by weight - items.sort(key=lambda t: t["weight"]) - self.setItems(items) diff --git a/cura/Sidebar/__init__.py b/cura/Sidebar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index d4def4c00e..6d4e56473d 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from PyQt5.QtCore import pyqtProperty, QObject from UM.Stage import Stage @@ -8,8 +9,10 @@ class CuraStage(Stage): def __init__(self): super().__init__() - def getMainView(self): - return self.getView("main") + @pyqtProperty(QObject, constant = True) + def mainComponent(self): + return self.getDisplayComponent("main") - def getSidebarView(self): - return self.getView("sidebar") + @pyqtProperty(QObject, constant = True) + def sidebarComponent(self): + return self.getDisplayComponent("sidebar") diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index e9d8f75645..8b98f2872c 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,6 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +from UM.Application import Application from cura.Stages.CuraStage import CuraStage @@ -9,3 +9,7 @@ class MonitorStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index e18ecba7f9..884d43a8af 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -10,10 +10,11 @@ def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 1, - "icon": "" + "weight": 1 } } def register(app): - return { "stage": MonitorStage.MonitorStage() } + return { + "stage": MonitorStage.MonitorStage() + } diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index bbd2275d2e..63086b3e93 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import os.path +from UM.Application import Application +from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -9,3 +11,9 @@ class PrepareStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") + sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) + self.addDisplayComponent("sidebar", sidebar_component) diff --git a/plugins/PrepareStage/__init__.py b/plugins/PrepareStage/__init__.py index d74cf27e39..f085d788f9 100644 --- a/plugins/PrepareStage/__init__.py +++ b/plugins/PrepareStage/__init__.py @@ -15,4 +15,6 @@ def getMetaData(): } def register(app): - return { "stage": PrepareStage.PrepareStage() } + return { + "stage": PrepareStage.PrepareStage() + } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bf67efdba3..3c9ca25b05 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,8 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Sidebar - { + Loader { id: sidebar anchors @@ -379,7 +378,8 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width z: 1 - monitoringPrint: base.showPrintMonitor + + sourceComponent: UM.Controller.activeStage.sidebarComponent } Rectangle diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 3bfc803a78..7429ef26df 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,603 +10,606 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -Rectangle +Component { - id: base; - - property int currentModeIndex; - property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" - - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state - - property bool monitoringPrint: false - - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} - - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: - { - base.showTooltip(base, {x: 0, y: item.y}, text); - } - } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - onWheel: - { - wheel.accepted = true; - } - } - - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - - Label { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - width: Math.floor(parent.width * 0.45) - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView - } - - Rectangle { - id: settingsModeSelection - color: "transparent" - width: Math.floor(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView - Component{ - id: wizardDelegate - Button { - height: settingsModeSelection.height - anchors.left: parent.left - anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - width: Math.floor(0.5 * parent.width) - text: model.text - exclusiveGroup: modeMenuGroup; - checkable: true; - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start(); - } - else - { - tooltipDelayTimer.stop(); - base.hideTooltip(); - } - } - - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text") - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } - } - } - ExclusiveGroup { id: modeMenuGroup; } - - ListView - { - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top - anchors.left: parent.left - width: parent.width - } - } - - StackView - { - id: sidebarContents - - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !monitoringPrint && !hideSettings - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation - { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(monitoringPrint && connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - if(monitoringPrint) - { - return "PrintMonitor.qml" - } else - { - return "SidebarContents.qml" - } - } - else - { - return "" - } - } - } - Rectangle { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) - } + id: base; - Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - visible: !monitoringPrint - clip: true + property int currentModeIndex; + property bool hideSettings: PrintInformation.preSliced + property bool hideView: Cura.MachineManager.activeMachineName == "" - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + // Is there an output device for this printer? + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state - MouseArea + property bool monitoringPrint: false + + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + color: UM.Theme.getColor("sidebar") + UM.I18nCatalog { id: catalog; name:"cura"} + + Timer { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
" + feature + ":  %1  %1%
"; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } + base.showTooltip(base, {x: 0, y: item.y}, text); } } - Label + function showTooltip(item, position, text) { - function formatRow(items) + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } + + function hideTooltip() + { + tooltip.hide(); + } + + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); + + var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + return finalTime; + } + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons; + + onWheel: { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; + wheel.accepted = true; } + } - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
"; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle + SidebarHeader { + id: header width: parent.width - property string tooltipText - text: + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + + Label { + id: settingsModeLabel + text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: headerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + width: Math.floor(parent.width * 0.45) + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + visible: !monitoringPrint && !hideView + } + + Rectangle { + id: settingsModeSelection + color: "transparent" + width: Math.floor(parent.width * 0.55) + height: UM.Theme.getSize("sidebar_header_mode_toggle").height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) + if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - if(someCostsKnown) - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + return settingsModeLabel.bottom; } else { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + return headerSeparator.bottom; } } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + visible: !monitoringPrint && !hideSettings && !hideView + Component{ + id: wizardDelegate + Button { + height: settingsModeSelection.height + anchors.left: parent.left + anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + width: Math.floor(0.5 * parent.width) + text: model.text + exclusiveGroup: modeMenuGroup; + checkable: true; + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - onEntered: - { + onHoveredChanged: { + if (hovered) + { + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start(); + } + else + { + tooltipDelayTimer.stop(); + base.hideTooltip(); + } + } - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + style: ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : + control.hovered ? UM.Theme.getColor("action_button_hovered_border") : + UM.Theme.getColor("action_button_border") + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : + control.hovered ? UM.Theme.getColor("action_button_hovered") : + UM.Theme.getColor("action_button") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : + UM.Theme.getColor("action_button_text") + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } } } - onExited: + } + ExclusiveGroup { id: modeMenuGroup; } + + ListView + { + id: modesList + property var index: 0 + model: modesListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + } + } + + StackView + { + id: sidebarContents + + anchors.bottom: footerSeparator.top + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: base.left + anchors.right: base.right + visible: !monitoringPrint && !hideSettings + + delegate: StackViewDelegate + { + function transitionFinished(properties) { - base.hideTooltip(); + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } } } } - } - // SaveButton and MonitorButton are actually the bottom footer panels. - // "!monitoringPrint" currently means "show-settings-mode" - SaveButton - { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: !monitoringPrint - } - - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } - - - SidebarTooltip - { - id: tooltip; - } - - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel; - } - - SidebarSimple - { - id: sidebarSimple; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced - }) - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - - var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) - if(index) + Loader { - currentModeIndex = index; + id: controlItem + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + sourceComponent: + { + if(monitoringPrint && connectedPrinter != null) + { + if(connectedPrinter.controlItem != null) + { + return connectedPrinter.controlItem + } + } + return null + } + } + + Loader + { + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + source: + { + if(controlItem.sourceComponent == null) + { + if(monitoringPrint) + { + return "PrintMonitor.qml" + } else + { + return "SidebarContents.qml" + } + } + else + { + return "" + } + } + } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.bottom: printSpecs.top + anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) + } + + Item + { + id: printSpecs + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + height: timeDetails.height + costSpec.height + width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) + visible: !monitoringPrint + clip: true + + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
" + feature + ":  %1  %1%
"; + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
"; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + anchors.left: parent.left + anchors.bottom: parent.bottom + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + } + } + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + } + + // SaveButton and MonitorButton are actually the bottom footer panels. + // "!monitoringPrint" currently means "show-settings-mode" + SaveButton + { + id: saveButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: !monitoringPrint + } + + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } + + + SidebarTooltip + { + id: tooltip; + } + + // Setting mode: Recommended or Custom + ListModel + { + id: modesListModel; + } + + SidebarSimple + { + id: sidebarSimple; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + id: sidebarAdvanced; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Component.onCompleted: + { + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), + item: sidebarSimple + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), + item: sidebarAdvanced + }) + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); + + var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) + if(index) + { + currentModeIndex = index; + } + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBed + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 0 } } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 - } } diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 50e90a9a37..5817aff13b 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.2 as UM +import UM 1.4 as UM import Cura 1.0 as Cura import "Menus" @@ -83,34 +83,13 @@ Rectangle style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height onClicked: UM.Controller.setActiveStage(model.id) + iconSource: model.stage.iconSource + + property color overlayColor: "transparent" + property string overlayIconSource: "" } } -// Button -// { -// id: showSettings -// height: UM.Theme.getSize("sidebar_header").height -// text: catalog.i18nc("@title:tab", "Prepare") -// checkable: true -// checked: isChecked() -// exclusiveGroup: sidebarHeaderBarGroup -// style: UM.Theme.styles.topbar_header_tab -// -// // We use a Qt.binding to re-bind the checkbox state after manually setting it -// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing -// onClicked: { -// base.stopMonitoringPrint() -// checked = Qt.binding(isChecked) -// } -// -// function isChecked () { -// return !base.monitoringPrint -// } -// -// property color overlayColor: "transparent" -// property string overlayIconSource: "" -// } - // Button // { // id: showMonitor