Browse Source

Merge pull request #13774 from Ultimaker/CURA-9424_update_loading_projects_design

Cura 9424 update loading projects design
Casper Lamboo 2 years ago
parent
commit
63b27d3ca8

+ 45 - 30
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",
+        })

+ 4 - 0
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)

+ 0 - 43
plugins/3MFReader/UpdatableMachinesModel.py

@@ -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)

+ 27 - 6
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

+ 192 - 323
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")
-                    }
-
-                    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
+                        spacing: UM.Theme.getSize("default_margin").height
+                        leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
 
-                    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:
-                            {
-                                if (model.getItem(currentIndex).id == "new"
-                                    && model.getItem(currentIndex).type == "default_option")
-                                {
-                                    manager.setResolveStrategy("machine", "new")
-                                }
-                                else
-                                {
-                                    manager.setResolveStrategy("machine", "override")
-                                    manager.setMachineToOverride(model.getItem(currentIndex).id)
-                                }
-                            }
-
-                            onVisibleChanged:
-                            {
-                                if (!visible) {return}
+                            leftLabelText: catalog.i18nc("@action:label", "Type")
+                            rightLabelText: manager.machineType
+                        }
 
-                                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
-                                }
-                            }
+                        WorkspaceRow
+                        {
+                            leftLabelText: catalog.i18nc("@action:label", manager.isPrinterGroup ? "Printer Group" : "Printer Name")
+                            rightLabelText: manager.machineName
                         }
                     }
 
-                    Column
+                    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: childrenRect.height
+                        height: parent.height
+                        machineListModel: manager.updatableMachinesModel
+                        machineName: manager.machineName
 
-                        UM.Label
-                        {
-                            id: printer_settings_label
-                            text: catalog.i18nc("@action:label", "Printer settings")
-                            font: UM.Theme.getFont("default_bold")
-                        }
+                        isConnectedCloudPrinter: false
+                        isCloudRegistered: false
+                        isNetworkPrinter: manager.isNetworked
+                        isGroup: manager.isAbstractMachine
+                        connectionStatus: ""
 
-                        Row
-                        {
-                            width: parent.width
-                            height: childrenRect.height
+                        minDropDownWidth: machineSelector.width
 
-                            UM.Label
+                        buttons: [
+                            Cura.SecondaryButton
                             {
-                                text: catalog.i18nc("@action:label", "Type")
-                                width: (parent.width / 3) | 0
-                            }
-                            UM.Label
-                            {
-                                text: manager.machineType
-                                width: (parent.width / 3) | 0
+                                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")
+                                }
                             }
-                        }
+                        ]
 
-                        Row
+                        onSelectPrinter: function(machine)
                         {
-                            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
-                        visible: manager.qualityChangesConflict
-                        text: catalog.i18nc("@info:tooltip", "How should the conflict in the profile be resolved?")
-                        Cura.ComboBox
+                        id: profileSettingsValuesTable
+                        spacing: UM.Theme.getSize("default_margin").height
+                        leftPadding: UM.Theme.getSize("medium_button_icon").width + UM.Theme.getSize("default_margin").width
+
+                        WorkspaceRow
                         {
-                            model: resolveStrategiesModel
-                            textRole: "label"
-                            id: qualityChangesResolveComboBox
-                            width: parent.width
-                            height: UM.Theme.getSize("button").height
-                            onActivated:
-                            {
-                                manager.setResolveStrategy("quality_changes", resolveStrategiesModel.get(index).key)
-                            }
+                            leftLabelText: catalog.i18nc("@action:label", "Name")
+                            rightLabelText: manager.qualityName
                         }
-                    }
-
-                    Column
-                    {
-                        width: parent.width
-                        height: childrenRect.height
 
-                        UM.Label
+                        WorkspaceRow
                         {
-                            text: catalog.i18nc("@action:label", "Profile settings")
-                            font: UM.Theme.getFont("default_bold")
+                            leftLabelText: catalog.i18nc("@action:label", "Intent")
+                            rightLabelText: manager.intentName
                         }
 
-                        Row
+                        WorkspaceRow
                         {
-                            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
-                            }
+                            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
                         }
 
-                        Row
+                        WorkspaceRow
                         {
-                            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
-                            }
+                            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
                         }
+                    }
 
-                        Row
-                        {
-                            width: parent.width
-                            height: childrenRect.height
+                    comboboxVisible: manager.qualityChangesConflict
+                    combobox: Cura.ComboBox
+                    {
+                        id: qualityChangesResolveComboBox
+                        model: resolveStrategiesModel
+                        textRole: "label"
+                        visible: manager.qualityChangesConflict
 
-                            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
-                            }
+                        // This is a hack. This will trigger onCurrentIndexChanged and set the index when this component in loaded
+                        currentIndex:
+                        {
+                            currentIndex = 0
                         }
 
-                        Row
+                        onCurrentIndexChanged:
                         {
-                            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
                             }
                         }
                     }
-                }
 
-                Column
-                {
-                    width: parent.width
-                    height: childrenRect.height
+                    comboboxVisible: manager.materialConflict
 
-                    UM.Label
+                    combobox: Cura.ComboBox
                     {
-                        text: catalog.i18nc("@action:label", "Setting visibility")
-                        font: UM.Theme.getFont("default_bold")
-                    }
-                    Row
-                    {
-                        width: parent.width
-                        height: childrenRect.height
-                        UM.Label
+                        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:
                         {
-                            text: catalog.i18nc("@action:label", "Mode")
-                            width: (parent.width / 3) | 0
+                            currentIndex = 0
                         }
-                        UM.Label
+
+                        onCurrentIndexChanged:
                         {
-                            text: manager.activeMode
-                            width: (parent.width / 3) | 0
+                            manager.setResolveStrategy("material", resolveStrategiesModel.get(currentIndex).key)
                         }
                     }
-                    Row
+                }
+
+                WorkspaceSection
+                {
+                    id: visibilitySection
+                    title: catalog.i18nc("@action:label", "Setting visibility")
+                    iconSource: UM.Theme.getIcon("Eye")
+                    content: Column
                     {
-                        width: parent.width
-                        height: childrenRect.height
-                        visible: manager.hasVisibleSettingsField
-                        UM.Label
+                        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
+
+                        WorkspaceRow
                         {
-                            text: catalog.i18nc("@action:label", "Visible settings:")
-                            width: (parent.width / 3) | 0
+                            leftLabelText: catalog.i18nc("@action:label", "Mode")
+                            rightLabelText: manager.activeMode
                         }
-                        UM.Label
+
+                        WorkspaceRow
                         {
-                            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()
+        }
+    }
 }

+ 34 - 0
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
+    }
+}

+ 126 - 0
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
+    }
+}

+ 2 - 0
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 {}
     }
 }

+ 1 - 0
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
             {

+ 44 - 0
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

Some files were not shown because too many files changed in this diff