diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 02b708f58c..833f43e29c 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -67,9 +67,9 @@ from cura.Machines.Models.NozzleModel import NozzleModel from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel -from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel +from cura.Machines.Models.FavoriteMaterialsModel import FavoriteMaterialsModel from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel -from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel +from cura.Machines.Models.MaterialBrandsModel import MaterialBrandsModel from cura.Machines.Models.QualityManagementModel import QualityManagementModel from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel from cura.Machines.Models.MachineManagementModel import MachineManagementModel @@ -480,7 +480,9 @@ class CuraApplication(QtApplication): preferences.addPreference("view/filter_current_build_plate", False) preferences.addPreference("cura/sidebar_collapsed", False) - self._need_to_show_user_agreement = not self.getPreferences().getValue("general/accepted_user_agreement") + preferences.addPreference("cura/favorite_materials", ";".join([])) + + self._need_to_show_user_agreement = not preferences.getValue("general/accepted_user_agreement") for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin @@ -911,9 +913,9 @@ class CuraApplication(QtApplication): qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") + qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") - qmlRegisterType(BrandMaterialsModel, "Cura", 1, 0, "BrandMaterialsModel") - qmlRegisterType(MaterialManagementModel, "Cura", 1, 0, "MaterialManagementModel") + qmlRegisterType(MaterialBrandsModel, "Cura", 1, 0, "MaterialBrandsModel") qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") qmlRegisterType(MachineManagementModel, "Cura", 1, 0, "MachineManagementModel") diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index d5a7d5d089..1463f2e40e 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -4,6 +4,7 @@ from collections import defaultdict, OrderedDict import copy import uuid +import json from typing import Dict, Optional, TYPE_CHECKING from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot @@ -38,7 +39,8 @@ if TYPE_CHECKING: # class MaterialManager(QObject): - materialsUpdated = pyqtSignal() # Emitted whenever the material lookup tables are updated. + 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) @@ -75,6 +77,8 @@ class MaterialManager(QObject): self._container_registry.containerAdded.connect(self._onContainerMetadataChanged) self._container_registry.containerRemoved.connect(self._onContainerMetadataChanged) + self._favorites = set() + def initialize(self): # Find all materials and put them in a matrix for quick search. material_metadatas = {metadata["id"]: metadata for metadata in @@ -194,6 +198,11 @@ class MaterialManager(QObject): self.materialsUpdated.emit() + favorites = self._application.getPreferences().getValue("cura/favorite_materials") + for item in favorites.split(";"): + self._favorites.add(item) + self.favoritesUpdated.emit() + def __addMaterialMetadataIntoLookupTree(self, material_metadata: dict) -> None: material_id = material_metadata["id"] @@ -608,3 +617,25 @@ class MaterialManager(QObject): new_base_id = new_id, new_metadata = new_metadata) return new_id + + @pyqtSlot(str) + def addFavorite(self, root_material_id: str): + self._favorites.add(root_material_id) + self.favoritesUpdated.emit() + + # Ensure all settings are saved. + self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites))) + self._application.saveSettings() + + @pyqtSlot(str) + def removeFavorite(self, root_material_id: str): + self._favorites.remove(root_material_id) + self.favoritesUpdated.emit() + + # Ensure all settings are saved. + self._application.getPreferences().setValue("cura/favorite_materials", ";".join(list(self._favorites))) + self._application.saveSettings() + + @pyqtSlot() + def getFavorites(self): + return self._favorites \ No newline at end of file diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 4759c8b5b0..1b20e1188c 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -2,45 +2,63 @@ # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty - -from UM.Application import Application from UM.Qt.ListModel import ListModel -# -# This is the base model class for GenericMaterialsModel and BrandMaterialsModel -# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately. -# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top -# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu -# +## This is the base model class for GenericMaterialsModel and MaterialBrandsModel. +# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately. +# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top +# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu class BaseMaterialsModel(ListModel): - RootMaterialIdRole = Qt.UserRole + 1 - IdRole = Qt.UserRole + 2 - NameRole = Qt.UserRole + 3 - BrandRole = Qt.UserRole + 4 - MaterialRole = Qt.UserRole + 5 - ColorRole = Qt.UserRole + 6 - ContainerNodeRole = Qt.UserRole + 7 extruderPositionChanged = pyqtSignal() def __init__(self, parent = None): super().__init__(parent) - self._application = Application.getInstance() - self._machine_manager = self._application.getMachineManager() - self.addRoleName(self.RootMaterialIdRole, "root_material_id") - self.addRoleName(self.IdRole, "id") - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.BrandRole, "brand") - self.addRoleName(self.MaterialRole, "material") - self.addRoleName(self.ColorRole, "color_name") - self.addRoleName(self.ContainerNodeRole, "container_node") + from cura.CuraApplication import CuraApplication + + self._application = CuraApplication.getInstance() + + # Make these managers available to all material models + self._container_registry = self._application.getInstance().getContainerRegistry() + self._machine_manager = self._application.getMachineManager() + self._material_manager = self._application.getMaterialManager() + + # Update the stack and the model data when the machine changes + self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) + + # Update this model when switching machines + self._machine_manager.activeStackChanged.connect(self._update) + + # 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") + self.addRoleName(Qt.UserRole + 4, "name") + self.addRoleName(Qt.UserRole + 5, "brand") + self.addRoleName(Qt.UserRole + 6, "description") + self.addRoleName(Qt.UserRole + 7, "material") + self.addRoleName(Qt.UserRole + 8, "color_name") + self.addRoleName(Qt.UserRole + 9, "color_code") + self.addRoleName(Qt.UserRole + 10, "density") + self.addRoleName(Qt.UserRole + 11, "diameter") + self.addRoleName(Qt.UserRole + 12, "approximate_diameter") + self.addRoleName(Qt.UserRole + 13, "adhesion_info") + self.addRoleName(Qt.UserRole + 14, "is_read_only") + self.addRoleName(Qt.UserRole + 15, "container_node") + self.addRoleName(Qt.UserRole + 16, "is_favorite") self._extruder_position = 0 self._extruder_stack = None - # Update the stack and the model data when the machine changes - self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) + + self._available_materials = None + self._favorite_ids = None def _updateExtruderStack(self): global_stack = self._machine_manager.activeMachine @@ -65,8 +83,55 @@ class BaseMaterialsModel(ListModel): def extruderPosition(self) -> int: return self._extruder_position - # - # This is an abstract method that needs to be implemented by - # + ## This is an abstract method that needs to be implemented by the specific + # models themselves. def _update(self): pass + + ## This method is used by all material models in the beginning of the + # _update() method in order to prevent errors. It's the same in all models + # so it's placed here for easy access. + def _canUpdate(self): + global_stack = self._machine_manager.activeMachine + + if global_stack is None: + return False + + extruder_position = str(self._extruder_position) + + if extruder_position not in global_stack.extruders: + return False + + extruder_stack = global_stack.extruders[extruder_position] + + self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) + if self._available_materials is None: + return False + + return True + + ## This is another convenience function which is shared by all material + # models so it's put here to avoid having so much duplicated code. + def _createMaterialItem(self, root_material_id, container_node): + metadata = container_node.metadata + item = { + "root_material_id": root_material_id, + "id": metadata["id"], + "container_id": metadata["id"], # TODO: Remove duplicate in material manager qml + "GUID": metadata["GUID"], + "name": metadata["name"], + "brand": metadata["brand"], + "description": metadata["description"], + "material": metadata["material"], + "color_name": metadata["color_name"], + "color_code": metadata.get("color_code", ""), + "density": metadata.get("properties", {}).get("density", ""), + "diameter": metadata.get("properties", {}).get("diameter", ""), + "approximate_diameter": metadata["approximate_diameter"], + "adhesion_info": metadata["adhesion_info"], + "is_read_only": self._container_registry.isReadOnly(metadata["id"]), + "container_node": container_node, + "is_favorite": root_material_id in self._favorite_ids + } + return item + diff --git a/cura/Machines/Models/BrandMaterialsModel.py b/cura/Machines/Models/BrandMaterialsModel.py deleted file mode 100644 index ad48b3ea21..0000000000 --- a/cura/Machines/Models/BrandMaterialsModel.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty - -from UM.Qt.ListModel import ListModel -from UM.Logger import Logger -from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel - - -# -# This is an intermediate model to group materials with different colours for a same brand and type. -# -class MaterialsModelGroupedByType(ListModel): - NameRole = Qt.UserRole + 1 - ColorsRole = Qt.UserRole + 2 - - def __init__(self, parent = None): - super().__init__(parent) - - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.ColorsRole, "colors") - - -# -# This model is used to show branded materials in the material drop down menu. -# The structure of the menu looks like this: -# Brand -> Material Type -> list of materials -# -# To illustrate, a branded material menu may look like this: -# Ultimaker -> PLA -> Yellow PLA -# -> Black PLA -# -> ... -# -> ABS -> White ABS -# ... -# -class BrandMaterialsModel(ListModel): - NameRole = Qt.UserRole + 1 - MaterialsRole = Qt.UserRole + 2 - - extruderPositionChanged = pyqtSignal() - - def __init__(self, parent = None): - super().__init__(parent) - - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.MaterialsRole, "materials") - - self._extruder_position = 0 - self._extruder_stack = None - - from cura.CuraApplication import CuraApplication - self._machine_manager = CuraApplication.getInstance().getMachineManager() - self._extruder_manager = CuraApplication.getInstance().getExtruderManager() - self._material_manager = CuraApplication.getInstance().getMaterialManager() - - self._machine_manager.globalContainerChanged.connect(self._updateExtruderStack) - self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines. - self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes. - self._update() - - def _updateExtruderStack(self): - global_stack = self._machine_manager.activeMachine - if global_stack is None: - return - - if self._extruder_stack is not None: - self._extruder_stack.pyqtContainersChanged.disconnect(self._update) - self._extruder_stack = global_stack.extruders.get(str(self._extruder_position)) - if self._extruder_stack is not None: - self._extruder_stack.pyqtContainersChanged.connect(self._update) - # Force update the model when the extruder stack changes - self._update() - - def setExtruderPosition(self, position: int): - if self._extruder_stack is None or self._extruder_position != position: - self._extruder_position = position - self._updateExtruderStack() - self.extruderPositionChanged.emit() - - @pyqtProperty(int, fset=setExtruderPosition, notify=extruderPositionChanged) - def extruderPosition(self) -> int: - return self._extruder_position - - def _update(self): - Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) - global_stack = self._machine_manager.activeMachine - if global_stack is None: - self.setItems([]) - return - extruder_position = str(self._extruder_position) - if extruder_position not in global_stack.extruders: - self.setItems([]) - return - extruder_stack = global_stack.extruders[str(self._extruder_position)] - - available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, - extruder_stack) - if available_material_dict is None: - self.setItems([]) - return - - brand_item_list = [] - brand_group_dict = {} - for root_material_id, container_node in available_material_dict.items(): - metadata = container_node.metadata - brand = metadata["brand"] - # Only add results for generic materials - if brand.lower() == "generic": - continue - - # Do not include the materials from a to-be-removed package - if bool(metadata.get("removed", False)): - continue - - if brand not in brand_group_dict: - brand_group_dict[brand] = {} - - material_type = metadata["material"] - if material_type not in brand_group_dict[brand]: - brand_group_dict[brand][material_type] = [] - - item = {"root_material_id": root_material_id, - "id": metadata["id"], - "name": metadata["name"], - "brand": metadata["brand"], - "material": metadata["material"], - "color_name": metadata["color_name"], - "container_node": container_node - } - brand_group_dict[brand][material_type].append(item) - - for brand, material_dict in brand_group_dict.items(): - brand_item = {"name": brand, - "materials": MaterialsModelGroupedByType(self)} - - material_type_item_list = [] - for material_type, material_list in material_dict.items(): - material_type_item = {"name": material_type, - "colors": BaseMaterialsModel(self)} - material_type_item["colors"].clear() - - # Sort materials by name - material_list = sorted(material_list, key = lambda x: x["name"].upper()) - material_type_item["colors"].setItems(material_list) - - material_type_item_list.append(material_type_item) - - # Sort material type by name - material_type_item_list = sorted(material_type_item_list, key = lambda x: x["name"].upper()) - brand_item["materials"].setItems(material_type_item_list) - - brand_item_list.append(brand_item) - - # Sort brand by name - brand_item_list = sorted(brand_item_list, key = lambda x: x["name"].upper()) - self.setItems(brand_item_list) diff --git a/cura/Machines/Models/FavoriteMaterialsModel.py b/cura/Machines/Models/FavoriteMaterialsModel.py new file mode 100644 index 0000000000..be3f0f605f --- /dev/null +++ b/cura/Machines/Models/FavoriteMaterialsModel.py @@ -0,0 +1,42 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Logger import Logger +from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel + +class FavoriteMaterialsModel(BaseMaterialsModel): + + def __init__(self, parent = None): + super().__init__(parent) + self._update() + + def _update(self): + + # Perform standard check and reset if the check fails + if not self._canUpdate(): + self.setItems([]) + return + + # Get updated list of favorites + self._favorite_ids = self._material_manager.getFavorites() + + item_list = [] + + for root_material_id, container_node in self._available_materials.items(): + metadata = container_node.metadata + + # Do not include the materials from a to-be-removed package + if bool(metadata.get("removed", False)): + continue + + # Only add results for favorite materials + if root_material_id not in self._favorite_ids: + continue + + item = self._createMaterialItem(root_material_id, container_node) + item_list.append(item) + + # Sort the item list alphabetically by name + item_list = sorted(item_list, key = lambda d: d["brand"].upper()) + + self.setItems(item_list) diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py index f14b039c91..27e6fdfd7c 100644 --- a/cura/Machines/Models/GenericMaterialsModel.py +++ b/cura/Machines/Models/GenericMaterialsModel.py @@ -4,63 +4,39 @@ from UM.Logger import Logger from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel - class GenericMaterialsModel(BaseMaterialsModel): def __init__(self, parent = None): super().__init__(parent) - - from cura.CuraApplication import CuraApplication - self._machine_manager = CuraApplication.getInstance().getMachineManager() - self._extruder_manager = CuraApplication.getInstance().getExtruderManager() - self._material_manager = CuraApplication.getInstance().getMaterialManager() - - self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines. - self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes. self._update() def _update(self): - Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) - global_stack = self._machine_manager.activeMachine - if global_stack is None: + # Perform standard check and reset if the check fails + if not self._canUpdate(): self.setItems([]) return - extruder_position = str(self._extruder_position) - if extruder_position not in global_stack.extruders: - self.setItems([]) - return - extruder_stack = global_stack.extruders[extruder_position] - available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, - extruder_stack) - if available_material_dict is None: - self.setItems([]) - return + # Get updated list of favorites + self._favorite_ids = self._material_manager.getFavorites() item_list = [] - for root_material_id, container_node in available_material_dict.items(): - metadata = container_node.metadata - # Only add results for generic materials - if metadata["brand"].lower() != "generic": - continue + for root_material_id, container_node in self._available_materials.items(): + metadata = container_node.metadata # Do not include the materials from a to-be-removed package if bool(metadata.get("removed", False)): continue - item = {"root_material_id": root_material_id, - "id": metadata["id"], - "name": metadata["name"], - "brand": metadata["brand"], - "material": metadata["material"], - "color_name": metadata["color_name"], - "container_node": container_node - } + # Only add results for generic materials + if metadata["brand"].lower() != "generic": + continue + + item = self._createMaterialItem(root_material_id, container_node) item_list.append(item) - # Sort the item list by material name alphabetically + # Sort the item list alphabetically by name item_list = sorted(item_list, key = lambda d: d["name"].upper()) self.setItems(item_list) diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py new file mode 100644 index 0000000000..3f917abb16 --- /dev/null +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -0,0 +1,107 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty +from UM.Qt.ListModel import ListModel +from UM.Logger import Logger +from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel + +class MaterialTypesModel(ListModel): + + def __init__(self, parent = None): + super().__init__(parent) + + self.addRoleName(Qt.UserRole + 1, "name") + self.addRoleName(Qt.UserRole + 2, "colors") + +class MaterialBrandsModel(BaseMaterialsModel): + + extruderPositionChanged = pyqtSignal() + + def __init__(self, parent = None): + super().__init__(parent) + + self.addRoleName(Qt.UserRole + 1, "name") + self.addRoleName(Qt.UserRole + 2, "material_types") + + self._update() + + def _update(self): + + # Perform standard check and reset if the check fails + if not self._canUpdate(): + self.setItems([]) + return + + # Get updated list of favorites + self._favorite_ids = self._material_manager.getFavorites() + + brand_item_list = [] + brand_group_dict = {} + + # Part 1: Generate the entire tree of brands -> material types -> spcific materials + for root_material_id, container_node in self._available_materials.items(): + metadata = container_node.metadata + + # Do not include the materials from a to-be-removed package + if bool(metadata.get("removed", False)): + continue + + # Add brands we haven't seen yet to the dict, skipping generics + brand = metadata["brand"] + if brand.lower() == "generic": + continue + if brand not in brand_group_dict: + brand_group_dict[brand] = {} + + # Add material types we haven't seen yet to the dict + material_type = metadata["material"] + if material_type not in brand_group_dict[brand]: + brand_group_dict[brand][material_type] = [] + + # Now handle the individual materials + item = self._createMaterialItem(root_material_id, container_node) + brand_group_dict[brand][material_type].append(item) + + # Part 2: Organize the tree into models + # + # Normally, the structure of the menu looks like this: + # Brand -> Material Type -> Specific Material + # + # To illustrate, a branded material menu may look like this: + # Ultimaker ┳ PLA ┳ Yellow PLA + # ┃ ┣ Black PLA + # ┃ ┗ ... + # ┃ + # ┗ ABS ┳ White ABS + # ┗ ... + for brand, material_dict in brand_group_dict.items(): + + material_type_item_list = [] + brand_item = { + "name": brand, + "material_types": MaterialTypesModel(self) + } + + for material_type, material_list in material_dict.items(): + material_type_item = { + "name": material_type, + "colors": BaseMaterialsModel(self) + } + material_type_item["colors"].clear() + + # Sort materials by name + material_list = sorted(material_list, key = lambda x: x["name"].upper()) + material_type_item["colors"].setItems(material_list) + + material_type_item_list.append(material_type_item) + + # Sort material type by name + material_type_item_list = sorted(material_type_item_list, key = lambda x: x["name"].upper()) + brand_item["material_types"].setItems(material_type_item_list) + + brand_item_list.append(brand_item) + + # Sort brand by name + brand_item_list = sorted(brand_item_list, key = lambda x: x["name"].upper()) + self.setItems(brand_item_list) diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py deleted file mode 100644 index 46e9cb887a..0000000000 --- a/cura/Machines/Models/MaterialManagementModel.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from PyQt5.QtCore import Qt - -from UM.Logger import Logger -from UM.Qt.ListModel import ListModel - - -# -# This model is for the Material management page. -# -class MaterialManagementModel(ListModel): - RootMaterialIdRole = Qt.UserRole + 1 - DisplayNameRole = Qt.UserRole + 2 - BrandRole = Qt.UserRole + 3 - MaterialTypeRole = Qt.UserRole + 4 - ColorNameRole = Qt.UserRole + 5 - ColorCodeRole = Qt.UserRole + 6 - ContainerNodeRole = Qt.UserRole + 7 - ContainerIdRole = Qt.UserRole + 8 - - DescriptionRole = Qt.UserRole + 9 - AdhesionInfoRole = Qt.UserRole + 10 - ApproximateDiameterRole = Qt.UserRole + 11 - GuidRole = Qt.UserRole + 12 - DensityRole = Qt.UserRole + 13 - DiameterRole = Qt.UserRole + 14 - IsReadOnlyRole = Qt.UserRole + 15 - - def __init__(self, parent = None): - super().__init__(parent) - - self.addRoleName(self.RootMaterialIdRole, "root_material_id") - self.addRoleName(self.DisplayNameRole, "name") - self.addRoleName(self.BrandRole, "brand") - self.addRoleName(self.MaterialTypeRole, "material") - self.addRoleName(self.ColorNameRole, "color_name") - self.addRoleName(self.ColorCodeRole, "color_code") - self.addRoleName(self.ContainerNodeRole, "container_node") - self.addRoleName(self.ContainerIdRole, "container_id") - - self.addRoleName(self.DescriptionRole, "description") - self.addRoleName(self.AdhesionInfoRole, "adhesion_info") - self.addRoleName(self.ApproximateDiameterRole, "approximate_diameter") - self.addRoleName(self.GuidRole, "guid") - self.addRoleName(self.DensityRole, "density") - self.addRoleName(self.DiameterRole, "diameter") - self.addRoleName(self.IsReadOnlyRole, "is_read_only") - - from cura.CuraApplication import CuraApplication - self._container_registry = CuraApplication.getInstance().getContainerRegistry() - self._machine_manager = CuraApplication.getInstance().getMachineManager() - self._extruder_manager = CuraApplication.getInstance().getExtruderManager() - self._material_manager = CuraApplication.getInstance().getMaterialManager() - - self._machine_manager.globalContainerChanged.connect(self._update) - self._extruder_manager.activeExtruderChanged.connect(self._update) - self._material_manager.materialsUpdated.connect(self._update) - - self._update() - - def _update(self): - Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) - - global_stack = self._machine_manager.activeMachine - if global_stack is None: - self.setItems([]) - return - active_extruder_stack = self._machine_manager.activeStack - - available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, - active_extruder_stack) - if available_material_dict is None: - self.setItems([]) - return - - material_list = [] - for root_material_id, container_node in available_material_dict.items(): - keys_to_fetch = ("name", - "brand", - "material", - "color_name", - "color_code", - "description", - "adhesion_info", - "approximate_diameter",) - - item = {"root_material_id": container_node.metadata["base_file"], - "container_node": container_node, - "guid": container_node.metadata["GUID"], - "container_id": container_node.metadata["id"], - "density": container_node.metadata.get("properties", {}).get("density", ""), - "diameter": container_node.metadata.get("properties", {}).get("diameter", ""), - "is_read_only": self._container_registry.isReadOnly(container_node.metadata["id"]), - } - - for key in keys_to_fetch: - item[key] = container_node.metadata.get(key, "") - - material_list.append(item) - - material_list = sorted(material_list, key = lambda k: (k["brand"].upper(), k["name"].upper())) - self.setItems(material_list) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index df3bec0827..cb2776429f 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -259,8 +259,12 @@ class QualityManager(QObject): # 2. machine-nozzle-and-material-specific qualities if exist # 3. machine-nozzle-specific qualities if exist # 4. machine-material-specific qualities if exist - # 5. machine-specific qualities if exist - # 6. generic qualities if exist + # 5. machine-specific global qualities if exist, otherwise generic global qualities + # NOTE: We DO NOT fail back to generic global qualities if machine-specific global qualities exist. + # This is because when a machine defines its own global qualities such as Normal, Fine, etc., + # it is intended to maintain those specific qualities ONLY. If we still fail back to the generic + # global qualities, there can be unimplemented quality types e.g. "coarse", and this is not + # correct. # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch # qualities from there. @@ -289,7 +293,13 @@ class QualityManager(QObject): addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0) - nodes_to_check += [machine_node, default_machine_node] + # The last fall back will be the global qualities (either from the machine-specific node or the generic + # node), but we only use one. For details see the overview comments above. + if machine_node.quality_type_map: + nodes_to_check += [machine_node] + else: + nodes_to_check += [default_machine_node] + for node in nodes_to_check: if node and node.quality_type_map: if has_variant_materials: diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 21e2040dc1..8527da1b8a 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -300,7 +300,7 @@ class PrintInformation(QObject): def _updateJobName(self): if self._base_name == "": - self._job_name = "unnamed" + self._job_name = "Untitled" self._is_user_specified_job_name = False self.jobNameChanged.emit() return diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index 5d15123017..5e41975752 100644 --- a/plugins/3MFReader/plugin.json +++ b/plugins/3MFReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading 3MF files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 22d18b2cf1..9ec4fb0c20 100644 --- a/plugins/3MFWriter/plugin.json +++ b/plugins/3MFWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for writing 3MF files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ChangeLogPlugin/plugin.json b/plugins/ChangeLogPlugin/plugin.json index e9414b9b71..e09a08564a 100644 --- a/plugins/ChangeLogPlugin/plugin.json +++ b/plugins/ChangeLogPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Shows changes since latest checked version.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 0ebcafdbb2..1295390c22 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -220,8 +220,10 @@ class StartSliceJob(Job): stack = global_stack skip_group = False for node in group: + # Only check if the printing extruder is enabled for printing meshes + is_non_printing_mesh = node.callDecoration("evaluateIsNonPrintingMesh") extruder_position = node.callDecoration("getActiveExtruderPosition") - if not extruders_enabled[extruder_position]: + if not is_non_printing_mesh and not extruders_enabled[extruder_position]: skip_group = True has_model_with_disabled_extruders = True associated_disabled_extruders.add(extruder_position) diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index e5df06f228..111698d8d1 100644 --- a/plugins/CuraEngineBackend/plugin.json +++ b/plugins/CuraEngineBackend/plugin.json @@ -2,7 +2,7 @@ "name": "CuraEngine Backend", "author": "Ultimaker B.V.", "description": "Provides the link to the CuraEngine slicing backend.", - "api": 4, + "api": 5, "version": "1.0.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index 004a1ade4d..66a2a6a56b 100644 --- a/plugins/CuraProfileReader/plugin.json +++ b/plugins/CuraProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing Cura profiles.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index d9accce770..16c8c34152 100644 --- a/plugins/CuraProfileWriter/plugin.json +++ b/plugins/CuraProfileWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for exporting Cura profiles.", - "api": 4, + "api": 5, "i18n-catalog":"cura" } diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index d6a9f9fbd7..cbbd41e420 100644 --- a/plugins/FirmwareUpdateChecker/plugin.json +++ b/plugins/FirmwareUpdateChecker/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Checks for firmware updates.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index e9f14724e0..3bd6a4097d 100644 --- a/plugins/GCodeGzReader/plugin.json +++ b/plugins/GCodeGzReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Reads g-code from a compressed archive.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index 9774e9a25c..4c6497317b 100644 --- a/plugins/GCodeGzWriter/plugin.json +++ b/plugins/GCodeGzWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Writes g-code to a compressed archive.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index f8f7d4c291..9677628c85 100644 --- a/plugins/GCodeProfileReader/plugin.json +++ b/plugins/GCodeProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing profiles from g-code files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index f72a8cc12c..75b4d0cd4f 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -3,6 +3,6 @@ "author": "Victor Larchenko", "version": "1.0.0", "description": "Allows loading and displaying G-code files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index 5fcb1a3bd7..3bbbab8b95 100644 --- a/plugins/GCodeWriter/plugin.json +++ b/plugins/GCodeWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Writes g-code to a file.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index 2752c6e8f4..08195863e8 100644 --- a/plugins/ImageReader/plugin.json +++ b/plugins/ImageReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Enables ability to generate printable geometry from 2D image files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 2dc71511a9..179f5444e0 100644 --- a/plugins/LegacyProfileReader/plugin.json +++ b/plugins/LegacyProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing profiles from legacy Cura versions.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 703a145deb..571658e40a 100644 --- a/plugins/MachineSettingsAction/plugin.json +++ b/plugins/MachineSettingsAction/plugin.json @@ -3,6 +3,6 @@ "author": "fieldOfView", "version": "1.0.0", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index a9190adcaa..3753c0cc88 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -2,7 +2,7 @@ "name": "Model Checker", "author": "Ultimaker B.V.", "version": "0.1", - "api": 4, + "api": 5, "description": "Checks models and print configuration for possible printing issues and give suggestions.", "i18n-catalog": "cura" } diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json index cb3f55a80d..88b53840e0 100644 --- a/plugins/MonitorStage/plugin.json +++ b/plugins/MonitorStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a monitor stage in Cura.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index 3254662d33..15fde63387 100644 --- a/plugins/PerObjectSettingsTool/plugin.json +++ b/plugins/PerObjectSettingsTool/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the Per Model Settings.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index da971a8e61..b28a028325 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -1,5 +1,6 @@ -# Copyright (c) 2015 Jaime van Kessel, Ultimaker B.V. +# Copyright (c) 2018 Jaime van Kessel, Ultimaker B.V. # The PostProcessingPlugin is released under the terms of the AGPLv3 or higher. + from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot from UM.PluginRegistry import PluginRegistry @@ -260,6 +261,9 @@ class PostProcessingPlugin(QObject, Extension): # Create the plugin dialog component path = os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml") self._view = Application.getInstance().createQmlComponent(path, {"manager": self}) + if self._view is None: + Logger.log("e", "Not creating PostProcessing button near save button because the QML component failed to be created.") + return Logger.log("d", "Post processing view created.") # Create the save button component @@ -269,6 +273,9 @@ class PostProcessingPlugin(QObject, Extension): def showPopup(self): if self._view is None: self._createView() + if self._view is None: + Logger.log("e", "Not creating PostProcessing window since the QML component failed to be created.") + return self._view.show() ## Property changed: trigger re-slice diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index ebfef8145a..fea061e93b 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -2,7 +2,7 @@ "name": "Post Processing", "author": "Ultimaker", "version": "2.2", - "api": 4, + "api": 5, "description": "Extension that allows for user created scripts for post processing", "catalog": "cura" } \ No newline at end of file diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json index 4fd55e955e..f0464313c7 100644 --- a/plugins/PrepareStage/plugin.json +++ b/plugins/PrepareStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a prepare stage in Cura.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index df11644256..36bb9ae186 100644 --- a/plugins/RemovableDriveOutputDevice/plugin.json +++ b/plugins/RemovableDriveOutputDevice/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Provides removable drive hotplugging and writing support.", "version": "1.0.0", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index 0e7bec0626..93df98068f 100644 --- a/plugins/SimulationView/plugin.json +++ b/plugins/SimulationView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the Simulation view.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index d1c643266b..939e5ff235 100644 --- a/plugins/SliceInfoPlugin/plugin.json +++ b/plugins/SliceInfoPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Submits anonymous slice info. Can be disabled through preferences.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index 6d6bda96f0..e70ec224dd 100644 --- a/plugins/SolidView/plugin.json +++ b/plugins/SolidView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a normal solid mesh view.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index 5ccb639913..7af35e0fb5 100644 --- a/plugins/SupportEraser/plugin.json +++ b/plugins/SupportEraser/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Creates an eraser mesh to block the printing of support in certain places", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json index 12d4042b6b..2557185524 100644 --- a/plugins/Toolbox/plugin.json +++ b/plugins/Toolbox/plugin.json @@ -2,6 +2,6 @@ "name": "Toolbox", "author": "Ultimaker B.V.", "version": "1.0.0", - "api": 4, + "api": 5, "description": "Find, manage and install new Cura packages." } diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index 7d10b89ad4..ab590353e0 100644 --- a/plugins/UFPWriter/plugin.json +++ b/plugins/UFPWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for writing Ultimaker Format Packages.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index 127b3c35bd..b5b80a3010 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -364,7 +364,6 @@ Cura.MachineAction { id: addressField width: parent.width - maximumLength: 40 validator: RegExpValidator { regExp: /[a-zA-Z0-9\.\-\_]*/ diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index e7b59fadd6..d415338374 100644 --- a/plugins/UM3NetworkPrinting/plugin.json +++ b/plugins/UM3NetworkPrinting/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Manages network connections to Ultimaker 3 printers.", "version": "1.0.0", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 27e07c45b2..3484c8a48a 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -2,7 +2,7 @@ "name": "USB printing", "author": "Ultimaker B.V.", "version": "1.0.0", - "api": 4, + "api": 5, "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" } diff --git a/plugins/UltimakerMachineActions/plugin.json b/plugins/UltimakerMachineActions/plugin.json index 57b3e6bc8f..b60c7df88e 100644 --- a/plugins/UltimakerMachineActions/plugin.json +++ b/plugins/UltimakerMachineActions/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/UserAgreement/plugin.json b/plugins/UserAgreement/plugin.json index b10abc5640..50a2aa0441 100644 --- a/plugins/UserAgreement/plugin.json +++ b/plugins/UserAgreement/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Ask the user once if he/she agrees with our license.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index 79115f931e..463fcdc941 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index d213042ad2..e7a0b1c559 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index 759b6368fd..3029539887 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index 3c3d7fff8c..225da67235 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index 3df84ff7e6..9a139851ec 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index d80b820976..cf42b3f6cd 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index fbce09c807..f9cc968dae 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index 164b79d504..f5ba7235d1 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json index 1059ca3e57..c07ae31c0a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.4 to Cura 4.0.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index f18c7f033d..9ee09e43df 100644 --- a/plugins/X3DReader/plugin.json +++ b/plugins/X3DReader/plugin.json @@ -3,6 +3,6 @@ "author": "Seva Alekseyev", "version": "0.5.0", "description": "Provides support for reading X3D files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 4e89690c13..576dec4656 100644 --- a/plugins/XRayView/plugin.json +++ b/plugins/XRayView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the X-Ray view.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index 17056dcb3e..4b2901c375 100644 --- a/plugins/XmlMaterialProfile/plugin.json +++ b/plugins/XmlMaterialProfile/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides capabilities to read and write XML-based material profiles.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 3eb7cb1c32..610ee1473e 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1819,9 +1819,9 @@ "unit": "mm", "type": "float", "default_value": 0.1, - "minimum_value": "resolveOrValue('layer_height')", + "minimum_value": "resolveOrValue('layer_height') if infill_line_distance > 0 else -999999", "maximum_value_warning": "0.75 * machine_nozzle_size", - "maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8)", + "maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8) if infill_line_distance > 0 else 999999", "value": "resolveOrValue('layer_height')", "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled", "limit_to_extruder": "infill_extruder_nr", diff --git a/resources/extruders/builder_premium_large_front.def.json b/resources/extruders/builder_premium_large_front.def.json index 059f7ef8a7..4834bc8fd9 100644 --- a/resources/extruders/builder_premium_large_front.def.json +++ b/resources/extruders/builder_premium_large_front.def.json @@ -16,13 +16,13 @@ "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, - - "machine_extruder_start_pos_abs": { "default_value": true }, + + "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/extruders/builder_premium_large_rear.def.json b/resources/extruders/builder_premium_large_rear.def.json index 769178a8b4..f257749ea4 100644 --- a/resources/extruders/builder_premium_large_rear.def.json +++ b/resources/extruders/builder_premium_large_rear.def.json @@ -20,9 +20,9 @@ "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, - "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/extruders/builder_premium_medium_front.def.json b/resources/extruders/builder_premium_medium_front.def.json index bd735fbe25..05dcb3d49f 100644 --- a/resources/extruders/builder_premium_medium_front.def.json +++ b/resources/extruders/builder_premium_medium_front.def.json @@ -16,13 +16,13 @@ "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, - - "machine_extruder_start_pos_abs": { "default_value": true }, + + "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/extruders/builder_premium_medium_rear.def.json b/resources/extruders/builder_premium_medium_rear.def.json index 59e688ff71..3461e07f09 100644 --- a/resources/extruders/builder_premium_medium_rear.def.json +++ b/resources/extruders/builder_premium_medium_rear.def.json @@ -20,9 +20,9 @@ "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, - "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/extruders/builder_premium_small_front.def.json b/resources/extruders/builder_premium_small_front.def.json index 17fb914a42..7a1c352c73 100644 --- a/resources/extruders/builder_premium_small_front.def.json +++ b/resources/extruders/builder_premium_small_front.def.json @@ -16,13 +16,13 @@ "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, - - "machine_extruder_start_pos_abs": { "default_value": true }, + + "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/extruders/builder_premium_small_rear.def.json b/resources/extruders/builder_premium_small_rear.def.json index 70a2dbf1aa..7085236a5c 100644 --- a/resources/extruders/builder_premium_small_rear.def.json +++ b/resources/extruders/builder_premium_small_rear.def.json @@ -20,9 +20,9 @@ "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, - "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_abs": { "default_value": true }, "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }, - "extruder_prime_pos_abs": { "default_value": true } + "extruder_prime_pos_abs": { "default_value": true } } } diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 21e6eebf58..d5572298f7 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -110,35 +110,35 @@ Item Action { id: view3DCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&3D View"); + text: catalog.i18nc("@action:inmenu menubar:view","3D View"); onTriggered: UM.Controller.rotateView("3d", 0); } Action { id: viewFrontCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Front View"); + text: catalog.i18nc("@action:inmenu menubar:view","Front View"); onTriggered: UM.Controller.rotateView("home", 0); } Action { id: viewTopCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Top View"); + text: catalog.i18nc("@action:inmenu menubar:view","Top View"); onTriggered: UM.Controller.rotateView("y", 90); } Action { id: viewLeftSideCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Left Side View"); + text: catalog.i18nc("@action:inmenu menubar:view","Left Side View"); onTriggered: UM.Controller.rotateView("x", 90); } Action { id: viewRightSideCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Right Side View"); + text: catalog.i18nc("@action:inmenu menubar:view","Right Side View"); onTriggered: UM.Controller.rotateView("x", -90); } @@ -229,7 +229,7 @@ Item Action { id: deleteSelectionAction; - text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount); + text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete Selected Model", "Delete Selected Models", UM.Selection.selectionCount); enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection; iconName: "edit-delete"; shortcut: StandardKey.Delete; @@ -239,7 +239,7 @@ Item Action //Also add backspace as the same function as delete because on Macintosh keyboards the button called "delete" is actually a backspace, and the user expects it to function as a delete. { id: backspaceSelectionAction - text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount) + text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete Selected Model", "Delete Selected Models", UM.Selection.selectionCount) enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection iconName: "edit-delete" shortcut: StandardKey.Backspace @@ -328,7 +328,7 @@ Item Action { id: selectAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Models"); + text: catalog.i18nc("@action:inmenu menubar:edit","Select All Models"); enabled: UM.Controller.toolsEnabled; iconName: "edit-select-all"; shortcut: "Ctrl+A"; @@ -386,7 +386,7 @@ Item Action { id: resetAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations"); + text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Transformations"); onTriggered: CuraApplication.resetAll(); } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 046a887a71..33e456c758 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -117,7 +117,7 @@ UM.MainWindow MenuItem { id: saveWorkspaceMenu - text: catalog.i18nc("@title:menu menubar:file","&Save...") + text: catalog.i18nc("@title:menu menubar:file","Save...") onTriggered: { var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/x-curaproject+xml" }; @@ -541,7 +541,7 @@ UM.MainWindow insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml")); - insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/MaterialsPage.qml")); + insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/Materials/MaterialsPage.qml")); insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml")); diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 20ec8ce289..31ca84d66e 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -81,7 +81,7 @@ Item { text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight onEditingFinished: { - var new_name = text == "" ? catalog.i18nc("@text Print job name", "unnamed") : text; + var new_name = text == "" ? catalog.i18nc("@text Print job name", "Untitled") : text; PrintInformation.setJobName(new_name, true); printJobTextfield.focus = false; } diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 64b3130724..f9e343d2dd 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -10,28 +10,74 @@ import Cura 1.0 as Cura Menu { id: menu - title: "Material" + title: catalog.i18nc("@label:category menu label", "Material") property int extruderIndex: 0 + Cura.FavoriteMaterialsModel + { + id: favoriteMaterialsModel + extruderPosition: menu.extruderIndex + } + + Cura.GenericMaterialsModel + { + id: genericMaterialsModel + extruderPosition: menu.extruderIndex + } + + Cura.MaterialBrandsModel + { + id: brandModel + extruderPosition: menu.extruderIndex + } + + MenuItem + { + text: catalog.i18nc("@label:category menu label", "Favorites") + enabled: false + visible: favoriteMaterialsModel.items.length > 0 + } Instantiator { - model: genericMaterialsModel - MenuItem + model: favoriteMaterialsModel + delegate: MenuItem { - text: model.name + text: model.brand + " " + model.name checkable: true checked: model.root_material_id == Cura.MachineManager.currentRootMaterialId[extruderIndex] + onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) exclusiveGroup: group - onTriggered: - { - Cura.MachineManager.setMaterial(extruderIndex, model.container_node); - } } onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) + onObjectRemoved: menu.removeItem(object) // TODO: This ain't gonna work, removeItem() takes an index, not object } - MenuSeparator { } + + MenuSeparator {} + + Menu + { + id: genericMenu + title: catalog.i18nc("@label:category menu label", "Generic") + + Instantiator + { + model: genericMaterialsModel + delegate: MenuItem + { + text: model.name + checkable: true + checked: model.root_material_id == Cura.MachineManager.currentRootMaterialId[extruderIndex] + exclusiveGroup: group + onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) + } + onObjectAdded: genericMenu.insertItem(index, object) + onObjectRemoved: genericMenu.removeItem(object) // TODO: This ain't gonna work, removeItem() takes an index, not object + } + } + + MenuSeparator {} + Instantiator { model: brandModel @@ -40,12 +86,12 @@ Menu id: brandMenu title: brandName property string brandName: model.name - property var brandMaterials: model.materials + property var brandMaterials: model.material_types Instantiator { model: brandMaterials - Menu + delegate: Menu { id: brandMaterialsMenu title: materialName @@ -55,16 +101,13 @@ Menu Instantiator { model: brandMaterialColors - MenuItem + delegate: MenuItem { text: model.name checkable: true checked: model.id == Cura.MachineManager.allActiveMaterialIds[Cura.ExtruderManager.extruderIds[extruderIndex]] exclusiveGroup: group - onTriggered: - { - Cura.MachineManager.setMaterial(extruderIndex, model.container_node); - } + onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) } onObjectAdded: brandMaterialsMenu.insertItem(index, object) onObjectRemoved: brandMaterialsMenu.removeItem(object) @@ -78,21 +121,14 @@ Menu onObjectRemoved: menu.removeItem(object) } - Cura.GenericMaterialsModel - { - id: genericMaterialsModel - extruderPosition: menu.extruderIndex + ExclusiveGroup { + id: group } - Cura.BrandMaterialsModel + MenuSeparator {} + + MenuItem { - id: brandModel - extruderPosition: menu.extruderIndex + action: Cura.Actions.manageMaterials } - - ExclusiveGroup { id: group } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.manageMaterials } } diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml new file mode 100644 index 0000000000..1077cbff6f --- /dev/null +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -0,0 +1,100 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +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 + height: childrenRect.height + width: parent.width + Rectangle + { + id: brand_header_background + color: UM.Theme.getColor("favorites_header_bar") + anchors.fill: brand_header + } + Row + { + id: brand_header + width: parent.width + Label + { + id: brand_name + text: model.name + height: UM.Theme.getSize("favorites_row").height + width: parent.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: brand_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + } + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + color: "transparent" + } + } + } + } + MouseArea + { + anchors.fill: brand_header + onPressed: + { + const i = base.collapsed_brands.indexOf(model.name) + if (i > -1) + { + // Remove it + base.collapsed_brands.splice(i, 1) + brand_section.expanded = false + } + else + { + // Add it + base.collapsed_brands.push(model.name) + brand_section.expanded = true + } + } + } + Column + { + 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 {} + } + } +} \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml new file mode 100644 index 0000000000..ad9f0e3766 --- /dev/null +++ b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Item +{ + id: detailsPanel + + property var currentItem: base.currentItem + + onCurrentItemChanged: { updateMaterialPropertiesObject(currentItem) } + + function updateMaterialPropertiesObject( currentItem ) + { + materialProperties.name = currentItem.name || "Unknown" + 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.density = currentItem.density || 0.0 + materialProperties.diameter = currentItem.diameter || 0.0 + materialProperties.approximate_diameter = currentItem.approximate_diameter || "0" + } + + Item + { + anchors.fill: parent + + Item // Material title Label + { + id: profileName + + width: parent.width + height: childrenRect.height + + Label { + text: materialProperties.name + font: UM.Theme.getFont("large") + } + } + + MaterialsView // Material detailed information view below the title Label + { + id: materialDetailsView + anchors + { + left: parent.left + right: parent.right + top: profileName.bottom + topMargin: UM.Theme.getSize("default_margin").height + bottom: parent.bottom + } + + editingEnabled: base.currentItem != null && !base.currentItem.is_read_only + + properties: materialProperties + containerId: base.currentItem != null ? base.currentItem.id : "" + currentMaterialNode: base.currentItem.container_node + + + } + + QtObject + { + id: materialProperties + + property string guid: "00000000-0000-0000-0000-000000000000" + property string container_id: "Unknown"; + property string name: "Unknown"; + property string profile_type: "Unknown"; + property string brand: "Unknown"; + property string material: "Unknown"; // This needs to be named as "material" to be consistent with + // the material container's metadata entry + + property string color_name: "Yellow"; + property color color_code: "yellow"; + + property real density: 0.0; + property real diameter: 0.0; + property string approximate_diameter: "0"; + + property real spool_cost: 0.0; + property real spool_weight: 0.0; + property real spool_length: 0.0; + property real cost_per_meter: 0.0; + + property string description: ""; + property string adhesion_info: ""; + } + } +} \ No newline at end of file diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml new file mode 100644 index 0000000000..4a1a330ed6 --- /dev/null +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -0,0 +1,199 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Item +{ + id: materialList + width: materialScrollView.width - 17 + height: childrenRect.height + + // Children + UM.I18nCatalog { id: catalog; name: "cura"; } + Cura.MaterialBrandsModel { id: materialsModel } + Cura.FavoriteMaterialsModel { id: favoriteMaterialsModel } + Cura.GenericMaterialsModel { id: genericMaterialsModel } + Column + { + Rectangle + { + property var expanded: true + + 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 + { + 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 + } + } + } + } + Repeater + { + id: brand_list + model: materialsModel + delegate: MaterialsBrandSection {} + } + } +} \ No newline at end of file diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml similarity index 56% rename from resources/qml/Preferences/MaterialsPage.qml rename to resources/qml/Preferences/Materials/MaterialsPage.qml index e2e3edec2f..0b81df5fa1 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -9,25 +9,110 @@ import QtQuick.Dialogs 1.2 import UM 1.2 as UM import Cura 1.0 as Cura - Item { id: base property QtObject materialManager: CuraApplication.getMaterialManager() - property var resetEnabled: false // Keep PreferencesDialog happy - - UM.I18nCatalog { id: catalog; name: "cura"; } - - Cura.MaterialManagementModel + // Keep PreferencesDialog happy + property var resetEnabled: false + property var currentItem: null + property var isCurrentItemActivated: { - id: materialsModel + 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: [] + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + Cura.MaterialBrandsModel { id: materialsModel } + + function findModelByRootId( search_root_id ) + { + 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; + } + } + + // Main layout Label { id: titleLabel - anchors { top: parent.top @@ -35,45 +120,12 @@ Item right: parent.right margins: 5 * screenScaleFactor } - font.pointSize: 18 text: catalog.i18nc("@title:tab", "Materials") } - property var hasCurrentItem: materialListView.currentItem != null - - property var currentItem: - { // is soon to be overwritten - var current_index = materialListView.currentIndex; - return materialsModel.getItem(current_index); - } - - 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; - } - - 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]; - var itemIndex = -1; - for (var i = 0; i < materialsModel.rowCount(); ++i) - { - var item = materialsModel.getItem(i); - if (item.root_material_id == active_root_material_id) - { - itemIndex = i; - break; - } - } - materialListView.currentIndex = itemIndex; - } - - Row // Button Row + // Button Row + Row { id: buttonRow anchors @@ -166,63 +218,102 @@ Item } } - property string newRootMaterialIdToSwitchTo: "" - property bool toActivateNewMaterial: false - - // This connection makes sure that we will switch to the new - Connections - { - target: materialsModel - onItemsChanged: + Item { + id: contentsItem + anchors { - var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id; - var position = Cura.ExtruderManager.activeExtruderIndex; + top: titleLabel.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + margins: 5 * screenScaleFactor + bottomMargin: 0 + } + clip: true + } - // try to pick the currently selected item; it may have been moved - if (base.newRootMaterialIdToSwitchTo == "") + Item + { + anchors + { + top: buttonRow.bottom + topMargin: UM.Theme.getSize("default_margin").height + left: parent.left + right: parent.right + bottom: parent.bottom + } + + SystemPalette { id: palette } + + Label + { + id: captionLabel + anchors { - base.newRootMaterialIdToSwitchTo = currentItemId; + top: parent.top + left: parent.left } - - for (var idx = 0; idx < materialsModel.rowCount(); ++idx) + visible: text != "" + text: { - var item = materialsModel.getItem(idx); - if (item.root_material_id == base.newRootMaterialIdToSwitchTo) + var caption = catalog.i18nc("@action:label", "Printer") + ": " + Cura.MachineManager.activeMachineName; + if (Cura.MachineManager.hasVariants) { - // 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 + caption += ", " + Cura.MachineManager.activeDefinitionVariantsName + ": " + Cura.MachineManager.activeVariantName; } + return caption; + } + width: materialScrollView.width + elide: Text.ElideRight + } + + ScrollView + { + id: materialScrollView + anchors + { + top: captionLabel.visible ? captionLabel.bottom : parent.top + topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0 + bottom: parent.bottom + left: parent.left } - materialListView.currentIndex = 0; - materialListView.activateDetailsWithIndex(materialListView.currentIndex); - if (base.toActivateNewMaterial) + Rectangle { - Cura.MachineManager.setMaterial(position, materialsModel.getItem(0).container_node); + parent: viewport + anchors.fill: parent + color: palette.light + } + + width: true ? (parent.width * 0.4) | 0 : parent.width + frameVisible: true + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn + + MaterialsList {} + } + + MaterialsDetailsPanel + { + anchors + { + left: materialScrollView.right + leftMargin: UM.Theme.getSize("default_margin").width + top: parent.top + bottom: parent.bottom + right: parent.right } - base.newRootMaterialIdToSwitchTo = ""; - base.toActivateNewMaterial = false; } } + // Dialogs MessageDialog { id: confirmRemoveMaterialDialog - icon: StandardIcon.Question; title: catalog.i18nc("@title:window", "Confirm Remove") text: catalog.i18nc("@label (%1 is object name)", "Are you sure you wish to remove %1? This cannot be undone!").arg(base.currentItem.name) standardButtons: StandardButton.Yes | StandardButton.No modality: Qt.ApplicationModal - onYes: { base.materialManager.removeMaterial(base.currentItem.container_node); @@ -292,281 +383,4 @@ Item { id: messageDialog } - - - Item { - id: contentsItem - - anchors - { - top: titleLabel.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - margins: 5 * screenScaleFactor - bottomMargin: 0 - } - - clip: true - } - - Item - { - anchors - { - top: buttonRow.bottom - topMargin: UM.Theme.getSize("default_margin").height - left: parent.left - right: parent.right - bottom: parent.bottom - } - - SystemPalette { id: palette } - - Label - { - id: captionLabel - anchors - { - top: parent.top - left: parent.left - } - visible: text != "" - text: - { - var caption = catalog.i18nc("@action:label", "Printer") + ": " + Cura.MachineManager.activeMachineName; - if (Cura.MachineManager.hasVariants) - { - caption += ", " + Cura.MachineManager.activeDefinitionVariantsName + ": " + Cura.MachineManager.activeVariantName; - } - return caption; - } - width: materialScrollView.width - elide: Text.ElideRight - } - - ScrollView - { - id: materialScrollView - anchors - { - top: captionLabel.visible ? captionLabel.bottom : parent.top - topMargin: captionLabel.visible ? UM.Theme.getSize("default_margin").height : 0 - bottom: parent.bottom - left: parent.left - } - - Rectangle - { - parent: viewport - anchors.fill: parent - color: palette.light - } - - width: true ? (parent.width * 0.4) | 0 : parent.width - frameVisible: true - - ListView - { - id: materialListView - - model: materialsModel - - section.property: "brand" - section.criteria: ViewSection.FullString - section.delegate: Rectangle - { - width: materialScrollView.width - height: childrenRect.height - color: palette.light - - Label - { - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_lining").width - text: section - font.bold: true - color: palette.text - } - } - - delegate: Rectangle - { - width: materialScrollView.width - height: childrenRect.height - color: ListView.isCurrentItem ? palette.highlight : (model.index % 2) ? palette.base : palette.alternateBase - - Row - { - id: materialRow - spacing: (UM.Theme.getSize("default_margin").width / 2) | 0 - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.right: parent.right - - property bool isCurrentItem: parent.ListView.isCurrentItem - - property bool isItemActivated: - { - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; - return model.root_material_id == root_material_id; - } - - Rectangle - { - width: Math.floor(parent.height * 0.8) - height: Math.floor(parent.height * 0.8) - color: model.color_code - border.color: materialRow.isCurrentItem ? palette.highlightedText : palette.text; - anchors.verticalCenter: parent.verticalCenter - } - Label - { - width: Math.floor((parent.width * 0.3)) - text: model.material - elide: Text.ElideRight - font.italic: materialRow.isItemActivated - color: materialRow.isCurrentItem ? palette.highlightedText : palette.text; - } - Label - { - text: (model.name != model.material) ? model.name : "" - elide: Text.ElideRight - font.italic: materialRow.isItemActivated - color: materialRow.isCurrentItem ? palette.highlightedText : palette.text; - } - } - - MouseArea - { - anchors.fill: parent - onClicked: - { - parent.ListView.view.currentIndex = model.index; - } - } - } - - function activateDetailsWithIndex(index) - { - var model = materialsModel.getItem(index); - base.currentItem = model; - materialDetailsView.containerId = model.container_id; - materialDetailsView.currentMaterialNode = model.container_node; - - detailsPanel.updateMaterialPropertiesObject(); - } - - onCurrentIndexChanged: - { - forceActiveFocus(); // causes the changed fields to be saved - activateDetailsWithIndex(currentIndex); - } - } - } - - - Item - { - id: detailsPanel - - anchors - { - left: materialScrollView.right - leftMargin: UM.Theme.getSize("default_margin").width - top: parent.top - bottom: parent.bottom - right: parent.right - } - - function updateMaterialPropertiesObject() - { - var currentItem = materialsModel.getItem(materialListView.currentIndex); - - materialProperties.name = currentItem.name ? currentItem.name : "Unknown"; - materialProperties.guid = currentItem.guid; - materialProperties.container_id = currentItem.container_id; - - materialProperties.brand = currentItem.brand ? currentItem.brand : "Unknown"; - materialProperties.material = currentItem.material ? currentItem.material : "Unknown"; - materialProperties.color_name = currentItem.color_name ? currentItem.color_name : "Yellow"; - materialProperties.color_code = currentItem.color_code ? currentItem.color_code : "yellow"; - - materialProperties.description = currentItem.description ? currentItem.description : ""; - materialProperties.adhesion_info = currentItem.adhesion_info ? currentItem.adhesion_info : ""; - - materialProperties.density = currentItem.density ? currentItem.density : 0.0; - materialProperties.diameter = currentItem.diameter ? currentItem.diameter : 0.0; - materialProperties.approximate_diameter = currentItem.approximate_diameter ? currentItem.approximate_diameter : "0"; - } - - Item - { - anchors.fill: parent - - Item // Material title Label - { - id: profileName - - width: parent.width - height: childrenRect.height - - Label { - text: materialProperties.name - font: UM.Theme.getFont("large") - } - } - - MaterialView // Material detailed information view below the title Label - { - id: materialDetailsView - anchors - { - left: parent.left - right: parent.right - top: profileName.bottom - topMargin: UM.Theme.getSize("default_margin").height - bottom: parent.bottom - } - - editingEnabled: base.currentItem != null && !base.currentItem.is_read_only - - properties: materialProperties - containerId: base.currentItem != null ? base.currentItem.container_id : "" - currentMaterialNode: base.currentItem.container_node - - property alias pane: base - } - - QtObject - { - id: materialProperties - - property string guid: "00000000-0000-0000-0000-000000000000" - property string container_id: "Unknown"; - property string name: "Unknown"; - property string profile_type: "Unknown"; - property string brand: "Unknown"; - property string material: "Unknown"; // This needs to be named as "material" to be consistent with - // the material container's metadata entry - - property string color_name: "Yellow"; - property color color_code: "yellow"; - - property real density: 0.0; - property real diameter: 0.0; - property string approximate_diameter: "0"; - - property real spool_cost: 0.0; - property real spool_weight: 0.0; - property real spool_length: 0.0; - property real cost_per_meter: 0.0; - - property string description: ""; - property string adhesion_info: ""; - } - } - } - } } diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml new file mode 100644 index 0000000000..ab0dd23750 --- /dev/null +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -0,0 +1,120 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Rectangle +{ + id: material_slot + property var material + property var hovered: false + property var is_favorite: material.is_favorite + + height: UM.Theme.getSize("favorites_row").height + width: parent.width + color: base.currentItem == model ? UM.Theme.getColor("favorites_row_selected") : "transparent" + + Item + { + height: parent.height + width: parent.width + Rectangle + { + 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 + } + Label + { + 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 + } + MouseArea + { + 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 + return + } + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + color: "transparent" + } + } + UM.RecolorImage { + anchors + { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.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) + { + return UM.Theme.getColor("primary_hover") + } + else + { + if (material_slot.is_favorite) + { + return UM.Theme.getColor("primary") + } + else + { + UM.Theme.getColor("text_inactive") + } + } + } + source: material_slot.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 new file mode 100644 index 0000000000..11bf2385e1 --- /dev/null +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -0,0 +1,109 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +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 + height: childrenRect.height + width: parent.width + Rectangle + { + id: material_type_header_background + color: UM.Theme.getColor("lining") + anchors.bottom: material_type_header.bottom + anchors.left: material_type_header.left + height: UM.Theme.getSize("default_lining").height + width: material_type_header.width + } + Row + { + id: material_type_header + width: parent.width - 8 + anchors + { + left: parent.left + leftMargin: 8 + } + Label + { + text: model.name + height: UM.Theme.getSize("favorites_row").height + width: parent.width - UM.Theme.getSize("favorites_button").width + id: material_type_name + verticalAlignment: Text.AlignVCenter + } + 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: material_type_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + } + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + color: "transparent" + } + } + } + } + MouseArea + { + anchors.fill: material_type_header + onPressed: + { + const i = base.collapsed_types.indexOf(model.brand + "_" + model.name) + if (i > -1) + { + // Remove it + base.collapsed_types.splice(i, 1) + material_type_section.expanded = false + } + else + { + // Add it + base.collapsed_types.push(model.brand + "_" + model.name) + material_type_section.expanded = true + } + } + } + Column + { + height: material_type_section.expanded ? childrenRect.height : 0 + visible: material_type_section.expanded + width: parent.width + anchors.top: material_type_header.bottom + anchors.left: parent.left + Repeater + { + model: colors_model + delegate: MaterialsSlot { + material: model + } + } + } +} \ No newline at end of file diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml similarity index 99% rename from resources/qml/Preferences/MaterialView.qml rename to resources/qml/Preferences/Materials/MaterialsView.qml index 97184ab558..a03e5c48d7 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -8,6 +8,8 @@ import QtQuick.Dialogs 1.2 import UM 1.2 as UM import Cura 1.0 as Cura +import ".." // Access to ReadOnlyTextArea.qml + TabView { id: base @@ -38,7 +40,7 @@ TabView { return "" } - var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentMaterialNode, true); + var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.currentItem.container_node, true); if (linkedMaterials.length == 0) { return "" diff --git a/resources/themes/cura-light/icons/favorites_star_empty.svg b/resources/themes/cura-light/icons/favorites_star_empty.svg new file mode 100644 index 0000000000..bb1205e7a7 --- /dev/null +++ b/resources/themes/cura-light/icons/favorites_star_empty.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/resources/themes/cura-light/icons/favorites_star_full.svg b/resources/themes/cura-light/icons/favorites_star_full.svg new file mode 100644 index 0000000000..aad45c5d02 --- /dev/null +++ b/resources/themes/cura-light/icons/favorites_star_full.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 7bcdafce98..c408146669 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -315,7 +315,13 @@ "tab_status_disconnected": [200, 200, 200, 255], "printer_config_matched": [12, 169, 227, 255], - "printer_config_mismatch": [127, 127, 127, 255] + "printer_config_mismatch": [127, 127, 127, 255], + + "favorites_header_bar": [245, 245, 245, 255], + "favorites_header_hover": [245, 245, 245, 255], + "favorites_header_text": [31, 36, 39, 255], + "favorites_header_text_hover": [31, 36, 39, 255], + "favorites_row_selected": [196, 239, 255, 255] }, "sizes": { @@ -372,6 +378,10 @@ "small_button": [2, 2], "small_button_icon": [1.5, 1.5], + "favorites_row": [2, 2], + "favorites_button": [2, 2], + "favorites_button_icon": [1.2, 1.2], + "printer_status_icon": [1.8, 1.8], "printer_sync_icon": [1.2, 1.2],