mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Merge pull request #6289 from Ultimaker/feature_intent_interface
Feature intent interface
This commit is contained in:
commit
3f3aac7ce5
28 changed files with 875 additions and 784 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
})
|
||||
result.sort(key = lambda k: k["weight"])
|
||||
self.setItems(result)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
# Otherwise, there are no intents for global profiles.
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"))
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
65
resources/qml/LabelBar.qml
Normal file
65
resources/qml/LabelBar.qml
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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 }
|
||||
}
|
|
@ -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 }
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -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 += " <font color=\"" + UM.Theme.getColor("text_detail") + "\">"
|
||||
result += " - "
|
||||
result += Cura.MachineManager.activeQualityLayerHeight + "mm"
|
||||
result += "</font>"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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 += " <font color=\"" + UM.Theme.getColor("text_detail") + "\">"
|
||||
result += " - "
|
||||
result += Cura.MachineManager.activeQualityLayerHeight + "mm"
|
||||
result += "</font>"
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
52
resources/qml/PrintSetupSelector/Custom/MenuButton.qml
Normal file
52
resources/qml/PrintSetupSelector/Custom/MenuButton.qml
Normal file
|
@ -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), ("<u>" + txt.substr(index + 1, 1) + "</u>"))
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
UM.SimpleButton
|
||||
{
|
||||
id: customisedSettings
|
||||
|
||||
visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
|
||||
height: visible ? UM.Theme.getSize("print_setup_icon").height : 0
|
||||
width: 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
|
||||
}
|
||||
|
||||
color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
|
||||
iconSource: UM.Theme.getIcon("reset")
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
onClicked:
|
||||
Controls2.ButtonGroup
|
||||
{
|
||||
// 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()
|
||||
}
|
||||
id: activeProfileButtonGroup
|
||||
exclusive: true
|
||||
onClicked: Cura.IntentManager.selectIntent(button.modelData.intent_category, button.modelData.quality_type)
|
||||
}
|
||||
|
||||
// Show titles for the each quality slider ticks
|
||||
Item
|
||||
{
|
||||
anchors.left: speedSlider.left
|
||||
anchors.top: speedSlider.bottom
|
||||
height: childrenRect.height
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
Cura.IconWithText
|
||||
{
|
||||
id: profileLabel
|
||||
source: UM.Theme.getIcon("category_layer_height")
|
||||
text: catalog.i18nc("@label", "Profiles")
|
||||
font: UM.Theme.getFont("medium")
|
||||
width: labelColumnWidth
|
||||
}
|
||||
|
||||
Cura.LabelBar
|
||||
{
|
||||
id: labelbar
|
||||
anchors
|
||||
{
|
||||
left: profileLabel.right
|
||||
right: parent.right
|
||||
}
|
||||
|
||||
model: Cura.QualityProfilesDropDownMenuModel
|
||||
modelKey: "layer_height"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Repeater
|
||||
{
|
||||
model: qualityModel
|
||||
model: Cura.IntentCategoryModel {}
|
||||
Item
|
||||
{
|
||||
anchors
|
||||
{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
height: intentCategoryLabel.height
|
||||
|
||||
Label
|
||||
{
|
||||
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:
|
||||
{
|
||||
var result = ""
|
||||
if(Cura.MachineManager.activeMachine != null)
|
||||
{
|
||||
result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height
|
||||
|
||||
if(result == undefined)
|
||||
{
|
||||
result = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
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 result
|
||||
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
|
||||
}
|
||||
|
||||
x:
|
||||
Cura.RadioCheckbar
|
||||
{
|
||||
// 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
|
||||
left: intentCategoryLabel.right
|
||||
right: parent.right
|
||||
verticalCenter: titleRow.verticalCenter
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Draw unavailable slider
|
||||
Slider
|
||||
if(modelItem === null)
|
||||
{
|
||||
id: unavailableSlider
|
||||
return false
|
||||
}
|
||||
return Cura.MachineManager.activeQualityType == modelItem.quality_type && Cura.MachineManager.activeIntentCategory == modelItem.intent_category
|
||||
}
|
||||
|
||||
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")
|
||||
isCheckedFunction: checkedFunction
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
153
resources/qml/RadioCheckbar.qml
Normal file
153
resources/qml/RadioCheckbar.qml
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ SettingItem
|
|||
textRole: "value"
|
||||
|
||||
anchors.fill: parent
|
||||
highlighted: base.hovered
|
||||
|
||||
onActivated:
|
||||
{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
assert "empty_intent" in node.intents
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue