diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7e5e98adef..cdb640c3e2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -482,7 +482,9 @@ class CuraApplication(QtApplication): preferences.addPreference("view/filter_current_build_plate", False) preferences.addPreference("cura/sidebar_collapsed", False) - preferences.addPreference("cura/favorite_materials", ";".join([])) + preferences.addPreference("cura/favorite_materials", "") + preferences.addPreference("cura/expanded_brands", "") + preferences.addPreference("cura/expanded_types", "") self._need_to_show_user_agreement = not preferences.getValue("general/accepted_user_agreement") diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 1463f2e40e..7075eaa004 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -40,7 +40,6 @@ if TYPE_CHECKING: class MaterialManager(QObject): materialsUpdated = pyqtSignal() # Emitted whenever the material lookup tables are updated. - favoritesUpdated = pyqtSignal() # Emitted whenever the favorites are changed def __init__(self, container_registry, parent = None): super().__init__(parent) @@ -196,12 +195,11 @@ class MaterialManager(QObject): for material_metadata in material_metadatas.values(): self.__addMaterialMetadataIntoLookupTree(material_metadata) - self.materialsUpdated.emit() - favorites = self._application.getPreferences().getValue("cura/favorite_materials") for item in favorites.split(";"): self._favorites.add(item) - self.favoritesUpdated.emit() + + self.materialsUpdated.emit() def __addMaterialMetadataIntoLookupTree(self, material_metadata: dict) -> None: material_id = material_metadata["id"] @@ -621,7 +619,7 @@ class MaterialManager(QObject): @pyqtSlot(str) def addFavorite(self, root_material_id: str): self._favorites.add(root_material_id) - self.favoritesUpdated.emit() + self.materialsUpdated.emit() # Ensure all settings are saved. self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites))) @@ -630,7 +628,7 @@ class MaterialManager(QObject): @pyqtSlot(str) def removeFavorite(self, root_material_id: str): self._favorites.remove(root_material_id) - self.favoritesUpdated.emit() + self.materialsUpdated.emit() # Ensure all settings are saved. self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites))) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 1b20e1188c..c08013566b 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -34,9 +34,6 @@ class BaseMaterialsModel(ListModel): # Update this model when list of materials changes self._material_manager.materialsUpdated.connect(self._update) - # Update this model when list of favorites changes - self._material_manager.favoritesUpdated.connect(self._update) - self.addRoleName(Qt.UserRole + 1, "root_material_id") self.addRoleName(Qt.UserRole + 2, "id") self.addRoleName(Qt.UserRole + 3, "GUID") diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py index 3f917abb16..2e2232c2dc 100644 --- a/cura/Machines/Models/MaterialBrandsModel.py +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -12,7 +12,8 @@ class MaterialTypesModel(ListModel): super().__init__(parent) self.addRoleName(Qt.UserRole + 1, "name") - self.addRoleName(Qt.UserRole + 2, "colors") + self.addRoleName(Qt.UserRole + 2, "brand") + self.addRoleName(Qt.UserRole + 3, "colors") class MaterialBrandsModel(BaseMaterialsModel): @@ -86,6 +87,7 @@ class MaterialBrandsModel(BaseMaterialsModel): for material_type, material_list in material_dict.items(): material_type_item = { "name": material_type, + "brand": brand, "colors": BaseMaterialsModel(self) } material_type_item["colors"].clear() diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 6dcaa3f475..1540f053f5 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -39,6 +39,7 @@ Item { property real lowerValue: minimumValue property bool layersVisible: true + property bool manuallyChanged: true // Indicates whether the value was changed manually or during simulation function getUpperValueFromSliderHandle() { return upperHandle.getValue() @@ -96,7 +97,8 @@ Item { visible: sliderRoot.layersVisible // set the new value when dragging - function onHandleDragged () { + function onHandleDragged() { + sliderRoot.manuallyChanged = true upperHandle.y = y - upperHandle.height lowerHandle.y = y + height @@ -109,7 +111,7 @@ Item { UM.SimulationView.setMinimumLayer(lowerValue) } - function setValue (value) { + function setValue(value) { var range = sliderRoot.upperValue - sliderRoot.lowerValue value = Math.min(value, sliderRoot.maximumValue) value = Math.max(value, sliderRoot.minimumValue + range) @@ -168,7 +170,8 @@ Item { color: upperHandleLabel.activeFocus ? sliderRoot.handleActiveColor : sliderRoot.upperHandleColor visible: sliderRoot.layersVisible - function onHandleDragged () { + function onHandleDragged() { + sliderRoot.manuallyChanged = true // don't allow the lower handle to be heigher than the upper handle if (lowerHandle.y - (y + height) < sliderRoot.minimumRangeHandleSize) { @@ -183,7 +186,7 @@ Item { } // get the upper value based on the slider position - function getValue () { + function getValue() { var result = y / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) result = sliderRoot.maximumValue + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumValue)) result = sliderRoot.roundValues ? Math.round(result) : result @@ -191,7 +194,7 @@ Item { } // set the slider position based on the upper value - function setValue (value) { + function setValue(value) { // Normalize values between range, since using arrow keys will create out-of-the-range values value = sliderRoot.normalizeValue(value) @@ -205,8 +208,16 @@ Item { sliderRoot.updateRangeHandle() } - Keys.onUpPressed: upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) - Keys.onDownPressed: upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + Keys.onUpPressed: + { + sliderRoot.manuallyChanged = true + upperHandleLabel.setValue(upperHandleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + } + Keys.onDownPressed: + { + sliderRoot.manuallyChanged = true + upperHandleLabel.setValue(upperHandleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + } // dragging MouseArea { @@ -256,7 +267,8 @@ Item { visible: sliderRoot.layersVisible - function onHandleDragged () { + function onHandleDragged() { + sliderRoot.manuallyChanged = true // don't allow the upper handle to be lower than the lower handle if (y - (upperHandle.y + upperHandle.height) < sliderRoot.minimumRangeHandleSize) { @@ -271,7 +283,7 @@ Item { } // get the lower value from the current slider position - function getValue () { + function getValue() { var result = (y - (sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)) / (sliderRoot.height - (2 * sliderRoot.handleSize + sliderRoot.minimumRangeHandleSize)); result = sliderRoot.maximumValue - sliderRoot.minimumRange + result * (sliderRoot.minimumValue - (sliderRoot.maximumValue - sliderRoot.minimumRange)) result = sliderRoot.roundValues ? Math.round(result) : result @@ -279,7 +291,7 @@ Item { } // set the slider position based on the lower value - function setValue (value) { + function setValue(value) { // Normalize values between range, since using arrow keys will create out-of-the-range values value = sliderRoot.normalizeValue(value) diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml index 999912e3ba..7eb56cc0b8 100644 --- a/plugins/SimulationView/PathSlider.qml +++ b/plugins/SimulationView/PathSlider.qml @@ -34,6 +34,7 @@ Item { property real handleValue: maximumValue property bool pathsVisible: true + property bool manuallyChanged: true // Indicates whether the value was changed manually or during simulation function getHandleValueFromSliderHandle () { return handle.getValue() @@ -97,6 +98,7 @@ Item { visible: sliderRoot.pathsVisible function onHandleDragged () { + sliderRoot.manuallyChanged = true // update the range handle sliderRoot.updateRangeHandle() @@ -128,8 +130,16 @@ Item { sliderRoot.updateRangeHandle() } - Keys.onRightPressed: handleLabel.setValue(handleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) - Keys.onLeftPressed: handleLabel.setValue(handleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + Keys.onRightPressed: + { + sliderRoot.manuallyChanged = true + handleLabel.setValue(handleLabel.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + } + Keys.onLeftPressed: + { + sliderRoot.manuallyChanged = true + handleLabel.setValue(handleLabel.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + } // dragging MouseArea { diff --git a/plugins/SimulationView/SimulationView.qml b/plugins/SimulationView/SimulationView.qml index b4ca9584c7..be124157fb 100644 --- a/plugins/SimulationView/SimulationView.qml +++ b/plugins/SimulationView/SimulationView.qml @@ -623,7 +623,15 @@ Item { target: UM.SimulationView onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) - onCurrentPathChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) + onCurrentPathChanged: + { + // Only pause the simulation when the layer was changed manually, not when the simulation is running + if (pathSlider.manuallyChanged) + { + playButton.pauseSimulation() + } + pathSlider.setHandleValue(UM.SimulationView.currentPath) + } } // make sure the slider handlers show the correct value after switching views @@ -667,9 +675,14 @@ Item { target: UM.SimulationView onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer) + onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer) onCurrentLayerChanged: { - playButton.pauseSimulation() + // Only pause the simulation when the layer was changed manually, not when the simulation is running + if (layerSlider.manuallyChanged) + { + playButton.pauseSimulation() + } layerSlider.setUpperValue(UM.SimulationView.currentLayer) } } @@ -719,6 +732,8 @@ Item iconSource = "./resources/simulation_resume.svg" simulationTimer.stop() status = 0 + layerSlider.manuallyChanged = true + pathSlider.manuallyChanged = true } function resumeSimulation() @@ -726,7 +741,8 @@ Item UM.SimulationView.setSimulationRunning(true) iconSource = "./resources/simulation_pause.svg" simulationTimer.start() - status = 1 + layerSlider.manuallyChanged = false + pathSlider.manuallyChanged = false } } @@ -770,7 +786,6 @@ Item { UM.SimulationView.setCurrentLayer(currentLayer+1) UM.SimulationView.setCurrentPath(0) - playButton.resumeSimulation() } } else @@ -778,6 +793,8 @@ Item UM.SimulationView.setCurrentPath(currentPath+1) } } + // The status must be set here instead of in the resumeSimulation function otherwise it won't work + // correctly, because part of the logic is in this trigger function. playButton.status = 1 } } diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 4a1203c412..ceb27542c1 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -39,7 +39,7 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication - self._sdk_version = None # type: Optional[int] + self._sdk_version = None # type: Optional[Union[str, int]] self._cloud_api_version = None # type: Optional[int] self._cloud_api_root = None # type: Optional[str] self._api_url = None # type: Optional[str] @@ -207,14 +207,14 @@ class Toolbox(QObject, Extension): return cura.CuraVersion.CuraCloudAPIVersion # type: ignore # Get the packages version depending on Cura version settings. - def _getSDKVersion(self) -> int: + def _getSDKVersion(self) -> Union[int, str]: if not hasattr(cura, "CuraVersion"): return self._plugin_registry.APIVersion - if not hasattr(cura.CuraVersion, "CuraSDKVersion"): # type: ignore + if not hasattr(cura.CuraVersion, "CuraSDKVersion"): # type: ignore return self._plugin_registry.APIVersion - if not cura.CuraVersion.CuraSDKVersion: # type: ignore + if not cura.CuraVersion.CuraSDKVersion: # type: ignore return self._plugin_registry.APIVersion - return cura.CuraVersion.CuraSDKVersion # type: ignore + return cura.CuraVersion.CuraSDKVersion # type: ignore @pyqtSlot() def browsePackages(self) -> None: @@ -499,17 +499,17 @@ class Toolbox(QObject, Extension): local_version = Version(local_package["package_version"]) remote_version = Version(remote_package["package_version"]) if self._getSDKVersion() == "dev": - sdk_version = int(self._plugin_registry.APIVersion) + sdk_version = self._plugin_registry.APIVersion else: - sdk_version = int(self._getSDKVersion()) + sdk_version = self._getSDKVersion() can_upgrade = False if remote_version > local_version: can_upgrade = True # A package with the same version can be built to have different SDK versions. So, for a package with the same # version, we also need to check if the current one has a lower SDK version. If so, this package should also # be upgradable. - elif remote_version == local_version and local_package.get("sdk_version", 0) < sdk_version: - can_upgrade = True + elif remote_version == local_version: + can_upgrade = local_package.get("sdk_version", 0) < remote_package.get("sdk_version", 0) return can_upgrade diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index 1077cbff6f..c8f391dfb0 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -13,14 +13,28 @@ import Cura 1.0 as Cura Rectangle { id: brand_section - property var expanded: base.collapsed_brands.indexOf(model.name) > -1 - property var types_model: model.material_types + + property var sectionName: "" + property var elementsModel // This can be a MaterialTypesModel or GenericMaterialsModel or FavoriteMaterialsModel + property var hasMaterialTypes: true // It indicates wheather it has material types or not + property var expanded: materialList.expandedBrands.indexOf(sectionName) > -1 + height: childrenRect.height width: parent.width Rectangle { id: brand_header_background - color: UM.Theme.getColor("favorites_header_bar") + color: + { + if(!expanded && sectionName == materialList.currentBrand) + { + return UM.Theme.getColor("favorites_row_selected") + } + else + { + return UM.Theme.getColor("favorites_header_bar") + } + } anchors.fill: brand_header } Row @@ -30,11 +44,11 @@ Rectangle Label { id: brand_name - text: model.name + text: sectionName height: UM.Theme.getSize("favorites_row").height width: parent.width - UM.Theme.getSize("favorites_button").width verticalAlignment: Text.AlignVCenter - leftPadding: 4 + leftPadding: (UM.Theme.getSize("default_margin").width / 2) | 0 } Button { @@ -69,32 +83,68 @@ Rectangle anchors.fill: brand_header onPressed: { - const i = base.collapsed_brands.indexOf(model.name) + const i = materialList.expandedBrands.indexOf(sectionName) if (i > -1) { // Remove it - base.collapsed_brands.splice(i, 1) + materialList.expandedBrands.splice(i, 1) brand_section.expanded = false } else { // Add it - base.collapsed_brands.push(model.name) + materialList.expandedBrands.push(sectionName) brand_section.expanded = true } + UM.Preferences.setValue("cura/expanded_brands", materialList.expandedBrands.join(";")); } } Column { + id: brandMaterialList anchors.top: brand_header.bottom width: parent.width anchors.left: parent.left height: brand_section.expanded ? childrenRect.height : 0 visible: brand_section.expanded + Repeater { - model: types_model - delegate: MaterialsTypeSection {} + model: elementsModel + delegate: Loader + { + id: loader + width: parent.width + property var element: model + sourceComponent: hasMaterialTypes ? materialsTypeSection : materialSlot + } + } + } + + Component + { + id: materialsTypeSection + MaterialsTypeSection + { + materialType: element + } + } + + Component + { + id: materialSlot + MaterialsSlot + { + material: element + } + } + + Connections + { + target: UM.Preferences + onPreferenceChanged: + { + expanded = materialList.expandedBrands.indexOf(sectionName) > -1 } } } \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml index ad9f0e3766..f72d93aff3 100644 --- a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml +++ b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml @@ -13,21 +13,29 @@ Item { id: detailsPanel - property var currentItem: base.currentItem + property var currentItem: null - onCurrentItemChanged: { updateMaterialPropertiesObject(currentItem) } + onCurrentItemChanged: + { + // When the current item changes, the detail view needs to be updated + if (currentItem != null) + { + updateMaterialPropertiesObject() + materialDetailsView.currentMaterialNode = currentItem.container_node + } + } - function updateMaterialPropertiesObject( currentItem ) + function updateMaterialPropertiesObject() { materialProperties.name = currentItem.name || "Unknown" - materialProperties.guid = currentItem.GUID; + materialProperties.guid = currentItem.GUID materialProperties.container_id = currentItem.id materialProperties.brand = currentItem.brand || "Unknown" materialProperties.material = currentItem.material || "Unknown" materialProperties.color_name = currentItem.color_name || "Yellow" materialProperties.color_code = currentItem.color_code || "yellow" materialProperties.description = currentItem.description || "" - materialProperties.adhesion_info = currentItem.adhesion_info || ""; + materialProperties.adhesion_info = currentItem.adhesion_info || "" materialProperties.density = currentItem.density || 0.0 materialProperties.diameter = currentItem.diameter || 0.0 materialProperties.approximate_diameter = currentItem.approximate_diameter || "0" @@ -62,13 +70,11 @@ Item bottom: parent.bottom } - editingEnabled: base.currentItem != null && !base.currentItem.is_read_only + editingEnabled: currentItem != null && !currentItem.is_read_only properties: materialProperties - containerId: base.currentItem != null ? base.currentItem.id : "" - currentMaterialNode: base.currentItem.container_node - - + containerId: currentItem != null ? currentItem.id : "" + currentMaterialNode: currentItem.container_node } QtObject diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 4a1a330ed6..cb5de685e2 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -13,7 +13,6 @@ import Cura 1.0 as Cura Item { id: materialList - width: materialScrollView.width - 17 height: childrenRect.height // Children @@ -21,179 +20,135 @@ Item Cura.MaterialBrandsModel { id: materialsModel } Cura.FavoriteMaterialsModel { id: favoriteMaterialsModel } Cura.GenericMaterialsModel { id: genericMaterialsModel } + + property var currentType: null + property var currentBrand: null + property var expandedBrands: UM.Preferences.getValue("cura/expanded_brands").split(";") + property var expandedTypes: UM.Preferences.getValue("cura/expanded_types").split(";") + + // Store information about which parts of the tree are expanded + function persistExpandedCategories() + { + UM.Preferences.setValue("cura/expanded_brands", materialList.expandedBrands.join(";")) + UM.Preferences.setValue("cura/expanded_types", materialList.expandedTypes.join(";")) + } + + // Expand the list of materials in order to select the current material + function expandActiveMaterial(search_root_id) + { + if (search_root_id == "") + { + // When this happens it means that the information of one of the materials has changed, so the model + // was updated and the list has to highlight the current item. + var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id + search_root_id = currentItemId + } + for (var material_idx = 0; material_idx < genericMaterialsModel.rowCount(); material_idx++) + { + var material = genericMaterialsModel.getItem(material_idx) + if (material.root_material_id == search_root_id) + { + if (materialList.expandedBrands.indexOf("Generic") == -1) + { + materialList.expandedBrands.push("Generic") + } + materialList.currentBrand = "Generic" + base.currentItem = material + persistExpandedCategories() + return true + } + } + for (var brand_idx = 0; brand_idx < materialsModel.rowCount(); brand_idx++) + { + var brand = materialsModel.getItem(brand_idx) + var types_model = brand.material_types + for (var type_idx = 0; type_idx < types_model.rowCount(); type_idx++) + { + var type = types_model.getItem(type_idx) + var colors_model = type.colors + for (var material_idx = 0; material_idx < colors_model.rowCount(); material_idx++) + { + var material = colors_model.getItem(material_idx) + if (material.root_material_id == search_root_id) + { + if (materialList.expandedBrands.indexOf(brand.name) == -1) + { + materialList.expandedBrands.push(brand.name) + } + materialList.currentBrand = brand.name + if (materialList.expandedTypes.indexOf(brand.name + "_" + type.name) == -1) + { + materialList.expandedTypes.push(brand.name + "_" + type.name) + } + materialList.currentType = brand.name + "_" + type.name + base.currentItem = material + persistExpandedCategories() + return true + } + } + } + } + return false + } + + function updateAfterModelChanges() + { + var correctlyExpanded = materialList.expandActiveMaterial(base.newRootMaterialIdToSwitchTo) + if (correctlyExpanded) + { + if (base.toActivateNewMaterial) + { + var position = Cura.ExtruderManager.activeExtruderIndex + Cura.MachineManager.setMaterial(position, base.currentItem.container_node) + } + base.newRootMaterialIdToSwitchTo = "" + base.toActivateNewMaterial = false + } + } + + Connections + { + target: materialsModel + onItemsChanged: updateAfterModelChanges() + } + + Connections + { + target: genericMaterialsModel + onItemsChanged: updateAfterModelChanges() + } + Column { - Rectangle - { - property var expanded: true + width: materialList.width + height: childrenRect.height - id: favorites_section - height: childrenRect.height - width: materialList.width - Rectangle - { - id: favorites_header_background - color: UM.Theme.getColor("favorites_header_bar") - anchors.fill: favorites_header - } - Row - { - id: favorites_header - Label - { - id: favorites_name - text: "Favorites" - height: UM.Theme.getSize("favorites_row").height - width: materialList.width - UM.Theme.getSize("favorites_button").width - verticalAlignment: Text.AlignVCenter - leftPadding: 4 - } - Button - { - text: "" - implicitWidth: UM.Theme.getSize("favorites_button").width - implicitHeight: UM.Theme.getSize("favorites_button").height - UM.RecolorImage { - anchors - { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height - color: "black" - source: favorites_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - } - style: ButtonStyle - { - background: Rectangle - { - anchors.fill: parent - color: "transparent" - } - } - } - } - MouseArea - { - anchors.fill: favorites_header - onPressed: - { - favorites_section.expanded = !favorites_section.expanded - } - } - Column - { - anchors.top: favorites_header.bottom - anchors.left: parent.left - width: materialList.width - height: favorites_section.expanded ? childrenRect.height : 0 - visible: favorites_section.expanded - Repeater - { - model: favoriteMaterialsModel - delegate: MaterialsSlot { - material: model - } - } - } - } - Rectangle + MaterialsBrandSection { - property var expanded: base.collapsed_brands.indexOf("Generic") > -1 - - id: generic_section - height: childrenRect.height - width: materialList.width - Rectangle - { - id: generic_header_background - color: UM.Theme.getColor("favorites_header_bar") - anchors.fill: generic_header - } - Row - { - id: generic_header - Label - { - id: generic_name - text: "Generic" - height: UM.Theme.getSize("favorites_row").height - width: materialList.width - UM.Theme.getSize("favorites_button").width - verticalAlignment: Text.AlignVCenter - leftPadding: 4 - } - Button - { - text: "" - implicitWidth: UM.Theme.getSize("favorites_button").width - implicitHeight: UM.Theme.getSize("favorites_button").height - UM.RecolorImage { - anchors - { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height - color: "black" - source: generic_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - } - style: ButtonStyle - { - background: Rectangle - { - anchors.fill: parent - color: "transparent" - } - } - } - } - MouseArea - { - anchors.fill: generic_header - onPressed: - { - const i = base.collapsed_brands.indexOf("Generic") - if (i > -1) - { - // Remove it - base.collapsed_brands.splice(i, 1) - generic_section.expanded = false - } - else - { - // Add it - base.collapsed_brands.push("Generic") - generic_section.expanded = true - } - } - } - Column - { - anchors.top: generic_header.bottom - width: materialList.width - anchors.left: parent.left - height: generic_section.expanded ? childrenRect.height : 0 - visible: generic_section.expanded - Repeater - { - model: genericMaterialsModel - delegate: MaterialsSlot { - material: model - } - } - } + id: favoriteSection + sectionName: "Favorites" + elementsModel: favoriteMaterialsModel + hasMaterialTypes: false } + + MaterialsBrandSection + { + id: genericSection + sectionName: "Generic" + elementsModel: genericMaterialsModel + hasMaterialTypes: false + } + Repeater { - id: brand_list model: materialsModel - delegate: MaterialsBrandSection {} + delegate: MaterialsBrandSection + { + id: brandSection + sectionName: model.name + elementsModel: model.material_types + hasMaterialTypes: true + } } } } \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml index 0b81df5fa1..a00a2340cd 100644 --- a/resources/qml/Preferences/Materials/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -17,96 +17,38 @@ Item // Keep PreferencesDialog happy property var resetEnabled: false property var currentItem: null + + property var hasCurrentItem: base.currentItem != null property var isCurrentItemActivated: { - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; - return base.currentItem.root_material_id == root_material_id; + if (!hasCurrentItem) + { + return false + } + const extruder_position = Cura.ExtruderManager.activeExtruderIndex + const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position] + return base.currentItem.root_material_id == root_material_id } property string newRootMaterialIdToSwitchTo: "" property bool toActivateNewMaterial: false - // TODO: Save these to preferences - property var collapsed_brands: [] - property var collapsed_types: [] + property var extruder_position: Cura.ExtruderManager.activeExtruderIndex + property var active_root_material_id: Cura.MachineManager.currentRootMaterialId[extruder_position] UM.I18nCatalog { id: catalog name: "cura" } - Cura.MaterialBrandsModel { id: materialsModel } - function findModelByRootId( search_root_id ) + // When loaded, try to select the active material in the tree + Component.onCompleted: materialListView.expandActiveMaterial(active_root_material_id) + + // Every time the selected item has changed, notify to the details panel + onCurrentItemChanged: { - for (var i = 0; i < materialsModel.rowCount(); i++) - { - var types_model = materialsModel.getItem(i).material_types; - for (var j = 0; j < types_model.rowCount(); j++) - { - var colors_model = types_model.getItem(j).colors; - for (var k = 0; k < colors_model.rowCount(); k++) - { - var material = colors_model.getItem(k); - if (material.root_material_id == search_root_id) - { - return material - } - } - } - } - } - Component.onCompleted: - { - // Select the activated material when this page shows up - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - const active_root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; - console.log("goign to search for", active_root_material_id) - base.currentItem = findModelByRootId(active_root_material_id) - } - - onCurrentItemChanged: { MaterialsDetailsPanel.currentItem = currentItem } - Connections - { - target: materialsModel - onItemsChanged: - { - var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id; - var position = Cura.ExtruderManager.activeExtruderIndex; - - // try to pick the currently selected item; it may have been moved - if (base.newRootMaterialIdToSwitchTo == "") - { - base.newRootMaterialIdToSwitchTo = currentItemId; - } - - for (var idx = 0; idx < materialsModel.rowCount(); ++idx) - { - var item = materialsModel.getItem(idx); - if (item.root_material_id == base.newRootMaterialIdToSwitchTo) - { - // Switch to the newly created profile if needed - materialListView.currentIndex = idx; - materialListView.activateDetailsWithIndex(materialListView.currentIndex); - if (base.toActivateNewMaterial) - { - Cura.MachineManager.setMaterial(position, item.container_node); - } - base.newRootMaterialIdToSwitchTo = ""; - base.toActivateNewMaterial = false; - return - } - } - - materialListView.currentIndex = 0; - materialListView.activateDetailsWithIndex(materialListView.currentIndex); - if (base.toActivateNewMaterial) - { - Cura.MachineManager.setMaterial(position, materialsModel.getItem(0).container_node); - } - base.newRootMaterialIdToSwitchTo = ""; - base.toActivateNewMaterial = false; - } + forceActiveFocus() + materialDetailsPanel.currentItem = currentItem } // Main layout @@ -146,8 +88,10 @@ Item { forceActiveFocus() - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - Cura.MachineManager.setMaterial(extruder_position, base.currentItem.container_node); + // Set the current material as the one to be activated (needed to force the UI update) + base.newRootMaterialIdToSwitchTo = base.currentItem.root_material_id + const extruder_position = Cura.ExtruderManager.activeExtruderIndex + Cura.MachineManager.setMaterial(extruder_position, base.currentItem.container_node) } } @@ -214,7 +158,7 @@ Item forceActiveFocus(); exportMaterialDialog.open(); } - enabled: currentItem != null + enabled: base.hasCurrentItem } } @@ -285,15 +229,20 @@ Item color: palette.light } - width: true ? (parent.width * 0.4) | 0 : parent.width + width: (parent.width * 0.4) | 0 frameVisible: true - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - MaterialsList {} + MaterialsList + { + id: materialListView + width: materialScrollView.viewport.width + } } MaterialsDetailsPanel { + id: materialDetailsPanel anchors { left: materialScrollView.right @@ -316,6 +265,8 @@ Item modality: Qt.ApplicationModal onYes: { + // Set the active material as the fallback. It will be selected when the current material is deleted + base.newRootMaterialIdToSwitchTo = base.active_root_material_id base.materialManager.removeMaterial(base.currentItem.container_node); } } diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index ab0dd23750..a474b52838 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -12,109 +12,109 @@ import Cura 1.0 as Cura Rectangle { - id: material_slot - property var material + id: materialSlot + property var material: null property var hovered: false - property var is_favorite: material.is_favorite + property var is_favorite: material != null ? material.is_favorite : false height: UM.Theme.getSize("favorites_row").height width: parent.width - color: base.currentItem == model ? UM.Theme.getColor("favorites_row_selected") : "transparent" - - Item + color: material != null ? (base.currentItem.root_material_id == material.root_material_id ? UM.Theme.getColor("favorites_row_selected") : "transparent") : "transparent" + + Rectangle { + id: swatch + color: material != null ? material.color_code : "transparent" + border.width: UM.Theme.getSize("default_lining").width + border.color: "black" + width: UM.Theme.getSize("favorites_button_icon").width + height: UM.Theme.getSize("favorites_button_icon").height + anchors.verticalCenter: materialSlot.verticalCenter + anchors.left: materialSlot.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + } + Label + { + text: material != null ? material.brand + " " + material.name : "" + verticalAlignment: Text.AlignVCenter height: parent.height - width: parent.width - Rectangle + anchors.left: swatch.right + anchors.verticalCenter: materialSlot.verticalCenter + anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + } + MouseArea + { + anchors.fill: parent + onClicked: { - id: swatch - color: material.color_code - border.width: UM.Theme.getSize("default_lining").width - border.color: "black" - width: UM.Theme.getSize("favorites_button_icon").width - height: UM.Theme.getSize("favorites_button_icon").height - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width + materialList.currentBrand = material.brand + materialList.currentType = material.brand + "_" + material.material + base.currentItem = material } - Label + hoverEnabled: true + onEntered: { materialSlot.hovered = true } + onExited: { materialSlot.hovered = false } + } + Button + { + id: favorite_button + text: "" + implicitWidth: UM.Theme.getSize("favorites_button").width + implicitHeight: UM.Theme.getSize("favorites_button").height + visible: materialSlot.hovered || materialSlot.is_favorite || favorite_button.hovered + anchors { - text: material.brand + " " + material.name - verticalAlignment: Text.AlignVCenter - height: parent.height - anchors.left: swatch.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + right: materialSlot.right + verticalCenter: materialSlot.verticalCenter } - MouseArea + onClicked: { - anchors.fill: parent - onClicked: { base.currentItem = material } - hoverEnabled: true - onEntered: { material_slot.hovered = true } - onExited: { material_slot.hovered = false } - } - Button - { - id: favorite_button - text: "" - implicitWidth: UM.Theme.getSize("favorites_button").width - implicitHeight: UM.Theme.getSize("favorites_button").height - visible: material_slot.hovered || material_slot.is_favorite || favorite_button.hovered - anchors - { - right: parent.right - verticalCenter: parent.verticalCenter - } - onClicked: - { - if (material_slot.is_favorite) { - base.materialManager.removeFavorite(material.root_material_id) - material_slot.is_favorite = false - return - } - base.materialManager.addFavorite(material.root_material_id) - material_slot.is_favorite = true + if (materialSlot.is_favorite) { + base.materialManager.removeFavorite(material.root_material_id) + materialSlot.is_favorite = false return } - style: ButtonStyle + base.materialManager.addFavorite(material.root_material_id) + materialSlot.is_favorite = true + return + } + style: ButtonStyle + { + background: Rectangle { - background: Rectangle - { - anchors.fill: parent - color: "transparent" - } + anchors.fill: parent + color: "transparent" } - UM.RecolorImage { - anchors + } + UM.RecolorImage { + anchors + { + verticalCenter: favorite_button.verticalCenter + horizontalCenter: favorite_button.horizontalCenter + } + width: UM.Theme.getSize("favorites_button_icon").width + height: UM.Theme.getSize("favorites_button_icon").height + sourceSize.width: width + sourceSize.height: height + color: + { + if (favorite_button.hovered) { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter + return UM.Theme.getColor("primary_hover") } - width: UM.Theme.getSize("favorites_button_icon").width - height: UM.Theme.getSize("favorites_button_icon").height - sourceSize.width: width - sourceSize.height: height - color: + else { - if (favorite_button.hovered) + if (materialSlot.is_favorite) { - return UM.Theme.getColor("primary_hover") + return UM.Theme.getColor("primary") } else { - if (material_slot.is_favorite) - { - return UM.Theme.getColor("primary") - } - else - { - UM.Theme.getColor("text_inactive") - } + UM.Theme.getColor("text_inactive") } } - source: material_slot.is_favorite ? UM.Theme.getIcon("favorites_star_full") : UM.Theme.getIcon("favorites_star_empty") } + source: materialSlot.is_favorite ? UM.Theme.getIcon("favorites_star_full") : UM.Theme.getIcon("favorites_star_empty") } } } \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index 11bf2385e1..f62fc4ee16 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -13,14 +13,32 @@ import Cura 1.0 as Cura Rectangle { id: material_type_section - property var expanded: base.collapsed_types.indexOf(model.brand + "_" + model.name) > -1 - property var colors_model: model.colors + property var materialType + property var expanded: materialList.expandedTypes.indexOf(materialType.brand + "_" + materialType.name) > -1 + property var colorsModel: materialType.colors height: childrenRect.height width: parent.width Rectangle { id: material_type_header_background - color: UM.Theme.getColor("lining") + color: + { + if(!expanded && materialType.brand + "_" + materialType.name == materialList.currentType) + { + return UM.Theme.getColor("favorites_row_selected") + } + else + { + return "transparent" + } + } + width: parent.width + height: material_type_header.height + } + Rectangle + { + id: material_type_header_border + color: UM.Theme.getColor("favorites_header_bar") anchors.bottom: material_type_header.bottom anchors.left: material_type_header.left height: UM.Theme.getSize("default_lining").height @@ -29,17 +47,17 @@ Rectangle Row { id: material_type_header - width: parent.width - 8 + width: parent.width + leftPadding: UM.Theme.getSize("default_margin").width anchors { left: parent.left - leftMargin: 8 } Label { - text: model.name + text: materialType.name height: UM.Theme.getSize("favorites_row").height - width: parent.width - UM.Theme.getSize("favorites_button").width + width: parent.width - parent.leftPadding - UM.Theme.getSize("favorites_button").width id: material_type_name verticalAlignment: Text.AlignVCenter } @@ -76,19 +94,21 @@ Rectangle anchors.fill: material_type_header onPressed: { - const i = base.collapsed_types.indexOf(model.brand + "_" + model.name) + const identifier = materialType.brand + "_" + materialType.name; + const i = materialList.expandedTypes.indexOf(identifier) if (i > -1) { // Remove it - base.collapsed_types.splice(i, 1) + materialList.expandedTypes.splice(i, 1) material_type_section.expanded = false } else { // Add it - base.collapsed_types.push(model.brand + "_" + model.name) + materialList.expandedTypes.push(identifier) material_type_section.expanded = true } + UM.Preferences.setValue("cura/expanded_types", materialList.expandedTypes.join(";")); } } Column @@ -97,13 +117,22 @@ Rectangle visible: material_type_section.expanded width: parent.width anchors.top: material_type_header.bottom - anchors.left: parent.left Repeater { - model: colors_model - delegate: MaterialsSlot { + model: colorsModel + delegate: MaterialsSlot + { material: model } } } + + Connections + { + target: UM.Preferences + onPreferenceChanged: + { + expanded = materialList.expandedTypes.indexOf(materialType.brand + "_" + materialType.name) > -1 + } + } } \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index a03e5c48d7..56fa12877f 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -40,7 +40,7 @@ TabView { return "" } - var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentItem.container_node, true); + var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true); if (linkedMaterials.length == 0) { return "" @@ -191,6 +191,7 @@ TabView ReadOnlyTextField { id: colorLabel; + width: parent.width - colorSelector.width - parent.spacing text: properties.color_name; readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("color_name", properties.color_name, text) @@ -567,25 +568,25 @@ TabView // don't change when new name is the same if (old_name == new_name) { - return; + return } // update the values - base.materialManager.setMaterialName(base.currentMaterialNode, new_name); - materialProperties.name = new_name; + base.materialManager.setMaterialName(base.currentMaterialNode, new_name) + properties.name = new_name } // update the type of the material - function updateMaterialType (old_type, new_type) + function updateMaterialType(old_type, new_type) { - base.setMetaDataEntry("material", old_type, new_type); - materialProperties.material= new_type; + base.setMetaDataEntry("material", old_type, new_type) + properties.material = new_type } // update the brand of the material - function updateMaterialBrand (old_brand, new_brand) + function updateMaterialBrand(old_brand, new_brand) { - base.setMetaDataEntry("brand", old_brand, new_brand); - materialProperties.brand = new_brand; + base.setMetaDataEntry("brand", old_brand, new_brand) + properties.brand = new_brand } }