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],