diff --git a/cura/Machines/IntentNode.py b/cura/Machines/IntentNode.py
index 8ec8dd6d6b..dbf37a341a 100644
--- a/cura/Machines/IntentNode.py
+++ b/cura/Machines/IntentNode.py
@@ -17,4 +17,4 @@ class IntentNode(ContainerNode):
def __init__(self, container_id: str, quality: "QualityNode") -> None:
super().__init__(container_id)
self.quality = quality
- self.intent_category = ContainerRegistry.getInstance().findContainersMetadata(id = container_id)[0].get("intent_category", "default")
\ No newline at end of file
+ self.intent_category = ContainerRegistry.getInstance().findContainersMetadata(id = container_id)[0].get("intent_category", "default")
diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py
index d62b848343..66718e8e4f 100644
--- a/cura/Machines/Models/BaseMaterialsModel.py
+++ b/cura/Machines/Models/BaseMaterialsModel.py
@@ -104,6 +104,8 @@ class BaseMaterialsModel(ListModel):
# tree. This change may trigger an _update() call when the materials
# changed for the configuration that this model is looking for.
def _materialsListChanged(self, material: MaterialNode) -> None:
+ if self._extruder_stack is None:
+ return
if material.variant.container_id != self._extruder_stack.variant.getId():
return
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py
index 9a520a2d31..5211889801 100644
--- a/cura/Machines/Models/IntentCategoryModel.py
+++ b/cura/Machines/Models/IntentCategoryModel.py
@@ -5,9 +5,11 @@ from PyQt5.QtCore import Qt
import collections
from typing import TYPE_CHECKING
+from cura.Machines.Models.IntentModel import IntentModel
from cura.Settings.IntentManager import IntentManager
from UM.Qt.ListModel import ListModel
from UM.Settings.ContainerRegistry import ContainerRegistry #To update the list if anything changes.
+from PyQt5.QtCore import pyqtProperty, pyqtSignal
if TYPE_CHECKING:
from UM.Settings.ContainerRegistry import ContainerInterface
@@ -15,12 +17,14 @@ if TYPE_CHECKING:
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
+
## Lists the intent categories that are available for the current printer
# configuration.
class IntentCategoryModel(ListModel):
NameRole = Qt.UserRole + 1
IntentCategoryRole = Qt.UserRole + 2
WeightRole = Qt.UserRole + 3
+ QualitiesRole = Qt.UserRole + 4
#Translations to user-visible string. Ordered by weight.
#TODO: Create a solution for this name and weight to be used dynamically.
@@ -29,6 +33,8 @@ class IntentCategoryModel(ListModel):
name_translation["engineering"] = catalog.i18nc("@label", "Engineering")
name_translation["smooth"] = catalog.i18nc("@label", "Smooth")
+ modelUpdated = pyqtSignal()
+
## Creates a new model for a certain intent category.
# \param The category to list the intent profiles for.
def __init__(self, intent_category: str) -> None:
@@ -38,6 +44,7 @@ class IntentCategoryModel(ListModel):
self.addRoleName(self.NameRole, "name")
self.addRoleName(self.IntentCategoryRole, "intent_category")
self.addRoleName(self.WeightRole, "weight")
+ self.addRoleName(self.QualitiesRole, "qualities")
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange)
ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange)
@@ -55,9 +62,13 @@ class IntentCategoryModel(ListModel):
available_categories = IntentManager.getInstance().currentAvailableIntentCategories()
result = []
for category in available_categories:
+ qualities = IntentModel()
+ qualities.setIntentCategory(category)
result.append({
"name": self.name_translation.get(category, catalog.i18nc("@label", "Unknown")),
"intent_category": category,
- "weight": list(self.name_translation.keys()).index(category)
+ "weight": list(self.name_translation.keys()).index(category),
+ "qualities": qualities
})
- self.setItems(result)
\ No newline at end of file
+ result.sort(key = lambda k: k["weight"])
+ self.setItems(result)
diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py
index c61d0bfcca..61fdecb559 100644
--- a/cura/Machines/Models/IntentModel.py
+++ b/cura/Machines/Models/IntentModel.py
@@ -6,8 +6,11 @@ from typing import Optional, List, Dict, Any
from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal
from UM.Qt.ListModel import ListModel
+from UM.Settings.ContainerRegistry import ContainerRegistry
+from UM.Settings.SettingFunction import SettingFunction
from cura.Machines.ContainerTree import ContainerTree
+from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.IntentManager import IntentManager
import cura.CuraApplication
@@ -15,18 +18,26 @@ import cura.CuraApplication
class IntentModel(ListModel):
NameRole = Qt.UserRole + 1
QualityTypeRole = Qt.UserRole + 2
+ LayerHeightRole = Qt.UserRole + 3
+ AvailableRole = Qt.UserRole + 4
+ IntentRole = Qt.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.AvailableRole, "available")
+ self.addRoleName(self.IntentRole, "intent_category")
self._intent_category = "engineering"
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
machine_manager.globalContainerChanged.connect(self._update)
- machine_manager.activeStackChanged.connect(self._update)
+ ContainerRegistry.getInstance().containerAdded.connect(self._onChanged)
+ ContainerRegistry.getInstance().containerRemoved.connect(self._onChanged)
+ self._layer_height_unit = "" # This is cached
self._update()
intentCategoryChanged = pyqtSignal()
@@ -41,6 +52,10 @@ class IntentModel(ListModel):
def intentCategory(self) -> str:
return self._intent_category
+ def _onChanged(self, container):
+ if container.getMetaDataEntry("type") == "intent":
+ self._update()
+
def _update(self) -> None:
new_items = [] # type: List[Dict[str, Any]]
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
@@ -49,11 +64,76 @@ class IntentModel(ListModel):
return
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
- for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents():
- if intent_category == self._intent_category:
- new_items.append({"name": quality_groups[quality_type].name, "quality_type": quality_type})
- if self._intent_category == "default": #For Default we always list all quality types. We can't filter on available profiles since the empty intent is not a specific quality type.
- for quality_type in quality_groups.keys():
- new_items.append({"name": quality_groups[quality_type].name, "quality_type": quality_type})
+ container_tree = ContainerTree.getInstance()
+ machine_node = container_tree.machines[global_stack.definition.getId()]
+ active_extruder = ExtruderManager.getInstance().getActiveExtruderStack()
+ if not active_extruder:
+ return
+ active_variant_name = active_extruder.variant.getMetaDataEntry("name")
+ active_variant_node = machine_node.variants[active_variant_name]
+ active_material_node = active_variant_node.materials[active_extruder.material.getMetaDataEntry("base_file")]
+ layer_heights_added = []
+ for quality_id, quality_node in active_material_node.qualities.items():
+ quality_group = quality_groups[quality_node.quality_type]
+ layer_height = self._fetchLayerHeight(quality_group)
+ for intent_id, intent_node in quality_node.intents.items():
+ if intent_node.intent_category != self._intent_category:
+ continue
+ layer_heights_added.append(layer_height)
+ new_items.append({"name": quality_group.name,
+ "quality_type": quality_group.quality_type,
+ "layer_height": layer_height,
+ "available": quality_group.is_available,
+ "intent_category": self._intent_category
+ })
+
+ # Now that we added all intents that we found something for, ensure that we set add ticks (and layer_heights)
+ # for all groups that we don't have anything for (and set it to not available)
+ for quality_tuple, quality_group in quality_groups.items():
+ # Add the intents that are of the correct category
+ if quality_tuple[0] != self._intent_category:
+ layer_height = self._fetchLayerHeight(quality_group)
+ if layer_height not in layer_heights_added:
+ new_items.append({"name": "Unavailable",
+ "quality_type": "",
+ "layer_height": layer_height,
+ "intent_category": self._intent_category,
+ "available": False})
+ layer_heights_added.append(layer_height)
+
+ new_items = sorted(new_items, key=lambda x: x["layer_height"])
self.setItems(new_items)
+
+ #TODO: Copied this from QualityProfilesDropdownMenuModel for the moment. This code duplication should be fixed.
+ def _fetchLayerHeight(self, quality_group) -> float:
+ global_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMachine
+ if not self._layer_height_unit:
+ unit = global_stack.definition.getProperty("layer_height", "unit")
+ if not unit:
+ unit = ""
+ self._layer_height_unit = unit
+
+ default_layer_height = global_stack.definition.getProperty("layer_height", "value")
+
+ # Get layer_height from the quality profile for the GlobalStack
+ if quality_group.node_for_global is None:
+ return float(default_layer_height)
+ container = quality_group.node_for_global.getContainer()
+
+ layer_height = default_layer_height
+ if container and container.hasProperty("layer_height", "value"):
+ layer_height = container.getProperty("layer_height", "value")
+ else:
+ # Look for layer_height in the GlobalStack from material -> definition
+ container = global_stack.definition
+ if container and container.hasProperty("layer_height", "value"):
+ layer_height = container.getProperty("layer_height", "value")
+
+ if isinstance(layer_height, SettingFunction):
+ layer_height = layer_height(global_stack)
+
+ return float(layer_height)
+
+ def __repr__(self):
+ return str(self.items)
diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py
index 451c8babfb..7980f4ed63 100644
--- a/cura/Machines/QualityNode.py
+++ b/cura/Machines/QualityNode.py
@@ -31,8 +31,8 @@ class QualityNode(ContainerNode):
# Find all intent profiles that fit the current configuration.
from cura.Machines.MachineNode import MachineNode
if not isinstance(self.parent, MachineNode): # Not a global profile.
- for intent in container_registry.findInstanceContainersMetadata(type = "intent", definition = self.parent.variant.machine.quality_definition, variant = self.parent.variant.variant_name, material = self.parent.base_file):
+ for intent in container_registry.findInstanceContainersMetadata(type = "intent", definition = self.parent.variant.machine.quality_definition, variant = self.parent.variant.variant_name, material = self.parent.base_file, quality_type = self.quality_type):
self.intents[intent["id"]] = IntentNode(intent["id"], quality = self)
- if not self.intents:
- self.intents["empty_intent"] = IntentNode("empty_intent", quality = self)
+
+ self.intents["empty_intent"] = IntentNode("empty_intent", quality = self)
# Otherwise, there are no intents for global profiles.
\ No newline at end of file
diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py
index 30eb0b009c..33b0fd8d2e 100644
--- a/cura/Settings/CuraStackBuilder.py
+++ b/cura/Settings/CuraStackBuilder.py
@@ -160,6 +160,7 @@ class CuraStackBuilder:
stack.variant = variant_container
stack.material = material_container
stack.quality = quality_container
+ stack.intent = application.empty_intent_container
stack.qualityChanges = application.empty_quality_changes_container
stack.userChanges = user_container
@@ -208,6 +209,7 @@ class CuraStackBuilder:
stack.variant = variant_container
stack.material = material_container
stack.quality = quality_container
+ stack.intent = application.empty_intent_container
stack.qualityChanges = application.empty_quality_changes_container
stack.userChanges = user_container
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index a7447aaa1e..79417e8ed4 100755
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -132,6 +132,7 @@ class MachineManager(QObject):
activeMaterialChanged = pyqtSignal()
activeVariantChanged = pyqtSignal()
activeQualityChanged = pyqtSignal()
+ activeIntentChanged = pyqtSignal()
activeStackChanged = pyqtSignal() # Emitted whenever the active stack is changed (ie: when changing between extruders, changing a profile, but not when changing a value)
extruderChanged = pyqtSignal()
@@ -270,6 +271,7 @@ class MachineManager(QObject):
self.activeQualityChanged.emit()
self.activeVariantChanged.emit()
self.activeMaterialChanged.emit()
+ self.activeIntentChanged.emit()
self.rootMaterialChanged.emit()
self.numberExtrudersEnabledChanged.emit()
@@ -609,6 +611,14 @@ class MachineManager(QObject):
return False
return Util.parseBool(global_container_stack.quality.getMetaDataEntry("is_experimental", False))
+ @pyqtProperty(str, notify=activeIntentChanged)
+ def activeIntentCategory(self):
+
+ if not self._active_container_stack:
+ return ""
+ intent_category = self._active_container_stack.intent.getMetaDataEntry("intent_category")
+ return intent_category
+
## Returns whether there is anything unsupported in the current set-up.
#
# The current set-up signifies the global stack and all extruder stacks,
diff --git a/cura/Settings/cura_empty_instance_containers.py b/cura/Settings/cura_empty_instance_containers.py
index e8a6df8ff1..0ab37c5435 100644
--- a/cura/Settings/cura_empty_instance_containers.py
+++ b/cura/Settings/cura_empty_instance_containers.py
@@ -47,6 +47,7 @@ EMPTY_INTENT_CONTAINER_ID = "empty_intent"
empty_intent_container = copy.deepcopy(empty_container)
empty_intent_container.setMetaDataEntry("id", EMPTY_INTENT_CONTAINER_ID)
empty_intent_container.setMetaDataEntry("type", "intent")
+empty_intent_container.setMetaDataEntry("intent_category", "default")
empty_intent_container.setName(catalog.i18nc("@info:No intent profile selected", "Default"))
diff --git a/resources/intent/smooth.inst.cfg b/resources/intent/smooth.inst.cfg
index cfaa18c2bf..2ec33da504 100644
--- a/resources/intent/smooth.inst.cfg
+++ b/resources/intent/smooth.inst.cfg
@@ -4,7 +4,7 @@ name = Smooth (TEST INTENT)
definition = ultimaker3
[metadata]
-setting_version = 8
+setting_version = 9
type = intent
intent_category = smooth
quality_type = draft
diff --git a/resources/intent/strong.inst.cfg b/resources/intent/strong.inst.cfg
index e90b73d7d8..d0354070a0 100644
--- a/resources/intent/strong.inst.cfg
+++ b/resources/intent/strong.inst.cfg
@@ -4,7 +4,7 @@ name = Strong (TEST INTENT)
definition = ultimaker3
[metadata]
-setting_version = 8
+setting_version = 9
type = intent
intent_category = engineering
quality_type = draft
diff --git a/resources/qml/LabelBar.qml b/resources/qml/LabelBar.qml
new file mode 100644
index 0000000000..9a870811ca
--- /dev/null
+++ b/resources/qml/LabelBar.qml
@@ -0,0 +1,65 @@
+// Copyright (c) 2019 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 UM 1.2 as UM
+
+// The labelBar shows a set of labels that are evenly spaced from oneother.
+// The first item is aligned to the left, the last is aligned to the right.
+// It's intended to be used together with RadioCheckBar. As such, it needs
+// to know what the used itemSize is, so it can ensure the labels are aligned correctly.
+Item
+{
+ id: base
+ property var model: null
+ property string modelKey: ""
+ property int itemSize: 14
+ height: childrenRect.height
+ RowLayout
+ {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: 0
+ Repeater
+ {
+ id: repeater
+ model: base.model
+
+ Item
+ {
+ Layout.fillWidth: true
+ Layout.maximumWidth: Math.round(index + 1 === repeater.count || repeater.count <= 1 ? itemSize : base.width / (repeater.count - 1))
+ height: label.height
+
+ Label
+ {
+ id: label
+ text: model[modelKey]
+ color: UM.Theme.getColor("text")
+ font: UM.Theme.getFont("default")
+ renderType: Text.NativeRendering
+ height: contentHeight
+ anchors
+ {
+ // Some magic to ensure that the items are aligned properly.
+ // We want the following:
+ // First item should be aligned to the left, no margin.
+ // Last item should be aligned to the right, no margin.
+ // The middle item(s) should be aligned to the center of the "item" it's showing (hence half the itemsize as offset).
+ // We want the center of the label to align with the center of the item, so we negatively offset by half the contentWidth
+ right: index + 1 === repeater.count ? parent.right: undefined
+ left: index + 1 === repeater.count || index === 0 ? undefined: parent.left
+ leftMargin: Math.round((itemSize - contentWidth) * 0.5)
+
+ // For some reason, the last label in the row gets misaligned with Qt 5.10. This lines seems to
+ // fix it.
+ verticalCenter: parent.verticalCenter
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/resources/qml/Menus/BuildplateMenu.qml b/resources/qml/Menus/BuildplateMenu.qml
deleted file mode 100644
index b924aa0879..0000000000
--- a/resources/qml/Menus/BuildplateMenu.qml
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.7
-import QtQuick.Controls 1.4
-
-import UM 1.2 as UM
-import Cura 1.0 as Cura
-
-Menu
-{
- id: menu
- title: "Build plate"
-
- property var buildPlateModel: CuraApplication.getBuildPlateModel()
-
- Instantiator
- {
- model: menu.buildPlateModel
-
- MenuItem {
- text: model.name
- checkable: true
- checked: model.name == Cura.MachineManager.globalVariantName
- exclusiveGroup: group
- onTriggered: {
- Cura.MachineManager.setGlobalVariant(model.container_node);
- }
- }
-
- onObjectAdded: menu.insertItem(index, object)
- onObjectRemoved: menu.removeItem(object)
- }
-
- ExclusiveGroup { id: group }
-}
diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
index 77164429b3..959d498054 100644
--- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
+++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
@@ -1,8 +1,8 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import QtQuick 2.7
-import QtQuick.Controls 2.0
+import QtQuick 2.10
+import QtQuick.Controls 2.3
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
@@ -99,12 +99,14 @@ Cura.ExpandablePopup
left: extruderIcon.right
leftMargin: UM.Theme.getSize("default_margin").width
top: typeAndBrandNameLabel.bottom
+ right: parent.right
+ rightMargin: UM.Theme.getSize("default_margin").width
}
}
}
}
- //Placeholder text if there is a configuration to select but no materials (so we can't show the materials per extruder).
+ // Placeholder text if there is a configuration to select but no materials (so we can't show the materials per extruder).
Label
{
text: catalog.i18nc("@label", "Select configuration")
diff --git a/resources/qml/Menus/IntentMenu.qml b/resources/qml/Menus/IntentMenu.qml
deleted file mode 100644
index 8aba7cbde6..0000000000
--- a/resources/qml/Menus/IntentMenu.qml
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2019 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.7
-import QtQuick.Controls 1.4
-
-import UM 1.2 as UM
-import Cura 1.6 as Cura
-
-Menu
-{
- id: menu
- title: "Intent"
-
- property int extruderIndex: 0
-
- Cura.IntentCategoryModel
- {
- id: intentCategoryModel
- }
-
- Instantiator
- {
- model: intentCategoryModel
-
- MenuItem //Section header.
- {
- text: model.name
- enabled: false
- checked: false
-
- property var per_category_intents: Cura.IntentModel
- {
- id: intentModel
- intentCategory: model.intent_category
- }
-
- property var intent_instantiator: Instantiator
- {
- model: intentModel
- MenuItem
- {
- text: model.name
- checkable: true
- checked: false
- Binding on checked
- {
- when: Cura.MachineManager.activeStack != null
- value: Cura.MachineManager.activeStack.intent.metaData["intent_category"] == intentModel.intentCategory && Cura.MachineManager.activeStack.quality.metaData["quality_type"] == model.quality_type
- }
- exclusiveGroup: group
- onTriggered: Cura.IntentManager.selectIntent(intentModel.intentCategory, model.quality_type)
- }
-
- onObjectAdded: menu.insertItem(index, object)
- onObjectRemoved: menu.removeItem(object)
- }
- }
-
- onObjectAdded: menu.insertItem(index, object)
- onObjectRemoved: menu.removeItem(object)
- }
- ExclusiveGroup { id: group }
-}
diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml
deleted file mode 100644
index 68260f2502..0000000000
--- a/resources/qml/Menus/ProfileMenu.qml
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.7
-import QtQuick.Controls 1.4
-
-import UM 1.2 as UM
-import Cura 1.0 as Cura
-
-Menu
-{
- id: menu
-
- Instantiator
- {
- model: Cura.QualityProfilesDropDownMenuModel
-
- MenuItem
- {
- text:
- {
- var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name
- full_text += model.is_experimental ? " - " + catalog.i18nc("@label", "Experimental") : ""
- return full_text
- }
- checkable: true
- checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
- exclusiveGroup: group
- onTriggered: Cura.MachineManager.setQualityGroup(model.quality_group)
- visible: model.available
- }
-
- onObjectAdded: menu.insertItem(index, object)
- onObjectRemoved: menu.removeItem(object)
- }
-
- MenuSeparator
- {
- id: customSeparator
- visible: Cura.CustomQualityProfilesDropDownMenuModel.count > 0
- }
-
- Instantiator
- {
- id: customProfileInstantiator
- model: Cura.CustomQualityProfilesDropDownMenuModel
-
- Connections
- {
- target: Cura.CustomQualityProfilesDropDownMenuModel
- onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.count > 0
- }
-
- MenuItem
- {
- text: model.name
- checkable: true
- checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name
- exclusiveGroup: group
- onTriggered: Cura.MachineManager.setQualityChangesGroup(model.quality_changes_group)
- }
-
- onObjectAdded:
- {
- customSeparator.visible = model.count > 0;
- menu.insertItem(index, object);
- }
- onObjectRemoved:
- {
- customSeparator.visible = model.count > 0;
- menu.removeItem(object);
- }
- }
-
- ExclusiveGroup { id: group; }
-
- MenuSeparator { id: profileMenuSeparator }
-
- MenuItem { action: Cura.Actions.addProfile }
- MenuItem { action: Cura.Actions.updateProfile }
- MenuItem { action: Cura.Actions.resetProfile }
- MenuSeparator { }
- MenuItem { action: Cura.Actions.manageProfiles }
-}
diff --git a/resources/qml/Menus/SettingsMenu.qml b/resources/qml/Menus/SettingsMenu.qml
index 2845101dbf..17a4f6734a 100644
--- a/resources/qml/Menus/SettingsMenu.qml
+++ b/resources/qml/Menus/SettingsMenu.qml
@@ -57,14 +57,6 @@ Menu
onObjectRemoved: base.removeItem(object)
}
- // TODO Only show in dev mode. Remove check when feature ready
- BuildplateMenu
- {
- title: catalog.i18nc("@title:menu", "&Build plate")
- visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates
- }
- ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") }
-
MenuSeparator { }
MenuItem { action: Cura.Actions.configureSettingVisibility }
diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml
index e6a35455f2..682aefa821 100644
--- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml
+++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml
@@ -1,12 +1,12 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import QtQuick 2.7
-import QtQuick.Controls 2.0
-import QtQuick.Controls 1.1 as OldControls
+import QtQuick 2.10
+import QtQuick.Controls 2.3
+import QtQuick.Controls 1.4 as OldControls
import UM 1.3 as UM
-import Cura 1.0 as Cura
+import Cura 1.6 as Cura
Item
@@ -18,18 +18,6 @@ Item
property var extrudersModel: CuraApplication.getExtrudersModel()
- // Profile selector row
- GlobalProfileSelector
- {
- id: globalProfileRow
- anchors
- {
- top: parent.top
- left: parent.left
- right: parent.right
- margins: parent.padding
- }
- }
Item
{
id: intent
@@ -37,7 +25,7 @@ Item
anchors
{
- top: globalProfileRow.bottom
+ top: parent.top
topMargin: UM.Theme.getSize("default_margin").height
left: parent.left
leftMargin: parent.padding
@@ -47,7 +35,7 @@ Item
Label
{
- id: intentLabel
+ id: profileLabel
anchors
{
top: parent.top
@@ -55,25 +43,130 @@ Item
left: parent.left
right: intentSelection.left
}
- text: catalog.i18nc("@label", "Intent")
+ text: catalog.i18nc("@label", "Profile")
font: UM.Theme.getFont("medium")
+ renderType: Text.NativeRendering
color: UM.Theme.getColor("text")
verticalAlignment: Text.AlignVCenter
}
- OldControls.ToolButton
+
+ Button
{
id: intentSelection
- text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.intent.name : ""
- tooltip: text
- height: UM.Theme.getSize("print_setup_big_item").height
- width: UM.Theme.getSize("print_setup_big_item").width
- anchors.right: parent.right
- style: UM.Theme.styles.print_setup_header_button
- activeFocusOnPress: true
+ onClicked: menu.opened ? menu.close() : menu.open()
+ text: generateActiveQualityText()
- menu: Cura.IntentMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex }
+ anchors.right: parent.right
+ width: UM.Theme.getSize("print_setup_big_item").width
+ height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height
+ hoverEnabled: true
+
+ baselineOffset: null // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway...
+
+ contentItem: Label
+ {
+ id: textLabel
+ text: intentSelection.text
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width
+ anchors.verticalCenter: intentSelection.verticalCenter
+ height: contentHeight
+ verticalAlignment: Text.AlignVCenter
+ renderType: Text.NativeRendering
+ }
+
+ background: Rectangle
+ {
+ id: backgroundItem
+ border.color: intentSelection.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border")
+ border.width: UM.Theme.getSize("default_lining").width
+ radius: UM.Theme.getSize("default_radius").width
+ color: UM.Theme.getColor("main_background")
+ }
+
+ function generateActiveQualityText()
+ {
+
+ var result = ""
+ if(Cura.MachineManager.activeIntentCategory != "default")
+ {
+ result += Cura.MachineManager.activeIntentCategory + " - "
+ }
+
+ result += Cura.MachineManager.activeQualityOrQualityChangesName
+ if (Cura.MachineManager.isActiveQualityExperimental)
+ {
+ result += " (Experimental)"
+ }
+
+ if (Cura.MachineManager.isActiveQualitySupported)
+ {
+ if (Cura.MachineManager.activeQualityLayerHeight > 0)
+ {
+ result += " "
+ result += " - "
+ result += Cura.MachineManager.activeQualityLayerHeight + "mm"
+ result += ""
+ }
+ }
+
+ return result
+ }
+
+ UM.SimpleButton
+ {
+ id: customisedSettings
+
+ visible: Cura.MachineManager.hasUserSettings
+ width: UM.Theme.getSize("print_setup_icon").width
+ height: UM.Theme.getSize("print_setup_icon").height
+
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: downArrow.left
+ anchors.rightMargin: UM.Theme.getSize("default_margin").width
+
+ color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button");
+ iconSource: UM.Theme.getIcon("star")
+
+ onClicked:
+ {
+ forceActiveFocus();
+ Cura.Actions.manageProfiles.trigger()
+ }
+ onEntered:
+ {
+ var content = catalog.i18nc("@tooltip", "Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.")
+ base.showTooltip(intent, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content)
+ }
+ onExited: base.hideTooltip()
+ }
+ UM.RecolorImage
+ {
+ id: downArrow
+
+
+ source: UM.Theme.getIcon("arrow_bottom")
+ width: UM.Theme.getSize("standard_arrow").width
+ height: UM.Theme.getSize("standard_arrow").height
+
+ anchors
+ {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ rightMargin: UM.Theme.getSize("default_margin").width
+ }
+
+ color: UM.Theme.getColor("setting_control_button")
+ }
}
+ QualitiesWithIntentMenu
+ {
+ id: menu
+ y: intentSelection.y + intentSelection.height
+ x: intentSelection.x
+ width: intentSelection.width
+ }
}
UM.TabRow
@@ -143,7 +236,7 @@ Item
{
anchors
{
- top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom
+ top: tabBar.visible ? tabBar.bottom : intent.bottom
topMargin: -UM.Theme.getSize("default_lining").width
left: parent.left
leftMargin: parent.padding
diff --git a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml
deleted file mode 100644
index 32c07a52a6..0000000000
--- a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (c) 2018 Ultimaker B.V.
-// Cura is released under the terms of the LGPLv3 or higher.
-
-import QtQuick 2.7
-import QtQuick.Controls 1.1
-import QtQuick.Controls.Styles 1.1
-import QtQuick.Layouts 1.2
-
-import UM 1.2 as UM
-import Cura 1.0 as Cura
-
-Item
-{
- id: globalProfileRow
- height: childrenRect.height
-
- Label
- {
- id: globalProfileLabel
- anchors
- {
- top: parent.top
- bottom: parent.bottom
- left: parent.left
- right: globalProfileSelection.left
- }
- text: catalog.i18nc("@label", "Profile")
- font: UM.Theme.getFont("medium")
- color: UM.Theme.getColor("text")
- verticalAlignment: Text.AlignVCenter
- }
-
- ToolButton
- {
- id: globalProfileSelection
-
- text: generateActiveQualityText()
- width: UM.Theme.getSize("print_setup_big_item").width
- height: UM.Theme.getSize("print_setup_big_item").height
- anchors
- {
- top: parent.top
- right: parent.right
- }
- tooltip: Cura.MachineManager.activeQualityOrQualityChangesName
- style: UM.Theme.styles.print_setup_header_button
- activeFocusOnPress: true
- menu: Cura.ProfileMenu { }
-
- function generateActiveQualityText()
- {
- var result = Cura.MachineManager.activeQualityOrQualityChangesName
- if (Cura.MachineManager.isActiveQualityExperimental)
- {
- result += " (Experimental)"
- }
-
- if (Cura.MachineManager.isActiveQualitySupported)
- {
- if (Cura.MachineManager.activeQualityLayerHeight > 0)
- {
- result += " "
- result += " - "
- result += Cura.MachineManager.activeQualityLayerHeight + "mm"
- result += ""
- }
- }
-
- return result
- }
-
- UM.SimpleButton
- {
- id: customisedSettings
-
- visible: Cura.MachineManager.hasUserSettings
- width: UM.Theme.getSize("print_setup_icon").width
- height: UM.Theme.getSize("print_setup_icon").height
-
- anchors.verticalCenter: parent.verticalCenter
- anchors.right: parent.right
- anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("thick_margin").width)
-
- color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button");
- iconSource: UM.Theme.getIcon("star")
-
- onClicked:
- {
- forceActiveFocus();
- Cura.Actions.manageProfiles.trigger()
- }
- onEntered:
- {
- var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.")
- base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content)
- }
- onExited: base.hideTooltip()
- }
- }
-}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector/Custom/MenuButton.qml b/resources/qml/PrintSetupSelector/Custom/MenuButton.qml
new file mode 100644
index 0000000000..29436da9cf
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Custom/MenuButton.qml
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 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.2 as UM
+import Cura 1.6 as Cura
+
+Button
+{
+ // This is a work around for a qml issue. Since the default button uses a private implementation for contentItem
+ // (the so called IconText), which handles the mnemonic conversion (aka; ensuring that &Button) text property
+ // is rendered with the B underlined. Since we're also forced to mix controls 1.0 and 2.0 actions together,
+ // we need a special property for the text of the label if we do want it to be rendered correclty, but don't want
+ // another shortcut to be added (which will cause for "QQuickAction::event: Ambiguous shortcut overload: " to
+ // happen.
+ property string labelText: ""
+ id: button
+ hoverEnabled: true
+
+ background: Rectangle
+ {
+ id: backgroundRectangle
+ border.width: 1
+ border.color: button.checked ? UM.Theme.getColor("setting_control_border_highlight") : "transparent"
+ color: button.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent"
+ radius: UM.Theme.getSize("action_button_radius").width
+ }
+
+ // Workarround to ensure that the mnemonic highlighting happens correctly
+ function replaceText(txt)
+ {
+ var index = txt.indexOf("&")
+ if(index >= 0)
+ {
+ txt = txt.replace(txt.substr(index, 2), ("" + txt.substr(index + 1, 1) + ""))
+ }
+ return txt
+ }
+
+ contentItem: Label
+ {
+ id: textLabel
+ text: button.text != "" ? replaceText(button.text) : replaceText(button.labelText)
+ height: contentHeight
+ verticalAlignment: Text.AlignVCenter
+ anchors.left: button.left
+ anchors.leftMargin: UM.Theme.getSize("wide_margin").width
+ renderType: Text.NativeRendering
+ }
+}
\ No newline at end of file
diff --git a/resources/qml/PrintSetupSelector/Custom/QualitiesWithIntentMenu.qml b/resources/qml/PrintSetupSelector/Custom/QualitiesWithIntentMenu.qml
new file mode 100644
index 0000000000..96c8431112
--- /dev/null
+++ b/resources/qml/PrintSetupSelector/Custom/QualitiesWithIntentMenu.qml
@@ -0,0 +1,228 @@
+// Copyright (c) 2019 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.2 as UM
+import Cura 1.6 as Cura
+
+Popup
+{
+ id: popup
+ implicitWidth: 400
+ property var dataModel: Cura.IntentCategoryModel {}
+
+ property int defaultMargin: UM.Theme.getSize("default_margin").width
+ property color backgroundColor: UM.Theme.getColor("main_background")
+ property color borderColor: UM.Theme.getColor("lining")
+
+ topPadding: UM.Theme.getSize("narrow_margin").height
+ rightPadding: UM.Theme.getSize("default_lining").width
+ leftPadding: UM.Theme.getSize("default_lining").width
+
+ padding: 0
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ background: Cura.RoundedRectangle
+ {
+ color: backgroundColor
+ border.width: UM.Theme.getSize("default_lining").width
+ border.color: borderColor
+ cornerSide: Cura.RoundedRectangle.Direction.Down
+ }
+
+ ButtonGroup
+ {
+ id: buttonGroup
+ exclusive: true
+ onClicked: popup.visible = false
+ }
+
+ contentItem: Column
+ {
+ // This repeater adds the intent labels
+ Repeater
+ {
+ model: dataModel
+ delegate: Item
+ {
+ // We need to set it like that, otherwise we'd have to set the sub model with model: model.qualities
+ // Which obviously won't work due to naming conflicts.
+ property variant subItemModel: model.qualities
+
+ height: childrenRect.height
+ anchors
+ {
+ left: parent.left
+ right: parent.right
+ }
+
+ Label
+ {
+ id: headerLabel
+ text: model.name
+ renderType: Text.NativeRendering
+ height: visible ? contentHeight: 0
+ enabled: false
+ visible: qualitiesList.visibleChildren.length > 0
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width
+ }
+
+ Column
+ {
+ id: qualitiesList
+ anchors.top: headerLabel.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ // We set it by means of a binding, since then we can use the when condition, which we need to
+ // prevent a binding loop.
+ Binding
+ {
+ target: parent
+ property: "height"
+ value: parent.childrenRect.height
+ when: parent.visibleChildren.length > 0
+ }
+
+ // Add the qualities that belong to the intent
+ Repeater
+ {
+ visible: false
+ model: subItemModel
+ MenuButton
+ {
+ id: button
+
+ onClicked: Cura.IntentManager.selectIntent(model.intent_category, model.quality_type)
+
+ width: parent.width
+ checkable: true
+ visible: model.available
+ text: model.name + " - " + model.layer_height + " mm"
+ checked:
+ {
+ if(Cura.MachineManager.hasCustomQuality)
+ {
+ // When user created profile is active, no quality tickbox should be active.
+ return false
+ }
+ return Cura.MachineManager.activeQualityType == model.quality_type && Cura.MachineManager.activeIntentCategory == model.intent_category
+ }
+ ButtonGroup.group: buttonGroup
+
+ }
+ }
+ }
+ }
+ }
+
+ Rectangle
+ {
+ height: 1
+ anchors.left: parent.left
+ anchors.right: parent.right
+ color: borderColor
+ }
+ MenuButton
+ {
+ labelText: Cura.Actions.addProfile.text
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ enabled: Cura.Actions.addProfile.enabled
+ onClicked:
+ {
+ Cura.Actions.addProfile.trigger()
+ popup.visible = false
+ }
+ }
+ MenuButton
+ {
+ labelText: Cura.Actions.updateProfile.text
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ enabled: Cura.Actions.updateProfile.enabled
+
+ onClicked:
+ {
+ popup.visible = false
+ Cura.Actions.updateProfile.trigger()
+ }
+ }
+ MenuButton
+ {
+ text: catalog.i18nc("@action:button", "Discard current changes")
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ enabled: Cura.MachineManager.hasUserSettings
+
+ onClicked:
+ {
+ popup.visible = false
+ Cura.ContainerManager.clearUserContainers()
+ }
+ }
+ Rectangle
+ {
+ height: 1
+ anchors.left: parent.left
+ anchors.right: parent.right
+ color: borderColor
+ }
+
+ MenuButton
+ {
+ id: manageProfilesButton
+ text: Cura.Actions.manageProfiles.text
+ anchors
+ {
+ left: parent.left
+ right: parent.right
+ }
+
+ height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height
+
+ contentItem: Item
+ {
+ width: manageProfilesButton.width
+ Label
+ {
+ id: textLabel
+ text: manageProfilesButton.text
+ height: contentHeight
+ anchors.left: parent.left
+ anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("narrow_margin").width
+ verticalAlignment: Text.AlignVCenter
+ renderType: Text.NativeRendering
+ }
+ Label
+ {
+ id: shortcutLabel
+ text: Cura.Actions.manageProfiles.shortcut
+ height: contentHeight
+ anchors.right: parent.right
+ anchors.rightMargin: UM.Theme.getSize("default_margin").width
+ verticalAlignment: Text.AlignVCenter
+ renderType: Text.NativeRendering
+ }
+ }
+ onClicked:
+ {
+ popup.visible = false
+ Cura.Actions.manageProfiles.trigger()
+ }
+ }
+ // spacer
+ Item
+ {
+ width: 2
+ height: UM.Theme.getSize("default_radius").width
+ }
+ }
+}
diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml
index 96b244d803..5628867922 100644
--- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml
+++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml
@@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import QtQuick 2.7
+import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
@@ -20,10 +20,16 @@ RowLayout
{
if (Cura.MachineManager.activeStack)
{
- var text = Cura.MachineManager.activeQualityOrQualityChangesName
+ var text = ""
+ if(Cura.MachineManager.activeIntentCategory != "default")
+ {
+ text += Cura.MachineManager.activeIntentCategory + " - "
+ }
+
+ text += Cura.MachineManager.activeQualityOrQualityChangesName
if (!Cura.MachineManager.hasNotSupportedQuality)
{
- text += " " + layerHeight.properties.value + "mm"
+ text += " - " + layerHeight.properties.value + "mm"
text += Cura.MachineManager.isActiveQualityExperimental ? " - " + catalog.i18nc("@label", "Experimental") : ""
}
return text
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
index 6885f8c041..a180ad6324 100644
--- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml
@@ -1,7 +1,7 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import QtQuick 2.7
+import QtQuick 2.10
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
@@ -27,7 +27,6 @@ Item
Column
{
- width: parent.width - 2 * parent.padding
spacing: UM.Theme.getSize("wide_margin").height
anchors
diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
index 0486f5d2d7..68a3e4811d 100644
--- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
+++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml
@@ -1,17 +1,14 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
-import QtQuick 2.7
+import QtQuick 2.10
import QtQuick.Controls 1.4
+import QtQuick.Controls 2.3 as Controls2
import QtQuick.Controls.Styles 1.4
import UM 1.2 as UM
-import Cura 1.0 as Cura
+import Cura 1.6 as Cura
-
-//
-// Quality profile
-//
Item
{
id: qualityRow
@@ -20,436 +17,111 @@ Item
property real labelColumnWidth: Math.round(width / 3)
property real settingsColumnWidth: width - labelColumnWidth
- Timer
- {
- id: qualitySliderChangeTimer
- interval: 50
- running: false
- repeat: false
- onTriggered:
- {
- var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value);
- Cura.MachineManager.activeQualityGroup = item.quality_group;
- }
- }
-
- Component.onCompleted: qualityModel.update()
-
- Connections
- {
- target: Cura.QualityProfilesDropDownMenuModel
- onItemsChanged: qualityModel.update()
- }
-
- Connections {
- target: base
- onVisibleChanged:
- {
- // update needs to be called when the widgets are visible, otherwise the step width calculation
- // will fail because the width of an invisible item is 0.
- if (visible)
- {
- qualityModel.update();
- }
- }
- }
-
- ListModel
- {
- id: qualityModel
-
- property var totalTicks: 0
- property var availableTotalTicks: 0
- property var existingQualityProfile: 0
-
- property var qualitySliderActiveIndex: 0
- property var qualitySliderStepWidth: 0
- property var qualitySliderAvailableMin: 0
- property var qualitySliderAvailableMax: 0
- property var qualitySliderMarginRight: 0
-
- function update ()
- {
- reset()
-
- var availableMin = -1
- var availableMax = -1
-
- for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++)
- {
- var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i)
-
- // Add each quality item to the UI quality model
- qualityModel.append(qualityItem)
-
- // Set selected value
- if (Cura.MachineManager.activeQualityType == qualityItem.quality_type)
- {
- // set to -1 when switching to user created profile so all ticks are clickable
- if (Cura.MachineManager.hasCustomQuality)
- {
- qualityModel.qualitySliderActiveIndex = -1
- }
- else
- {
- qualityModel.qualitySliderActiveIndex = i
- }
-
- qualityModel.existingQualityProfile = 1
- }
-
- // Set min available
- if (qualityItem.available && availableMin == -1)
- {
- availableMin = i
- }
-
- // Set max available
- if (qualityItem.available)
- {
- availableMax = i
- }
- }
-
- // Set total available ticks for active slider part
- if (availableMin != -1)
- {
- qualityModel.availableTotalTicks = availableMax - availableMin + 1
- }
-
- // Calculate slider values
- calculateSliderStepWidth(qualityModel.totalTicks)
- calculateSliderMargins(availableMin, availableMax, qualityModel.totalTicks)
-
- qualityModel.qualitySliderAvailableMin = availableMin
- qualityModel.qualitySliderAvailableMax = availableMax
- }
-
- function calculateSliderStepWidth (totalTicks)
- {
- // Do not use Math.round otherwise the tickmarks won't be aligned
- qualityModel.qualitySliderStepWidth = totalTicks != 0 ?
- ((settingsColumnWidth - UM.Theme.getSize("print_setup_slider_handle").width) / (totalTicks)) : 0
- }
-
- function calculateSliderMargins (availableMin, availableMax, totalTicks)
- {
- if (availableMin == -1 || (availableMin == 0 && availableMax == 0))
- {
- // Do not use Math.round otherwise the tickmarks won't be aligned
- qualityModel.qualitySliderMarginRight = settingsColumnWidth / 2
- }
- else if (availableMin == availableMax)
- {
- // Do not use Math.round otherwise the tickmarks won't be aligned
- qualityModel.qualitySliderMarginRight = (totalTicks - availableMin) * qualitySliderStepWidth
- }
- else
- {
- // Do not use Math.round otherwise the tickmarks won't be aligned
- qualityModel.qualitySliderMarginRight = (totalTicks - availableMax) * qualitySliderStepWidth
- }
- }
-
- function reset () {
- qualityModel.clear()
- qualityModel.availableTotalTicks = 0
- qualityModel.existingQualityProfile = 0
-
- // check, the ticks count cannot be less than zero
- qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1)
- }
- }
-
// Here are the elements that are shown in the left column
- Item
- {
- id: titleRow
- width: labelColumnWidth
- height: childrenRect.height
- Cura.IconWithText
+ Column
+ {
+ anchors
{
- id: qualityRowTitle
- source: UM.Theme.getIcon("category_layer_height")
- text: catalog.i18nc("@label", "Layer Height")
- font: UM.Theme.getFont("medium")
- anchors.left: parent.left
- anchors.right: customisedSettings.left
+ left: parent.left
+ right: parent.right
}
- UM.SimpleButton
- {
- id: customisedSettings
+ spacing: UM.Theme.getSize("default_margin").height
- visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
- height: visible ? UM.Theme.getSize("print_setup_icon").height : 0
- width: height
+ Controls2.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
- rightMargin: UM.Theme.getSize("default_margin").width
- leftMargin: UM.Theme.getSize("default_margin").width
- verticalCenter: parent.verticalCenter
+ }
+ Cura.IconWithText
+ {
+ id: profileLabel
+ source: UM.Theme.getIcon("category_layer_height")
+ text: catalog.i18nc("@label", "Profiles")
+ font: UM.Theme.getFont("medium")
+ width: labelColumnWidth
}
- color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
- iconSource: UM.Theme.getIcon("reset")
+ Cura.LabelBar
+ {
+ id: labelbar
+ anchors
+ {
+ left: profileLabel.right
+ right: parent.right
+ }
- onClicked:
- {
- // if the current profile is user-created, switch to a built-in quality
- Cura.MachineManager.resetToUseDefaultQuality()
+ model: Cura.QualityProfilesDropDownMenuModel
+ modelKey: "layer_height"
}
- 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()
}
- }
- // Show titles for the each quality slider ticks
- Item
- {
- anchors.left: speedSlider.left
- anchors.top: speedSlider.bottom
- height: childrenRect.height
Repeater
{
- model: qualityModel
-
- Label
+ model: Cura.IntentCategoryModel {}
+ Item
{
- anchors.verticalCenter: parent.verticalCenter
- anchors.top: parent.top
- // The height has to be set manually, otherwise it's not automatically calculated in the repeater
- height: UM.Theme.getSize("default_margin").height
- color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable")
- text:
+ anchors
{
- var result = ""
- if(Cura.MachineManager.activeMachine != null)
- {
- result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height
+ left: parent.left
+ right: parent.right
+ }
+ height: intentCategoryLabel.height
- if(result == undefined)
+ 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")
+ color: UM.Theme.getColor("text")
+ renderType: Text.NativeRendering
+ elide: Text.ElideRight
+ }
+
+ Cura.RadioCheckbar
+ {
+ anchors
+ {
+ left: intentCategoryLabel.right
+ right: parent.right
+ }
+ dataModel: model["qualities"]
+ buttonGroup: activeProfileButtonGroup
+
+ function checkedFunction(modelItem)
+ {
+ if(Cura.MachineManager.hasCustomQuality)
{
- result = "";
+ // When user created profile is active, no quality tickbox should be active.
+ return false
}
- else
+
+ if(modelItem === null)
{
- result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult...
- if (result == undefined || result != result) //Parse failure.
- {
- result = "";
- }
+ return false
}
+ return Cura.MachineManager.activeQualityType == modelItem.quality_type && Cura.MachineManager.activeIntentCategory == modelItem.intent_category
}
- return result
- }
- x:
- {
- // Make sure the text aligns correctly with each tick
- if (qualityModel.totalTicks == 0)
- {
- // If there is only one tick, align it centrally
- return Math.round(((settingsColumnWidth) - width) / 2)
- }
- else if (index == 0)
- {
- return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index
- }
- else if (index == qualityModel.totalTicks)
- {
- return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width
- }
- else
- {
- return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2))
- }
- }
- font: UM.Theme.getFont("default")
- }
- }
- }
-
- // Print speed slider
- // Two sliders are created, one at the bottom with the unavailable qualities
- // and the other at the top with the available quality profiles and so the handle to select them.
- Item
- {
- id: speedSlider
- height: childrenRect.height
-
- anchors
- {
- left: titleRow.right
- right: parent.right
- verticalCenter: titleRow.verticalCenter
- }
-
- // Draw unavailable slider
- Slider
- {
- id: unavailableSlider
-
- width: parent.width
- height: qualitySlider.height // Same height as the slider that is on top
- updateValueWhileDragging : false
- tickmarksEnabled: true
-
- minimumValue: 0
- // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly
- // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available)
- maximumValue: qualityModel.totalTicks
- stepSize: 1
-
- style: SliderStyle
- {
- //Draw Unvailable line
- groove: Item
- {
- Rectangle
- {
- height: UM.Theme.getSize("print_setup_slider_groove").height
- width: control.width - UM.Theme.getSize("print_setup_slider_handle").width
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- color: UM.Theme.getColor("quality_slider_unavailable")
- }
- }
-
- handle: Item {}
-
- tickmarks: Repeater
- {
- id: qualityRepeater
- model: qualityModel.totalTicks > 0 ? qualityModel : 0
-
- Rectangle
- {
- color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable")
- implicitWidth: UM.Theme.getSize("print_setup_slider_tickmarks").width
- implicitHeight: UM.Theme.getSize("print_setup_slider_tickmarks").height
- anchors.verticalCenter: parent.verticalCenter
-
- // Do not use Math.round otherwise the tickmarks won't be aligned
- x: ((UM.Theme.getSize("print_setup_slider_handle").width / 2) - (implicitWidth / 2) + (qualityModel.qualitySliderStepWidth * index))
- radius: Math.round(implicitWidth / 2)
- }
+ isCheckedFunction: checkedFunction
}
}
- // Create a mouse area on top of the unavailable profiles to show a specific tooltip
- MouseArea
- {
- anchors.fill: parent
- hoverEnabled: true
- enabled: !Cura.MachineManager.hasCustomQuality
- onEntered:
- {
- var tooltipContent = catalog.i18nc("@tooltip", "This quality profile is not available for your current material and nozzle configuration. Please change these to enable this quality profile.")
- base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent)
- }
- onExited: base.hideTooltip()
- }
- }
-
- // Draw available slider
- Slider
- {
- id: qualitySlider
-
- width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) + UM.Theme.getSize("print_setup_slider_handle").width
- height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider
- enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized
- visible: qualityModel.availableTotalTicks > 0
- updateValueWhileDragging : false
-
- anchors
- {
- right: parent.right
- rightMargin: qualityModel.qualitySliderMarginRight
- }
-
- minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0
- // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly
- // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available)
- maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1
- stepSize: 1
-
- value: qualityModel.qualitySliderActiveIndex
-
- style: SliderStyle
- {
- // Draw Available line
- groove: Item
- {
- Rectangle
- {
- height: UM.Theme.getSize("print_setup_slider_groove").height
- width: control.width - UM.Theme.getSize("print_setup_slider_handle").width
- anchors.verticalCenter: parent.verticalCenter
-
- // Do not use Math.round otherwise the tickmarks won't be aligned
- x: UM.Theme.getSize("print_setup_slider_handle").width / 2
- color: UM.Theme.getColor("quality_slider_available")
- }
- }
-
- handle: Rectangle
- {
- id: qualityhandleButton
- color: UM.Theme.getColor("primary")
- implicitWidth: UM.Theme.getSize("print_setup_slider_handle").width
- implicitHeight: implicitWidth
- radius: Math.round(implicitWidth / 2)
- visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.MachineManager.hasCustomQuality && qualityModel.existingQualityProfile
- }
- }
-
- onValueChanged:
- {
- // only change if an active machine is set and the slider is visible at all.
- if (Cura.MachineManager.activeMachine != null && visible)
- {
- // prevent updating during view initializing. Trigger only if the value changed by user
- if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1)
- {
- // start updating with short delay
- qualitySliderChangeTimer.start()
- }
- }
- }
-
- // This mouse area is only used to capture the onHover state and don't propagate it to the unavailable mouse area
- MouseArea
- {
- anchors.fill: parent
- hoverEnabled: true
- acceptedButtons: Qt.NoButton
- enabled: !Cura.MachineManager.hasCustomQuality
- }
- }
-
- // This mouse area will only take the mouse events and show a tooltip when the profile in use is
- // a user created profile
- MouseArea
- {
- anchors.fill: parent
- hoverEnabled: true
- visible: Cura.MachineManager.hasCustomQuality
-
- onEntered:
- {
- var tooltipContent = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab")
- base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent)
- }
- onExited: base.hideTooltip()
}
}
}
\ No newline at end of file
diff --git a/resources/qml/RadioCheckbar.qml b/resources/qml/RadioCheckbar.qml
new file mode 100644
index 0000000000..3c767a6201
--- /dev/null
+++ b/resources/qml/RadioCheckbar.qml
@@ -0,0 +1,153 @@
+// Copyright (c) 2019 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 UM 1.1 as UM
+
+Item
+{
+ id: base
+ property ButtonGroup buttonGroup: null
+
+ property color activeColor: UM.Theme.getColor("primary")
+ property color inactiveColor: UM.Theme.getColor("slider_groove")
+ property color defaultItemColor: UM.Theme.getColor("small_button_active")
+ property int checkboxSize: UM.Theme.getSize("radio_button").height * 0.75
+ property int inactiveMarkerSize: 2 * barSize
+ property int barSize: UM.Theme.getSize("slider_groove_radius").height
+ property var isCheckedFunction // Function that accepts the modelItem and returns if the item should be active.
+
+ implicitWidth: 200
+ implicitHeight: checkboxSize
+
+ property var dataModel: null
+
+ // The horizontal inactive bar that sits behind the buttons
+ Rectangle
+ {
+ id: inactiveLine
+ color: inactiveColor
+
+ height: barSize
+
+ anchors
+ {
+ left: buttonBar.left
+ right: buttonBar.right
+ leftMargin: (checkboxSize - inactiveMarkerSize) / 2
+ rightMargin: (checkboxSize - inactiveMarkerSize) / 2
+ verticalCenter: parent.verticalCenter
+ }
+ }
+
+
+ RowLayout
+ {
+ id: buttonBar
+ anchors.top: parent.top
+ height: checkboxSize
+ width: parent.width
+ spacing: 0
+
+ Repeater
+ {
+ id: repeater
+ model: base.dataModel
+ height: checkboxSize
+ Item
+ {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ // The last item of the repeater needs to be shorter, as we don't need another part to fit
+ // the horizontal bar. The others should essentially not be limited.
+ Layout.maximumWidth: index + 1 === repeater.count ? activeComponent.width: 200000000
+
+ property bool isEnabled: model.available
+ // The horizontal bar between the checkable options.
+ // Note that the horizontal bar points towards the previous item.
+ Rectangle
+ {
+ property Item previousItem: repeater.itemAt(index - 1)
+
+ height: barSize
+ width: buttonBar.width / (repeater.count - 1) - activeComponent.width - 2
+ color: defaultItemColor
+
+ anchors
+ {
+ right: activeComponent.left
+ verticalCenter: parent.verticalCenter
+ }
+ visible: previousItem !== null && previousItem.isEnabled && isEnabled
+ }
+ Loader
+ {
+ id: activeComponent
+ sourceComponent: isEnabled? checkboxComponent : disabledComponent
+ width: checkboxSize
+
+ property var modelItem: model
+ }
+ }
+ }
+ }
+
+ Component
+ {
+ id: disabledComponent
+ Item
+ {
+ height: checkboxSize
+ width: checkboxSize
+
+ Rectangle
+ {
+ // This can (and should) be done wiht a verticalCenter. For some reason it does work in QtCreator
+ // but not when using the exact same QML in Cura.
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ height: inactiveMarkerSize
+ width: inactiveMarkerSize
+ radius: width / 2
+ color: inactiveColor
+ }
+ }
+ }
+
+ Component
+ {
+ id: checkboxComponent
+ CheckBox
+ {
+ id: checkbox
+ ButtonGroup.group: buttonGroup
+ width: checkboxSize
+ height: checkboxSize
+ property var modelData: modelItem
+
+ checked: isCheckedFunction(modelItem)
+ indicator: Rectangle
+ {
+ height: checkboxSize
+ width: checkboxSize
+ radius: width / 2
+
+ border.color: defaultItemColor
+
+ Rectangle
+ {
+ anchors
+ {
+ margins: 3
+ fill: parent
+ }
+ radius: width / 2
+ color: activeColor
+ visible: checkbox.checked
+ }
+ }
+ }
+ }
+}
diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml
index 0b7f494a7d..cbabb3ffd4 100644
--- a/resources/qml/Settings/SettingComboBox.qml
+++ b/resources/qml/Settings/SettingComboBox.qml
@@ -20,7 +20,6 @@ SettingItem
textRole: "value"
anchors.fill: parent
- highlighted: base.hovered
onActivated:
{
diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml
index d1edcca69c..5a1ff16b95 100644
--- a/resources/qml/Widgets/ComboBox.qml
+++ b/resources/qml/Widgets/ComboBox.qml
@@ -14,40 +14,34 @@ import Cura 1.1 as Cura
ComboBox
{
id: control
- property bool highlighted: False
+
+ states: [
+ State
+ {
+ name: "disabled"
+ when: !control.enabled
+ PropertyChanges { target: backgroundRectangle.border; color: UM.Theme.getColor("setting_control_disabled_border")}
+ PropertyChanges { target: backgroundRectangle; color: UM.Theme.getColor("setting_control_disabled")}
+ PropertyChanges { target: contentLabel; color: UM.Theme.getColor("setting_control_disabled_text")}
+ },
+ State
+ {
+ name: "highlighted"
+ when: control.hovered || control.activeFocus
+ PropertyChanges { target: backgroundRectangle.border; color: UM.Theme.getColor("setting_control_border_highlight") }
+ PropertyChanges { target: backgroundRectangle; color: UM.Theme.getColor("setting_control_highlight")}
+ }
+ ]
+
background: Rectangle
{
- color:
- {
- if (!enabled)
- {
- return UM.Theme.getColor("setting_control_disabled")
- }
-
- if (control.hovered || control.activeFocus || control.highlighted)
- {
- return UM.Theme.getColor("setting_control_highlight")
- }
-
- return UM.Theme.getColor("setting_control")
- }
+ id: backgroundRectangle
+ color: UM.Theme.getColor("setting_control")
radius: UM.Theme.getSize("setting_control_radius").width
border.width: UM.Theme.getSize("default_lining").width
- border.color:
- {
- if (!enabled)
- {
- return UM.Theme.getColor("setting_control_disabled_border")
- }
+ border.color: UM.Theme.getColor("setting_control_border")
- if (control.hovered || control.activeFocus || control.highlighted)
- {
- return UM.Theme.getColor("setting_control_border_highlight")
- }
-
- return UM.Theme.getColor("setting_control_border")
- }
}
indicator: UM.RecolorImage
@@ -67,6 +61,7 @@ ComboBox
contentItem: Label
{
+ id: contentLabel
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("setting_unit_margin").width
anchors.verticalCenter: parent.verticalCenter
@@ -76,7 +71,7 @@ ComboBox
textFormat: Text.PlainText
renderType: Text.NativeRendering
font: UM.Theme.getFont("default")
- color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
+ color: UM.Theme.getColor("setting_control_text")
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
diff --git a/tests/Machines/TestQualityNode.py b/tests/Machines/TestQualityNode.py
index 64685689c2..54266cb6ad 100644
--- a/tests/Machines/TestQualityNode.py
+++ b/tests/Machines/TestQualityNode.py
@@ -44,6 +44,7 @@ def test_qualityNode_machine_1(container_registry):
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
node = QualityNode("quality_1", material_node)
- assert len(node.intents) == 2
+ assert len(node.intents) == 3
assert "intent_3" in node.intents
- assert "intent_4" in node.intents
\ No newline at end of file
+ assert "intent_4" in node.intents
+ assert "empty_intent" in node.intents
\ No newline at end of file
diff --git a/tests/Settings/TestCuraStackBuilder.py b/tests/Settings/TestCuraStackBuilder.py
index 9f2db2cf19..7a1e05296c 100644
--- a/tests/Settings/TestCuraStackBuilder.py
+++ b/tests/Settings/TestCuraStackBuilder.py
@@ -28,6 +28,14 @@ def quality_container():
return container
+@pytest.fixture
+def intent_container():
+ container = InstanceContainer(container_id="intent container")
+ container.setMetaDataEntry("type", "intent")
+
+ return container
+
+
@pytest.fixture
def quality_changes_container():
container = InstanceContainer(container_id="quality changes container")
@@ -44,7 +52,8 @@ def test_createMachineWithUnknownDefinition(application, container_registry):
assert mocked_config_error.addFaultyContainers.called_with("NOPE")
-def test_createMachine(application, container_registry, definition_container, global_variant, material_instance_container, quality_container, quality_changes_container):
+def test_createMachine(application, container_registry, definition_container, global_variant, material_instance_container,
+ quality_container, intent_container, quality_changes_container):
variant_manager = MagicMock(name = "Variant Manager")
quality_manager = MagicMock(name = "Quality Manager")
global_variant_node = MagicMock( name = "global variant node")
@@ -61,6 +70,7 @@ def test_createMachine(application, container_registry, definition_container, gl
application.getQualityManager = MagicMock(return_value = quality_manager)
application.empty_material_container = material_instance_container
application.empty_quality_container = quality_container
+ application.empty_intent_container = intent_container
application.empty_quality_changes_container = quality_changes_container
application.empty_variant_container = global_variant
@@ -83,9 +93,11 @@ def test_createMachine(application, container_registry, definition_container, gl
assert machine.variant == global_variant
-def test_createExtruderStack(application, definition_container, global_variant, material_instance_container, quality_container, quality_changes_container):
+def test_createExtruderStack(application, definition_container, global_variant, material_instance_container,
+ quality_container, intent_container, quality_changes_container):
application.empty_material_container = material_instance_container
application.empty_quality_container = quality_container
+ application.empty_intent_container = intent_container
application.empty_quality_changes_container = quality_changes_container
with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)):
extruder_stack = CuraStackBuilder.createExtruderStack("Whatever", definition_container, "meh", 0, global_variant, material_instance_container, quality_container)