diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 3a3ac17cdf..e1805584b0 100755
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -115,6 +115,8 @@ from . import CuraActions
from . import PlatformPhysics
from . import PrintJobPreviewImageProvider
from .AutoSave import AutoSave
+from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
+from .Machines.Models.IntentSelectionModel import IntentSelectionModel
from .SingleInstance import SingleInstance
if TYPE_CHECKING:
@@ -1192,6 +1194,8 @@ class CuraApplication(QtApplication):
qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")
qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel")
qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel")
+ qmlRegisterType(IntentSelectionModel, "Cura", 1, 7, "IntentSelectionModel")
+ qmlRegisterType(ActiveIntentQualitiesModel, "Cura", 1, 7, "ActiveIntentQualitiesModel")
self.processEvents()
qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
diff --git a/cura/Machines/Models/ActiveIntentQualitiesModel.py b/cura/Machines/Models/ActiveIntentQualitiesModel.py
new file mode 100644
index 0000000000..c20ccde383
--- /dev/null
+++ b/cura/Machines/Models/ActiveIntentQualitiesModel.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2022 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+from typing import Optional, Set, Dict, List, Any
+
+from PyQt6.QtCore import Qt, QObject, QTimer
+
+import cura.CuraApplication
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from cura.Machines.ContainerTree import ContainerTree
+from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
+from cura.Machines.MaterialNode import MaterialNode
+from cura.Machines.QualityGroup import QualityGroup
+from cura.Settings.IntentManager import IntentManager
+
+
+class ActiveIntentQualitiesModel(ListModel):
+ NameRole = Qt.ItemDataRole.UserRole + 1
+ DisplayTextRole = Qt.ItemDataRole.UserRole + 2
+ QualityTypeRole = Qt.ItemDataRole.UserRole + 3
+ LayerHeightRole = Qt.ItemDataRole.UserRole + 4
+ IntentCategeoryRole = Qt.ItemDataRole.UserRole + 5
+
+ def __init__(self, parent: Optional[QObject] = None) -> None:
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.QualityTypeRole, "quality_type")
+ self.addRoleName(self.LayerHeightRole, "layer_height")
+ self.addRoleName(self.DisplayTextRole, "display_text")
+ self.addRoleName(self.IntentCategeoryRole, "intent_category")
+
+ self._intent_category = ""
+
+ IntentManager.intentCategoryChangedSignal.connect(self._update)
+ machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
+ machine_manager.activeQualityGroupChanged.connect(self._update)
+
+ self._update_timer = QTimer()
+ self._update_timer.setInterval(100)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._update)
+
+ self._update()
+
+ def _updateDelayed(self):
+ self._update_timer.start()
+
+ def _onChanged(self, container: ContainerStack) -> None:
+ if container.getMetaDataEntry("type") == "intent":
+ self._updateDelayed()
+
+ def _update(self):
+ active_extruder_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStack
+ if active_extruder_stack:
+ self._intent_category = active_extruder_stack.intent.getMetaDataEntry("intent_category", "")
+
+ new_items: List[Dict[str, Any]] = []
+ global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
+ if not global_stack:
+ self.setItems(new_items)
+ return
+ quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
+
+ material_nodes = self._getActiveMaterials()
+
+ added_quality_type_set: Set[str] = set()
+ for material_node in material_nodes:
+ intents = self._getIntentsForMaterial(material_node, quality_groups)
+ for intent in intents:
+ if intent["quality_type"] not in added_quality_type_set:
+ new_items.append(intent)
+ added_quality_type_set.add(intent["quality_type"])
+
+ new_items = sorted(new_items, key=lambda x: x["layer_height"])
+ self.setItems(new_items)
+
+ def _getActiveMaterials(self) -> Set["MaterialNode"]:
+ """Get the active materials for all extruders. No duplicates will be returned"""
+
+ global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
+ if global_stack is None:
+ return set()
+
+ container_tree = ContainerTree.getInstance()
+ machine_node = container_tree.machines[global_stack.definition.getId()]
+ nodes: Set[MaterialNode] = set()
+
+ for extruder in global_stack.extruderList:
+ active_variant_name = extruder.variant.getMetaDataEntry("name")
+ if active_variant_name not in machine_node.variants:
+ Logger.log("w", "Could not find the variant %s", active_variant_name)
+ continue
+ active_variant_node = machine_node.variants[active_variant_name]
+ active_material_node = active_variant_node.materials.get(extruder.material.getMetaDataEntry("base_file"))
+ if active_material_node is None:
+ Logger.log("w", "Could not find the material %s", extruder.material.getMetaDataEntry("base_file"))
+ continue
+ nodes.add(active_material_node)
+
+ return nodes
+
+ def _getIntentsForMaterial(self, active_material_node: "MaterialNode", quality_groups: Dict[str, "QualityGroup"]) -> List[Dict[str, Any]]:
+ extruder_intents: List[Dict[str, Any]] = []
+
+ for quality_id, quality_node in active_material_node.qualities.items():
+ if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively).
+ continue
+ quality_group = quality_groups[quality_node.quality_type]
+
+ if not quality_group.is_available:
+ continue
+
+ layer_height = fetchLayerHeight(quality_group)
+
+ for intent_id, intent_node in quality_node.intents.items():
+ if intent_node.intent_category != self._intent_category:
+ continue
+
+ extruder_intents.append({"name": quality_group.name,
+ "display_text": f"{quality_group.name} - {layer_height}mm",
+ "quality_type": quality_group.quality_type,
+ "layer_height": layer_height,
+ "intent_category": self._intent_category
+ })
+ return extruder_intents
+
+
diff --git a/cura/Machines/Models/IntentSelectionModel.py b/cura/Machines/Models/IntentSelectionModel.py
new file mode 100644
index 0000000000..ec6957832c
--- /dev/null
+++ b/cura/Machines/Models/IntentSelectionModel.py
@@ -0,0 +1,129 @@
+# Copyright (c) 2022 Ultimaker B.V.
+# Cura is released under the terms of the LGPLv3 or higher.
+
+import collections
+from typing import OrderedDict, Optional
+
+from PyQt6.QtCore import Qt, QTimer, QObject
+
+import cura
+from UM import i18nCatalog
+from UM.Logger import Logger
+from UM.Qt.ListModel import ListModel
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.Interfaces import ContainerInterface
+from cura.Settings.IntentManager import IntentManager
+
+catalog = i18nCatalog("cura")
+
+
+class IntentSelectionModel(ListModel):
+
+ NameRole = Qt.ItemDataRole.UserRole + 1
+ IntentCategoryRole = Qt.ItemDataRole.UserRole + 2
+ WeightRole = Qt.ItemDataRole.UserRole + 3
+ DescriptionRole = Qt.ItemDataRole.UserRole + 4
+ IconRole = Qt.ItemDataRole.UserRole + 5
+
+ def __init__(self, parent: Optional[QObject] = None) -> None:
+ super().__init__(parent)
+
+ self.addRoleName(self.NameRole, "name")
+ self.addRoleName(self.IntentCategoryRole, "intent_category")
+ self.addRoleName(self.WeightRole, "weight")
+ self.addRoleName(self.DescriptionRole, "description")
+ self.addRoleName(self.IconRole, "icon")
+
+ application = cura.CuraApplication.CuraApplication.getInstance()
+
+ ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange)
+ ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange)
+ machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
+ machine_manager.activeMaterialChanged.connect(self._update)
+ machine_manager.activeVariantChanged.connect(self._update)
+ machine_manager.extruderChanged.connect(self._update)
+
+ extruder_manager = application.getExtruderManager()
+ extruder_manager.extrudersChanged.connect(self._update)
+
+ self._update_timer: QTimer = QTimer()
+ self._update_timer.setInterval(100)
+ self._update_timer.setSingleShot(True)
+ self._update_timer.timeout.connect(self._update)
+
+ self._onChange()
+
+ @staticmethod
+ def _getDefaultProfileInformation() -> OrderedDict[str, dict]:
+ """ Default information user-visible string. Ordered by weight. """
+ default_profile_information = collections.OrderedDict()
+ default_profile_information["default"] = {
+ "name": catalog.i18nc("@label", "Default"),
+ "icon": "GearCheck"
+ }
+ default_profile_information["visual"] = {
+ "name": catalog.i18nc("@label", "Visual"),
+ "description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality."),
+ "icon" : "Visual"
+ }
+ default_profile_information["engineering"] = {
+ "name": catalog.i18nc("@label", "Engineering"),
+ "description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances."),
+ "icon": "Nut"
+ }
+ default_profile_information["quick"] = {
+ "name": catalog.i18nc("@label", "Draft"),
+ "description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction."),
+ "icon": "SpeedOMeter"
+ }
+ return default_profile_information
+
+ def _onContainerChange(self, container: ContainerInterface) -> None:
+ """Updates the list of intents if an intent profile was added or removed."""
+
+ if container.getMetaDataEntry("type") == "intent":
+ self._update()
+
+ def _onChange(self) -> None:
+ self._update_timer.start()
+
+ def _update(self) -> None:
+ Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
+
+ global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
+ if global_stack is None:
+ self.setItems([])
+ Logger.log("d", "No active GlobalStack, set quality profile model as empty.")
+ return
+
+ # Check for material compatibility
+ if not cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMaterialsCompatible():
+ Logger.log("d", "No active material compatibility, set quality profile model as empty.")
+ self.setItems([])
+ return
+
+ default_profile_info = self._getDefaultProfileInformation()
+
+ available_categories = IntentManager.getInstance().currentAvailableIntentCategories()
+ result = []
+ for i, category in enumerate(available_categories):
+ profile_info = default_profile_info.get(category, {})
+
+ try:
+ weight = list(default_profile_info.keys()).index(category)
+ except ValueError:
+ weight = len(available_categories) + i
+
+ result.append({
+ "name": profile_info.get("name", category.title()),
+ "description": profile_info.get("description", None),
+ "icon" : profile_info.get("icon", ""),
+ "intent_category": category,
+ "weight": weight,
+ })
+
+ result.sort(key=lambda k: k["weight"])
+
+ self.setItems(result)
+
+
diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py
index 2dfec02201..7d3e2659bf 100644
--- a/cura/Settings/IntentManager.py
+++ b/cura/Settings/IntentManager.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2019 Ultimaker B.V.
+# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
@@ -8,6 +8,7 @@ from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer
import cura.CuraApplication
+from UM.Signal import Signal
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container
@@ -29,6 +30,7 @@ class IntentManager(QObject):
return cls.__instance
intentCategoryChanged = pyqtSignal() #Triggered when we switch categories.
+ intentCategoryChangedSignal = Signal()
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
"""Gets the metadata dictionaries of all intent profiles for a given
@@ -189,3 +191,4 @@ class IntentManager(QObject):
application.getMachineManager().setQualityGroupByQualityType(quality_type)
if old_intent_category != intent_category:
self.intentCategoryChanged.emit()
+ self.intentCategoryChangedSignal.emit()
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index 9b98179bff..64d34d6c3e 100755
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -1778,3 +1778,31 @@ class MachineManager(QObject):
abbr_machine += stripped_word
return abbr_machine
+
+ @pyqtSlot(str, str, result = bool)
+ def intentCategoryHasQuality(self, intent_category: str, quality_type: str) -> bool:
+ """ Checks if there are any quality groups for active extruders that have an intent category """
+ quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
+
+ if quality_type in quality_groups:
+ quality_group = quality_groups[quality_type]
+ for node in quality_group.nodes_for_extruders.values():
+ if any(intent.intent_category == intent_category for intent in node.intents.values()):
+ return True
+
+ return False
+
+ @pyqtSlot(str, result = str)
+ def getDefaultQualityTypeForIntent(self, intent_category) -> str:
+ """ If there is an intent category for the default machine quality return it, otherwise return the first quality for this intent category """
+ machine = ContainerTree.getInstance().machines.get(self._global_container_stack.definition.getId())
+
+ if self.intentCategoryHasQuality(intent_category, machine.preferred_quality_type):
+ return machine.preferred_quality_type
+
+ for quality_type, quality_group in ContainerTree.getInstance().getCurrentQualityGroups().items():
+ for node in quality_group.nodes_for_extruders.values():
+ if any(intent.intent_category == intent_category for intent in node.intents.values()):
+ return quality_type
+
+ return ""
diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml
index cc0834d7ea..24f211ae17 100644
--- a/resources/qml/IconWithText.qml
+++ b/resources/qml/IconWithText.qml
@@ -22,6 +22,7 @@ Item
property alias elide: label.elide
property real margin: UM.Theme.getSize("narrow_margin").width
property alias wrapMode: label.wrapMode
+ property real spacing: UM.Theme.getSize("narrow_margin").width
// These properties can be used in combination with layouts.
readonly property real contentWidth: icon.width + margin + label.contentWidth
@@ -61,6 +62,7 @@ Item
top: parent.top
bottom: parent.bottom
rightMargin: 0
+ leftMargin: spacing
margins: margin
}
}
diff --git a/resources/qml/PrintSetupSelector/Recommended/ProfileWarningReset.qml b/resources/qml/PrintSetupSelector/Recommended/ProfileWarningReset.qml
new file mode 100644
index 0000000000..c9abcab1c8
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Recommended/ProfileWarningReset.qml
@@ -0,0 +1,108 @@
+import QtQuick 2.10
+
+import UM 1.6 as UM
+import Cura 1.6 as Cura
+
+Rectangle
+{
+ height: visible ? UM.Theme.getSize("action_button_icon").height : 0
+ visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
+ anchors.topMargin: UM.Theme.getSize("default_margin")
+ anchors.bottomMargin: UM.Theme.getSize("default_margin")
+
+
+ Rectangle
+ {
+ id: warningIcon
+ color: UM.Theme.getColor("um_yellow_5")
+ height: UM.Theme.getSize("action_button_icon").height
+ width: height
+ radius: width
+ anchors
+ {
+ left: parent.left
+ verticalCenter: parent.verticalCenter
+ }
+ UM.ColorImage
+ {
+ height: UM.Theme.getSize("action_button_icon").height
+ width: height
+ source: UM.Theme.getIcon("Warning", "low")
+ }
+ }
+
+ UM.Label
+ {
+ id: warning
+ anchors
+ {
+ left: warningIcon.right
+ verticalCenter: parent.verticalCenter
+ leftMargin: UM.Theme.getSize("thin_margin").width
+ }
+
+ text: ""
+
+ states: [
+ State
+ {
+ name: "settings changed and custom quality"
+ when: Cura.SimpleModeSettingsManager.isProfileCustomized && Cura.MachineManager.hasCustomQuality
+ PropertyChanges
+ {
+ target: warning
+ text: {
+ var profile_name = Cura.MachineManager.activeQualityChangesGroup.name
+ return "%1 %2".arg(profile_name).arg(catalog.i18nc("@info", "custom profile is active and you overwrote some settings."))
+ }
+ }
+
+ },
+ State
+ {
+ name: "custom quality"
+ when: Cura.MachineManager.hasCustomQuality
+ PropertyChanges
+ {
+ target: warning
+ text: {
+ var profile_name = Cura.MachineManager.activeQualityChangesGroup.name
+ return "%1 %2".arg(profile_name).arg(catalog.i18nc("@info", "custom profile is overriding some settings."))
+ }
+ }
+ },
+ State
+ {
+ name: "settings changed"
+ when: Cura.SimpleModeSettingsManager.isProfileCustomized
+ PropertyChanges
+ {
+ target: warning
+ text: catalog.i18nc("@info", "Some settings were changed.")
+ }
+ }
+ ]
+
+ }
+
+ UM.SimpleButton
+ {
+ id: resetToDefaultQualityButton
+ height: UM.Theme.getSize("action_button_icon").height
+ width: height
+ iconSource: UM.Theme.getIcon("ArrowReset")
+ anchors
+ {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+
+ color: UM.Theme.getColor("accent_1")
+
+ onClicked:
+ {
+ Cura.MachineManager.resetToUseDefaultQuality()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
index de8cce6e94..aac8dcecfb 100644
--- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
@@ -1,10 +1,11 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
+//Copyright (c) 2022 Ultimaker B.V.
+//Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
+import QtQuick.Layouts 1.1
-import UM 1.2 as UM
-import Cura 1.0 as Cura
+import UM 1.6 as UM
+import Cura 1.6 as Cura
Item
{
@@ -13,11 +14,11 @@ Item
height: childrenRect.height + 2 * padding
property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1
- property real padding: UM.Theme.getSize("thick_margin").width
+ property real padding: UM.Theme.getSize("default_margin").width
- Column
+ ColumnLayout
{
- spacing: UM.Theme.getSize("wide_margin").height
+ spacing: UM.Theme.getSize("default_margin").height
anchors
{
@@ -30,11 +31,53 @@ Item
// TODO
property real firstColumnWidth: Math.round(width / 3)
+ UM.Label
+ {
+ text: catalog.i18nc("@label", "Profiles")
+ font: UM.Theme.getFont("medium")
+ }
+
RecommendedQualityProfileSelector
{
width: parent.width
- // TODO Create a reusable component with these properties to not define them separately for each component
- labelColumnWidth: parent.firstColumnWidth
+ visible: recommendedResolutionSelector.visible
+ }
+
+ RecommendedResolutionSelector
+ {
+ id: recommendedResolutionSelector
+ Layout.fillWidth: true
+ width: parent.width
+ }
+
+ UnsupportedProfileIndication
+ {
+ width: parent.width
+ visible: !recommendedResolutionSelector.visible
+ }
+
+
+ ProfileWarningReset
+ {
+ width: parent.width
+ Layout.fillWidth: true
+ }
+
+ //Line between the sections.
+ Rectangle
+ {
+ width: parent.width
+ height: UM.Theme.getSize("default_lining").height
+ Layout.topMargin: UM.Theme.getSize("narrow_margin").height
+ Layout.bottomMargin: UM.Theme.getSize("narrow_margin").height
+ Layout.fillWidth: true
+ color: UM.Theme.getColor("lining")
+ }
+
+ UM.Label
+ {
+ text: catalog.i18nc("@label", "Print settings")
+ font: UM.Theme.getFont("medium")
}
RecommendedInfillDensitySelector
@@ -42,6 +85,9 @@ Item
width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth
+ Layout.fillWidth: true
+ Layout.leftMargin: UM.Theme.getSize("default_margin").width
+ Layout.rightMargin: UM.Theme.getSize("default_margin").width
}
RecommendedSupportSelector
@@ -49,6 +95,7 @@ Item
width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth
+ Layout.leftMargin: UM.Theme.getSize("default_margin").width
}
RecommendedAdhesionSelector
@@ -56,6 +103,7 @@ Item
width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth
+ Layout.leftMargin: UM.Theme.getSize("default_margin").width
}
}
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
index 3d0077abb0..6bcca955d1 100644
--- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
@@ -3,180 +3,46 @@
import QtQuick 2.10
import QtQuick.Controls 2.3
+import QtQuick.Layouts 2.10
import UM 1.5 as UM
-import Cura 1.6 as Cura
+import Cura 1.7 as Cura
import ".."
Item
{
id: qualityRow
height: childrenRect.height
+ visible: intentSelectionRepeater.count > 1 //Only show selector if there's more options than just "default".
- property real labelColumnWidth: Math.round(width / 3)
- property real settingsColumnWidth: width - labelColumnWidth
-
- // Here are the elements that are shown in the left column
-
- Column
+ RowLayout
{
- anchors
- {
- left: parent.left
- right: parent.right
- }
-
- spacing: UM.Theme.getSize("default_margin").height
-
- ButtonGroup
- {
- id: activeProfileButtonGroup
- exclusive: true
- onClicked: Cura.IntentManager.selectIntent(button.modelData.intent_category, button.modelData.quality_type)
- }
-
- Item
- {
- height: childrenRect.height
- anchors
- {
- left: parent.left
- right: parent.right
- }
- Cura.IconWithText
- {
- id: profileLabel
- source: UM.Theme.getIcon("PrintQuality")
- text: catalog.i18nc("@label", "Profiles")
- font: UM.Theme.getFont("medium")
- width: labelColumnWidth
- iconSize: UM.Theme.getSize("medium_button_icon").width
- }
- UM.SimpleButton
- {
- id: resetToDefaultQualityButton
-
- visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
- height: visible ? UM.Theme.getSize("print_setup_icon").height : 0
- width: height
- anchors
- {
- right: profileLabel.right
- rightMargin: UM.Theme.getSize("default_margin").width
- leftMargin: UM.Theme.getSize("default_margin").width
- verticalCenter: parent.verticalCenter
- }
-
- color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
- iconSource: UM.Theme.getIcon("ArrowReset")
-
- onClicked:
- {
- // if the current profile is user-created, switch to a built-in quality
- Cura.MachineManager.resetToUseDefaultQuality()
- }
- onEntered:
- {
- var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.")
- base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), tooltipContent)
- }
- onExited: base.hideTooltip()
- }
-
- Cura.LabelBar
- {
- id: labelbar
- anchors
- {
- left: profileLabel.right
- right: parent.right
- verticalCenter: profileLabel.verticalCenter
- }
-
- model: Cura.QualityProfilesDropDownMenuModel
- modelKey: "layer_height"
- }
- }
-
+ id: intentRow
+ width: parent.width
Repeater
{
- model: Cura.IntentCategoryModel {}
- Item
+ id: intentSelectionRepeater
+ model: Cura.IntentSelectionModel {}
+
+ RecommendedQualityProfileSelectorButton
{
- anchors
- {
- left: parent.left
- right: parent.right
- }
- height: intentCategoryLabel.height
+ profileName: model.name
+ icon: model.icon
- UM.Label
- {
- id: intentCategoryLabel
- text: model.name
- width: labelColumnWidth - UM.Theme.getSize("section_icon").width
- anchors.left: parent.left
- anchors.leftMargin: UM.Theme.getSize("section_icon").width + UM.Theme.getSize("narrow_margin").width
- font: UM.Theme.getFont("medium")
- elide: Text.ElideRight
- }
- Cura.RadioCheckbar
- {
- anchors
+ selected: Cura.MachineManager.activeIntentCategory == model.intent_category
+
+ onClicked: {
+ var qualityType
+ if (Cura.MachineManager.intentCategoryHasQuality(model.intent_category, Cura.MachineManager.activeQualityType))
{
- left: intentCategoryLabel.right
- right: parent.right
- }
- dataModel: model["qualities"]
- buttonGroup: activeProfileButtonGroup
-
- function checkedFunction(modelItem)
- {
- if(Cura.MachineManager.hasCustomQuality)
- {
- // When user created profile is active, no quality tickbox should be active.
- return false
- }
-
- if(modelItem === null)
- {
- return false
- }
- return Cura.MachineManager.activeQualityType == modelItem.quality_type && Cura.MachineManager.activeIntentCategory == modelItem.intent_category
- }
-
- isCheckedFunction: checkedFunction
- }
-
- MouseArea // Intent description tooltip hover area
- {
- id: intentDescriptionHoverArea
- anchors.fill: parent
- hoverEnabled: true
- enabled: model.description !== undefined
- acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
-
- Timer
- {
- id: intentTooltipTimer
- interval: 500
- running: false
- repeat: false
- onTriggered: base.showTooltip(
- intentCategoryLabel,
- Qt.point(-(intentCategoryLabel.x - qualityRow.x) - UM.Theme.getSize("thick_margin").width, 0),
- model.description
- )
- }
-
- onEntered: intentTooltipTimer.start()
- onExited:
- {
- base.hideTooltip()
- intentTooltipTimer.stop()
+ qualityType = Cura.MachineManager.activeQualityType
+ } else {
+ qualityType = Cura.MachineManager.getDefaultQualityTypeForIntent(model.intent_category)
+ print(Cura.MachineManager.getDefaultQualityTypeForIntent(model.intent_category))
}
+ Cura.IntentManager.selectIntent(model.intent_category, qualityType)
}
}
}
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelectorButton.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelectorButton.qml
new file mode 100644
index 0000000000..6804e7e5ba
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelectorButton.qml
@@ -0,0 +1,93 @@
+// 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 2.10
+
+import UM 1.5 as UM
+import Cura 1.7 as Cura
+
+
+Rectangle
+{
+ id: base
+ height: 60
+ Layout.fillWidth: true
+ color: mouseArea.containsMouse || selected ? UM.Theme.getColor("background_3") : UM.Theme.getColor("background_1")
+
+ property bool selected: false
+ property string profileName: ""
+ property string icon: ""
+
+ signal clicked()
+
+ MouseArea
+ {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: base.clicked()
+ }
+
+ Item
+ {
+ width: intentIcon.width
+ anchors
+ {
+ top: parent.top
+ bottom: qualityLabel.top
+ horizontalCenter: parent.horizontalCenter
+ topMargin: UM.Theme.getSize("narrow_margin").height
+ }
+
+ Item
+ {
+ id: intentIcon
+ width: UM.Theme.getSize("recommended_button_icon").width
+ height: UM.Theme.getSize("recommended_button_icon").height
+ UM.ColorImage
+ {
+ anchors.fill: parent
+ anchors.centerIn: parent
+ visible: icon != ""
+ source: UM.Theme.getIcon(icon)
+ color: UM.Theme.getColor("icon")
+ }
+
+ Rectangle
+ {
+ id: circle
+ anchors.fill: parent
+ radius: width
+ anchors.verticalCenter: parent.verticalCenter
+ visible: icon == ""
+ border.width: UM.Theme.getSize("thick_lining").width
+ border.color: UM.Theme.getColor("text")
+
+ UM.Label
+ {
+ id: initialLabel
+ anchors.centerIn: parent
+ text: profileName.charAt(0).toUpperCase()
+ font: UM.Theme.getFont("small_bold")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+
+
+ }
+ }
+
+ UM.Label
+ {
+ id: qualityLabel
+ text: profileName
+ anchors
+ {
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ bottomMargin: UM.Theme.getSize("narrow_margin").height
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedResolutionSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedResolutionSelector.qml
new file mode 100644
index 0000000000..6217cfe1cc
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedResolutionSelector.qml
@@ -0,0 +1,84 @@
+// Copyright (c) 2022 Ultimaker B.V.
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.10
+
+import UM 1.6 as UM
+import Cura 1.7 as Cura
+
+Item
+{
+ id: recommendedResolutionSelector
+ height: childrenRect.height
+
+ property real labelColumnWidth: Math.round(width / 3)
+ property string _previousResolution: "" //Internal variable to detect changes.
+ Component.onCompleted: _previousResolution = Cura.MachineManager.activeQualityType;
+
+ visible: visibilityPreset.count > 0 //Only show if there are quality types to select from.
+
+ Cura.IconWithText
+ {
+ id: resolutionTitle
+ anchors.top: parent.top
+ anchors.left: parent.left
+ source: UM.Theme.getIcon("PrintQuality")
+ text: catalog.i18nc("@label", "Resolution")
+ width: labelColumnWidth
+ height: parent.height
+ spacing: UM.Theme.getSize("thick_margin").width
+ iconSize: UM.Theme.getSize("medium_button_icon").width
+ }
+
+ Cura.ComboBox
+ {
+ id: visibilityPreset
+ implicitHeight: UM.Theme.getSize("combobox").height
+ implicitWidth: UM.Theme.getSize("combobox").width
+ anchors
+ {
+ top: parent.top
+ right: parent.right
+ }
+
+ textRole: "display_text"
+ textFormat: Text.StyledText
+
+ model: Cura.ActiveIntentQualitiesModel{}
+
+ currentIndex:
+ {
+ var current_quality_type = Cura.MachineManager.activeQualityType
+
+ var index = 0
+ for (var i = 0; i < model.count; i++)
+ {
+ if (model.getItem(i).quality_type == current_quality_type)
+ {
+ index = i
+ break
+ }
+ }
+ return index
+ }
+
+ onActivated:
+ {
+ var selected_item = model.getItem(currentIndex)
+ Cura.IntentManager.selectIntent(selected_item.intent_category, selected_item.quality_type)
+ }
+
+ Connections
+ {
+ target: Cura.IntentManager
+ function onIntentCategoryChanged()
+ {
+ if(recommendedResolutionSelector._previousResolution !== Cura.MachineManager.activeQualityType)
+ {
+ visibilityPreset.pulse();
+ }
+ recommendedResolutionSelector._previousResolution = Cura.MachineManager.activeQualityType;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector/Recommended/UnsupportedProfileIndication.qml b/resources/qml/PrintSetupSelector/Recommended/UnsupportedProfileIndication.qml
new file mode 100644
index 0000000000..ff12fa9307
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Recommended/UnsupportedProfileIndication.qml
@@ -0,0 +1,54 @@
+//Copyright (c) 2022 Ultimaker B.V.
+//Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.15
+
+import Cura 1.6 as Cura
+import UM 1.6 as UM
+
+//Message showing the user that the configuration they have selected has no profiles.
+Column
+{
+ spacing: UM.Theme.getSize("default_margin").height
+
+ Row
+ {
+ width: parent.width
+
+ spacing: UM.Theme.getSize("thin_margin").width
+
+ UM.StatusIcon
+ {
+ width: UM.Theme.getSize("notification_icon").width
+ status: UM.StatusIcon.Status.ERROR
+ }
+
+ UM.Label
+ {
+ width: parent.width
+
+ font: UM.Theme.getFont("default_bold")
+ text: catalog.i18nc("@error", "Configuration not supported")
+ }
+ }
+
+ UM.Label
+ {
+ width: parent.width
+
+ text: catalog.i18nc("@message:text %1 is the name the printer uses for 'nozzle'.", "No profiles are available for the selected material/%1 configuration. Please change your configuration."
+ ).arg(Cura.MachineManager.activeDefinitionVariantsName)
+ }
+
+ Cura.TertiaryButton
+ {
+ anchors.right: parent.right
+
+ text: catalog.i18nc("@button:label", "Learn more")
+ textFont: UM.Theme.getFont("default")
+ iconSource: UM.Theme.getIcon("LinkExternal")
+ isIconOnRightSide: true
+
+ onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360012909099")
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml
index 988b7c3782..77e6c489e9 100644
--- a/resources/qml/Widgets/ComboBox.qml
+++ b/resources/qml/Widgets/ComboBox.qml
@@ -17,6 +17,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
+
enabled: delegateModel.count > 0
onVisibleChanged: { popup.close() }
@@ -52,7 +54,34 @@ ComboBox
}
]
- background: UM.UnderlineBackground{}
+ background: UM.UnderlineBackground
+ {
+ //Rectangle for highlighting when this combobox needs to pulse.
+ Rectangle
+ {
+ anchors.fill: parent
+ opacity: 0
+ color: UM.Theme.getColor("warning")
+
+ SequentialAnimation on opacity
+ {
+ id: pulseAnimation
+ running: false
+ loops: 1
+ alwaysRunToEnd: true
+ PropertyAnimation
+ {
+ to: 1
+ duration: 300
+ }
+ PropertyAnimation
+ {
+ to: 0
+ duration : 2000
+ }
+ }
+ }
+ }
indicator: UM.ColorImage
{
@@ -146,7 +175,7 @@ ComboBox
anchors.rightMargin: UM.Theme.getSize("setting_unit_margin").width
text: delegateItem.text
- textFormat: Text.PlainText
+ textFormat: control.textFormat
color: UM.Theme.getColor("setting_control_text")
elide: Text.ElideRight
wrapMode: Text.NoWrap
@@ -162,4 +191,9 @@ ComboBox
text: delegateLabel.truncated ? delegateItem.text : ""
}
}
+
+ function pulse()
+ {
+ pulseAnimation.restart();
+ }
}
diff --git a/resources/themes/cura-light/icons/default/GearCheck.svg b/resources/themes/cura-light/icons/default/GearCheck.svg
new file mode 100644
index 0000000000..1ec89a5a8d
--- /dev/null
+++ b/resources/themes/cura-light/icons/default/GearCheck.svg
@@ -0,0 +1,4 @@
+
diff --git a/resources/themes/cura-light/icons/default/Nut.svg b/resources/themes/cura-light/icons/default/Nut.svg
new file mode 100644
index 0000000000..77df86d31b
--- /dev/null
+++ b/resources/themes/cura-light/icons/default/Nut.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/resources/themes/cura-light/icons/default/Visual.svg b/resources/themes/cura-light/icons/default/Visual.svg
new file mode 100644
index 0000000000..8d75b7feb4
--- /dev/null
+++ b/resources/themes/cura-light/icons/default/Visual.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json
index b3ee4c912b..83eb13f0cb 100644
--- a/resources/themes/cura-light/theme.json
+++ b/resources/themes/cura-light/theme.json
@@ -133,6 +133,11 @@
"weight": 400,
"family": "Noto Sans"
},
+ "small_bold": {
+ "size": 0.9,
+ "weight": 700,
+ "family": "Noto Sans"
+ },
"small_ja_JP": {
"size": 0.9,
"weight": 400,
@@ -637,6 +642,10 @@
"marketplace_large_icon": [4.0, 4.0],
- "preferences_page_list_item": [8.0, 2.0]
+ "preferences_page_list_item": [8.0, 2.0],
+
+ "recommended_button_icon": [1.7, 1.7],
+
+ "reset_profile_icon": [1, 1]
}
}