diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index aa843bccdb..892440cba0 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -180,6 +180,8 @@ class CuraApplication(QtApplication): ContainerRegistry.getInstance().addContainer(empty_material_container) empty_quality_container = copy.deepcopy(empty_container) empty_quality_container._id = "empty_quality" + empty_quality_container.setName("Not supported") + empty_quality_container.addMetaDataEntry("quality_type", "normal") empty_quality_container.addMetaDataEntry("type", "quality") ContainerRegistry.getInstance().addContainer(empty_quality_container) empty_quality_changes_container = copy.deepcopy(empty_container) @@ -509,8 +511,6 @@ class CuraApplication(QtApplication): if self.getController().getActiveTool(): self._previous_active_tool = self.getController().getActiveTool().getPluginId() self.getController().setActiveTool(None) - else: - self._previous_active_tool = None def _onToolOperationStopped(self, event): if self._center_after_select: diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index c62113916d..c5eb4b699e 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -14,8 +14,9 @@ class LayerPolygon: SupportInfillType = 7 MoveCombingType = 8 MoveRetractionType = 9 + SupportInterfaceType = 10 - __jump_map = numpy.logical_or( numpy.arange(10) == NoneType, numpy.arange(10) >= MoveCombingType ) + __jump_map = numpy.logical_or( numpy.arange(11) == NoneType, numpy.arange(11) >= MoveRetractionType ) def __init__(self, mesh, extruder, line_types, data, line_widths): self._mesh = mesh @@ -178,10 +179,11 @@ class LayerPolygon: SkinType: Color(1.0, 1.0, 0.0, 1.0), SupportType: Color(0.0, 1.0, 1.0, 1.0), SkirtType: Color(0.0, 1.0, 1.0, 1.0), - InfillType: Color(1.0, 0.74, 0.0, 1.0), + InfillType: Color(1.0, 0.75, 0.0, 1.0), SupportInfillType: Color(0.0, 1.0, 1.0, 1.0), MoveCombingType: Color(0.0, 0.0, 1.0, 1.0), MoveRetractionType: Color(0.5, 0.5, 1.0, 1.0), + SupportInterfaceType: Color(0.25, 0.75, 1.0, 1.0), } # Should be generated in better way, not hardcoded. @@ -192,8 +194,9 @@ class LayerPolygon: [1.0, 1.0, 0.0, 1.0], [0.0, 1.0, 1.0, 1.0], [0.0, 1.0, 1.0, 1.0], - [1.0, 0.74, 0.0, 1.0], + [1.0, 0.75, 0.0, 1.0], [0.0, 1.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0], - [0.5, 0.5, 1.0, 1.0] + [0.5, 0.5, 1.0, 1.0], + [0.25, 0.75, 1.0, 1.0] ]) \ No newline at end of file diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index c9afb4ccbd..d718f25b87 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -29,6 +29,8 @@ class PlatformPhysics: self._change_timer.setInterval(100) self._change_timer.setSingleShot(True) self._change_timer.timeout.connect(self._onChangeTimerFinished) + self._move_factor = 1.1 # By how much should we multiply overlap to calculate a new spot? + self._max_overlap_checks = 10 # How many times should we try to find a new spot per tick? Preferences.getInstance().addPreference("physics/automatic_push_free", True) Preferences.getInstance().addPreference("physics/automatic_drop_down", True) @@ -97,29 +99,42 @@ class PlatformPhysics: continue if other_node in transformed_nodes: - continue # Other node is already moving, wait for next pass. + continue # Other node is already moving, wait for next pass. - # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. - head_hull = node.callDecoration("getConvexHullHead") - if head_hull: - overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) - if not overlap: - other_head_hull = other_node.callDecoration("getConvexHullHead") - if other_head_hull: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) - else: - own_convex_hull = node.callDecoration("getConvexHull") - other_convex_hull = other_node.callDecoration("getConvexHull") - if own_convex_hull and other_convex_hull: - overlap = own_convex_hull.intersectsPolygon(other_convex_hull) + overlap = (0, 0) # Start loop with no overlap + move_vector = move_vector.set(x=overlap[0] * self._move_factor, z=overlap[1] * self._move_factor) + current_overlap_checks = 0 + # Continue to check the overlap until we no longer find one. + while overlap and current_overlap_checks < self._max_overlap_checks: + current_overlap_checks += 1 + head_hull = node.callDecoration("getConvexHullHead") + if head_hull: # One at a time intersection. + overlap = head_hull.translate(move_vector.x, move_vector.z).intersectsPolygon(other_node.callDecoration("getConvexHull")) + if not overlap: + other_head_hull = other_node.callDecoration("getConvexHullHead") + if other_head_hull: + overlap = node.callDecoration("getConvexHull").translate(move_vector.x, move_vector.z).intersectsPolygon(other_head_hull) + if overlap: + # Moving ensured that overlap was still there. Try anew! + move_vector = move_vector.set(x=move_vector.x + overlap[0] * self._move_factor, + z=move_vector.z + overlap[1] * self._move_factor) + else: + # Moving ensured that overlap was still there. Try anew! + move_vector = move_vector.set(x=move_vector.x + overlap[0] * self._move_factor, + z=move_vector.z + overlap[1] * self._move_factor) else: - # This can happen in some cases if the object is not yet done with being loaded. - # Simply waiting for the next tick seems to resolve this correctly. - overlap = None + own_convex_hull = node.callDecoration("getConvexHull") + other_convex_hull = other_node.callDecoration("getConvexHull") + if own_convex_hull and other_convex_hull: + overlap = own_convex_hull.translate(move_vector.x, move_vector.z).intersectsPolygon(other_convex_hull) + if overlap: # Moving ensured that overlap was still there. Try anew! + move_vector = move_vector.set(x=move_vector.x + overlap[0] * self._move_factor, + z=move_vector.z + overlap[1] * self._move_factor) + else: + # This can happen in some cases if the object is not yet done with being loaded. + # Simply waiting for the next tick seems to resolve this correctly. + overlap = None - if overlap is None: - continue - move_vector = move_vector.set(x=overlap[0] * 1.1, z=overlap[1] * 1.1) convex_hull = node.callDecoration("getConvexHull") if convex_hull: if not convex_hull.isValid(): diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index ccd14fcb1d..3e6b55ec7e 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QMessageBox from UM.Application import Application from UM.Preferences import Preferences from UM.Logger import Logger - +from UM.Message import Message from UM.Settings.SettingRelation import RelationType import UM.Settings @@ -547,6 +547,7 @@ class MachineManager(QObject): preferred_material = None if old_material: preferred_material_name = old_material.getName() + self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id) else: Logger.log("w", "While trying to set the active variant, no variant was found to replace.") @@ -806,15 +807,15 @@ class MachineManager(QObject): if containers: return containers[0] - if "name" in search_criteria or "id" in search_criteria: + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if "variant" in search_criteria or "id" in search_criteria: # If a material by this name can not be found, try a wider set of search criteria - search_criteria.pop("name", None) + search_criteria.pop("variant", None) search_criteria.pop("id", None) - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if containers: return containers[0] - + Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.") return self._empty_material_container def _updateQualityContainer(self, definition, variant_container, material_container = None, preferred_quality_name = None): @@ -855,10 +856,9 @@ class MachineManager(QObject): containers = container_registry.findInstanceContainers(**search_criteria) if containers: return containers[0] - # We still weren't able to find a quality for this specific material. # Try to find qualities for a generic version of the material. - material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic" } + material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"} if definition.getMetaDataEntry("has_machine_quality"): if material_container: material_search_criteria["definition"] = material_container.getDefinition().id @@ -872,7 +872,6 @@ class MachineManager(QObject): material_search_criteria["variant"] = variant_container.id else: material_search_criteria["definition"] = "fdmprinter" - material_containers = container_registry.findInstanceContainers(**material_search_criteria) if material_containers: search_criteria["material"] = material_containers[0].getId() @@ -890,6 +889,9 @@ class MachineManager(QObject): if containers: return containers[0] + # Notify user that we were unable to find a matching quality + message = Message(catalog.i18nc("@info:status", "Unable to find a quality profile for this combination. Default settings will be used instead.")) + message.show() return self._empty_quality_container ## Finds a quality-changes container to use if any other container diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 289c0a98a0..e26b3040f0 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -56,6 +56,7 @@ message Polygon { SupportInfillType = 7; MoveCombingType = 8; MoveRetractionType = 9; + SupportInterfaceType = 10; } Type type = 1; // Type of move bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 9e6bec45e4..72884ce5a5 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -229,7 +229,7 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.SettingError: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors.")) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. Please check your settings for errors.")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: @@ -238,7 +238,7 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable models found.")) + self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit.")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 801ce4743f..248649f431 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -103,10 +103,11 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): self._firmware_view.show() - @pyqtSlot() - def updateAllFirmware(self): + @pyqtSlot(str) + def updateAllFirmware(self, file_name): + file_name = file_name.replace("file://", "") # File dialogs prepend the path with file://, which we don't need / want if not self._usb_output_devices: - Message(i18n_catalog.i18nc("@info","Cannot update firmware, there were no connected printers found.")).show() + Message(i18n_catalog.i18nc("@info", "Cannot update firmware, there were no connected printers found.")).show() return for printer_connection in self._usb_output_devices: @@ -114,26 +115,26 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): self.spawnFirmwareInterface("") for printer_connection in self._usb_output_devices: try: - self._usb_output_devices[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) + self._usb_output_devices[printer_connection].updateFirmware(file_name) except FileNotFoundError: # Should only happen in dev environments where the resources/firmware folder is absent. self._usb_output_devices[printer_connection].setProgress(100, 100) - Logger.log("w", "No firmware found for printer %s called '%s'" %(printer_connection, self._getDefaultFirmwareName())) + Logger.log("w", "No firmware found for printer %s called '%s'", printer_connection, file_name) Message(i18n_catalog.i18nc("@info", "Could not find firmware required for the printer at %s.") % printer_connection).show() self._firmware_view.close() continue - @pyqtSlot(str, result = bool) - def updateFirmwareBySerial(self, serial_port): + @pyqtSlot(str, str, result = bool) + def updateFirmwareBySerial(self, serial_port, file_name): if serial_port in self._usb_output_devices: self.spawnFirmwareInterface(self._usb_output_devices[serial_port].getSerialPort()) try: - self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) + self._usb_output_devices[serial_port].updateFirmware(file_name) except FileNotFoundError: self._firmware_view.close() - Logger.log("e", "Could not find firmware required for this machine called '%s'" %(self._getDefaultFirmwareName())) + Logger.log("e", "Could not find firmware required for this machine called '%s'", file_name) return False return True return False @@ -147,7 +148,8 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): return USBPrinterOutputDeviceManager._instance - def _getDefaultFirmwareName(self): + @pyqtSlot(result = str) + def getDefaultFirmwareName(self): # Check if there is a valid global container stack global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: @@ -193,13 +195,13 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): Logger.log("d", "Choosing basic firmware for machine %s.", machine_id) hex_file = machine_without_extras[machine_id] # Return "basic" firmware else: - Logger.log("e", "There is no firmware for machine %s.", machine_id) + Logger.log("w", "There is no firmware for machine %s.", machine_id) if hex_file: - return hex_file.format(baudrate=baudrate) + return Resources.getPath(CuraApplication.ResourceTypes.Firmware, hex_file.format(baudrate=baudrate)) else: - Logger.log("e", "Could not find any firmware for machine %s.", machine_id) - raise FileNotFoundError() + Logger.log("w", "Could not find any firmware for machine %s.", machine_id) + return "" ## Helper to identify serial ports (and scan for them) def _addRemovePorts(self, serial_ports): diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py index 53476207fd..71d3f0b55b 100644 --- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py +++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py @@ -1,9 +1,17 @@ from cura.MachineAction import MachineAction from UM.i18n import i18nCatalog +import cura.Settings.CuraContainerRegistry +import UM.Settings.DefinitionContainer catalog = i18nCatalog("cura") class UpgradeFirmwareMachineAction(MachineAction): def __init__(self): super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Upgrade Firmware")) - self._qml_url = "UpgradeFirmwareMachineAction.qml" \ No newline at end of file + self._qml_url = "UpgradeFirmwareMachineAction.qml" + cura.Settings.CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) + + def _onContainerAdded(self, container): + # Add this action as a supported action to all machine definitions + if isinstance(container, UM.Settings.DefinitionContainer) and container.getMetaDataEntry("type") == "machine" and container.getMetaDataEntry("supports_usb_connection"): + UM.Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey()) diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml index 0c9b80c010..153d8254ef 100644 --- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml +++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml @@ -5,6 +5,7 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 +import QtQuick.Dialogs 1.2 // For filedialog import UM 1.2 as UM import Cura 1.0 as Cura @@ -44,34 +45,45 @@ Cura.MachineAction anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "The firmware shipping with new Ultimakers works, but upgrades have been made to make better prints, and make calibration easier."); + text: catalog.i18nc("@label", "The firmware shipping with new printers works, but new versions tend to have more features and improvements."); } - Label - { - id: upgradeText2 - anchors.top: upgradeText1.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now."); - } Row { - anchors.top: upgradeText2.bottom + anchors.top: upgradeText1.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.horizontalCenter: parent.horizontalCenter width: childrenRect.width spacing: UM.Theme.getSize("default_margin").width + property var firmwareName: Cura.USBPrinterManager.getDefaultFirmwareName() Button { - id: upgradeButton - text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware"); + id: autoUpgradeButton + text: catalog.i18nc("@action:button", "Automatically upgrade Firmware"); + enabled: parent.firmwareName != "" onClicked: { - Cura.USBPrinterManager.updateAllFirmware() + Cura.USBPrinterManager.updateAllFirmware(parent.firmwareName) + } + } + Button + { + id: manualUpgradeButton + text: catalog.i18nc("@action:button", "Upload custom Firmware"); + onClicked: + { + customFirmwareDialog.open() } } } + + FileDialog + { + id: customFirmwareDialog + title: catalog.i18nc("@title:window", "Select custom firmware") + nameFilters: "Firmware image files (*.hex)" + selectExisting: true + onAccepted: Cura.USBPrinterManager.updateAllFirmware(fileUrl) + } } } \ No newline at end of file diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index a219347f10..e76d277b0c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -15,7 +15,8 @@ "machine_extruder_trains": { "0": "fdmextruder" - } + }, + "supports_usb_connection": true }, "settings": { @@ -402,7 +403,7 @@ "description": "The maximum speed of the filament.", "unit": "mm/s", "type": "float", - "default_value": 25, + "default_value": 299792458000, "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false @@ -1160,7 +1161,7 @@ "default_value": 25, "minimum_value": "0", "maximum_value": "machine_max_feedrate_e", - "maximum_value_warning": "100", + "maximum_value_warning": "25", "enabled": "retraction_enable", "settable_per_mesh": false, "settable_per_extruder": true, @@ -1173,7 +1174,7 @@ "default_value": 25, "minimum_value": "0", "maximum_value": "machine_max_feedrate_e", - "maximum_value_warning": "100", + "maximum_value_warning": "25", "enabled": "retraction_enable", "value": "retraction_speed", "settable_per_mesh": false, @@ -1187,7 +1188,7 @@ "default_value": 25, "minimum_value": "0", "maximum_value": "machine_max_feedrate_e", - "maximum_value_warning": "100", + "maximum_value_warning": "25", "enabled": "retraction_enable", "value": "retraction_speed", "settable_per_mesh": false, @@ -3518,7 +3519,7 @@ }, "experimental": { - "label": "Experimental Modes", + "label": "Experimental", "type": "category", "icon": "category_experimental", "description": "experimental!", @@ -3763,6 +3764,7 @@ "type": "float", "unit": "mm", "default_value": 3, + "value": "machine_nozzle_head_distance", "minimum_value": "0.0001", "maximum_value_warning": "20", "enabled": "wireframe_enabled", diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index dc52b00dcc..0a3729c766 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -9,6 +9,9 @@ "visible": false }, "overrides": { + "machine_max_feedrate_e": { + "default_value": 45 + }, "material_print_temperature": { "minimum_value": "0" }, diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 34dbcf1b67..42c2ad9cf4 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -346,6 +346,7 @@ UM.MainWindow bottom: parent.bottom; right: parent.right; } + z: 1 onMonitoringPrintChanged: base.monitoringPrint = monitoringPrint width: UM.Theme.getSize("sidebar").width; } diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 2a8e2f4097..6f36c694ed 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -4,7 +4,6 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 -import QtQuick.Dialogs 1.1 import QtQuick.Layouts 1.1 import UM 1.1 as UM @@ -161,7 +160,7 @@ Rectangle anchors.rightMargin: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@label:", "Abort Print") - onClicked: confirmationDialog.visible = true + onClicked: Cura.MachineManager.printerOutputDevices[0].setJobState("abort") style: ButtonStyle { @@ -221,9 +220,9 @@ Rectangle { id: confirmationDialog - title: catalog.i18nc("@text:MessageDialog", "Abort print") + title: catalog.i18nc("@window:title", "Abort print") icon: StandardIcon.Warning - text: catalog.i18nc("@text:MessageDialog", "Do you really want to abort the print?") + text: catalog.i18nc("@label", "Are you sure you want to abort the print?") standardButtons: StandardButton.Yes | StandardButton.No Component.onCompleted: visible = false onYes: Cura.MachineManager.printerOutputDevices[0].setJobState("abort") diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 3350ebeb7b..c9c0e722eb 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -277,7 +277,7 @@ Column height: UM.Theme.getSize("setting_control").height tooltip: Cura.MachineManager.activeQualityName style: UM.Theme.styles.sidebar_header_button - + property var valueWarning: Cura.MachineManager.activeQualityId == "empty_quality" menu: ProfileMenu { } UM.SimpleButton diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 8d813bc2b5..3cfb4514ee 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -11,7 +11,22 @@ QtObject { property Component sidebar_header_button: Component { ButtonStyle { background: Rectangle { - color: control.enabled ? Theme.getColor("setting_control") : Theme.getColor("setting_control_disabled") + color: + { + if(control.enabled) + { + if(control.valueWarning) + { + return Theme.getColor("setting_validation_warning"); + } else + { + return Theme.getColor("setting_control"); + } + } else { + return Theme.getColor("setting_control_disabled"); + } + } + border.width: Theme.getSize("default_lining").width border.color: !control.enabled ? Theme.getColor("setting_control_disabled_border") : control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border")