diff --git a/cura/Machines/Models/MachineListModel.py b/cura/Machines/Models/MachineListModel.py index 24375b72ce..b84dc26f0a 100644 --- a/cura/Machines/Models/MachineListModel.py +++ b/cura/Machines/Models/MachineListModel.py @@ -5,7 +5,7 @@ # online cloud connected printers are represented within this ListModel. Additional information such as the number of # connected printers for each printer type is gathered. -from typing import Optional +from typing import Optional, List, cast from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSlot, pyqtProperty, pyqtSignal @@ -14,7 +14,6 @@ from UM.Settings.ContainerStack import ContainerStack from UM.Settings.Interfaces import ContainerInterface from UM.i18n import i18nCatalog from UM.Util import parseBool -from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.GlobalStack import GlobalStack @@ -29,11 +28,13 @@ class MachineListModel(ListModel): MachineCountRole = Qt.ItemDataRole.UserRole + 6 IsAbstractMachineRole = Qt.ItemDataRole.UserRole + 7 ComponentTypeRole = Qt.ItemDataRole.UserRole + 8 + IsNetworkedMachineRole = Qt.ItemDataRole.UserRole + 9 - def __init__(self, parent: Optional[QObject] = None) -> None: + def __init__(self, parent: Optional[QObject] = None, machines_filter: List[GlobalStack] = None, listenToChanges: bool = True) -> None: super().__init__(parent) self._show_cloud_printers = False + self._machines_filter = machines_filter self._catalog = i18nCatalog("cura") @@ -45,17 +46,18 @@ class MachineListModel(ListModel): self.addRoleName(self.MachineCountRole, "machineCount") self.addRoleName(self.IsAbstractMachineRole, "isAbstractMachine") self.addRoleName(self.ComponentTypeRole, "componentType") + self.addRoleName(self.IsNetworkedMachineRole, "isNetworked") self._change_timer = QTimer() self._change_timer.setInterval(200) self._change_timer.setSingleShot(True) self._change_timer.timeout.connect(self._update) - # Listen to changes - CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) - CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) - CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) - self._updateDelayed() + if listenToChanges: + CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) + self._updateDelayed() showCloudPrintersChanged = pyqtSignal(bool) @@ -79,17 +81,33 @@ class MachineListModel(ListModel): def _updateDelayed(self) -> None: self._change_timer.start() + def _getMachineStacks(self) -> List[ContainerStack]: + return CuraContainerRegistry.getInstance().findContainerStacks(type = "machine") + + def _getAbstractMachineStacks(self) -> List[ContainerStack]: + return CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True") + + def set_machines_filter(self, machines_filter: Optional[List[GlobalStack]]) -> None: + self._machines_filter = machines_filter + self._update() + def _update(self) -> None: self.clear() from cura.CuraApplication import CuraApplication machines_manager = CuraApplication.getInstance().getMachineManager() - other_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type="machine") + other_machine_stacks = self._getMachineStacks() other_machine_stacks.sort(key = lambda machine: machine.getName().upper()) - abstract_machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(is_abstract_machine = "True") + abstract_machine_stacks = self._getAbstractMachineStacks() abstract_machine_stacks.sort(key = lambda machine: machine.getName().upper(), reverse = True) + + if self._machines_filter is not None: + filter_ids = [machine_filter.id for machine_filter in self._machines_filter] + other_machine_stacks = [machine for machine in other_machine_stacks if machine.id in filter_ids] + abstract_machine_stacks = [machine for machine in abstract_machine_stacks if machine.id in filter_ids] + for abstract_machine in abstract_machine_stacks: definition_id = abstract_machine.definition.getId() online_machine_stacks = machines_manager.getMachinesWithDefinition(definition_id, online_only = True) @@ -113,18 +131,13 @@ class MachineListModel(ListModel): other_machine_stacks.remove(stack) if len(abstract_machine_stacks) > 0: - if self._show_cloud_printers: - self.appendItem({"componentType": "HIDE_BUTTON", - "isOnline": True, - "isAbstractMachine": False, - "machineCount": 0 - }) - else: - self.appendItem({"componentType": "SHOW_BUTTON", - "isOnline": True, - "isAbstractMachine": False, - "machineCount": 0 - }) + self.appendItem({ + "componentType": "HIDE_BUTTON" if self._show_cloud_printers else "SHOW_BUTTON", + "isOnline": True, + "isAbstractMachine": False, + "machineCount": 0, + "catergory": "connected", + }) for stack in other_machine_stacks: self.addItem(stack, False) @@ -134,11 +147,13 @@ class MachineListModel(ListModel): return self.appendItem({ - "componentType": "MACHINE", - "name": container_stack.getName(), - "id": container_stack.getId(), - "metadata": container_stack.getMetaData().copy(), - "isOnline": is_online, - "isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)), - "machineCount": machine_count, - }) + "componentType": "MACHINE", + "name": container_stack.getName(), + "id": container_stack.getId(), + "metadata": container_stack.getMetaData().copy(), + "isOnline": is_online, + "isAbstractMachine": parseBool(container_stack.getMetaDataEntry("is_abstract_machine", False)), + "isNetworked": cast(GlobalStack, container_stack).hasNetworkedConnection() if isinstance(container_stack, GlobalStack) else False, + "machineCount": machine_count, + "catergory": "connected" if is_online else "other", + }) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index bec50b5660..0163877e3a 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -9,6 +9,7 @@ from typing import cast, Dict, List, Optional, Tuple, Any, Set import xml.etree.ElementTree as ET +from UM.Util import parseBool from UM.Workspace.WorkspaceReader import WorkspaceReader from UM.Application import Application @@ -600,6 +601,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._dialog.setActiveMode(active_mode) self._dialog.setUpdatableMachines(updatable_machines) self._dialog.setMachineName(machine_name) + self._dialog.setIsNetworkedMachine(existing_global_stack.hasNetworkedConnection()) + self._dialog.setIsAbstractMachine(parseBool(existing_global_stack.getMetaDataEntry("is_abstract_machine", False))) + self._dialog.setMachineToOverride(global_stack_id) self._dialog.setMaterialLabels(material_labels) self._dialog.setMachineType(machine_type) self._dialog.setExtruders(extruders) diff --git a/plugins/3MFReader/UpdatableMachinesModel.py b/plugins/3MFReader/UpdatableMachinesModel.py deleted file mode 100644 index 9d6eee6c3e..0000000000 --- a/plugins/3MFReader/UpdatableMachinesModel.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2020 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from typing import Dict, List - -from PyQt6.QtCore import Qt - -from UM.Qt.ListModel import ListModel -from cura.Settings.GlobalStack import GlobalStack - -create_new_list_item = { - "id": "new", - "name": "Create new", - "displayName": "Create new", - "type": "default_option" # to make sure we are not mixing the "Create new" option with a printer with id "new" -} # type: Dict[str, str] - - -class UpdatableMachinesModel(ListModel): - """Model that holds cura packages. - - By setting the filter property the instances held by this model can be changed. - """ - - def __init__(self, parent = None) -> None: - super().__init__(parent) - - self.addRoleName(Qt.ItemDataRole.UserRole + 1, "id") - self.addRoleName(Qt.ItemDataRole.UserRole + 2, "name") - self.addRoleName(Qt.ItemDataRole.UserRole + 3, "displayName") - self.addRoleName(Qt.ItemDataRole.UserRole + 4, "type") # Either "default_option" or "machine" - - def update(self, machines: List[GlobalStack]) -> None: - items = [create_new_list_item] # type: List[Dict[str, str]] - - for machine in sorted(machines, key = lambda printer: printer.name): - items.append({ - "id": machine.id, - "name": machine.name, - "displayName": "Update " + machine.name, - "type": "machine" - }) - self.setItems(items) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 0a8f7784b2..afa1deecfd 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -5,6 +5,7 @@ from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication, QU from PyQt6.QtGui import QDesktopServices from typing import List, Optional, Dict, cast +from cura.Machines.Models.MachineListModel import MachineListModel from cura.Settings.GlobalStack import GlobalStack from UM.Application import Application from UM.FlameProfiler import pyqtSlot @@ -14,8 +15,6 @@ from UM.Message import Message from UM.PluginRegistry import PluginRegistry from UM.Settings.ContainerRegistry import ContainerRegistry -from .UpdatableMachinesModel import UpdatableMachinesModel - import os import threading import time @@ -63,10 +62,12 @@ class WorkspaceDialog(QObject): self._extruders = [] self._objects_on_plate = False self._is_printer_group = False - self._updatable_machines_model = UpdatableMachinesModel(self) + self._updatable_machines_model = MachineListModel(self, listenToChanges=False) self._missing_package_metadata: List[Dict[str, str]] = [] self._plugin_registry: PluginRegistry = CuraApplication.getInstance().getPluginRegistry() self._install_missing_package_dialog: Optional[QObject] = None + self._is_abstract_machine = False + self._is_networked_machine = False machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() @@ -80,6 +81,8 @@ class WorkspaceDialog(QObject): intentNameChanged = pyqtSignal() machineNameChanged = pyqtSignal() updatableMachinesChanged = pyqtSignal() + isAbstractMachineChanged = pyqtSignal() + isNetworkedChanged = pyqtSignal() materialLabelsChanged = pyqtSignal() objectsOnPlateChanged = pyqtSignal() numUserSettingsChanged = pyqtSignal() @@ -161,13 +164,31 @@ class WorkspaceDialog(QObject): self.machineNameChanged.emit() @pyqtProperty(QObject, notify = updatableMachinesChanged) - def updatableMachinesModel(self) -> UpdatableMachinesModel: - return cast(UpdatableMachinesModel, self._updatable_machines_model) + def updatableMachinesModel(self) -> MachineListModel: + return cast(MachineListModel, self._updatable_machines_model) def setUpdatableMachines(self, updatable_machines: List[GlobalStack]) -> None: - self._updatable_machines_model.update(updatable_machines) + self._updatable_machines_model.set_machines_filter(updatable_machines) self.updatableMachinesChanged.emit() + @pyqtProperty(bool, notify = isAbstractMachineChanged) + def isAbstractMachine(self) -> bool: + return self._is_abstract_machine + + @pyqtSlot(bool) + def setIsAbstractMachine(self, is_abstract_machine: bool) -> None: + self._is_abstract_machine = is_abstract_machine + self.isAbstractMachineChanged.emit() + + @pyqtProperty(bool, notify = isNetworkedChanged) + def isNetworked(self) -> bool: + return self._is_networked_machine + + @pyqtSlot(bool) + def setIsNetworkedMachine(self, is_networked_machine: bool) -> None: + self._is_networked_machine = is_networked_machine + self.isNetworkedChanged.emit() + @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self) -> str: return self._quality_type diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 4899c6873a..8dcf1e8454 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Ultimaker B.V. +// Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 @@ -11,47 +11,48 @@ import Cura 1.1 as Cura UM.Dialog { - id: base + id: workspaceDialog title: catalog.i18nc("@title:window", "Open Project") - minimumWidth: UM.Theme.getSize("popup_dialog").width - minimumHeight: UM.Theme.getSize("popup_dialog").height - width: minimumWidth - backgroundColor: UM.Theme.getColor("main_background") margin: UM.Theme.getSize("default_margin").width - property int comboboxHeight: UM.Theme.getSize("default_margin").height + minimumWidth: UM.Theme.getSize("modal_window_minimum").width + minimumHeight: UM.Theme.getSize("modal_window_minimum").height - onClosing: manager.notifyClosed() - onVisibleChanged: + backgroundColor: UM.Theme.getColor("detail_background") + + headerComponent: Rectangle { - if (visible) + height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height + color: UM.Theme.getColor("main_background") + + UM.Label { - machineResolveComboBox.currentIndex = 0 - qualityChangesResolveComboBox.currentIndex = 0 - materialResolveComboBox.currentIndex = 0 + id: titleLabel + text: catalog.i18nc("@action:title", "Summary - Cura Project") + font: UM.Theme.getFont("large") + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.leftMargin: UM.Theme.getSize("default_margin").height } } - Flickable + Rectangle { - clip: true - width: parent.width - height: parent.height - contentHeight: dialogSummaryItem.height - ScrollBar.vertical: UM.ScrollBar { id: verticalScrollBar } + anchors.fill: parent + UM.I18nCatalog { id: catalog; name: "cura" } + color: UM.Theme.getColor("main_background") - Item + Flickable { id: dialogSummaryItem - width: verticalScrollBar.visible ? parent.width - verticalScrollBar.width - UM.Theme.getSize("default_margin").width : parent.width - height: childrenRect.height - anchors.margins: 10 * screenScaleFactor + width: parent.width + height: parent.height - UM.I18nCatalog - { - id: catalog - name: "cura" - } + clip: true + + contentHeight: contentColumn.height + ScrollBar.vertical: UM.ScrollBar { id: scrollbar } ListModel { @@ -68,373 +69,224 @@ UM.Dialog Column { - width: parent.width + id: contentColumn + width: parent.width - scrollbar.width - UM.Theme.getSize("default_margin").width height: childrenRect.height + spacing: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width - Column + WorkspaceSection { - width: parent.width - height: childrenRect.height - - UM.Label + id: printerSection + title: catalog.i18nc("@action:label", "Printer settings") + iconSource: UM.Theme.getIcon("Printer") + content: Column { - id: titleLabel - text: catalog.i18nc("@action:title", "Summary - Cura Project") - font: UM.Theme.getFont("large") - } + spacing: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width - Rectangle - { - id: separator - color: UM.Theme.getColor("text") - width: parent.width - height: UM.Theme.getSize("default_lining").height - } - } - - Item - { - width: parent.width - height: childrenRect.height - - UM.TooltipArea - { - id: machineResolveStrategyTooltip - anchors.top: parent.top - anchors.right: parent.right - width: (parent.width / 3) | 0 - height: visible ? comboboxHeight : 0 - visible: base.visible && machineResolveComboBox.model.count > 1 - text: catalog.i18nc("@info:tooltip", "How should the conflict in the machine be resolved?") - Cura.ComboBox + WorkspaceRow { - id: machineResolveComboBox - model: manager.updatableMachinesModel - visible: machineResolveStrategyTooltip.visible - textRole: "displayName" - width: parent.width - height: UM.Theme.getSize("button").height - onCurrentIndexChanged: + leftLabelText: catalog.i18nc("@action:label", "Type") + rightLabelText: manager.machineType + } + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name") + rightLabelText: manager.machineName + } + } + + comboboxTitle: catalog.i18nc("@action:label", "Open With") + comboboxTooltipText: catalog.i18nc("@info:tooltip", "Printer settings will be updated to match the settings saved with the project.") + comboboxVisible: workspaceDialog.visible && manager.updatableMachinesModel.count > 1 + combobox: Cura.MachineSelector + { + id: machineSelector + headerCornerSide: Cura.RoundedRectangle.Direction.All + width: parent.width + height: parent.height + machineListModel: manager.updatableMachinesModel + machineName: manager.machineName + + isConnectedCloudPrinter: false + isCloudRegistered: false + isNetworkPrinter: manager.isNetworked + isGroup: manager.isAbstractMachine + connectionStatus: "" + + minDropDownWidth: machineSelector.width + + buttons: [ + Cura.SecondaryButton { - if (model.getItem(currentIndex).id == "new" - && model.getItem(currentIndex).type == "default_option") + id: createNewPrinter + text: catalog.i18nc("@button", "Create new") + fixedWidthMode: true + width: parent.width - leftPadding * 1.5 + onClicked: { + machineSelector.machineName = catalog.i18nc("@button", "Create new") + manager.setIsAbstractMachine(false) + manager.setIsNetworkedMachine(false) + + toggleContent() manager.setResolveStrategy("machine", "new") } - else - { - manager.setResolveStrategy("machine", "override") - manager.setMachineToOverride(model.getItem(currentIndex).id) - } } + ] - onVisibleChanged: - { - if (!visible) {return} - - currentIndex = 0 - // If the project printer exists in Cura, set it as the default dropdown menu option. - // No need to check object 0, which is the "Create new" option - for (var i = 1; i < model.count; i++) - { - if (model.getItem(i).name == manager.machineName) - { - currentIndex = i - break - } - } - // The project printer does not exist in Cura. If there is at least one printer of the same - // type, select the first one, else set the index to "Create new" - if (currentIndex == 0 && model.count > 1) - { - currentIndex = 1 - } - } - } - } - - Column - { - width: parent.width - height: childrenRect.height - - UM.Label + onSelectPrinter: function(machine) { - id: printer_settings_label - text: catalog.i18nc("@action:label", "Printer settings") - font: UM.Theme.getFont("default_bold") - } - - Row - { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", "Type") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: manager.machineType - width: (parent.width / 3) | 0 - } - } - - Row - { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: manager.machineName - width: (parent.width / 3) | 0 - wrapMode: Text.WordWrap - } + toggleContent(); + manager.setResolveStrategy("machine", "override") + manager.setMachineToOverride(machine.id) + manager.setIsAbstractMachine(machine.isAbstractMachine) + manager.setIsNetworkedMachine(machine.isNetworked) + machineSelector.machineName = machine.name } } } - Item + WorkspaceSection { - width: parent.width - height: childrenRect.height - - UM.TooltipArea + id: profileSection + title: catalog.i18nc("@action:label", "Profile settings") + iconSource: UM.Theme.getIcon("Sliders") + content: Column { - anchors.right: parent.right - anchors.top: parent.top - width: (parent.width / 3) | 0 - height: visible ? comboboxHeight : 0 + id: profileSettingsValuesTable + spacing: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", "Name") + rightLabelText: manager.qualityName + } + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", "Intent") + rightLabelText: manager.intentName + } + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", "Not in profile") + rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings) + visible: manager.numUserSettings != 0 + } + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", "Derivative from") + rightLabelText: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges) + visible: manager.numSettingsOverridenByQualityChanges != 0 + } + } + + comboboxVisible: manager.qualityChangesConflict + combobox: Cura.ComboBox + { + id: qualityChangesResolveComboBox + model: resolveStrategiesModel + textRole: "label" visible: manager.qualityChangesConflict - text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?") - Cura.ComboBox - { - model: resolveStrategiesModel - textRole: "label" - id: qualityChangesResolveComboBox - width: parent.width - height: UM.Theme.getSize("button").height - onActivated: - { - manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key) - } - } - } - Column - { - width: parent.width - height: childrenRect.height - - UM.Label + // This is a hack. This will trigger onCurrentIndexChanged and set the index when this component in loaded + currentIndex: { - text: catalog.i18nc("@action:label", "Profile settings") - font: UM.Theme.getFont("default_bold") + currentIndex = 0 } - Row + onCurrentIndexChanged: { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", "Name") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: manager.qualityName - width: (parent.width / 3) | 0 - wrapMode: Text.WordWrap - } - } - - Row - { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", "Intent") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: manager.intentName - width: (parent.width / 3) | 0 - wrapMode: Text.WordWrap - } - } - - Row - { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", "Not in profile") - visible: manager.numUserSettings != 0 - width: (parent.width / 3) | 0 - } - UM.Label - { - text: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings) - visible: manager.numUserSettings != 0 - width: (parent.width / 3) | 0 - } - } - - Row - { - width: parent.width - height: childrenRect.height - - UM.Label - { - text: catalog.i18nc("@action:label", "Derivative from") - visible: manager.numSettingsOverridenByQualityChanges != 0 - width: (parent.width / 3) | 0 - } - UM.Label - { - text: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges) - width: (parent.width / 3) | 0 - visible: manager.numSettingsOverridenByQualityChanges != 0 - wrapMode: Text.WordWrap - } + manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(currentIndex).key) } } } - Item + WorkspaceSection { - width: parent.width - height: childrenRect.height - - UM.TooltipArea + id: materialSection + title: catalog.i18nc("@action:label", "Material settings") + iconSource: UM.Theme.getIcon("Spool") + content: Column { - id: materialResolveTooltip - anchors.right: parent.right - anchors.top: parent.top - width: (parent.width / 3) | 0 - height: visible ? comboboxHeight : 0 - visible: manager.materialConflict - text: catalog.i18nc("@info:tooltip", "How should the conflict in the material be resolved?") - Cura.ComboBox - { - model: resolveStrategiesModel - textRole: "label" - id: materialResolveComboBox - width: parent.width - height: UM.Theme.getSize("button").height - onActivated: - { - manager.setResolveStrategy("material", resolveStrategiesModel.get(index).key) - } - } - } - - Column - { - width: parent.width - height: childrenRect.height - Row - { - height: childrenRect.height - width: parent.width - spacing: UM.Theme.getSize("narrow_margin").width - - UM.Label - { - text: catalog.i18nc("@action:label", "Material settings") - font: UM.Theme.getFont("default_bold") - width: (parent.width / 3) | 0 - } - } + spacing: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width Repeater { model: manager.materialLabels - delegate: Row + delegate: WorkspaceRow { - width: parent.width - height: childrenRect.height - UM.Label - { - text: catalog.i18nc("@action:label", "Name") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: modelData - width: (parent.width / 3) | 0 - wrapMode: Text.WordWrap - } + leftLabelText: catalog.i18nc("@action:label", "Name") + rightLabelText: modelData } } } + + comboboxVisible: manager.materialConflict + + combobox: Cura.ComboBox + { + id: materialResolveComboBox + model: resolveStrategiesModel + textRole: "label" + visible: manager.materialConflict + + // This is a hack. This will trigger onCurrentIndexChanged and set the index when this component in loaded + currentIndex: + { + currentIndex = 0 + } + + onCurrentIndexChanged: + { + manager.setResolveStrategy("material", resolveStrategiesModel.get(currentIndex).key) + } + } } - Column + WorkspaceSection { - width: parent.width - height: childrenRect.height + id: visibilitySection + title: catalog.i18nc("@action:label", "Setting visibility") + iconSource: UM.Theme.getIcon("Eye") + content: Column + { + spacing: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width + bottomPadding: UM.Theme.getSize("narrow_margin").height - UM.Label - { - text: catalog.i18nc("@action:label", "Setting visibility") - font: UM.Theme.getFont("default_bold") - } - Row - { - width: parent.width - height: childrenRect.height - UM.Label + WorkspaceRow { - text: catalog.i18nc("@action:label", "Mode") - width: (parent.width / 3) | 0 + leftLabelText: catalog.i18nc("@action:label", "Mode") + rightLabelText: manager.activeMode } - UM.Label + + WorkspaceRow { - text: manager.activeMode - width: (parent.width / 3) | 0 - } - } - Row - { - width: parent.width - height: childrenRect.height - visible: manager.hasVisibleSettingsField - UM.Label - { - text: catalog.i18nc("@action:label", "Visible settings:") - width: (parent.width / 3) | 0 - } - UM.Label - { - text: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings) - width: (parent.width / 3) | 0 + leftLabelText: catalog.i18nc("@action:label", "%1 out of %2" ).arg(manager.numVisibleSettings).arg(manager.totalNumberOfSettings) + rightLabelText: manager.activeMode + visible: manager.hasVisibleSettingsField } } } Row { + id: clearBuildPlateWarning width: parent.width height: childrenRect.height + spacing: UM.Theme.getSize("default_margin").width visible: manager.hasObjectsOnPlate + UM.ColorImage { width: warningLabel.height @@ -459,14 +311,18 @@ UM.Dialog color: warning ? UM.Theme.getColor("warning") : "transparent" anchors.bottom: parent.bottom width: parent.width - height: childrenRect.height + 2 * base.margin + height: childrenRect.height + (warning ? 2 * workspaceDialog.margin : workspaceDialog.margin) Column { height: childrenRect.height - spacing: base.margin + spacing: workspaceDialog.margin + + anchors.leftMargin: workspaceDialog.margin + anchors.rightMargin: workspaceDialog.margin + anchors.bottomMargin: workspaceDialog.margin + anchors.topMargin: warning ? workspaceDialog.margin : 0 - anchors.margins: base.margin anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top @@ -476,7 +332,7 @@ UM.Dialog id: warningRow height: childrenRect.height visible: warning - spacing: base.margin + spacing: workspaceDialog.margin UM.ColorImage { width: UM.Theme.getSize("extruder_icon").width @@ -500,7 +356,7 @@ UM.Dialog } } - buttonSpacing: UM.Theme.getSize("default_margin").width + buttonSpacing: UM.Theme.getSize("wide_margin").width rightButtons: [ Cura.TertiaryButton @@ -532,6 +388,19 @@ UM.Dialog } ] + onClosing: manager.notifyClosed() onRejected: manager.onCancelButtonClicked() onAccepted: manager.onOkButtonClicked() + onVisibleChanged: + { + if (visible) + { + // Force relead the comboboxes + // Since this dialog is only created once the first time you open it, these comboxes need to be reloaded + // each time it is shown after the first time so that the indexes will update correctly. + materialSection.reloadValues() + profileSection.reloadValues() + printerSection.reloadValues() + } + } } diff --git a/plugins/3MFReader/WorkspaceRow.qml b/plugins/3MFReader/WorkspaceRow.qml new file mode 100644 index 0000000000..8d9f1f25b3 --- /dev/null +++ b/plugins/3MFReader/WorkspaceRow.qml @@ -0,0 +1,34 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.2 + +import UM 1.5 as UM +import Cura 1.1 as Cura + +Row +{ + property alias leftLabelText: leftLabel.text + property alias rightLabelText: rightLabel.text + + width: parent.width + height: visible ? childrenRect.height : 0 + + UM.Label + { + id: leftLabel + text: catalog.i18nc("@action:label", "Type") + width: Math.round(parent.width / 4) + wrapMode: Text.WordWrap + } + UM.Label + { + id: rightLabel + text: manager.machineType + width: Math.round(parent.width / 3) + wrapMode: Text.WordWrap + } +} \ No newline at end of file diff --git a/plugins/3MFReader/WorkspaceSection.qml b/plugins/3MFReader/WorkspaceSection.qml new file mode 100644 index 0000000000..6eb8f50bdf --- /dev/null +++ b/plugins/3MFReader/WorkspaceSection.qml @@ -0,0 +1,126 @@ +// Copyright (c) 2022 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.3 + + +import UM 1.5 as UM + + +Item +{ + property alias title: sectionTitle.text + property alias iconSource: sectionTitleIcon.source + property Component content: Item { visible: false } + + property alias comboboxTitle: comboboxLabel.text + property Component combobox: Item { visible: false } + property string comboboxTooltipText: "" + property bool comboboxVisible: false + + width: parent.width + height: childrenRect.height + anchors.leftMargin: UM.Theme.getSize("default_margin").width + + Row + { + id: sectionTitleRow + anchors.top: parent.top + bottomPadding: UM.Theme.getSize("default_margin").height + spacing: UM.Theme.getSize("default_margin").width + + UM.ColorImage + { + id: sectionTitleIcon + anchors.verticalCenter: parent.verticalCenter + source: "" + height: UM.Theme.getSize("medium_button_icon").height + width: height + } + UM.Label + { + id: sectionTitle + text: "" + anchors.verticalCenter: parent.verticalCenter + font: UM.Theme.getFont("default_bold") + } + } + + Item + { + id: comboboxTooltip + width: Math.round(parent.width / 2.5) + height: visible ? UM.Theme.getSize("default_margin").height : 0 + anchors.top: parent.top + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + visible: comboboxVisible + + UM.Label + { + id: comboboxLabel + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: UM.Theme.getSize("default_margin").height + visible: comboboxVisible && text != "" + text: "" + font: UM.Theme.getFont("default_bold") + } + + Loader + { + id: comboboxLoader + width: parent.width + height: UM.Theme.getSize("button").height + anchors.top: comboboxLabel.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.left: parent.left + sourceComponent: combobox + } + + MouseArea + { + id: helpIconMouseArea + anchors.right: parent.right + anchors.verticalCenter: comboboxLabel.verticalCenter + width: childrenRect.width + height: childrenRect.height + hoverEnabled: true + + UM.ColorImage + { + width: UM.Theme.getSize("section_icon").width + height: width + + visible: comboboxTooltipText != "" + source: UM.Theme.getIcon("Help") + + UM.ToolTip + { + text: comboboxTooltipText + visible: helpIconMouseArea.containsMouse + targetPoint: Qt.point(parent.x + Math.round(parent.width / 2), parent.y) + x: 0 + y: parent.y + parent.height + UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("tooltip").width + } + } + } + } + + + Loader + { + width: parent.width + height: content.height + anchors.top: sectionTitleRow.bottom + sourceComponent: content + } + + function reloadValues() + { + comboboxLoader.sourceComponent = null + comboboxLoader.sourceComponent = combobox + } +} \ No newline at end of file diff --git a/plugins/MonitorStage/MonitorMenu.qml b/plugins/MonitorStage/MonitorMenu.qml index bc95c276e8..ba99c3119e 100644 --- a/plugins/MonitorStage/MonitorMenu.qml +++ b/plugins/MonitorStage/MonitorMenu.qml @@ -19,5 +19,7 @@ Item width: UM.Theme.getSize("machine_selector_widget").width height: parent.height anchors.centerIn: parent + + machineListModel: Cura.MachineListModel {} } } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 2d4b3e01e5..be7aebcf7a 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -150,6 +150,7 @@ Item width: parent.width / 2 - UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("setting_control").height textRole: "text" + forceHighlight: base.hovered model: ListModel { diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 42c3c8dde6..ab9b9b1b3d 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -55,6 +55,50 @@ Item Layout.preferredWidth: parent.machineSelectorWidth Layout.fillWidth: true Layout.fillHeight: true + + machineManager: Cura.MachineManager + onSelectPrinter: function(machine) + { + toggleContent(); + Cura.MachineManager.setActiveMachine(machine.id); + } + + machineListModel: Cura.MachineListModel {} + + buttons: [ + Cura.SecondaryButton + { + id: addPrinterButton + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@button", "Add printer") + // The maximum width of the button is half of the total space, minus the padding of the parent, the left + // padding of the component and half the spacing because of the space between buttons. + fixedWidthMode: true + width: Math.round(parent.width / 2 - leftPadding * 1.5) + onClicked: + { + machineSelection.toggleContent() + Cura.Actions.addMachine.trigger() + } + }, + Cura.SecondaryButton + { + id: managePrinterButton + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@button", "Manage printers") + fixedWidthMode: true + // The maximum width of the button is half of the total space, minus the padding of the parent, the right + // padding of the component and half the spacing because of the space between buttons. + width: Math.round(parent.width / 2 - rightPadding * 1.5) + onClicked: + { + machineSelection.toggleContent() + Cura.Actions.configureMachines.trigger() + } + } + ] } Cura.ConfigurationMenu diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 0008529408..b8b27049f6 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -11,12 +11,29 @@ Cura.ExpandablePopup { id: machineSelector - property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasNetworkConnection - property bool isConnectedCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection - property bool isCloudRegistered: Cura.MachineManager.activeMachineHasCloudRegistration - property bool isGroup: Cura.MachineManager.activeMachineIsGroup + property Cura.MachineManager machineManager + property bool isNetworkPrinter: machineManager.activeMachineHasNetworkConnection + property bool isConnectedCloudPrinter: machineManager.activeMachineHasCloudConnection + property bool isCloudRegistered: machineManager.activeMachineHasCloudRegistration + property bool isGroup: machineManager.activeMachineIsGroup + property string machineName: { + if (isNetworkPrinter && machineManager.activeMachineNetworkGroupName != "") + { + return machineManager.activeMachineNetworkGroupName + } + if (machineManager.activeMachine != null) + { + return machineManager.activeMachine.name + } + return "" + } - readonly property string connectionStatus: { + property alias machineListModel: machineSelectorList.model + property alias onSelectPrinter: machineSelectorList.onSelectPrinter + + property list buttons + + property string connectionStatus: { if (isNetworkPrinter) { return "printer_connected" @@ -42,7 +59,7 @@ Cura.ExpandablePopup { if (Cura.API.account.isLoggedIn) { - if (Cura.MachineManager.activeMachineIsLinkedToCurrentAccount) + if (machineManager.activeMachineIsLinkedToCurrentAccount) { return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.") } @@ -55,7 +72,8 @@ Cura.ExpandablePopup { return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please sign in to connect to the cloud printer.") } - } else + } + else { return catalog.i18nc("@status", "The cloud connection is currently unavailable. Please check your internet connection.") } @@ -77,18 +95,8 @@ Cura.ExpandablePopup headerItem: Cura.IconWithText { - text: - { - if (isNetworkPrinter && Cura.MachineManager.activeMachineNetworkGroupName != "") - { - return Cura.MachineManager.activeMachineNetworkGroupName - } - if(Cura.MachineManager.activeMachine != null) - { - return Cura.MachineManager.activeMachine.name - } - return "" - } + text: machineName + source: { if (isGroup) @@ -140,7 +148,7 @@ Cura.ExpandablePopup color: connectionStatus == "printer_cloud_not_available" ? UM.Theme.getColor("cloud_unavailable") : UM.Theme.getColor("primary") - visible: isNetworkPrinter || isCloudRegistered + visible: (isNetworkPrinter || isCloudRegistered) && source != "" // Make a themable circle in the background so we can change it in other themes Rectangle @@ -156,7 +164,8 @@ Cura.ExpandablePopup } - MouseArea // Connection status tooltip hover area + // Connection status tooltip hover area + MouseArea { id: connectionStatusTooltipHoverArea anchors.fill: parent @@ -189,11 +198,14 @@ Cura.ExpandablePopup } } + property int minDropDownWidth: UM.Theme.getSize("machine_selector_widget_content").width + property int maxDropDownHeight: UM.Theme.getSize("machine_selector_widget_content").height + contentItem: Item { id: popup - implicitWidth: Math.max(machineSelector.width, UM.Theme.getSize("machine_selector_widget_content").width) - implicitHeight: Math.min(machineSelectorList.contentHeight + separator.height + buttonRow.height, UM.Theme.getSize("machine_selector_widget_content").height) //Maximum height is the theme entry. + implicitWidth: Math.max(machineSelector.width, minDropDownWidth) + implicitHeight: Math.min(machineSelectorList.contentHeight + separator.height + buttonRow.height, maxDropDownHeight) //Maximum height is the theme entry. MachineSelectorList { id: machineSelectorList @@ -229,39 +241,25 @@ Cura.ExpandablePopup padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width - Cura.SecondaryButton - { - id: addPrinterButton - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@button", "Add printer") - // The maximum width of the button is half of the total space, minus the padding of the parent, the left - // padding of the component and half the spacing because of the space between buttons. - fixedWidthMode: true - width: buttonRow.width / 2 - leftPadding * 1.5 - onClicked: - { - toggleContent() - Cura.Actions.addMachine.trigger() - } - } - - Cura.SecondaryButton - { - id: managePrinterButton - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@button", "Manage printers") - fixedWidthMode: true - // The maximum width of the button is half of the total space, minus the padding of the parent, the right - // padding of the component and half the spacing because of the space between buttons. - width: buttonRow.width / 2 - rightPadding * 1.5 - onClicked: - { - toggleContent() - Cura.Actions.configureMachines.trigger() - } - } + children: buttons } + + states: [ + State { + name: "noButtons" + when: !buttons || buttons.length == 0 + PropertyChanges + { + target: buttonRow + height: 0 + padding: 0 + } + PropertyChanges + { + target: separator + height: 0 + } + } + ] } } diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index a328ae69d9..ddf9586065 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -10,9 +10,9 @@ import Cura 1.0 as Cura ListView { id: listView - model: Cura.MachineListModel {} - section.property: "isOnline" + section.property: "category" property real contentHeight: childrenRect.height + property var onSelectPrinter ScrollBar.vertical: UM.ScrollBar { @@ -21,7 +21,17 @@ ListView section.delegate: UM.Label { - text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Other printers") + text: { + switch (section) + { + case "connected": + return catalog.i18nc("@label", "Connected printers"); + case "other": + return catalog.i18nc("@label", "Other printers"); + default: + return catalog.i18nc("@label", "Other printers"); + } + } height: UM.Theme.getSize("action_button").height width: parent.width - scrollBar.width leftPadding: UM.Theme.getSize("default_margin").width @@ -43,8 +53,7 @@ ListView listView.model.setShowCloudPrinters(true); break; case "MACHINE": - toggleContent() - Cura.MachineManager.setActiveMachine(model.id) + if (typeof onSelectPrinter === "function") onSelectPrinter(model); break; default: } diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml index cbabb3ffd4..c57c0a1548 100644 --- a/resources/qml/Settings/SettingComboBox.qml +++ b/resources/qml/Settings/SettingComboBox.qml @@ -18,6 +18,7 @@ SettingItem model: definition.options textRole: "value" + forceHighlight: base.hovered anchors.fill: parent diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index 567161b8f6..63413aa9bd 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -17,6 +17,8 @@ SettingItem id: control anchors.fill: parent + forceHighlight: base.hovered + property var extrudersModel: CuraApplication.getExtrudersModel() model: extrudersModel diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index 22f03d44a2..a9fdc626b9 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -23,6 +23,7 @@ SettingItem { id: control anchors.fill: parent + forceHighlight: base.hovered model: base.extrudersWithOptionalModel diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml index eac85bfb44..06ac2425bb 100644 --- a/resources/qml/Widgets/ComboBox.qml +++ b/resources/qml/Widgets/ComboBox.qml @@ -18,6 +18,8 @@ ComboBox property var defaultTextOnEmptyModel: catalog.i18nc("@label", "No items to select from") // Text displayed in the combobox when the model is empty property var defaultTextOnEmptyIndex: "" // Text displayed in the combobox when the model has items but no item is selected property alias textFormat: contentLabel.textFormat + property alias backgroundColor: background.color + property bool forceHighlight: false enabled: delegateModel.count > 0 @@ -45,7 +47,7 @@ ComboBox State { name: "highlighted" - when: (base.hovered || control.hovered) && !control.activeFocus + when: (control.hovered && !control.activeFocus) || forceHighlight PropertyChanges { target: background @@ -56,6 +58,7 @@ ComboBox background: UM.UnderlineBackground { + id: background // Rectangle for highlighting when this combobox needs to pulse. Rectangle {