├── .github ├── FUNDING.yml └── workflows │ ├── package.json │ └── create_tagged_release_with_packages.yaml ├── resources ├── icons │ ├── settings_dock.svg │ ├── settings_undock.svg │ ├── view_orthographic.svg │ └── view_perspective.svg └── qml │ ├── TabRow.qml │ ├── MachineSelector40.qml │ ├── NoIntentIcon.qml │ ├── MaterialSelection.qml │ ├── OpenFileButton40.qml │ ├── ModeToggleSwitch.qml │ ├── TabRowButton.qml │ ├── StageMain.qml │ ├── MachineSelector53.qml │ ├── ProfileSelector40.qml │ ├── SidebarFooter.qml │ ├── MonitorStageMenu.qml │ ├── PrintSetupSummary.qml │ ├── MaterialMenu.qml │ ├── SidebarToolWindow.qml │ ├── ExtruderTabs40.qml │ ├── ProfileSelector53.qml │ ├── OpenFileButton50.qml │ ├── OpenFileButton411.qml │ ├── ViewOptionsPanel50.qml │ ├── PrepareStageLegend50.qml │ ├── ViewOptionsPanel40.qml │ ├── ExtruderTabs50.qml │ ├── ExtruderTabs411.qml │ ├── ProfileSelector51.qml │ ├── PrepareStageLegend40.qml │ ├── ProfileSelector50.qml │ ├── ProfileSelector44.qml │ ├── SidebarContents.qml │ ├── SidebarStageMenu.qml │ └── MaterialBrandMenu.qml ├── plugin.json ├── .gitignore ├── README.md ├── __init__.py ├── SidebarGUIProxy.py ├── SidebarIncompatibleVersion.py └── SidebarGUIPlugin.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fieldofview 2 | custom: ["https://www.paypal.me/fieldofview"] 3 | -------------------------------------------------------------------------------- /resources/icons/settings_dock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /resources/icons/settings_undock.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /resources/icons/view_orthographic.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sidebar GUI", 3 | "author": "fieldOfView", 4 | "version": "4.6.9-DEV", 5 | "minimum_cura_version": "4.0", 6 | "maximum_cura_version": "5.10", 7 | "description": "Provides an alternative, setting-centric interface based around a fixed sidebar", 8 | "api": "6.0.0", 9 | "supported_sdk_versions": [ 10 | "6.0.0", "6.1.0", "6.2.0", "6.3.0", 11 | "7.0.0", "7.1.0", "7.2.0", "7.3.0", "7.4.0", "7.5.0", "7.6.0", "7.7.0", "7.8.0", "7.9.0", 12 | "8.0.0", "8.1.0", "8.2.0", "8.3.0", "8.4.0", "8.5.0", "8.6.0", "8.7.0", "8.8.0", "8.9.0", "8.10.0" 13 | ], 14 | "i18n-catalog": "cura" 15 | } -------------------------------------------------------------------------------- /resources/icons/view_perspective.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /.github/workflows/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "author_id": "fieldofview", 4 | "display_name": "fieldOfView", 5 | "email": "aldo@fieldofview.com", 6 | "website": "http://fieldofview.com" 7 | }, 8 | "description": "Provides an alternative interface which restores the sidebar.\n\nThe development of this plugin can be sponsored via Github Sponsors (https://github.com/sponsors/fieldofview) or Paypal (https://www.paypal.me/fieldofview)", 9 | "display_name": "Sidebar GUI", 10 | "package_id": "SidebarGUIPlugin", 11 | "package_type": "plugin", 12 | "package_version": "0.0.0", 13 | "sdk_version": 0, 14 | "sdk_version_semver": "0.0.0", 15 | "website": "https://github.com/fieldOfView/Cura-SidebarGUIPlugin" 16 | } -------------------------------------------------------------------------------- /.github/workflows/create_tagged_release_with_packages.yaml: -------------------------------------------------------------------------------- 1 | name: "Cura-plugin release" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | create-curapackages: 10 | name: "Tagged Release" 11 | runs-on: "ubuntu-latest" 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | path: "build" 17 | submodules: "recursive" 18 | - uses: fieldOfView/cura-plugin-packager-action@main 19 | with: 20 | source_folder: "build" 21 | package_info_path: "build/.github/workflows/package.json" 22 | - uses: "marvinpinto/action-automatic-releases@latest" 23 | with: 24 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 25 | prerelease: false 26 | files: | 27 | *.curapackage 28 | -------------------------------------------------------------------------------- /resources/qml/TabRow.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | // Copyright (c) 2018 Ultimaker B.V. 5 | // Uranium is released under the terms of the LGPLv3 or higher. 6 | 7 | import QtQuick 2.10 8 | import QtQuick.Controls 2.3 9 | import UM 1.2 as UM 10 | 11 | /* 12 | * Wrapper around TabBar that uses our theming and more sane defaults. 13 | */ 14 | TabBar 15 | { 16 | id: base 17 | 18 | width: parent.width 19 | height: visible ? 4 * UM.Theme.getSize("default_margin").height : 0 20 | 21 | spacing: UM.Theme.getSize("narrow_margin").width //Space between the tabs. 22 | 23 | background: Rectangle 24 | { 25 | width: parent.width 26 | anchors.bottom: parent.bottom 27 | height: UM.Theme.getSize("default_lining").height 28 | color: UM.Theme.getColor("lining") 29 | visible: parent.enabled 30 | } 31 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.qmlc 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | 62 | #Ipython Notebook 63 | .ipynb_checkpoints 64 | -------------------------------------------------------------------------------- /resources/qml/MachineSelector40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Cura.MachineSelector 12 | { 13 | id: machineSelection 14 | headerCornerSide: Cura.RoundedRectangle.Direction.All 15 | 16 | Component.onCompleted: 17 | { 18 | if(isLE410) 19 | { 20 | machineSelection.children[1].visible = false // remove shadow 21 | } 22 | 23 | if(isLE46) 24 | { 25 | var machineSelectionHeader = machineSelection.children[0].children[3].children[0] 26 | } else { 27 | var machineSelectionHeader = machineSelection.children[0].children[3].children[1] 28 | } 29 | // adjust header margins, because the height is smaller than designed 30 | machineSelectionHeader.anchors.topMargin = 0 31 | machineSelectionHeader.anchors.bottomMargin = 0 32 | } 33 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cura Sidebar GUI Plugin 2 | 3 | This plugin replaces collapsible settings panel in Cura 4.x with a sidebar like the previous versions. This results in a more settings-centric UX, for those who prefer it. The sidebar can be closed and even opened in a seperate window. It also adds a legend to the Prepare stage and makes X-Ray view part of this legend. Some of the UX improvements spearheaded in this plugin trickle down to the default Cura interface. 4 | 5 | The continued development of this plugin can be sponsored via [Github Sponsors](https://github.com/sponsors/fieldofview) or [Paypal](https://www.paypal.me/fieldofview). 6 | 7 | ## Cura version compatibility 8 | 9 | When a new release of Cura is available, the Sidebar GUI plugin always needs to be updated. 10 | 11 | In order for the Sidebar GUI plugin to work, it does quite a bit of "nasty" patching of the original interface. These patches may break the interface when the original interface is changed. So, as a security measure, the plugin checks to see if it has been "approved" for the version of Cura you are using it with. 12 | 13 | An updated version of the plugin is usually created during the beta period, so when a new stable release of Cura is available, the plugin update is available in the Marketplace. Because updates to plugins need to be checked and approved by Ultimaker developers, there is always a bit of a delay. 14 | -------------------------------------------------------------------------------- /resources/qml/NoIntentIcon.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Ultimaker B.V. 2 | // Cura is released under the terms of the LGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.2 as UM 8 | import Cura 1.6 as Cura 9 | 10 | Item 11 | { 12 | id: icon 13 | property var affected_extruders 14 | property var intent_type: "" 15 | 16 | implicitWidth: UM.Theme.getSize("section_icon").width 17 | implicitHeight: UM.Theme.getSize("section_icon").height 18 | 19 | UM.RecolorImage 20 | { 21 | source: UM.Theme.getIcon("info") 22 | color: UM.Theme.getColor("icon") 23 | anchors.fill: parent 24 | } 25 | MouseArea 26 | { 27 | anchors.fill: parent 28 | hoverEnabled: parent.visible 29 | onEntered: 30 | { 31 | var tooltipContent = catalog.i18ncp("@label %1 is filled in with the type of a profile. %2 is filled with a list of numbers (eg '1' or '1, 2')", "There is no %1 profile for the configuration in extruder %2. The default intent will be used instead", "There is no %1 profile for the configurations in extruders %2. The default intent will be used instead", affected_extruders.length).arg(intent_type).arg(affected_extruders) 32 | base.showTooltip(icon.parent, Qt.point(0, 0), tooltipContent) 33 | } 34 | onExited: base.hideTooltip() 35 | } 36 | } -------------------------------------------------------------------------------- /resources/qml/MaterialSelection.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Cura.PrintSetupHeaderButton 11 | { 12 | id: materialSelection 13 | 14 | property bool valueError: Cura.MachineManager.activeStack !== null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible") !== "True" : true 15 | property bool valueWarning: !Cura.MachineManager.isActiveQualitySupported 16 | 17 | text: Cura.MachineManager.activeStack !== null ? Cura.MachineManager.activeStack.material.name : "" 18 | tooltip: text 19 | enabled: (configurationMenu.enabledCheckbox != undefined) ? configurationMenu.enabledCheckbox.checked : false 20 | 21 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 22 | width: (configurationMenu.selectors != undefined) ? configurationMenu.selectors.controlWidth : 0 23 | 24 | focusPolicy: Qt.ClickFocus 25 | 26 | MaterialMenu 27 | { 28 | id: materialsMenu 29 | width: materialSelection.width 30 | extruderIndex: Cura.ExtruderManager.activeExtruderIndex 31 | updateModels: materialSelection.visible 32 | } 33 | onClicked: materialsMenu.popup(0, height - UM.Theme.getSize("default_lining").height) 34 | } 35 | -------------------------------------------------------------------------------- /resources/qml/OpenFileButton40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Button 11 | { 12 | id: openFileButton 13 | 14 | height: UM.Theme.getSize("button").height 15 | width: height 16 | onClicked: Cura.Actions.open.trigger() 17 | hoverEnabled: true 18 | 19 | contentItem: Item 20 | { 21 | anchors.fill: parent 22 | UM.RecolorImage 23 | { 24 | id: buttonIcon 25 | anchors.centerIn: parent 26 | source: UM.Theme.getIcon("load") 27 | width: UM.Theme.getSize("button_icon").width 28 | height: UM.Theme.getSize("button_icon").height 29 | color: UM.Theme.getColor("icon") 30 | 31 | sourceSize.height: height 32 | } 33 | } 34 | 35 | background: Cura.RoundedRectangle 36 | { 37 | id: background 38 | height: UM.Theme.getSize("stage_menu").height 39 | width: UM.Theme.getSize("stage_menu").height 40 | 41 | radius: UM.Theme.getSize("default_radius").width 42 | cornerSide: Cura.RoundedRectangle.Direction.Right 43 | color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") 44 | border.width: UM.Theme.getSize("default_lining").width 45 | border.color: UM.Theme.getColor("lining") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /resources/qml/ModeToggleSwitch.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Switch 11 | { 12 | id: modeToggleSwitch 13 | text: catalog.i18nc("@button", "Custom") 14 | checked: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 15 | 16 | onCheckedChanged: printSetupSelector.contentItem.currentModeIndex = checked ? Cura.PrintSetupSelectorContents.Mode.Custom : Cura.PrintSetupSelectorContents.Mode.Recommended; 17 | 18 | indicator: Rectangle 19 | { 20 | implicitWidth: Math.floor(implicitHeight * 1.5) 21 | implicitHeight: UM.Theme.getSize("checkbox").height 22 | y: parent.height / 2 - height / 2 23 | radius: height / 2 24 | color: modeToggleSwitch.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("main_background") 25 | border.color: modeToggleSwitch.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("setting_control_border") 26 | 27 | Behavior on color { ColorAnimation { duration: 100 } } 28 | 29 | Rectangle 30 | { 31 | x: modeToggleSwitch.checked ? parent.width - width : 0 32 | width: height 33 | height: parent.implicitHeight 34 | radius: height / 2 35 | color: UM.Theme.getColor("main_background") 36 | border.color: UM.Theme.getColor("setting_control_border") 37 | 38 | Behavior on x { NumberAnimation { duration: 100 } } 39 | } 40 | } 41 | 42 | contentItem: Label 43 | { 44 | text: modeToggleSwitch.text 45 | verticalAlignment: Text.AlignVCenter 46 | height: parent.height 47 | font: UM.Theme.getFont("default") 48 | color: UM.Theme.getColor("text") 49 | renderType: Text.NativeRendering 50 | leftPadding: modeToggleSwitch.indicator.width 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | # The SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import os, json 5 | 6 | from UM.i18n import i18nCatalog 7 | 8 | i18n_catalog = i18nCatalog("cura") 9 | 10 | from UM.Version import Version 11 | from UM.Application import Application 12 | from UM.Logger import Logger 13 | 14 | from . import SidebarGUIPlugin 15 | from . import SidebarIncompatibleVersion 16 | 17 | 18 | def getMetaData(): 19 | return {} 20 | 21 | 22 | def register(app): 23 | if __matchVersion(): 24 | return {"extension": SidebarGUIPlugin.SidebarGUIPlugin()} 25 | else: 26 | Logger.log("w", "Plugin not loaded because of a version mismatch") 27 | return {"extension": SidebarIncompatibleVersion.SidebarIncompatibleVersion()} 28 | 29 | 30 | def __matchVersion(): 31 | cura_version = Application.getInstance().getVersion() 32 | if cura_version == "master" or cura_version == "dev": 33 | Logger.log("d", "Running Cura from source; skipping version check") 34 | return True 35 | if cura_version.startswith("Arachne_engine"): 36 | Logger.log("d", "Running Cura Arachne preview; skipping version check") 37 | return True 38 | 39 | cura_version = Version(cura_version) 40 | cura_version = Version([cura_version.getMajor(), cura_version.getMinor()]) 41 | 42 | # Get version information from plugin.json 43 | plugin_file_path = os.path.join( 44 | os.path.dirname(os.path.abspath(__file__)), "plugin.json" 45 | ) 46 | try: 47 | with open(plugin_file_path) as plugin_file: 48 | plugin_info = json.load(plugin_file) 49 | minimum_cura_version = Version(plugin_info["minimum_cura_version"]) 50 | maximum_cura_version = Version(plugin_info["maximum_cura_version"]) 51 | except Exception: 52 | Logger.log("w", "Could not get version information for the plugin") 53 | return False 54 | 55 | if cura_version >= minimum_cura_version and cura_version <= maximum_cura_version: 56 | return True 57 | else: 58 | Logger.log( 59 | "d", 60 | "This version of the plugin is not compatible with this version of Cura. Please check for an update.", 61 | ) 62 | return False 63 | -------------------------------------------------------------------------------- /resources/qml/TabRowButton.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | // Copyright (c) 2018 Ultimaker B.V. 5 | // Uranium is released under the terms of the LGPLv3 or higher. 6 | 7 | import QtQuick 2.10 8 | import QtQuick.Controls 2.3 9 | import UM 1.2 as UM 10 | 11 | /* 12 | * Wrapper around TabButton to use our theming and sane defaults. 13 | */ 14 | TabButton 15 | { 16 | anchors.top: parent.top 17 | height: parent.height 18 | checked: model.index == 0 //First button is checked by default. 19 | 20 | background: Rectangle 21 | { 22 | radius: UM.Theme.getSize("default_radius").width 23 | border.color: UM.Theme.getColor("lining") 24 | border.width: UM.Theme.getSize("default_lining").width 25 | color: UM.Theme.getColor(parent.checked ? "main_background" : (parent.hovered ? "action_button_hovered" : "secondary")) 26 | visible: enabled 27 | 28 | //Make the lining go straight down on the bottom side of the left and right sides. 29 | Rectangle 30 | { 31 | anchors.bottom: parent.bottom 32 | width: parent.width 33 | //We take almost the entire height of the tab button, since this "manual" lining has no anti-aliasing. 34 | //We can hardly prevent anti-aliasing on the border of the tab since the tabs are positioned with some spacing that is not necessarily a multiple of the number of tabs. 35 | height: parent.height - (parent.radius + parent.border.width) 36 | color: parent.border.color 37 | 38 | //Don't add lining at the bottom side. 39 | Rectangle 40 | { 41 | anchors 42 | { 43 | bottom: parent.bottom 44 | bottomMargin: parent.parent.parent.checked ? 0 : parent.parent.border.width //Allow margin if tab is not selected. 45 | left: parent.left 46 | leftMargin: parent.parent.border.width 47 | right: parent.right 48 | rightMargin: parent.parent.border.width 49 | } 50 | color: parent.parent.color 51 | height: parent.height - anchors.bottomMargin 52 | } 53 | } 54 | } 55 | contentItem: Label 56 | { 57 | anchors.centerIn: parent 58 | horizontalAlignment: Text.AlignHCenter 59 | verticalAlignment: Text.AlignVCenter 60 | text: parent.text 61 | font: parent.checked ? UM.Theme.getFont("default_bold") : UM.Theme.getFont("default") 62 | color: UM.Theme.getColor("text") 63 | renderType: Text.NativeRendering 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /resources/qml/StageMain.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.4 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.0 as UM 8 | 9 | Loader 10 | { 11 | id: loader 12 | source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : "" 13 | 14 | property bool sidebarVisible: 15 | { 16 | return base.viewportRect.width != 1; 17 | } 18 | 19 | onLoaded: 20 | { 21 | var viewString = UM.Controller.activeView + ""; 22 | var activeView = viewString.substr(0, viewString.indexOf("(")); 23 | if(activeView == "SimulationView") 24 | { 25 | var sidebarFooter = stageMenu.item.children[5]; 26 | 27 | var pathSlider = item.children[0]; 28 | pathSlider.anchors.horizontalCenter = undefined; 29 | pathSlider.anchors.right = pathSlider.parent.right; 30 | pathSlider.anchors.rightMargin = Qt.binding(function() 31 | { 32 | if(sidebarVisible) 33 | return UM.Theme.getSize("print_setup_widget").width + UM.Theme.getSize("default_margin").width; 34 | else 35 | return UM.Theme.getSize("default_margin").width; 36 | }); 37 | pathSlider.anchors.bottomMargin = Qt.binding(function() 38 | { 39 | if(sidebarVisible) 40 | return UM.Theme.getSize("default_margin").height; 41 | else 42 | return sidebarFooter.height + UM.Theme.getSize("default_margin").height; 43 | }); 44 | 45 | var layerSlider = item.children[2]; 46 | layerSlider.anchors.right = pathSlider.right; 47 | layerSlider.anchors.rightMargin = 0; 48 | layerSlider.anchors.verticalCenter = undefined; 49 | layerSlider.anchors.top = undefined; 50 | layerSlider.anchors.bottom = pathSlider.top; 51 | layerSlider.anchors.bottomMargin = UM.Theme.getSize("default_margin").height; 52 | layerSlider.height = Qt.binding(function() 53 | { 54 | var unavailableHeight = (stageMenu.item.children[2].height + pathSlider.height + 5 * UM.Theme.getSize("default_margin").height); 55 | if(!sidebarVisible) 56 | unavailableHeight = (sidebarFooter.height + stageMenu.item.children[3].height + pathSlider.height + 5 * UM.Theme.getSize("default_margin").height) 57 | 58 | return Math.min( 59 | UM.Theme.getSize("slider_layerview_size").height, 60 | contentItem.height - unavailableHeight 61 | ); 62 | }) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /resources/qml/MachineSelector53.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Cura.MachineSelector 12 | { 13 | id: machineSelection 14 | headerCornerSide: Cura.RoundedRectangle.Direction.All 15 | 16 | machineManager: Cura.MachineManager 17 | onSelectPrinter: function(machine) 18 | { 19 | toggleContent(); 20 | Cura.MachineManager.setActiveMachine(machine.id); 21 | } 22 | machineListModel: Cura.MachineListModel {} 23 | buttons: [ 24 | Cura.SecondaryButton 25 | { 26 | id: addPrinterButton 27 | leftPadding: UM.Theme.getSize("default_margin").width 28 | rightPadding: UM.Theme.getSize("default_margin").width 29 | text: catalog.i18nc("@button", "Add printer") 30 | // The maximum width of the button is half of the total space, minus the padding of the parent, the left 31 | // padding of the component and half the spacing because of the space between buttons. 32 | fixedWidthMode: true 33 | width: Math.round(parent.width / 2 - leftPadding * 1.5) 34 | onClicked: 35 | { 36 | machineSelection.toggleContent() 37 | Cura.Actions.addMachine.trigger() 38 | } 39 | }, 40 | Cura.SecondaryButton 41 | { 42 | id: managePrinterButton 43 | leftPadding: UM.Theme.getSize("default_margin").width 44 | rightPadding: UM.Theme.getSize("default_margin").width 45 | text: catalog.i18nc("@button", "Manage printers") 46 | fixedWidthMode: true 47 | // The maximum width of the button is half of the total space, minus the padding of the parent, the right 48 | // padding of the component and half the spacing because of the space between buttons. 49 | width: Math.round(parent.width / 2 - rightPadding * 1.5) 50 | onClicked: 51 | { 52 | machineSelection.toggleContent() 53 | Cura.Actions.configureMachines.trigger() 54 | } 55 | } 56 | ] 57 | 58 | Component.onCompleted: 59 | { 60 | if(isLE410) 61 | { 62 | machineSelection.children[1].visible = false // remove shadow 63 | } 64 | 65 | if(isLE46) 66 | { 67 | var machineSelectionHeader = machineSelection.children[0].children[3].children[0] 68 | } else { 69 | var machineSelectionHeader = machineSelection.children[0].children[3].children[1] 70 | } 71 | // adjust header margins, because the height is smaller than designed 72 | machineSelectionHeader.anchors.topMargin = 0 73 | machineSelectionHeader.anchors.bottomMargin = 0 74 | } 75 | } -------------------------------------------------------------------------------- /resources/qml/ProfileSelector40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Item 11 | { 12 | width: parent.width - 2 * UM.Theme.getSize("thick_margin").width - 2 * UM.Theme.getSize("default_lining").width 13 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 14 | 15 | Cura.GlobalProfileSelector 16 | { 17 | id: globalProfileSelector 18 | visible: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 19 | anchors 20 | { 21 | left: parent.left 22 | right: modeToggleSwitch.left 23 | rightMargin: UM.Theme.getSize("default_margin").width 24 | } 25 | 26 | Component.onCompleted: 27 | { 28 | globalProfileSelector.children[0].visible = false // "Profile:" label 29 | // dropdown 30 | globalProfileSelector.children[1].width = parent.width - modeToggleSwitch.width - 31 | (2 * UM.Theme.getSize("default_margin").width + UM.Theme.getSize("thick_margin").width - 3 * UM.Theme.getSize("default_lining").width) 32 | } 33 | } 34 | 35 | ModeToggleSwitch 36 | { 37 | id: modeToggleSwitch 38 | anchors.right: dockButton.left 39 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 40 | } 41 | 42 | UM.SimpleButton 43 | { 44 | id: dockButton 45 | anchors 46 | { 47 | verticalCenter: collapseButton.verticalCenter 48 | 49 | right: collapseButton.left 50 | rightMargin: UM.Theme.getSize("default_margin").width 51 | } 52 | iconSource: settingsDocked ? "../icons/settings_undock.svg" : "../icons/settings_dock.svg" 53 | width: UM.Theme.getSize("print_setup_icon").width 54 | height: UM.Theme.getSize("print_setup_icon").height 55 | color: UM.Theme.getColor("small_button_text") 56 | 57 | onClicked: 58 | { 59 | UM.Preferences.setValue("sidebargui/docked_sidebar", !UM.Preferences.getValue("sidebargui/docked_sidebar")) 60 | stageMenu.settingsDocked = UM.Preferences.getValue("sidebargui/docked_sidebar") 61 | } 62 | } 63 | 64 | UM.SimpleButton 65 | { 66 | id: collapseButton 67 | anchors 68 | { 69 | top: parent.top 70 | topMargin: UM.Theme.getSize("default_margin").width 71 | 72 | right: parent.right 73 | rightMargin: UM.Theme.getSize("default_margin").width 74 | } 75 | iconSource: UM.Theme.getIcon("cross1") 76 | width: UM.Theme.getSize("default_arrow").width 77 | height: UM.Theme.getSize("default_arrow").height 78 | color: UM.Theme.getColor("small_button_text") 79 | 80 | onClicked: 81 | { 82 | UM.Preferences.setValue("view/settings_visible", false) 83 | stageMenu.settingsVisible = false 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /resources/qml/SidebarFooter.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Cura.RoundedRectangle 11 | { 12 | id: actionRow 13 | 14 | anchors.bottom: bottomRight.bottom 15 | anchors.right: bottomRight.right 16 | 17 | border.width: UM.Theme.getSize("default_lining").width 18 | border.color: UM.Theme.getColor("lining") 19 | color: UM.Theme.getColor("main_background") 20 | 21 | cornerSide: Cura.RoundedRectangle.Direction.Left 22 | radius: UM.Theme.getSize("default_radius").width 23 | 24 | Cura.ActionPanelWidget 25 | { 26 | id: actionPanelWidget 27 | visible: CuraApplication.platformActivity 28 | width: parent.width 29 | anchors.bottom: parent.bottom 30 | 31 | property string activeViewId: 32 | { 33 | var viewString = UM.Controller.activeView + ""; 34 | return viewString.substr(0, viewString.indexOf("(")); 35 | } 36 | 37 | onActiveViewIdChanged: updateHasPreviewButton(); 38 | 39 | function updateHasPreviewButton() 40 | { 41 | var actionPanelRect = actionPanelWidget.children[0]; 42 | 43 | if(actionPanelRect.outputAvailable) 44 | { 45 | actionPanelRect.children[0].item.hasPreviewButton = (actionPanelWidget.activeViewId != "SimulationView"); 46 | } 47 | } 48 | 49 | Connections 50 | { 51 | target: actionPanelWidget.children[0].children[0] 52 | onLoaded: 53 | { 54 | actionPanelWidget.updateHasPreviewButton(); 55 | 56 | // change left anchor of the time and cost information panel 57 | var actionPanelRect = actionPanelWidget.children[0]; 58 | if(actionPanelRect.children[0].item != null && actionPanelRect.children[0].item.children[0] != null) 59 | { 60 | var timeAndCostsInformation = actionPanelRect.children[0].item.children[0].children[1]; 61 | if (timeAndCostsInformation != undefined) 62 | { 63 | timeAndCostsInformation.anchors.leftMargin = Qt.binding(function() { return -actionPanelWidget.children[1].width - UM.Theme.getSize("thin_margin").width }); 64 | } 65 | } 66 | } 67 | } 68 | 69 | Component.onCompleted: 70 | { 71 | var actionPanelRect = actionPanelWidget.children[0]; 72 | var actionPanelAdditionals = actionPanelWidget.children[1]; 73 | 74 | actionPanelRect.border.width = 0; 75 | actionPanelRect.color = "transparent"; 76 | 77 | actionPanelAdditionals.anchors.right = undefined; 78 | actionPanelAdditionals.anchors.left = actionPanelAdditionals.parent.left; 79 | actionPanelAdditionals.anchors.leftMargin = UM.Theme.getSize("thick_margin").width; 80 | actionPanelAdditionals.anchors.bottomMargin = UM.Theme.getSize("thick_margin").height * 2 - UM.Theme.getSize("default_lining").height * 3; 81 | 82 | actionPanelRect.width = undefined; 83 | actionPanelRect.anchors.left = actionPanelAdditionals.right; 84 | actionPanelRect.anchors.leftMargin = -UM.Theme.getSize("default_margin").width; 85 | actionPanelRect.anchors.right = actionPanelRect.parent.right; 86 | } 87 | } 88 | 89 | width: UM.Theme.getSize("print_setup_widget").width 90 | height: CuraApplication.platformActivity ? actionPanelWidget.height : 0 91 | } 92 | -------------------------------------------------------------------------------- /resources/qml/MonitorStageMenu.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Item 11 | { 12 | id: stageMenu 13 | 14 | signal showTooltip(Item item, point location, string text) 15 | signal hideTooltip() 16 | 17 | property bool is40 18 | property bool isLE44 19 | property bool isLE46 20 | property bool isLE410 21 | property bool isLE413 22 | property bool isLE51 23 | property bool isLE52 24 | 25 | Component.onCompleted: 26 | { 27 | is40 = (CuraSDKVersion == "6.0.0") 28 | isLE44 = (CuraSDKVersion <= "7.0.0") 29 | isLE46 = (CuraSDKVersion <= "7.2.0") 30 | isLE410 = (CuraSDKVersion <= "7.6.0") 31 | isLE413 = (CuraSDKVersion <= "7.9.0") 32 | isLE51 = (CuraSDKVersion <= "8.1.0") 33 | isLE52 = (CuraSDKVersion <= "8.2.0") 34 | 35 | // adjust message stack position for sidebar 36 | var messageStack 37 | if(is40) 38 | { 39 | messageStack = base.contentItem.children[0].children[3].children[7] 40 | } 41 | else if(isLE44) 42 | { 43 | messageStack = base.contentItem.children[2].children[3].children[7] 44 | } 45 | else if(isLE413) 46 | { 47 | messageStack = base.contentItem.children[2].children[3].children[8] 48 | } 49 | else if(isLE52) 50 | { 51 | messageStack = base.contentItem.children[3].children[3].children[8] 52 | } 53 | else 54 | { 55 | messageStack = base.contentItem.children[4].children[3].children[8] 56 | } 57 | messageStack.anchors.horizontalCenter = undefined 58 | messageStack.anchors.left = messageStack.parent.left 59 | messageStack.anchors.leftMargin = Qt.binding(function() 60 | { 61 | return Math.floor((base.width - printSetupSelector.width) / 2) 62 | }) 63 | 64 | // adjust stages menu position for sidebar 65 | var stagesListContainer = mainWindowHeader.children[1] 66 | stagesListContainer.anchors.horizontalCenter = undefined 67 | stagesListContainer.anchors.left = stagesListContainer.parent.left 68 | stagesListContainer.anchors.leftMargin = Qt.binding(function() 69 | { 70 | return Math.floor((base.width - printSetupSelector.width - stagesListContainer.width) / 2) 71 | }) 72 | 73 | // hide application logo if there is no room for it 74 | var applicationLogo = mainWindowHeader.children[0] 75 | applicationLogo.visible = Qt.binding(function() 76 | { 77 | return stagesListContainer.anchors.leftMargin > applicationLogo.width + 2 * UM.Theme.getSize("default_margin").width 78 | }) 79 | } 80 | 81 | Loader 82 | { 83 | anchors.right: parent.right 84 | anchors.rightMargin: UM.Theme.getSize("print_setup_widget").width - width 85 | width: UM.Theme.getSize("machine_selector_widget").width 86 | height: 87 | { 88 | if (isLE413) 89 | { 90 | return UM.Theme.getSize("main_window_header_button").height 91 | } else { 92 | return Math.round(0.5 * UM.Theme.getSize("main_window_header").height) 93 | } 94 | } 95 | y: - Math.floor((UM.Theme.getSize("main_window_header").height + height) / 2) 96 | 97 | source: 98 | { 99 | if(isLE52) { 100 | return "MachineSelector40.qml"; 101 | } else { 102 | return "MachineSelector53.qml"; 103 | } 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /resources/qml/PrintSetupSummary.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Cura.RoundedRectangle 11 | { 12 | property bool summaryEnabled: (prepareStageActive || !preSlicedData) 13 | 14 | color: 15 | { 16 | if (!summaryEnabled) 17 | { 18 | return UM.Theme.getColor("action_button_disabled") 19 | } 20 | else if (mouseArea.containsMouse) 21 | { 22 | return UM.Theme.getColor("action_button_hovered") 23 | } 24 | return UM.Theme.getColor("action_button") 25 | } 26 | 27 | border.width: UM.Theme.getSize("default_lining").width 28 | border.color: UM.Theme.getColor("lining") 29 | radius: UM.Theme.getSize("default_radius").width 30 | cornerSide: Cura.RoundedRectangle.Direction.Left 31 | 32 | height: 33 | { 34 | var result = printSetupSummary.height + 2 * UM.Theme.getSize("default_margin").height 35 | if (extruderSummary.visible) 36 | { 37 | result += extruderSummary.height + UM.Theme.getSize("default_margin").height 38 | } 39 | return result 40 | } 41 | 42 | Cura.PrintSetupSelectorHeader 43 | { 44 | id: printSetupSummary 45 | visible: summaryEnabled 46 | 47 | anchors 48 | { 49 | left: parent.left 50 | right: parent.right 51 | top: parent.top 52 | 53 | leftMargin: UM.Theme.getSize("default_margin").width 54 | topMargin: UM.Theme.getSize("default_margin").width 55 | } 56 | } 57 | 58 | property bool hasMaterials: 59 | { 60 | if (CuraSDKVersion >= "6.2.0") { 61 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasMaterials : false 62 | } else { 63 | return Cura.MachineManager.hasMaterials 64 | } 65 | } 66 | property bool hasVariants: 67 | { 68 | if (CuraSDKVersion >= "6.2.0") { 69 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasVariants : false 70 | } else { 71 | return Cura.MachineManager.hasVariants 72 | } 73 | } 74 | 75 | Loader 76 | { 77 | id: extruderSummary 78 | enabled: false 79 | visible: summaryEnabled && (hasMaterials || hasVariants) 80 | 81 | anchors 82 | { 83 | left: parent.left 84 | right: parent.right 85 | bottom: parent.bottom 86 | 87 | leftMargin: UM.Theme.getSize("default_margin").width 88 | rightMargin: UM.Theme.getSize("default_margin").width 89 | bottomMargin: UM.Theme.getSize("default_margin").width 90 | } 91 | 92 | source: 93 | { 94 | if(isLE410) { 95 | return "ExtruderTabs40.qml"; 96 | } else if (isLE413) { 97 | return "ExtruderTabs411.qml"; 98 | } else { 99 | return "ExtruderTabs50.qml"; 100 | } 101 | } 102 | } 103 | 104 | Label 105 | { 106 | visible: !summaryEnabled 107 | anchors.top: parent.top 108 | anchors.topMargin: UM.Theme.getSize("thick_margin").height 109 | anchors.left: parent.left 110 | anchors.leftMargin: UM.Theme.getSize("thick_margin").height 111 | width: parent.width 112 | font: UM.Theme.getFont("medium_bold") 113 | color: UM.Theme.getColor("text") 114 | renderType: Text.NativeRendering 115 | 116 | text: catalog.i18nc("@label shown when we load a Gcode file", "Print setup disabled. G-code file can not be modified.") 117 | } 118 | 119 | MouseArea 120 | { 121 | id: mouseArea 122 | hoverEnabled: true 123 | enabled: summaryEnabled 124 | anchors.fill: parent 125 | 126 | onClicked: 127 | { 128 | UM.Preferences.setValue("view/settings_visible", true) 129 | stageMenu.settingsVisible = true 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /resources/qml/MaterialMenu.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | // Copyright (c) 2022 Ultimaker B.V. 4 | // Cura is released under the terms of the LGPLv3 or higher. 5 | 6 | import QtQuick 2.7 7 | import QtQuick.Controls 2.4 8 | 9 | import UM 1.5 as UM 10 | import Cura 1.0 as Cura 11 | 12 | Cura.Menu 13 | { 14 | id: materialMenu 15 | title: catalog.i18nc("@label:category menu label", "Material") 16 | 17 | property int extruderIndex: 0 18 | property string currentRootMaterialId: 19 | { 20 | var value = Cura.MachineManager.currentRootMaterialId[extruderIndex] 21 | return (value === undefined) ? "" : value 22 | } 23 | property var activeExtruder: 24 | { 25 | var activeMachine = Cura.MachineManager.activeMachine 26 | return (activeMachine === null) ? null : activeMachine.extruderList[extruderIndex] 27 | } 28 | property bool isActiveExtruderEnabled: (activeExtruder === null || activeExtruder === undefined) ? false : activeExtruder.isEnabled 29 | 30 | property string activeMaterialId: (activeExtruder === null || activeExtruder === undefined) ? "" : activeExtruder.material.id 31 | property bool updateModels: true 32 | Cura.FavoriteMaterialsModel 33 | { 34 | id: favoriteMaterialsModel 35 | extruderPosition: materialMenu.extruderIndex 36 | enabled: updateModels 37 | } 38 | 39 | Cura.GenericMaterialsModel 40 | { 41 | id: genericMaterialsModel 42 | extruderPosition: materialMenu.extruderIndex 43 | enabled: updateModels 44 | } 45 | 46 | Cura.MaterialBrandsModel 47 | { 48 | id: brandModel 49 | extruderPosition: materialMenu.extruderIndex 50 | enabled: updateModels 51 | } 52 | 53 | Cura.MenuItem 54 | { 55 | text: catalog.i18nc("@label:category menu label", "Favorites") 56 | enabled: false 57 | visible: favoriteMaterialsModel.items.length > 0 58 | } 59 | 60 | Instantiator 61 | { 62 | model: favoriteMaterialsModel 63 | delegate: Cura.MenuItem 64 | { 65 | text: model.brand + " " + model.name 66 | checkable: true 67 | enabled: isActiveExtruderEnabled 68 | checked: model.root_material_id === materialMenu.currentRootMaterialId 69 | onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) 70 | } 71 | onObjectAdded: function(index, object) { materialMenu.insertItem(index + 1, object) } 72 | onObjectRemoved: function(index, object) { materialMenu.removeItem(index) } 73 | } 74 | 75 | Cura.MenuSeparator { visible: favoriteMaterialsModel.items.length > 0} 76 | 77 | Cura.Menu 78 | { 79 | id: genericMenu 80 | title: catalog.i18nc("@label:category menu label", "Generic") 81 | 82 | Instantiator 83 | { 84 | model: genericMaterialsModel 85 | delegate: Cura.MenuItem 86 | { 87 | text: model.name 88 | checkable: true 89 | enabled: isActiveExtruderEnabled 90 | checked: model.root_material_id === materialMenu.currentRootMaterialId 91 | onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) 92 | } 93 | onObjectAdded: function(index, object) { genericMenu.insertItem(index, object)} 94 | onObjectRemoved: function(index, object) {genericMenu.removeItem(index) } 95 | } 96 | } 97 | 98 | Cura.MenuSeparator {} 99 | 100 | Instantiator 101 | { 102 | model: brandModel 103 | delegate: MaterialBrandMenu 104 | { 105 | materialTypesModel: model 106 | } 107 | onObjectAdded: function(index, object) { materialMenu.insertItem(index + 4, object)} 108 | onObjectRemoved: function(index, object) { materialMenu.removeItem(index) } 109 | } 110 | 111 | Cura.MenuSeparator {} 112 | 113 | Cura.MenuItem 114 | { 115 | action: Cura.Actions.manageMaterials 116 | } 117 | 118 | Cura.MenuSeparator {} 119 | 120 | Cura.MenuItem 121 | { 122 | action: Cura.Actions.marketplaceMaterials 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SidebarGUIProxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | # The SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | from UM.Application import Application 5 | from UM.Logger import Logger 6 | from UM.FlameProfiler import pyqtSlot 7 | 8 | try: 9 | from cura.ApplicationMetadata import CuraSDKVersion 10 | except ImportError: # Cura <= 3.6 11 | CuraSDKVersion = "6.0.0" 12 | if CuraSDKVersion >= "8.0.0": 13 | from PyQt6.QtCore import QObject, QRectF 14 | else: 15 | from PyQt5.QtCore import QObject, QRectF 16 | 17 | try: 18 | from cura.Machines.ContainerTree import ContainerTree 19 | except ImportError: 20 | ContainerTree = None # type: ignore 21 | 22 | from typing import TYPE_CHECKING 23 | 24 | if TYPE_CHECKING: 25 | from cura.Settings.ExtruderStack import ExtruderStack 26 | 27 | class SidebarGUIProxy(QObject): 28 | def __init__(self, parent=None) -> None: 29 | super().__init__(parent) 30 | Logger.log("d", "SidebarGUI proxy created") 31 | 32 | @pyqtSlot("QVariant", result=bool) 33 | def getExtruderHasQualityForMaterial(self, extruder_stack: "ExtruderStack") -> bool: 34 | application = Application.getInstance() 35 | global_stack = application.getGlobalContainerStack() 36 | if not global_stack or not extruder_stack: 37 | return False 38 | 39 | if not global_stack.getMetaDataEntry("has_materials"): 40 | return True 41 | 42 | if ContainerTree is not None: 43 | # Post Cura 4.4; use ContainerTree to find out if there are supported qualities 44 | machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()] 45 | 46 | active_variant_name = extruder_stack.variant.getMetaDataEntry("name") 47 | try: 48 | active_variant_node = machine_node.variants[active_variant_name] 49 | except KeyError: 50 | Logger.log("w", "Could not find the variant %s", active_variant_name) 51 | return True 52 | 53 | material_base_file = extruder_stack.material.getMetaDataEntry("base_file") 54 | try: 55 | active_material_node = active_variant_node.materials[material_base_file] 56 | except KeyError: 57 | Logger.log("w", "Could not find material %s for the current variant", material_base_file) 58 | return False 59 | 60 | active_material_node_qualities = active_material_node.qualities 61 | if not active_material_node_qualities: 62 | return False 63 | return list(active_material_node_qualities.keys())[0] != "empty_quality" 64 | 65 | else: 66 | # Pre Cura 4.4; use MaterialManager et al to find out if there are supported qualities 67 | search_criteria = { 68 | "type": "quality", 69 | } 70 | 71 | if global_stack.getMetaDataEntry("has_machine_quality"): 72 | search_criteria["definition"] = global_stack.getMetaDataEntry( 73 | "quality_definition", global_stack.definition.id 74 | ) 75 | else: 76 | return True 77 | 78 | container_registry = application.getContainerRegistry() 79 | if hasattr(application, "getMaterialManager"): 80 | material_manager = application.getMaterialManager() 81 | 82 | fallback_material = ( 83 | material_manager.getFallbackMaterialIdByMaterialType( 84 | extruder_stack.material.getMetaDataEntry("material") 85 | ) 86 | ) 87 | search_criteria["material"] = fallback_material 88 | 89 | if global_stack.getMetaDataEntry("has_variants"): 90 | search_criteria["variant"] = extruder_stack.variant.name 91 | 92 | containers_metadata = container_registry.findInstanceContainersMetadata( 93 | **search_criteria 94 | ) 95 | 96 | return containers_metadata != [] 97 | 98 | @pyqtSlot("QVariant", result=bool) 99 | def checkRectangleOnScreen(self, rectangle): 100 | # Check if rectangle is not outside the currently available screens 101 | application = Application.getInstance() 102 | screen_found = False 103 | try: 104 | # Qt6, Cura 5.0 and later 105 | for screen in application.screens(): 106 | if rectangle.intersects(QRectF(screen.availableGeometry())): 107 | screen_found = True 108 | break 109 | except AttributeError: 110 | # Qt5, Cura 4.13 and before 111 | for screen_number in range(0, application.desktop().screenCount()): 112 | if rectangle.intersects( 113 | QRectF(application.desktop().availableGeometry(screen_number)) 114 | ): 115 | screen_found = True 116 | break 117 | if not screen_found: 118 | return False 119 | return True 120 | -------------------------------------------------------------------------------- /SidebarIncompatibleVersion.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | # The SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import os.path 5 | import json 6 | 7 | try: 8 | from cura.ApplicationMetadata import CuraSDKVersion 9 | except ImportError: # Cura <= 3.6 10 | CuraSDKVersion = "6.0.0" 11 | if CuraSDKVersion >= "8.0.0": 12 | from PyQt6.QtCore import QUrl 13 | from PyQt6.QtGui import QDesktopServices 14 | else: 15 | from PyQt5.QtCore import QUrl 16 | from PyQt5.QtGui import QDesktopServices 17 | 18 | from cura.CuraApplication import CuraApplication 19 | from UM.Extension import Extension 20 | from UM.Message import Message 21 | from UM.Version import Version 22 | 23 | from UM.i18n import i18nCatalog 24 | 25 | i18n_catalog = i18nCatalog("cura") 26 | 27 | 28 | class SidebarIncompatibleVersion(Extension): 29 | HIDE_MESSAGE_PREFERENCE = "sidebargui/hide_incompatibility_message" 30 | 31 | def __init__(self): 32 | super().__init__() 33 | 34 | cura_version = CuraApplication.getInstance().getVersion() 35 | if cura_version == "master" or cura_version == "dev": 36 | cura_version = "" 37 | if cura_version.startswith("Arachne_engine"): 38 | cura_version = "" 39 | 40 | cura_version = Version(cura_version) 41 | cura_version = Version([cura_version.getMajor(), cura_version.getMinor()]) 42 | 43 | # Get version information from plugin.json 44 | plugin_file_path = os.path.join( 45 | os.path.dirname(os.path.abspath(__file__)), "plugin.json" 46 | ) 47 | try: 48 | with open(plugin_file_path) as plugin_file: 49 | plugin_info = json.load(plugin_file) 50 | plugin_version = Version(plugin_info["version"]) 51 | except Exception: 52 | plugin_version = Version() 53 | 54 | self._version_combination = "%s/%s" % (str(cura_version), str(plugin_version)) 55 | 56 | preferences = CuraApplication.getInstance().getPreferences() 57 | preferences.addPreference(self.HIDE_MESSAGE_PREFERENCE, []) 58 | if self._version_combination in preferences.getValue(self.HIDE_MESSAGE_PREFERENCE): 59 | return 60 | 61 | self._incompatibility_message = Message( 62 | i18n_catalog.i18nc( 63 | "@info:status", 64 | "Using this version of the Sidebar GUI plugin with this version of Cura is untested and might cause issues so it is disabled. " 65 | "An updated version of the plugin may be available on github and/or (soon) in the Marketplace.", 66 | ), 67 | title=i18n_catalog.i18nc("@label", "Sidebar GUI"), 68 | option_text=i18n_catalog.i18nc("@info:option_text", "Do not show this message again for this version of Cura"), 69 | option_state=False, 70 | ) 71 | self._incompatibility_message.optionToggled.connect(self._onDontTellMeAgain) 72 | self._incompatibility_message.addAction("github", 73 | name = i18n_catalog.i18nc("@action:button", "Open Github..."), 74 | icon = "", 75 | description = "Visit the releases page on Github to check for a new version", 76 | button_align = Message.ActionButtonAlignment.ALIGN_LEFT, 77 | button_style=2) 78 | self._incompatibility_message.addAction("marketplace", 79 | name = i18n_catalog.i18nc("@action:button", "Marketplace..."), 80 | icon = "", 81 | description = "Open Marketplace to check for a new version", 82 | button_align = Message.ActionButtonAlignment.ALIGN_LEFT, 83 | button_style=2) 84 | self._incompatibility_message.addAction("dismiss", 85 | name = i18n_catalog.i18nc("@action:button", "Ok"), 86 | icon = "", 87 | description = "Dismiss this message", 88 | button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) 89 | self._incompatibility_message.actionTriggered.connect(self._onMessageAction) 90 | self._incompatibility_message.show() 91 | 92 | def _onDontTellMeAgain(self, checked: bool) -> None: 93 | preferences = CuraApplication.getInstance().getPreferences() 94 | 95 | version_combinations = set(preferences.getValue(self.HIDE_MESSAGE_PREFERENCE).split(",")) 96 | if checked: 97 | version_combinations.add(self._version_combination) 98 | else: 99 | version_combinations.discard(self._version_combination) 100 | preferences.setValue(self.HIDE_MESSAGE_PREFERENCE, ";".join(version_combinations)) 101 | 102 | def _onMessageAction(self, message, action) -> None: 103 | if action=="dismiss": 104 | message.hide() 105 | 106 | elif action=="github": 107 | QDesktopServices.openUrl(QUrl("https://github.com/fieldOfView/Cura-SidebarGUIPlugin/releases")) 108 | 109 | elif action=="marketplace": 110 | marketplace = CuraApplication.getInstance().getPluginRegistry().getPluginObject("Marketplace") 111 | if marketplace: 112 | marketplace.show() 113 | 114 | -------------------------------------------------------------------------------- /SidebarGUIPlugin.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | # The SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import os.path 5 | from UM.Application import Application 6 | from UM.Extension import Extension 7 | from UM.Logger import Logger 8 | 9 | from .SidebarGUIProxy import SidebarGUIProxy 10 | 11 | 12 | class SidebarGUIPlugin(Extension): 13 | def __init__(self): 14 | super().__init__() 15 | 16 | self._prepare_stage_view_id = "SolidView" # can be "SolidView" or "XRayView" 17 | 18 | Application.getInstance().pluginsLoaded.connect(self._onPluginsLoaded) 19 | preferences = Application.getInstance().getPreferences() 20 | preferences.addPreference("sidebargui/expand_extruder_configuration", False) 21 | preferences.addPreference("sidebargui/expand_legend", True) 22 | preferences.addPreference("sidebargui/docked_sidebar", True) 23 | preferences.addPreference("sidebargui/settings_window_left", 65535) 24 | preferences.addPreference("sidebargui/settings_window_top", 65535) 25 | preferences.addPreference("sidebargui/settings_window_height", 0) 26 | 27 | self._controller = Application.getInstance().getController() 28 | self._controller.activeStageChanged.connect(self._onStageChanged) 29 | self._controller.activeViewChanged.connect(self._onViewChanged) 30 | 31 | self._proxy = SidebarGUIProxy() 32 | 33 | def _onPluginsLoaded(self): 34 | # delayed connection to engineCreatedSignal to force this plugin to receive that signal 35 | # AFTER the original stages are created 36 | Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) 37 | 38 | def _onEngineCreated(self): 39 | Logger.log("d", "Registering replacement stages") 40 | 41 | engine = Application.getInstance()._qml_engine 42 | engine.rootContext().setContextProperty("SidebarGUIPlugin", self._proxy) 43 | 44 | sidebar_component_path = os.path.join( 45 | os.path.dirname(os.path.abspath(__file__)), 46 | "resources", 47 | "qml", 48 | "SidebarStageMenu.qml", 49 | ) 50 | main_component_path = os.path.join( 51 | os.path.dirname(os.path.abspath(__file__)), 52 | "resources", 53 | "qml", 54 | "StageMain.qml", 55 | ) 56 | monitor_menu_component_path = os.path.join( 57 | os.path.dirname(os.path.abspath(__file__)), 58 | "resources", 59 | "qml", 60 | "MonitorStageMenu.qml", 61 | ) 62 | 63 | prepare_stage = self._controller.getStage("PrepareStage") 64 | prepare_stage.addDisplayComponent("menu", sidebar_component_path) 65 | prepare_stage.addDisplayComponent("main", main_component_path) 66 | 67 | preview_stage = self._controller.getStage("PreviewStage") 68 | preview_stage.addDisplayComponent("menu", sidebar_component_path) 69 | preview_stage.addDisplayComponent("main", main_component_path) 70 | 71 | # SmartSlicePlugin stage is provided by the SmartSlicePlugin plugin 72 | if "SmartSlicePlugin" in self._controller.getAllStages(): 73 | smartslice_stage = self._controller.getStage("SmartSlicePlugin") 74 | smartslice_stage.addDisplayComponent("menu", sidebar_component_path) 75 | smartslice_stage.addDisplayComponent("main", main_component_path) 76 | 77 | monitor_stage = self._controller.getStage("MonitorStage") 78 | monitor_stage.addDisplayComponent("menu", monitor_menu_component_path) 79 | 80 | def _onStageChanged(self): 81 | active_stage_id = self._controller.getActiveStage().getPluginId() 82 | view_id = "" 83 | 84 | if active_stage_id == "PrepareStage": 85 | view_id = self._prepare_stage_view_id 86 | elif active_stage_id == "PreviewStage": 87 | view_id = "SimulationView" 88 | 89 | if view_id and ( 90 | self._controller.getActiveView() is None 91 | or view_id != self._controller.getActiveView().getPluginId() 92 | ): 93 | self._controller.setActiveView(view_id) 94 | 95 | def _onViewChanged(self): 96 | active_stage_id = self._controller.getActiveStage().getPluginId() 97 | active_view_id = self._controller.getActiveView().getPluginId() 98 | 99 | if ( 100 | active_stage_id == "SmartSlicePlugin" 101 | ): # SmartSlicePlugin view is provided by the SmartSlicePlugin plugin 102 | return 103 | 104 | if active_stage_id == "PrepareStage": 105 | if active_view_id not in ["SolidView", "XRayView"]: 106 | self._controller.setActiveView("SolidView") 107 | return 108 | self._prepare_stage_view_id = active_view_id 109 | elif active_stage_id == "MonitorStage": 110 | return 111 | 112 | if active_view_id in [ 113 | "SimulationView", 114 | "FastView", 115 | ]: # FastView is provided by the RAWMouse plugin 116 | if active_stage_id != "PreviewStage": 117 | self._controller.setActiveStage("PreviewStage") 118 | else: 119 | if active_stage_id != "PrepareStage": 120 | self._controller.setActiveStage("PrepareStage") 121 | -------------------------------------------------------------------------------- /resources/qml/SidebarToolWindow.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Window 2.2 4 | 5 | import UM 1.3 as UM 6 | import Cura 1.1 as Cura 7 | 8 | Window 9 | { 10 | id: sidebarToolWindow 11 | title: catalog.i18nc("@title:window", "Print Settings") 12 | 13 | flags: Qt.Tool | Qt.WindowTitleHint | Qt.CustomizeWindowHint; 14 | 15 | function boolCheck(value) //Hack to ensure a good match between python and qml. 16 | { 17 | if(value == "True") 18 | { 19 | return true 20 | }else if(value == "False" || value == undefined) 21 | { 22 | return false 23 | } 24 | else 25 | { 26 | return value 27 | } 28 | } 29 | 30 | minimumWidth: UM.Theme.getSize("print_setup_widget").width 31 | maximumWidth: minimumWidth 32 | width: minimumWidth 33 | 34 | minimumHeight: Math.floor(1.5 * minimumWidth) 35 | height: UM.Preferences.getValue("sidebargui/settings_window_height") != 0 ? UM.Preferences.getValue("sidebargui/settings_window_height") : minimumHeight 36 | onHeightChanged: UM.Preferences.setValue("sidebargui/settings_window_height", height) 37 | 38 | x: 39 | { 40 | if (UM.Preferences.getValue("sidebargui/settings_window_left") != 65535 && boolCheck(UM.Preferences.getValue("general/restore_window_geometry"))) 41 | return UM.Preferences.getValue("sidebargui/settings_window_left") 42 | return base.x + base.width - sidebarToolWindow.width + UM.Theme.getSize("wide_margin").width 43 | } 44 | y: 45 | { 46 | if (UM.Preferences.getValue("sidebargui/settings_window_top") != 65535 && boolCheck(UM.Preferences.getValue("general/restore_window_geometry"))) 47 | return UM.Preferences.getValue("sidebargui/settings_window_top") 48 | return base.y + UM.Theme.getSize("wide_margin").width 49 | } 50 | onXChanged: UM.Preferences.setValue("sidebargui/settings_window_left", x) 51 | onYChanged: UM.Preferences.setValue("sidebargui/settings_window_top", y) 52 | 53 | property string toolTipText 54 | property int toolTipY 55 | function showTooltip() 56 | { 57 | toolTip.text = toolTipText 58 | toolTip.target = Qt.point(4 * UM.Theme.getSize("default_margin").width, toolTipY) 59 | if (toolTipY < (sidebarToolWindow.height / 2)) 60 | { 61 | toolTip.y = toolTipY + 2 * UM.Theme.getSize("default_margin").height 62 | toolTip.anchors.bottom = undefined 63 | } 64 | else 65 | { 66 | toolTip.y = toolTipY - toolTip.height - 1 * UM.Theme.getSize("default_margin").height 67 | toolTip.anchors.top = undefined 68 | } 69 | toolTip.opacity = 1 70 | } 71 | 72 | function hideTooltip() 73 | { 74 | toolTip.opacity = 0 75 | } 76 | 77 | visible: !settingsDocked && settingsVisible && (prepareStageActive || !preSlicedData) 78 | onVisibleChanged: 79 | { 80 | if (visible) 81 | { 82 | var sidebar_rect = Qt.rect(sidebarToolWindow.x, sidebarToolWindow.y, sidebarToolWindow.width, sidebarToolWindow.height) 83 | if(!SidebarGUIPlugin.checkRectangleOnScreen(sidebar_rect)) 84 | { 85 | sidebarToolWindow.x = base.x + base.width - sidebarToolWindow.width + UM.Theme.getSize("wide_margin").width 86 | sidebarToolWindow.y = base.y + UM.Theme.getSize("wide_margin").width 87 | } 88 | printSetupWindow.children = [sidebarContents, toolTip] 89 | printSetupTooltip.visible = false // hide vestigial tooltip in main window 90 | } 91 | else 92 | { 93 | printSetupSidebar.children = [sidebarContents] 94 | printSetupTooltip.visible = true 95 | } 96 | } 97 | 98 | Item 99 | { 100 | id: printSetupWindow 101 | 102 | anchors.fill: parent 103 | } 104 | 105 | Item 106 | { 107 | visible: false 108 | 109 | UM.PointingRectangle 110 | { 111 | id: toolTip 112 | 113 | width: printSetupWindow.width - 2 * UM.Theme.getSize("default_margin").width - UM.Theme.getSize("thick_margin").width 114 | x: UM.Theme.getSize("thick_margin").width 115 | height: Math.min(label.height + 2 * UM.Theme.getSize("default_margin").height, 400) 116 | 117 | property alias text: label.text 118 | color: UM.Theme.getColor("tooltip") 119 | arrowSize: UM.Theme.getSize("default_arrow").width 120 | 121 | opacity: 0 122 | // This should be disabled when invisible, otherwise it will catch mouse events. 123 | visible: opacity > 0 124 | enabled: visible 125 | 126 | Behavior on opacity 127 | { 128 | NumberAnimation { duration: 200; } 129 | } 130 | 131 | Label 132 | { 133 | id: label 134 | anchors.left: parent.left 135 | anchors.right: parent.right 136 | anchors.top: parent.top 137 | anchors.margins: UM.Theme.getSize("tooltip_margins").width 138 | textFormat: Text.RichText 139 | color: UM.Theme.getColor("tooltip_text") 140 | font: UM.Theme.getFont("default") 141 | wrapMode: Text.Wrap 142 | renderType: Qt.platform.os == "osx" ? Text.QtRendering : Text.NativeRendering 143 | } 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /resources/qml/ExtruderTabs40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | TabRow 11 | { 12 | id: tabBar 13 | 14 | property var extrudersModel: CuraApplication.getExtrudersModel() 15 | property bool hasMaterials: 16 | { 17 | if (CuraSDKVersion >= "6.2.0") { 18 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasMaterials : false 19 | } else { 20 | return Cura.MachineManager.hasMaterials 21 | } 22 | } 23 | property bool hasVariants: 24 | { 25 | if (CuraSDKVersion >= "6.2.0") { 26 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasVariants : false 27 | } else { 28 | return Cura.MachineManager.hasVariants 29 | } 30 | } 31 | 32 | visible: hasMaterials || hasVariants 33 | width: parent.width 34 | 35 | Repeater 36 | { 37 | id: repeater 38 | model: extrudersModel 39 | delegate: TabRowButton 40 | { 41 | contentItem: Item 42 | { 43 | Cura.ExtruderIcon 44 | { 45 | id: extruderIcon 46 | materialColor: model.color 47 | extruderEnabled: model.enabled 48 | 49 | anchors.left: parent.left 50 | height: parent.height 51 | width: height 52 | } 53 | 54 | // Label for the brand of the material 55 | Label 56 | { 57 | id: typeAndBrandNameLabel 58 | 59 | text: model.material_brand + " " + model.material 60 | elide: Text.ElideRight 61 | font: UM.Theme.getFont("default") 62 | color: UM.Theme.getColor("text") 63 | renderType: Text.NativeRendering 64 | wrapMode: Text.NoWrap 65 | visible: hasMaterials 66 | 67 | anchors 68 | { 69 | top: extruderIcon.top 70 | left: extruderIcon.right 71 | leftMargin: UM.Theme.getSize("default_margin").width 72 | right: configurationWarning.left 73 | rightMargin: UM.Theme.getSize("default_margin").width 74 | } 75 | } 76 | 77 | // Label that shows the name of the variant 78 | Label 79 | { 80 | id: variantLabel 81 | 82 | text: model.variant 83 | elide: Text.ElideRight 84 | font: UM.Theme.getFont("default_bold") 85 | color: UM.Theme.getColor("text") 86 | renderType: Text.NativeRendering 87 | wrapMode: Text.NoWrap 88 | visible: hasVariants 89 | 90 | anchors 91 | { 92 | left: extruderIcon.right 93 | leftMargin: UM.Theme.getSize("default_margin").width 94 | top: typeAndBrandNameLabel.bottom 95 | } 96 | } 97 | 98 | UM.RecolorImage 99 | { 100 | id: configurationWarning 101 | 102 | property var extruderStack: 103 | { 104 | if (CuraSDKVersion >= "7.0.0") { 105 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.extruderList[model.index] : undefined; 106 | } else if (CuraSDKVersion >= "6.2.0") { 107 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.extruders[model.index] : undefined; 108 | } else { 109 | return Cura.MachineManager.getExtruder(model.index); 110 | } 111 | } 112 | property bool valueWarning: !SidebarGUIPlugin.getExtruderHasQualityForMaterial(extruderStack) 113 | property bool valueError: extruderStack == undefined ? false : Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible", "") != "True" 114 | 115 | visible: (tabBar.hasMaterials || tabBar.hasVariants) && (valueWarning || valueError) 116 | 117 | anchors.right: parent.right 118 | anchors.verticalCenter: parent.verticalCenter 119 | 120 | source: valueError ? UM.Theme.getIcon("cross2") : UM.Theme.getIcon("warning") 121 | color: valueError ? UM.Theme.getColor("setting_validation_error_background") : UM.Theme.getColor("setting_validation_warning_background") 122 | width: visible ? UM.Theme.getSize("section_icon").width : 0 123 | height: width 124 | } 125 | } 126 | onClicked: 127 | { 128 | Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) 129 | } 130 | } 131 | } 132 | 133 | //When active extruder changes for some other reason, switch tabs. 134 | //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! 135 | //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. 136 | Connections 137 | { 138 | target: Cura.ExtruderManager 139 | onActiveExtruderChanged: 140 | { 141 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); 142 | } 143 | } 144 | 145 | //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. 146 | //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. 147 | //Therefore we need to change it back to what it was: The active extruder index. 148 | Connections 149 | { 150 | target: repeater.model 151 | onModelChanged: 152 | { 153 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 154 | } 155 | } 156 | 157 | //When switching back to the stage, make sure the active extruder is selected 158 | Component.onCompleted: 159 | { 160 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /resources/qml/ProfileSelector53.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Item 12 | { 13 | width: parent.width 14 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 15 | 16 | anchors 17 | { 18 | top: parent.top 19 | left: parent.left 20 | leftMargin: parent.padding 21 | right: parent.right 22 | rightMargin: parent.padding 23 | } 24 | 25 | Button 26 | { 27 | id: intentSelection 28 | onClicked: menu.opened ? menu.close() : menu.open() 29 | 30 | anchors.left: parent.left 31 | anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + UM.Theme.getSize("default_margin").width 32 | anchors.right: modeToggleSwitch.left 33 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 34 | height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height 35 | hoverEnabled: true 36 | visible: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 37 | 38 | baselineOffset: 0 // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... 39 | 40 | contentItem: RowLayout 41 | { 42 | spacing: 0 43 | anchors.left: parent.left 44 | anchors.right: customisedSettings.left 45 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 46 | 47 | Label 48 | { 49 | id: textLabel 50 | text: 51 | { 52 | return Cura.MachineManager.activeQualityDisplayNameMainStringParts.join(" - ") 53 | } 54 | font: UM.Theme.getFont("default") 55 | color: UM.Theme.getColor("text") 56 | Layout.margins: 0 57 | Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. 58 | height: contentHeight 59 | verticalAlignment: Text.AlignVCenter 60 | renderType: Text.NativeRendering 61 | elide: Text.ElideRight 62 | } 63 | 64 | Label 65 | { 66 | text: activeQualityDetailText() 67 | font: UM.Theme.getFont("default") 68 | color: UM.Theme.getColor("text_detail") 69 | Layout.margins: 0 70 | Layout.fillWidth: true 71 | 72 | height: contentHeight 73 | verticalAlignment: Text.AlignVCenter 74 | renderType: Text.NativeRendering 75 | elide: Text.ElideRight 76 | 77 | function activeQualityDetailText() 78 | { 79 | const string_parts = Cura.MachineManager.activeQualityDisplayNameTailStringParts; 80 | if (string_parts.length === 0) 81 | { 82 | return ""; 83 | } 84 | else 85 | { 86 | return ` - ${string_parts.join(" - ")}` 87 | } 88 | } 89 | } 90 | } 91 | 92 | background: Rectangle 93 | { 94 | id: backgroundItem 95 | border.color: intentSelection.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") 96 | border.width: UM.Theme.getSize("default_lining").width 97 | radius: UM.Theme.getSize("default_radius").width 98 | color: UM.Theme.getColor("main_background") 99 | } 100 | 101 | Cura.ProfileWarningReset 102 | { 103 | id: customisedSettings 104 | fullWarning: false 105 | visible: Cura.MachineManager.hasUserSettings 106 | 107 | anchors.verticalCenter: parent.verticalCenter 108 | anchors.right: downArrow.left 109 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 110 | } 111 | 112 | UM.ColorImage 113 | { 114 | id: downArrow 115 | 116 | source: UM.Theme.getIcon("ChevronSingleDown") 117 | width: UM.Theme.getSize("standard_arrow").width 118 | height: UM.Theme.getSize("standard_arrow").height 119 | 120 | anchors 121 | { 122 | right: parent.right 123 | verticalCenter: parent.verticalCenter 124 | rightMargin: UM.Theme.getSize("default_margin").width 125 | } 126 | 127 | color: UM.Theme.getColor("setting_control_button") 128 | } 129 | } 130 | 131 | Cura.QualitiesWithIntentMenu 132 | { 133 | id: menu 134 | y: intentSelection.y + intentSelection.height 135 | x: intentSelection.x 136 | width: intentSelection.width 137 | } 138 | 139 | ModeToggleSwitch 140 | { 141 | id: modeToggleSwitch 142 | anchors.right: dockButton.left 143 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 144 | } 145 | 146 | UM.SimpleButton 147 | { 148 | id: dockButton 149 | anchors 150 | { 151 | verticalCenter: modeToggleSwitch.verticalCenter 152 | 153 | right: collapseButton.left 154 | rightMargin: UM.Theme.getSize("default_margin").width 155 | } 156 | iconSource: Qt.resolvedUrl(settingsDocked ? "../icons/settings_undock.svg" : "../icons/settings_dock.svg") 157 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 158 | height: width 159 | color: UM.Theme.getColor("small_button_text") 160 | 161 | onClicked: 162 | { 163 | UM.Preferences.setValue("sidebargui/docked_sidebar", !UM.Preferences.getValue("sidebargui/docked_sidebar")) 164 | stageMenu.settingsDocked = UM.Preferences.getValue("sidebargui/docked_sidebar") 165 | } 166 | } 167 | 168 | UM.SimpleButton 169 | { 170 | id: collapseButton 171 | anchors 172 | { 173 | verticalCenter: modeToggleSwitch.verticalCenter 174 | 175 | right: parent.right 176 | rightMargin: UM.Theme.getSize("default_margin").width 177 | } 178 | iconSource: UM.Theme.getIcon("Cancel") 179 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 180 | height: width 181 | color: UM.Theme.getColor("small_button_text") 182 | 183 | onClicked: 184 | { 185 | UM.Preferences.setValue("view/settings_visible", false) 186 | stageMenu.settingsVisible = false 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /resources/qml/OpenFileButton50.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.9 5 | import QtQuick.Layouts 1.1 6 | import QtQuick.Controls 2.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Button 12 | { 13 | id: openFileButton 14 | 15 | property var fileProviderModel: CuraApplication.getFileProviderModel() 16 | 17 | height: UM.Theme.getSize("button").height 18 | width: height 19 | x: -4 * UM.Theme.getSize("default_lining").width 20 | onClicked: 21 | { 22 | if (fileProviderModel.count <= 1) 23 | { 24 | Cura.Actions.open.trigger() 25 | } 26 | else 27 | { 28 | toggleContent() 29 | } 30 | } 31 | function toggleContent() 32 | { 33 | if (openFileButtonMenu.visible) 34 | { 35 | openFileButtonMenu.close() 36 | } 37 | else 38 | { 39 | openFileButtonMenu.open() 40 | } 41 | } 42 | 43 | hoverEnabled: true 44 | 45 | contentItem: Rectangle 46 | { 47 | anchors.left: parent.left 48 | anchors.leftMargin: UM.Theme.getSize("thin_margin").width 49 | 50 | opacity: parent.enabled ? 1.0 : 0.2 51 | radius: Math.round(width * 0.5) 52 | 53 | color: 54 | { 55 | if(parent.hovered) 56 | { 57 | return UM.Theme.getColor("toolbar_button_hover") 58 | } 59 | return UM.Theme.getColor("toolbar_background") 60 | } 61 | 62 | UM.ColorImage 63 | { 64 | id: buttonIcon 65 | anchors.centerIn: parent 66 | width: height 67 | height: parent.height - UM.Theme.getSize("default_margin").height 68 | source: UM.Theme.getIcon("Folder", "medium") 69 | color: UM.Theme.getColor("icon") 70 | } 71 | 72 | UM.ColorImage 73 | { 74 | anchors 75 | { 76 | right: parent.right 77 | rightMargin: -4 * UM.Theme.getSize("default_lining").width 78 | bottom: parent.bottom 79 | bottomMargin: -2 * UM.Theme.getSize("default_lining").height 80 | } 81 | source: UM.Theme.getIcon("ChevronSingleDown") 82 | visible: fileProviderModel.count > 1 83 | width: UM.Theme.getSize("standard_arrow").width 84 | height: UM.Theme.getSize("standard_arrow").height 85 | color: UM.Theme.getColor("icon") 86 | } 87 | } 88 | 89 | background: Cura.RoundedRectangle 90 | { 91 | id: buttonBackground 92 | height: UM.Theme.getSize("button").height 93 | width: UM.Theme.getSize("button").width + UM.Theme.getSize("narrow_margin").width 94 | 95 | radius: UM.Theme.getSize("default_radius").width 96 | cornerSide: Cura.RoundedRectangle.Direction.Right 97 | 98 | color: UM.Theme.getColor("toolbar_background") 99 | border.width: UM.Theme.getSize("default_lining").width 100 | border.color: UM.Theme.getColor("lining") 101 | } 102 | 103 | Popup 104 | { 105 | id: openFileButtonMenu 106 | 107 | // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left. 108 | // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. 109 | y: buttonBackground.height + 2 * UM.Theme.getSize("default_lining").height 110 | padding: UM.Theme.getSize("default_margin").width 111 | closePolicy: Popup.CloseOnPressOutsideParent 112 | 113 | background: Cura.RoundedRectangle 114 | { 115 | cornerSide: Cura.RoundedRectangle.Direction.All 116 | color: UM.Theme.getColor("action_button") 117 | border.width: UM.Theme.getSize("default_lining").width 118 | border.color: UM.Theme.getColor("lining") 119 | radius: UM.Theme.getSize("default_radius").width 120 | } 121 | 122 | contentItem: Item 123 | { 124 | id: popup 125 | 126 | Column 127 | { 128 | id: openProviderColumn 129 | 130 | //The column doesn't automatically listen to its children rect if the children change internally, so we need to explicitly update the size. 131 | onChildrenRectChanged: 132 | { 133 | popup.height = childrenRect.height 134 | popup.width = childrenRect.width 135 | } 136 | onPositioningComplete: 137 | { 138 | popup.height = childrenRect.height 139 | popup.width = childrenRect.width 140 | } 141 | 142 | Repeater 143 | { 144 | model: openFileButton.fileProviderModel 145 | delegate: Button 146 | { 147 | leftPadding: UM.Theme.getSize("default_margin").width 148 | rightPadding: UM.Theme.getSize("default_margin").width 149 | width: contentItem.width + leftPadding + rightPadding 150 | height: UM.Theme.getSize("action_button").height 151 | hoverEnabled: true 152 | 153 | contentItem: Label 154 | { 155 | text: model.displayText 156 | color: UM.Theme.getColor("text") 157 | font: UM.Theme.getFont("medium") 158 | renderType: Text.NativeRendering 159 | verticalAlignment: Text.AlignVCenter 160 | 161 | width: contentWidth 162 | height: parent.height 163 | } 164 | 165 | onClicked: 166 | { 167 | if(model.index == 0) //The 0th element is the "From Disk" option, which should activate the open local file dialog. 168 | { 169 | Cura.Actions.open.trigger(); 170 | } 171 | else 172 | { 173 | openFileButton.fileProviderModel.trigger(model.name); 174 | } 175 | openFileButton.toggleContent(); 176 | } 177 | 178 | background: Rectangle 179 | { 180 | color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" 181 | radius: UM.Theme.getSize("action_button_radius").width 182 | width: popup.width 183 | } 184 | } 185 | } 186 | } 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /resources/qml/OpenFileButton411.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.9 5 | import QtQuick.Layouts 1.1 6 | import QtQuick.Controls 2.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Button 12 | { 13 | id: openFileButton 14 | 15 | property var fileProviderModel: CuraApplication.getFileProviderModel() 16 | 17 | height: UM.Theme.getSize("button").height 18 | width: Math.floor(height * 1.125) // Magic number is magic 19 | onClicked: 20 | { 21 | if (fileProviderModel.count <= 1) 22 | { 23 | Cura.Actions.open.trigger() 24 | } 25 | else 26 | { 27 | toggleContent() 28 | } 29 | } 30 | function toggleContent() 31 | { 32 | if (openFileButtonMenu.visible) 33 | { 34 | openFileButtonMenu.close() 35 | } 36 | else 37 | { 38 | openFileButtonMenu.open() 39 | } 40 | } 41 | 42 | hoverEnabled: true 43 | 44 | contentItem: Rectangle 45 | { 46 | anchors.left: parent.left 47 | anchors.leftMargin: UM.Theme.getSize("thin_margin").width 48 | 49 | opacity: parent.enabled ? 1.0 : 0.2 50 | radius: Math.round(width * 0.5) 51 | 52 | color: 53 | { 54 | if(parent.hovered) 55 | { 56 | return UM.Theme.getColor("toolbar_button_hover") 57 | } 58 | return UM.Theme.getColor("toolbar_background") 59 | } 60 | 61 | UM.RecolorImage 62 | { 63 | id: buttonIcon 64 | anchors.centerIn: parent 65 | width: parent.width - UM.Theme.getSize("default_margin").width 66 | height: parent.height - UM.Theme.getSize("default_margin").height 67 | source: UM.Theme.getIcon("Folder", "medium") 68 | color: UM.Theme.getColor("icon") 69 | 70 | sourceSize.height: height 71 | } 72 | 73 | UM.RecolorImage 74 | { 75 | anchors 76 | { 77 | right: parent.right 78 | rightMargin: -4 * UM.Theme.getSize("default_lining").width 79 | bottom: parent.bottom 80 | bottomMargin: -2 * UM.Theme.getSize("default_lining").height 81 | } 82 | source: UM.Theme.getIcon("ChevronSingleDown") 83 | visible: fileProviderModel.count > 1 84 | width: UM.Theme.getSize("standard_arrow").width 85 | height: UM.Theme.getSize("standard_arrow").height 86 | color: UM.Theme.getColor("icon") 87 | } 88 | } 89 | 90 | background: Cura.RoundedRectangle 91 | { 92 | id: buttonBackground 93 | height: UM.Theme.getSize("button").height 94 | width: UM.Theme.getSize("button").width + UM.Theme.getSize("narrow_margin").width 95 | 96 | radius: UM.Theme.getSize("default_radius").width 97 | cornerSide: Cura.RoundedRectangle.Direction.Right 98 | 99 | color: UM.Theme.getColor("toolbar_background") 100 | border.width: UM.Theme.getSize("default_lining").width 101 | border.color: UM.Theme.getColor("lining") 102 | } 103 | 104 | Popup 105 | { 106 | id: openFileButtonMenu 107 | 108 | // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left. 109 | // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. 110 | y: buttonBackground.height + 2 * UM.Theme.getSize("default_lining").height 111 | padding: UM.Theme.getSize("default_margin").width 112 | closePolicy: Popup.CloseOnPressOutsideParent 113 | 114 | background: Cura.RoundedRectangle 115 | { 116 | cornerSide: Cura.RoundedRectangle.Direction.All 117 | color: UM.Theme.getColor("action_button") 118 | border.width: UM.Theme.getSize("default_lining").width 119 | border.color: UM.Theme.getColor("lining") 120 | radius: UM.Theme.getSize("default_radius").width 121 | } 122 | 123 | contentItem: Item 124 | { 125 | id: popup 126 | 127 | Column 128 | { 129 | id: openProviderColumn 130 | 131 | //The column doesn't automatically listen to its children rect if the children change internally, so we need to explicitly update the size. 132 | onChildrenRectChanged: 133 | { 134 | popup.height = childrenRect.height 135 | popup.width = childrenRect.width 136 | } 137 | onPositioningComplete: 138 | { 139 | popup.height = childrenRect.height 140 | popup.width = childrenRect.width 141 | } 142 | 143 | Repeater 144 | { 145 | model: openFileButton.fileProviderModel 146 | delegate: Button 147 | { 148 | leftPadding: UM.Theme.getSize("default_margin").width 149 | rightPadding: UM.Theme.getSize("default_margin").width 150 | width: contentItem.width + leftPadding + rightPadding 151 | height: UM.Theme.getSize("action_button").height 152 | hoverEnabled: true 153 | 154 | contentItem: Label 155 | { 156 | text: model.displayText 157 | color: UM.Theme.getColor("text") 158 | font: UM.Theme.getFont("medium") 159 | renderType: Text.NativeRendering 160 | verticalAlignment: Text.AlignVCenter 161 | 162 | width: contentWidth 163 | height: parent.height 164 | } 165 | 166 | onClicked: 167 | { 168 | if(model.index == 0) //The 0th element is the "From Disk" option, which should activate the open local file dialog. 169 | { 170 | Cura.Actions.open.trigger(); 171 | } 172 | else 173 | { 174 | openFileButton.fileProviderModel.trigger(model.name); 175 | } 176 | openFileButton.toggleContent(); 177 | } 178 | 179 | background: Rectangle 180 | { 181 | color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" 182 | radius: UM.Theme.getSize("action_button_radius").width 183 | width: popup.width 184 | } 185 | } 186 | } 187 | } 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /resources/qml/ViewOptionsPanel50.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Rectangle 11 | { 12 | id: viewOptionsPanel 13 | 14 | border.width: UM.Theme.getSize("default_lining").width 15 | border.color: UM.Theme.getColor("lining") 16 | color: UM.Theme.getColor("main_background") 17 | radius: UM.Theme.getSize("default_radius").width 18 | clip: true 19 | 20 | Behavior on height { NumberAnimation { duration: 100 } } 21 | 22 | property string projectionType: UM.Preferences.getValue("general/camera_perspective_mode") 23 | 24 | Connections 25 | { 26 | target: UM.Preferences 27 | function onPreferenceChanged(preference) 28 | { 29 | if (preference !== "general/camera_perspective_mode") 30 | { 31 | return 32 | } 33 | projectionType = UM.Preferences.getValue("general/camera_perspective_mode") 34 | } 35 | } 36 | 37 | 38 | Column 39 | { 40 | id: viewOptions 41 | spacing: UM.Theme.getSize("thin_margin").height 42 | children: 43 | [ 44 | viewPanelOrientationControls, 45 | legendHeader, 46 | legendItems 47 | ] 48 | anchors.top: parent.top 49 | anchors.topMargin: UM.Theme.getSize("default_margin").height 50 | } 51 | 52 | Item { 53 | // hidden items 54 | visible: false 55 | 56 | Row 57 | { 58 | id: viewPanelOrientationControls 59 | 60 | spacing: UM.Theme.getSize("narrow_margin").width 61 | 62 | anchors.left: parent.left 63 | anchors.leftMargin: UM.Theme.getSize("default_margin").width - 2 * UM.Theme.getSize("default_lining").width 64 | 65 | Cura.ViewOrientationControls 66 | { 67 | Component.onCompleted: 68 | { 69 | for(var child_nr in children) 70 | { 71 | children[child_nr].iconMargin = 3 * UM.Theme.getSize("default_lining").width 72 | } 73 | } 74 | } 75 | 76 | UM.SimpleButton 77 | { 78 | id: projectionToggle 79 | iconSource: 80 | { 81 | if(viewOptionsPanel.projectionType == "orthographic") 82 | { 83 | return Qt.resolvedUrl("../icons/view_perspective.svg") 84 | } 85 | else 86 | { 87 | return Qt.resolvedUrl("../icons/view_orthographic.svg") 88 | } 89 | } 90 | onClicked: 91 | { 92 | if(viewOptionsPanel.projectionType == "orthographic") 93 | { 94 | UM.Preferences.setValue("general/camera_perspective_mode", "perspective") 95 | } 96 | else 97 | { 98 | UM.Preferences.setValue("general/camera_perspective_mode", "orthographic") 99 | } 100 | } 101 | 102 | width: UM.Theme.getSize("small_button").width 103 | height: UM.Theme.getSize("small_button").height 104 | hoverColor: UM.Theme.getColor("small_button_text_hover") 105 | color: UM.Theme.getColor("small_button_text") 106 | iconMargin: UM.Theme.getSize("thick_lining").width 107 | 108 | UM.TooltipArea 109 | { 110 | anchors.fill: parent 111 | text: 112 | { 113 | if(viewOptionsPanel.projectionType == "orthographic") 114 | { 115 | return catalog.i18nc("@info:tooltip", "Perspective View") 116 | } 117 | else 118 | { 119 | return catalog.i18nc("@info:tooltip", "Orthographic View") 120 | } 121 | } 122 | 123 | acceptedButtons: Qt.NoButton 124 | } 125 | } 126 | 127 | } 128 | 129 | Loader 130 | { 131 | id: viewMenuComponent 132 | height: parent.height 133 | width: UM.Theme.getSize("layerview_menu_size").width 134 | source: 135 | { 136 | if( 137 | UM.Controller.activeView != null && 138 | UM.Controller.activeView.stageMenuComponent != null && 139 | !UM.Controller.activeView.stageMenuComponent.toString().endsWith("EmptyViewMenuComponent.qml") 140 | ) 141 | { 142 | return UM.Controller.activeView.stageMenuComponent; 143 | } 144 | return "PrepareStageLegend50.qml"; 145 | } 146 | 147 | onLoaded: 148 | { 149 | legendHeaderItem.children = [ 150 | viewMenuComponent.item.contentItem.children[0], 151 | viewMenuComponent.item.contentItem.children[1] 152 | ] 153 | legendItems.children = [ 154 | viewMenuComponent.item.contentItem 155 | ] 156 | } 157 | } 158 | 159 | Item 160 | { 161 | id: legendHeader 162 | width: parent.width 163 | height: childrenRect.height 164 | 165 | anchors.left: parent.left 166 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 167 | 168 | Item { 169 | id: legendHeaderItem 170 | anchors 171 | { 172 | left: parent.left 173 | right: legendCollapseButton.left 174 | rightMargin: UM.Theme.getSize("default_margin").width 175 | } 176 | height: childrenRect.height 177 | 178 | // populated by viewMenuComponent 179 | } 180 | 181 | UM.SimpleButton 182 | { 183 | id: legendCollapseButton 184 | anchors 185 | { 186 | right: parent.right 187 | rightMargin: UM.Theme.getSize("narrow_margin").width 188 | verticalCenter: legendHeaderItem.verticalCenter 189 | } 190 | iconSource: legendItems.visible ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft") 191 | width: UM.Theme.getSize("standard_arrow").width 192 | height: UM.Theme.getSize("standard_arrow").height 193 | color: UM.Theme.getColor("setting_category_text") 194 | 195 | onClicked: 196 | { 197 | legendItems.visible = !legendItems.visible; 198 | UM.Preferences.setValue("sidebargui/expand_legend", legendItems.visible); 199 | } 200 | } 201 | } 202 | 203 | Column 204 | { 205 | id: legendItems 206 | visible: UM.Preferences.getValue("sidebargui/expand_legend") 207 | 208 | // populated by viewMenuComponent 209 | } 210 | } 211 | 212 | height: viewOptions.height + 2 * UM.Theme.getSize("default_margin").height 213 | width: UM.Theme.getSize("layerview_menu_size").width 214 | } 215 | -------------------------------------------------------------------------------- /resources/qml/PrepareStageLegend50.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Shapes 1.2 7 | 8 | import UM 1.5 as UM 9 | import Cura 1.0 as Cura 10 | 11 | Cura.ExpandableComponent 12 | { 13 | id: base 14 | 15 | contentHeaderTitle: catalog.i18nc("@label", "Legend") 16 | 17 | headerItem: Item 18 | { 19 | Label 20 | { 21 | text: catalog.i18nc("@label", "Legend") 22 | verticalAlignment: Text.AlignVCenter 23 | height: parent.height 24 | elide: Text.ElideRight 25 | font: UM.Theme.getFont("default") 26 | color: UM.Theme.getColor("text_medium") 27 | renderType: Text.NativeRendering 28 | } 29 | } 30 | 31 | contentItem: Column 32 | { 33 | id: viewSettings 34 | width: UM.Theme.getSize("layerview_menu_size").width - 2 * UM.Theme.getSize("default_margin").width 35 | height: implicitHeight 36 | spacing: UM.Theme.getSize("layerview_row_spacing").height 37 | 38 | onActiveViewChanged: 39 | { 40 | xrayViewCheckBox.checked = (activeView == "XRayView") 41 | xrayViewCheckBox.visible = (activeView != "FastView" && activeView != "SmartSliceView") 42 | switch(activeView) 43 | { 44 | case "FastView": 45 | externalViewLabel.visible = true 46 | externalViewLabel.text = catalog.i18nc("@label", "Fast View") 47 | break; 48 | case "SmartSliceView": 49 | externalViewLabel.visible = true 50 | externalViewLabel.text = catalog.i18nc("@label", "Smart Slice") 51 | break; 52 | default: 53 | externalViewLabel.visible = false 54 | break; 55 | } 56 | } 57 | 58 | property string activeView: 59 | { 60 | var viewString = UM.Controller.activeView + ""; 61 | return viewString.substr(0, viewString.indexOf("(")); 62 | } 63 | 64 | UM.CheckBox 65 | { 66 | id: xrayViewCheckBox 67 | checked: parent.activeView == "XRayView" 68 | visible: !externalViewLabel.visible 69 | onClicked: 70 | { 71 | if(checked && parent.activeView != "XRayView") 72 | { 73 | UM.Controller.setActiveView("XRayView") 74 | } 75 | else if(! checked && parent.activeView != "SolidView") 76 | { 77 | UM.Controller.setActiveView("SolidView") 78 | } 79 | } 80 | text: catalog.i18nc("@label", "X-Ray view") 81 | width: parent.width 82 | } 83 | 84 | UM.Label 85 | { 86 | id: externalViewLabel 87 | 88 | height: UM.Theme.getSize("layerview_row").height 89 | width: parent.width 90 | color: UM.Theme.getColor("setting_control_text") 91 | } 92 | 93 | Item 94 | { 95 | // mock item to compensate for compatibility mode label in simulation view 96 | visible: false 97 | } 98 | 99 | UM.Label 100 | { 101 | text: catalog.i18nc("@label", "Overhang") 102 | visible: parent.activeView == "SolidView" 103 | 104 | height: UM.Theme.getSize("layerview_row").height 105 | width: parent.width 106 | color: UM.Theme.getColor("setting_control_text") 107 | 108 | Rectangle 109 | { 110 | anchors.verticalCenter: parent.verticalCenter 111 | anchors.right: parent.right 112 | 113 | width: UM.Theme.getSize("layerview_legend_size").width 114 | height: UM.Theme.getSize("layerview_legend_size").height 115 | 116 | color: UM.Theme.getColor("model_overhang") 117 | 118 | border.width: UM.Theme.getSize("default_lining").width 119 | border.color: UM.Theme.getColor("lining") 120 | } 121 | } 122 | 123 | UM.Label 124 | { 125 | text: catalog.i18nc("@label", "Outside buildvolume") 126 | visible: parent.activeView == "SolidView" 127 | 128 | height: UM.Theme.getSize("layerview_row").height 129 | width: parent.width 130 | color: UM.Theme.getColor("setting_control_text") 131 | 132 | Rectangle 133 | { 134 | id: outsideBuildVolumeSwatch 135 | anchors.verticalCenter: parent.verticalCenter 136 | anchors.right: parent.right 137 | 138 | width: UM.Theme.getSize("layerview_legend_size").width 139 | height: UM.Theme.getSize("layerview_legend_size").height 140 | clip: true 141 | 142 | border.width: UM.Theme.getSize("default_lining").width 143 | border.color: UM.Theme.getColor("lining") 144 | 145 | Rectangle 146 | { 147 | anchors.fill: parent 148 | scale: Math.sqrt(2) 149 | rotation: 45 150 | gradient: Gradient 151 | { 152 | GradientStop { position: 0.5; color: UM.Theme.getColor("model_unslicable") } 153 | GradientStop { position: 0.5001; color: UM.Theme.getColor("model_unslicable_alt") } 154 | } 155 | } 156 | } 157 | } 158 | 159 | UM.Label 160 | { 161 | text: catalog.i18nc("@label", "Normal geometry") 162 | visible: parent.activeView == "XRayView" 163 | 164 | height: UM.Theme.getSize("layerview_row").height 165 | width: parent.width 166 | color: UM.Theme.getColor("setting_control_text") 167 | 168 | Rectangle 169 | { 170 | anchors.verticalCenter: parent.verticalCenter 171 | anchors.right: parent.right 172 | 173 | width: UM.Theme.getSize("layerview_legend_size").width 174 | height: UM.Theme.getSize("layerview_legend_size").height 175 | 176 | border.width: UM.Theme.getSize("default_lining").width 177 | border.color: UM.Theme.getColor("lining") 178 | 179 | gradient: LinearGradient 180 | { 181 | GradientStop { position: 0.0; color: UM.Theme.getColor("xray") } 182 | GradientStop { position: 1.0; color: "white" } 183 | } 184 | } 185 | } 186 | 187 | UM.Label 188 | { 189 | text: catalog.i18nc("@label", "Geometry error") 190 | visible: parent.activeView == "XRayView" 191 | 192 | height: UM.Theme.getSize("layerview_row").height 193 | width: parent.width 194 | color: UM.Theme.getColor("setting_control_text") 195 | 196 | Rectangle 197 | { 198 | anchors.verticalCenter: parent.verticalCenter 199 | anchors.right: parent.right 200 | 201 | width: UM.Theme.getSize("layerview_legend_size").width 202 | height: UM.Theme.getSize("layerview_legend_size").height 203 | 204 | color: UM.Theme.getColor("error_area") 205 | 206 | border.width: UM.Theme.getSize("default_lining").width 207 | border.color: UM.Theme.getColor("lining") 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /resources/qml/ViewOptionsPanel40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Rectangle 11 | { 12 | id: viewOptionsPanel 13 | 14 | border.width: UM.Theme.getSize("default_lining").width 15 | border.color: UM.Theme.getColor("lining") 16 | color: UM.Theme.getColor("main_background") 17 | radius: UM.Theme.getSize("default_radius").width 18 | clip: true 19 | 20 | Behavior on height { NumberAnimation { duration: 100 } } 21 | 22 | property string projectionType: UM.Preferences.getValue("general/camera_perspective_mode") 23 | 24 | Connections 25 | { 26 | target: UM.Preferences 27 | onPreferenceChanged: 28 | { 29 | if (preference !== "general/camera_perspective_mode") 30 | { 31 | return 32 | } 33 | projectionType = UM.Preferences.getValue("general/camera_perspective_mode") 34 | } 35 | } 36 | 37 | 38 | Column 39 | { 40 | id: viewOptions 41 | spacing: UM.Theme.getSize("thin_margin").height 42 | children: 43 | [ 44 | viewPanelOrientationControls, 45 | legendHeader, 46 | legendItems 47 | ] 48 | anchors.top: parent.top 49 | anchors.topMargin: UM.Theme.getSize("default_margin").height 50 | } 51 | 52 | Item { 53 | // hidden items 54 | visible: false 55 | 56 | Row 57 | { 58 | id: viewPanelOrientationControls 59 | 60 | spacing: UM.Theme.getSize("narrow_margin").width 61 | 62 | anchors.left: parent.left 63 | anchors.leftMargin: UM.Theme.getSize("default_margin").width - 2 * UM.Theme.getSize("default_lining").width 64 | 65 | Cura.ViewOrientationControls 66 | { 67 | Component.onCompleted: 68 | { 69 | if(!isLE410) 70 | { 71 | for(var child_nr in children) 72 | { 73 | children[child_nr].iconMargin = 3 * UM.Theme.getSize("default_lining").width 74 | } 75 | } 76 | } 77 | } 78 | 79 | UM.SimpleButton 80 | { 81 | id: projectionToggle 82 | iconSource: 83 | { 84 | if(viewOptionsPanel.projectionType == "orthographic") 85 | { 86 | return "../icons/view_perspective.svg" 87 | } 88 | else 89 | { 90 | return "../icons/view_orthographic.svg" 91 | } 92 | } 93 | onClicked: 94 | { 95 | if(viewOptionsPanel.projectionType == "orthographic") 96 | { 97 | UM.Preferences.setValue("general/camera_perspective_mode", "perspective") 98 | } 99 | else 100 | { 101 | UM.Preferences.setValue("general/camera_perspective_mode", "orthographic") 102 | } 103 | } 104 | 105 | width: UM.Theme.getSize("small_button").width 106 | height: UM.Theme.getSize("small_button").height 107 | hoverColor: UM.Theme.getColor("small_button_text_hover") 108 | color: UM.Theme.getColor("small_button_text") 109 | iconMargin: UM.Theme.getSize("thick_lining").width 110 | 111 | UM.TooltipArea 112 | { 113 | anchors.fill: parent 114 | text: 115 | { 116 | if(viewOptionsPanel.projectionType == "orthographic") 117 | { 118 | return catalog.i18nc("@info:tooltip", "Perspective View") 119 | } 120 | else 121 | { 122 | return catalog.i18nc("@info:tooltip", "Orthographic View") 123 | } 124 | } 125 | 126 | acceptedButtons: Qt.NoButton 127 | } 128 | } 129 | 130 | } 131 | 132 | Loader 133 | { 134 | id: viewMenuComponent 135 | height: parent.height 136 | width: UM.Theme.getSize("layerview_menu_size").width 137 | source: 138 | { 139 | if( 140 | UM.Controller.activeView != null && 141 | UM.Controller.activeView.stageMenuComponent != null && 142 | !UM.Controller.activeView.stageMenuComponent.toString().endsWith("EmptyViewMenuComponent.qml") 143 | ) 144 | { 145 | return UM.Controller.activeView.stageMenuComponent; 146 | } 147 | return "PrepareStageLegend40.qml"; 148 | } 149 | 150 | onLoaded: 151 | { 152 | legendHeaderItem.children = [ 153 | viewMenuComponent.item.contentItem.children[0], 154 | viewMenuComponent.item.contentItem.children[1] 155 | ] 156 | legendItems.children = [ 157 | viewMenuComponent.item.contentItem 158 | ] 159 | } 160 | } 161 | 162 | Item 163 | { 164 | id: legendHeader 165 | width: parent.width 166 | height: childrenRect.height 167 | 168 | anchors.left: parent.left 169 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 170 | 171 | Item { 172 | id: legendHeaderItem 173 | anchors 174 | { 175 | left: parent.left 176 | right: legendCollapseButton.left 177 | rightMargin: UM.Theme.getSize("default_margin").width 178 | } 179 | height: childrenRect.height 180 | 181 | // populated by viewMenuComponent 182 | } 183 | 184 | UM.SimpleButton 185 | { 186 | id: legendCollapseButton 187 | anchors 188 | { 189 | right: parent.right 190 | rightMargin: UM.Theme.getSize("narrow_margin").width 191 | verticalCenter: legendHeaderItem.verticalCenter 192 | } 193 | iconSource: legendItems.visible ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") 194 | width: UM.Theme.getSize("standard_arrow").width 195 | height: UM.Theme.getSize("standard_arrow").height 196 | color: UM.Theme.getColor("setting_category_text") 197 | 198 | onClicked: 199 | { 200 | legendItems.visible = !legendItems.visible; 201 | UM.Preferences.setValue("sidebargui/expand_legend", legendItems.visible); 202 | } 203 | } 204 | } 205 | 206 | Column 207 | { 208 | id: legendItems 209 | visible: UM.Preferences.getValue("sidebargui/expand_legend") 210 | 211 | // populated by viewMenuComponent 212 | } 213 | } 214 | 215 | height: viewOptions.height + 2 * UM.Theme.getSize("default_margin").height 216 | width: UM.Theme.getSize("layerview_menu_size").width 217 | } 218 | -------------------------------------------------------------------------------- /resources/qml/ExtruderTabs50.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.5 as UM 8 | import Cura 1.1 as Cura 9 | 10 | TabRow 11 | { 12 | id: tabBar 13 | 14 | property var extrudersModel: CuraApplication.getExtrudersModel() 15 | property bool hasMaterials: (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasMaterials : false 16 | property bool hasVariants: (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasVariants : false 17 | visible: hasMaterials || hasVariants 18 | width: parent.width 19 | height: UM.Theme.getSize("extruder_icon").height + UM.Theme.getSize("narrow_margin").height 20 | 21 | property int extrudersCount: extrudersModel.count 22 | 23 | Repeater 24 | { 25 | id: repeater 26 | model: extrudersModel 27 | delegate: TabRowButton 28 | { 29 | width: Math.floor((tabBar.width - (extrudersCount - 1) * UM.Theme.getSize("narrow_margin").width) / extrudersCount) 30 | contentItem: Item 31 | { 32 | Cura.ExtruderIcon 33 | { 34 | id: extruderIcon 35 | materialColor: model.color 36 | extruderEnabled: model.enabled 37 | 38 | anchors.left: parent.left 39 | height: parent.height 40 | width: height 41 | } 42 | 43 | // Label for the brand of the material 44 | UM.Label 45 | { 46 | id: typeAndBrandNameLabel 47 | 48 | text: model.material_brand + " " + model.material 49 | elide: Text.ElideRight 50 | font: UM.Theme.getFont("default") 51 | color: UM.Theme.getColor("text") 52 | wrapMode: Text.NoWrap 53 | visible: hasMaterials 54 | 55 | anchors 56 | { 57 | top: extruderIcon.top 58 | left: extruderIcon.right 59 | leftMargin: UM.Theme.getSize("default_margin").width 60 | right: configurationWarning.left 61 | rightMargin: UM.Theme.getSize("default_margin").width 62 | } 63 | } 64 | 65 | // Label that shows the name of the variant 66 | UM.Label 67 | { 68 | id: variantLabel 69 | 70 | text: model.variant 71 | elide: Text.ElideRight 72 | font: UM.Theme.getFont("default_bold") 73 | color: UM.Theme.getColor("text") 74 | wrapMode: Text.NoWrap 75 | visible: hasVariants 76 | 77 | anchors 78 | { 79 | left: extruderIcon.right 80 | leftMargin: UM.Theme.getSize("default_margin").width 81 | top: typeAndBrandNameLabel.bottom 82 | } 83 | } 84 | 85 | UM.StatusIcon 86 | { 87 | id: configurationWarning 88 | 89 | visible: status != UM.StatusIcon.Status.NEUTRAL 90 | height: visible ? UM.Theme.getSize("message_type_icon").height: 0 91 | width: visible ? UM.Theme.getSize("message_type_icon").height : 0 92 | 93 | property var extruderStack: 94 | { 95 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.extruderList[model.index] : undefined; 96 | } 97 | 98 | anchors.right: parent.right 99 | anchors.verticalCenter: parent.verticalCenter 100 | 101 | status: 102 | { 103 | if (model.enabled && (tabBar.hasMaterials || tabBar.hasVariants)) 104 | { 105 | if (extruderStack != undefined && Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible") != "True") 106 | { 107 | return UM.StatusIcon.Status.ERROR 108 | } 109 | if (!SidebarGUIPlugin.getExtruderHasQualityForMaterial(extruderStack)) 110 | { 111 | return UM.StatusIcon.Status.WARNING 112 | } 113 | } 114 | return UM.StatusIcon.Status.NEUTRAL 115 | } 116 | 117 | MouseArea // Connection status tooltip hover area 118 | { 119 | id: tooltipHoverArea 120 | anchors.fill: parent 121 | hoverEnabled: tooltip.text != "" 122 | acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks 123 | 124 | onEntered: tooltip.show() 125 | onExited: tooltip.hide() 126 | } 127 | 128 | UM.ToolTip 129 | { 130 | id: tooltip 131 | x: 0 132 | y: parent.height + UM.Theme.getSize("default_margin").height 133 | width: UM.Theme.getSize("tooltip").width 134 | targetPoint: Qt.point(Math.round(extruderIcon.width / 2), 0) 135 | text: 136 | { 137 | if (configurationWarning.status == UM.StatusIcon.Status.ERROR) 138 | { 139 | return catalog.i18nc("@tooltip", "The configuration of this extruder is not allowed, and prohibits slicing.") 140 | } 141 | if (configurationWarning.status == UM.StatusIcon.Status.WARNING) 142 | { 143 | return catalog.i18nc("@tooltip", "There are no profiles matching the configuration of this extruder.") 144 | } 145 | return "" 146 | } 147 | } 148 | } 149 | } 150 | onClicked: 151 | { 152 | Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) 153 | } 154 | } 155 | } 156 | 157 | //When active extruder changes for some other reason, switch tabs. 158 | //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! 159 | //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. 160 | Connections 161 | { 162 | target: Cura.ExtruderManager 163 | function onActiveExtruderChanged() 164 | { 165 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); 166 | } 167 | } 168 | 169 | //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. 170 | //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. 171 | //Therefore we need to change it back to what it was: The active extruder index. 172 | Connections 173 | { 174 | target: repeater.model 175 | function onModelChanged() 176 | { 177 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 178 | } 179 | } 180 | 181 | //When switching back to the stage, make sure the active extruder is selected 182 | Component.onCompleted: 183 | { 184 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /resources/qml/ExtruderTabs411.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.4 as UM 8 | import Cura 1.1 as Cura 9 | 10 | TabRow 11 | { 12 | id: tabBar 13 | 14 | property var extrudersModel: CuraApplication.getExtrudersModel() 15 | property bool hasMaterials: 16 | { 17 | if (CuraSDKVersion >= "6.2.0") { 18 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasMaterials : false 19 | } else { 20 | return Cura.MachineManager.hasMaterials 21 | } 22 | } 23 | property bool hasVariants: 24 | { 25 | if (CuraSDKVersion >= "6.2.0") { 26 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.hasVariants : false 27 | } else { 28 | return Cura.MachineManager.hasVariants 29 | } 30 | } 31 | 32 | visible: hasMaterials || hasVariants 33 | width: parent.width 34 | 35 | Repeater 36 | { 37 | id: repeater 38 | model: extrudersModel 39 | delegate: TabRowButton 40 | { 41 | contentItem: Item 42 | { 43 | Cura.ExtruderIcon 44 | { 45 | id: extruderIcon 46 | materialColor: model.color 47 | extruderEnabled: model.enabled 48 | 49 | anchors.left: parent.left 50 | height: parent.height 51 | width: height 52 | } 53 | 54 | // Label for the brand of the material 55 | Label 56 | { 57 | id: typeAndBrandNameLabel 58 | 59 | text: model.material_brand + " " + model.material 60 | elide: Text.ElideRight 61 | font: UM.Theme.getFont("default") 62 | color: UM.Theme.getColor("text") 63 | renderType: Text.NativeRendering 64 | wrapMode: Text.NoWrap 65 | visible: hasMaterials 66 | 67 | anchors 68 | { 69 | top: extruderIcon.top 70 | left: extruderIcon.right 71 | leftMargin: UM.Theme.getSize("default_margin").width 72 | right: configurationWarning.left 73 | rightMargin: UM.Theme.getSize("default_margin").width 74 | } 75 | } 76 | 77 | // Label that shows the name of the variant 78 | Label 79 | { 80 | id: variantLabel 81 | 82 | text: model.variant 83 | elide: Text.ElideRight 84 | font: UM.Theme.getFont("default_bold") 85 | color: UM.Theme.getColor("text") 86 | renderType: Text.NativeRendering 87 | wrapMode: Text.NoWrap 88 | visible: hasVariants 89 | 90 | anchors 91 | { 92 | left: extruderIcon.right 93 | leftMargin: UM.Theme.getSize("default_margin").width 94 | top: typeAndBrandNameLabel.bottom 95 | } 96 | } 97 | 98 | UM.StatusIcon 99 | { 100 | id: configurationWarning 101 | 102 | visible: status != UM.StatusIcon.Status.NEUTRAL 103 | height: visible ? UM.Theme.getSize("message_type_icon").height: 0 104 | width: visible ? UM.Theme.getSize("message_type_icon").height : 0 105 | 106 | property var extruderStack: 107 | { 108 | return (Cura.MachineManager.activeMachine != null) ? Cura.MachineManager.activeMachine.extruderList[model.index] : undefined; 109 | } 110 | 111 | anchors.right: parent.right 112 | anchors.verticalCenter: parent.verticalCenter 113 | 114 | status: 115 | { 116 | if (model.enabled && (tabBar.hasMaterials || tabBar.hasVariants)) 117 | { 118 | if (extruderStack != undefined && Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible", "") != "True") 119 | { 120 | return UM.StatusIcon.Status.ERROR 121 | } 122 | if (!SidebarGUIPlugin.getExtruderHasQualityForMaterial(extruderStack)) 123 | { 124 | return UM.StatusIcon.Status.WARNING 125 | } 126 | } 127 | return UM.StatusIcon.Status.NEUTRAL 128 | } 129 | 130 | MouseArea // Connection status tooltip hover area 131 | { 132 | id: tooltipHoverArea 133 | anchors.fill: parent 134 | hoverEnabled: tooltip.text != "" 135 | acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks 136 | 137 | onEntered: tooltip.show() 138 | onExited: tooltip.hide() 139 | } 140 | 141 | Cura.ToolTip 142 | { 143 | id: tooltip 144 | x: 0 145 | y: parent.height + UM.Theme.getSize("default_margin").height 146 | width: UM.Theme.getSize("tooltip").width 147 | targetPoint: Qt.point(Math.round(extruderIcon.width / 2), 0) 148 | text: 149 | { 150 | if (configurationWarning.status == UM.StatusIcon.Status.ERROR) 151 | { 152 | return catalog.i18nc("@tooltip", "The configuration of this extruder is not allowed, and prohibits slicing.") 153 | } 154 | if (configurationWarning.status == UM.StatusIcon.Status.WARNING) 155 | { 156 | return catalog.i18nc("@tooltip", "There are no profiles matching the configuration of this extruder.") 157 | } 158 | return "" 159 | } 160 | } 161 | } 162 | } 163 | onClicked: 164 | { 165 | Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) 166 | } 167 | } 168 | } 169 | 170 | //When active extruder changes for some other reason, switch tabs. 171 | //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! 172 | //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. 173 | Connections 174 | { 175 | target: Cura.ExtruderManager 176 | function onActiveExtruderChanged() 177 | { 178 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); 179 | } 180 | } 181 | 182 | //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. 183 | //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. 184 | //Therefore we need to change it back to what it was: The active extruder index. 185 | Connections 186 | { 187 | target: repeater.model 188 | function onModelChanged() 189 | { 190 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 191 | } 192 | } 193 | 194 | //When switching back to the stage, make sure the active extruder is selected 195 | Component.onCompleted: 196 | { 197 | tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /resources/qml/ProfileSelector51.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Item 12 | { 13 | width: parent.width 14 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 15 | 16 | anchors 17 | { 18 | top: parent.top 19 | left: parent.left 20 | leftMargin: parent.padding 21 | right: parent.right 22 | rightMargin: parent.padding 23 | } 24 | 25 | Button 26 | { 27 | id: intentSelection 28 | onClicked: menu.opened ? menu.close() : menu.open() 29 | 30 | anchors.left: parent.left 31 | anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + UM.Theme.getSize("default_margin").width 32 | anchors.right: modeToggleSwitch.left 33 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 34 | height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height 35 | hoverEnabled: true 36 | visible: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 37 | 38 | baselineOffset: 0 // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... 39 | 40 | contentItem: RowLayout 41 | { 42 | spacing: 0 43 | anchors.left: parent.left 44 | anchors.right: customisedSettings.left 45 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 46 | 47 | Label 48 | { 49 | id: textLabel 50 | text: Cura.MachineManager.activeQualityDisplayNameMap["main"] 51 | font: UM.Theme.getFont("default") 52 | color: UM.Theme.getColor("text") 53 | Layout.margins: 0 54 | Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. 55 | height: contentHeight 56 | verticalAlignment: Text.AlignVCenter 57 | renderType: Text.NativeRendering 58 | elide: Text.ElideRight 59 | } 60 | 61 | Label 62 | { 63 | text: activeQualityDetailText() 64 | font: UM.Theme.getFont("default") 65 | color: UM.Theme.getColor("text_detail") 66 | Layout.margins: 0 67 | Layout.fillWidth: true 68 | 69 | height: contentHeight 70 | verticalAlignment: Text.AlignVCenter 71 | renderType: Text.NativeRendering 72 | elide: Text.ElideRight 73 | 74 | function activeQualityDetailText() 75 | { 76 | var resultMap = Cura.MachineManager.activeQualityDisplayNameMap 77 | var resultSuffix = resultMap["suffix"] 78 | var result = "" 79 | 80 | if (Cura.MachineManager.isActiveQualityExperimental) 81 | { 82 | resultSuffix += " (Experimental)" 83 | } 84 | 85 | if (Cura.MachineManager.isActiveQualitySupported) 86 | { 87 | if (Cura.MachineManager.activeQualityLayerHeight > 0) 88 | { 89 | if (resultSuffix) 90 | { 91 | result += " - " + resultSuffix 92 | } 93 | result += " - " 94 | result += Cura.MachineManager.activeQualityLayerHeight + "mm" 95 | } 96 | } 97 | 98 | return result 99 | } 100 | } 101 | } 102 | 103 | background: Rectangle 104 | { 105 | id: backgroundItem 106 | border.color: intentSelection.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") 107 | border.width: UM.Theme.getSize("default_lining").width 108 | radius: UM.Theme.getSize("default_radius").width 109 | color: UM.Theme.getColor("main_background") 110 | } 111 | 112 | UM.SimpleButton 113 | { 114 | id: customisedSettings 115 | 116 | visible: Cura.MachineManager.hasUserSettings 117 | width: UM.Theme.getSize("print_setup_icon").width 118 | height: UM.Theme.getSize("print_setup_icon").height 119 | 120 | anchors.verticalCenter: parent.verticalCenter 121 | anchors.right: downArrow.left 122 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 123 | 124 | color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); 125 | iconSource: UM.Theme.getIcon("StarFilled") 126 | 127 | onClicked: 128 | { 129 | forceActiveFocus(); 130 | Cura.Actions.manageProfiles.trigger() 131 | } 132 | onEntered: 133 | { 134 | var content = catalog.i18nc("@tooltip", "Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") 135 | base.showTooltip(intent, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content) 136 | } 137 | onExited: base.hideTooltip() 138 | } 139 | UM.ColorImage 140 | { 141 | id: downArrow 142 | 143 | source: UM.Theme.getIcon("ChevronSingleDown") 144 | width: UM.Theme.getSize("standard_arrow").width 145 | height: UM.Theme.getSize("standard_arrow").height 146 | 147 | anchors 148 | { 149 | right: parent.right 150 | verticalCenter: parent.verticalCenter 151 | rightMargin: UM.Theme.getSize("default_margin").width 152 | } 153 | 154 | color: UM.Theme.getColor("setting_control_button") 155 | } 156 | } 157 | 158 | Cura.QualitiesWithIntentMenu 159 | { 160 | id: menu 161 | y: intentSelection.y + intentSelection.height 162 | x: intentSelection.x 163 | width: intentSelection.width 164 | } 165 | 166 | ModeToggleSwitch 167 | { 168 | id: modeToggleSwitch 169 | anchors.right: dockButton.left 170 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 171 | } 172 | 173 | UM.SimpleButton 174 | { 175 | id: dockButton 176 | anchors 177 | { 178 | verticalCenter: modeToggleSwitch.verticalCenter 179 | 180 | right: collapseButton.left 181 | rightMargin: UM.Theme.getSize("default_margin").width 182 | } 183 | iconSource: Qt.resolvedUrl(settingsDocked ? "../icons/settings_undock.svg" : "../icons/settings_dock.svg") 184 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 185 | height: width 186 | color: UM.Theme.getColor("small_button_text") 187 | 188 | onClicked: 189 | { 190 | UM.Preferences.setValue("sidebargui/docked_sidebar", !UM.Preferences.getValue("sidebargui/docked_sidebar")) 191 | stageMenu.settingsDocked = UM.Preferences.getValue("sidebargui/docked_sidebar") 192 | } 193 | } 194 | 195 | UM.SimpleButton 196 | { 197 | id: collapseButton 198 | anchors 199 | { 200 | verticalCenter: modeToggleSwitch.verticalCenter 201 | 202 | right: parent.right 203 | rightMargin: UM.Theme.getSize("default_margin").width 204 | } 205 | iconSource: UM.Theme.getIcon("Cancel") 206 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 207 | height: width 208 | color: UM.Theme.getColor("small_button_text") 209 | 210 | onClicked: 211 | { 212 | UM.Preferences.setValue("view/settings_visible", false) 213 | stageMenu.settingsVisible = false 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /resources/qml/PrepareStageLegend40.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.4 5 | import QtQuick.Controls 1.2 6 | import QtQuick.Controls.Styles 1.1 7 | import QtGraphicalEffects 1.0 // for the linear gradient 8 | 9 | import UM 1.0 as UM 10 | import Cura 1.0 as Cura 11 | 12 | Cura.ExpandableComponent 13 | { 14 | id: base 15 | 16 | contentHeaderTitle: catalog.i18nc("@label", "Legend") 17 | 18 | headerItem: Item 19 | { 20 | Label 21 | { 22 | text: catalog.i18nc("@label", "Legend") 23 | verticalAlignment: Text.AlignVCenter 24 | height: parent.height 25 | elide: Text.ElideRight 26 | font: UM.Theme.getFont("default") 27 | color: UM.Theme.getColor("text_medium") 28 | renderType: Text.NativeRendering 29 | } 30 | } 31 | 32 | contentItem: Column 33 | { 34 | id: viewSettings 35 | width: UM.Theme.getSize("layerview_menu_size").width - 2 * UM.Theme.getSize("default_margin").width 36 | height: implicitHeight 37 | spacing: UM.Theme.getSize("layerview_row_spacing").height 38 | 39 | onActiveViewChanged: 40 | { 41 | xrayViewCheckBox.checked = (activeView == "XRayView") 42 | xrayViewCheckBox.visible = (activeView != "FastView" && activeView != "SmartSliceView") 43 | switch(activeView) 44 | { 45 | case "FastView": 46 | externalViewLabel.visible = true 47 | externalViewLabel.text = catalog.i18nc("@label", "Fast View") 48 | break; 49 | case "SmartSliceView": 50 | externalViewLabel.visible = true 51 | externalViewLabel.text = catalog.i18nc("@label", "Smart Slice") 52 | break; 53 | default: 54 | externalViewLabel.visible = false 55 | break; 56 | } 57 | } 58 | 59 | property string activeView: 60 | { 61 | var viewString = UM.Controller.activeView + ""; 62 | return viewString.substr(0, viewString.indexOf("(")); 63 | } 64 | 65 | CheckBox 66 | { 67 | id: xrayViewCheckBox 68 | checked: parent.activeView == "XRayView" 69 | visible: !externalViewLabel.visible 70 | onClicked: 71 | { 72 | if(checked && parent.activeView != "XRayView") 73 | { 74 | UM.Controller.setActiveView("XRayView") 75 | } 76 | else if(! checked && parent.activeView != "SolidView") 77 | { 78 | UM.Controller.setActiveView("SolidView") 79 | } 80 | } 81 | text: catalog.i18nc("@label", "X-Ray view") 82 | style: UM.Theme.styles.checkbox 83 | width: parent.width 84 | } 85 | 86 | Label 87 | { 88 | id: externalViewLabel 89 | 90 | height: UM.Theme.getSize("layerview_row").height 91 | width: parent.width 92 | color: UM.Theme.getColor("setting_control_text") 93 | font: UM.Theme.getFont("default") 94 | renderType: Text.NativeRendering 95 | } 96 | 97 | Item 98 | { 99 | // mock item to compensate for compatibility mode label in simulation view 100 | visible: false 101 | } 102 | 103 | Label 104 | { 105 | text: catalog.i18nc("@label", "Overhang") 106 | visible: parent.activeView == "SolidView" 107 | 108 | height: UM.Theme.getSize("layerview_row").height 109 | width: parent.width 110 | color: UM.Theme.getColor("setting_control_text") 111 | font: UM.Theme.getFont("default") 112 | renderType: Text.NativeRendering 113 | Rectangle 114 | { 115 | anchors.verticalCenter: parent.verticalCenter 116 | anchors.right: parent.right 117 | 118 | width: UM.Theme.getSize("layerview_legend_size").width 119 | height: UM.Theme.getSize("layerview_legend_size").height 120 | 121 | color: UM.Theme.getColor("model_overhang") 122 | 123 | border.width: UM.Theme.getSize("default_lining").width 124 | border.color: UM.Theme.getColor("lining") 125 | } 126 | } 127 | 128 | Label 129 | { 130 | text: catalog.i18nc("@label", "Outside buildvolume") 131 | visible: parent.activeView == "SolidView" 132 | 133 | height: UM.Theme.getSize("layerview_row").height 134 | width: parent.width 135 | color: UM.Theme.getColor("setting_control_text") 136 | font: UM.Theme.getFont("default") 137 | renderType: Text.NativeRendering 138 | Rectangle 139 | { 140 | anchors.verticalCenter: parent.verticalCenter 141 | anchors.right: parent.right 142 | 143 | width: UM.Theme.getSize("layerview_legend_size").width 144 | height: UM.Theme.getSize("layerview_legend_size").height 145 | 146 | border.width: UM.Theme.getSize("default_lining").width 147 | border.color: UM.Theme.getColor("lining") 148 | 149 | LinearGradient 150 | { 151 | anchors.fill: parent 152 | anchors.margins: UM.Theme.getSize("default_lining").width 153 | start: Qt.point(0, 0) 154 | end: Qt.point(width, height) 155 | gradient: Gradient 156 | { 157 | GradientStop { position: 0.5; color: UM.Theme.getColor("model_unslicable") } 158 | GradientStop { position: 0.5001; color: UM.Theme.getColor("model_unslicable_alt") } 159 | } 160 | } 161 | } 162 | } 163 | 164 | Label 165 | { 166 | text: catalog.i18nc("@label", "Normal geometry") 167 | visible: parent.activeView == "XRayView" 168 | 169 | height: UM.Theme.getSize("layerview_row").height 170 | width: parent.width 171 | color: UM.Theme.getColor("setting_control_text") 172 | font: UM.Theme.getFont("default") 173 | renderType: Text.NativeRendering 174 | Rectangle 175 | { 176 | anchors.verticalCenter: parent.verticalCenter 177 | anchors.right: parent.right 178 | 179 | width: UM.Theme.getSize("layerview_legend_size").width 180 | height: UM.Theme.getSize("layerview_legend_size").height 181 | 182 | border.width: UM.Theme.getSize("default_lining").width 183 | border.color: UM.Theme.getColor("lining") 184 | 185 | LinearGradient 186 | { 187 | anchors.fill: parent 188 | anchors.margins: UM.Theme.getSize("default_lining").width 189 | start: Qt.point(0, 0) 190 | end: Qt.point(width, 0) 191 | gradient: Gradient 192 | { 193 | GradientStop { position: 0.0; color: UM.Theme.getColor("xray") } 194 | GradientStop { position: 1.0; color: "white" } 195 | } 196 | } 197 | } 198 | } 199 | 200 | Label 201 | { 202 | text: catalog.i18nc("@label", "Geometry error") 203 | visible: parent.activeView == "XRayView" 204 | 205 | height: UM.Theme.getSize("layerview_row").height 206 | width: parent.width 207 | color: UM.Theme.getColor("setting_control_text") 208 | font: UM.Theme.getFont("default") 209 | renderType: Text.NativeRendering 210 | Rectangle 211 | { 212 | anchors.verticalCenter: parent.verticalCenter 213 | anchors.right: parent.right 214 | 215 | width: UM.Theme.getSize("layerview_legend_size").width 216 | height: UM.Theme.getSize("layerview_legend_size").height 217 | 218 | color: UM.Theme.getColor("xray_error") 219 | 220 | border.width: UM.Theme.getSize("default_lining").width 221 | border.color: UM.Theme.getColor("lining") 222 | } 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /resources/qml/ProfileSelector50.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Item 12 | { 13 | width: parent.width 14 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 15 | 16 | anchors 17 | { 18 | top: parent.top 19 | left: parent.left 20 | leftMargin: parent.padding 21 | right: parent.right 22 | rightMargin: parent.padding 23 | } 24 | 25 | Cura.NoIntentIcon 26 | { 27 | id: noIntentIcon 28 | affected_extruders: Cura.MachineManager.extruderPositionsWithNonActiveIntent 29 | intent_type: Cura.MachineManager.activeIntentCategory 30 | anchors.right: modeToggleSwitch.left 31 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 32 | anchors.verticalCenter: intentSelection.verticalCenter 33 | width: height 34 | height: UM.Theme.getSize("default_margin").height + 2 * UM.Theme.getSize("default_lining").height 35 | visible: affected_extruders.length && intentSelection.visible 36 | } 37 | 38 | Button 39 | { 40 | id: intentSelection 41 | onClicked: menu.opened ? menu.close() : menu.open() 42 | 43 | anchors.left: parent.left 44 | anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + UM.Theme.getSize("default_margin").width 45 | anchors.right: noIntentIcon.left 46 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 47 | height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height 48 | hoverEnabled: true 49 | visible: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 50 | 51 | baselineOffset: 0 // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... 52 | 53 | contentItem: RowLayout 54 | { 55 | spacing: 0 56 | anchors.left: parent.left 57 | anchors.right: customisedSettings.left 58 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 59 | 60 | Label 61 | { 62 | id: textLabel 63 | text: Cura.MachineManager.activeQualityDisplayNameMap["main"] 64 | font: UM.Theme.getFont("default") 65 | color: UM.Theme.getColor("text") 66 | Layout.margins: 0 67 | Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. 68 | height: contentHeight 69 | verticalAlignment: Text.AlignVCenter 70 | renderType: Text.NativeRendering 71 | elide: Text.ElideRight 72 | } 73 | 74 | Label 75 | { 76 | text: activeQualityDetailText() 77 | font: UM.Theme.getFont("default") 78 | color: UM.Theme.getColor("text_detail") 79 | Layout.margins: 0 80 | Layout.fillWidth: true 81 | 82 | height: contentHeight 83 | verticalAlignment: Text.AlignVCenter 84 | renderType: Text.NativeRendering 85 | elide: Text.ElideRight 86 | 87 | function activeQualityDetailText() 88 | { 89 | var resultMap = Cura.MachineManager.activeQualityDisplayNameMap 90 | var resultSuffix = resultMap["suffix"] 91 | var result = "" 92 | 93 | if (Cura.MachineManager.isActiveQualityExperimental) 94 | { 95 | resultSuffix += " (Experimental)" 96 | } 97 | 98 | if (Cura.MachineManager.isActiveQualitySupported) 99 | { 100 | if (Cura.MachineManager.activeQualityLayerHeight > 0) 101 | { 102 | if (resultSuffix) 103 | { 104 | result += " - " + resultSuffix 105 | } 106 | result += " - " 107 | result += Cura.MachineManager.activeQualityLayerHeight + "mm" 108 | } 109 | } 110 | 111 | return result 112 | } 113 | } 114 | } 115 | 116 | background: Rectangle 117 | { 118 | id: backgroundItem 119 | border.color: intentSelection.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") 120 | border.width: UM.Theme.getSize("default_lining").width 121 | radius: UM.Theme.getSize("default_radius").width 122 | color: UM.Theme.getColor("main_background") 123 | } 124 | 125 | UM.SimpleButton 126 | { 127 | id: customisedSettings 128 | 129 | visible: Cura.MachineManager.hasUserSettings 130 | width: UM.Theme.getSize("print_setup_icon").width 131 | height: UM.Theme.getSize("print_setup_icon").height 132 | 133 | anchors.verticalCenter: parent.verticalCenter 134 | anchors.right: downArrow.left 135 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 136 | 137 | color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); 138 | iconSource: UM.Theme.getIcon("StarFilled") 139 | 140 | onClicked: 141 | { 142 | forceActiveFocus(); 143 | Cura.Actions.manageProfiles.trigger() 144 | } 145 | onEntered: 146 | { 147 | var content = catalog.i18nc("@tooltip", "Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") 148 | base.showTooltip(intent, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content) 149 | } 150 | onExited: base.hideTooltip() 151 | } 152 | UM.ColorImage 153 | { 154 | id: downArrow 155 | 156 | source: UM.Theme.getIcon("ChevronSingleDown") 157 | width: UM.Theme.getSize("standard_arrow").width 158 | height: UM.Theme.getSize("standard_arrow").height 159 | 160 | anchors 161 | { 162 | right: parent.right 163 | verticalCenter: parent.verticalCenter 164 | rightMargin: UM.Theme.getSize("default_margin").width 165 | } 166 | 167 | color: UM.Theme.getColor("setting_control_button") 168 | } 169 | } 170 | 171 | Cura.QualitiesWithIntentMenu 172 | { 173 | id: menu 174 | y: intentSelection.y + intentSelection.height 175 | x: intentSelection.x 176 | width: intentSelection.width 177 | } 178 | 179 | ModeToggleSwitch 180 | { 181 | id: modeToggleSwitch 182 | anchors.right: dockButton.left 183 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 184 | } 185 | 186 | UM.SimpleButton 187 | { 188 | id: dockButton 189 | anchors 190 | { 191 | verticalCenter: modeToggleSwitch.verticalCenter 192 | 193 | right: collapseButton.left 194 | rightMargin: UM.Theme.getSize("default_margin").width 195 | } 196 | iconSource: Qt.resolvedUrl(settingsDocked ? "../icons/settings_undock.svg" : "../icons/settings_dock.svg") 197 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 198 | height: width 199 | color: UM.Theme.getColor("small_button_text") 200 | 201 | onClicked: 202 | { 203 | UM.Preferences.setValue("sidebargui/docked_sidebar", !UM.Preferences.getValue("sidebargui/docked_sidebar")) 204 | stageMenu.settingsDocked = UM.Preferences.getValue("sidebargui/docked_sidebar") 205 | } 206 | } 207 | 208 | UM.SimpleButton 209 | { 210 | id: collapseButton 211 | anchors 212 | { 213 | verticalCenter: modeToggleSwitch.verticalCenter 214 | 215 | right: parent.right 216 | rightMargin: UM.Theme.getSize("default_margin").width 217 | } 218 | iconSource: UM.Theme.getIcon("Cancel") 219 | width: UM.Theme.getSize("default_arrow").width + 2 * UM.Theme.getSize("default_lining").width 220 | height: width 221 | color: UM.Theme.getColor("small_button_text") 222 | 223 | onClicked: 224 | { 225 | UM.Preferences.setValue("view/settings_visible", false) 226 | stageMenu.settingsVisible = false 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /resources/qml/ProfileSelector44.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Item 12 | { 13 | width: parent.width 14 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 15 | 16 | anchors 17 | { 18 | top: parent.top 19 | left: parent.left 20 | leftMargin: parent.padding 21 | right: parent.right 22 | rightMargin: parent.padding 23 | } 24 | 25 | NoIntentIcon 26 | { 27 | id: noIntentIcon 28 | affected_extruders: Cura.MachineManager.extruderPositionsWithNonActiveIntent 29 | intent_type: Cura.MachineManager.activeIntentCategory 30 | anchors.right: modeToggleSwitch.left 31 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 32 | anchors.verticalCenter: intentSelection.verticalCenter 33 | width: height 34 | height: UM.Theme.getSize("default_margin").height + 2 * UM.Theme.getSize("default_lining").height 35 | visible: affected_extruders.length && intentSelection.visible 36 | } 37 | 38 | Button 39 | { 40 | id: intentSelection 41 | onClicked: menu.opened ? menu.close() : menu.open() 42 | 43 | anchors.left: parent.left 44 | anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + UM.Theme.getSize("default_margin").width 45 | anchors.right: noIntentIcon.left 46 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 47 | height: textLabel.contentHeight + 2 * UM.Theme.getSize("narrow_margin").height 48 | hoverEnabled: true 49 | visible: printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom 50 | 51 | baselineOffset: 0 // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... 52 | 53 | contentItem: RowLayout 54 | { 55 | spacing: 0 56 | anchors.left: parent.left 57 | anchors.right: customisedSettings.left 58 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 59 | 60 | Label 61 | { 62 | id: textLabel 63 | text: Cura.MachineManager.activeQualityDisplayNameMap["main"] 64 | font: UM.Theme.getFont("default") 65 | color: UM.Theme.getColor("text") 66 | Layout.margins: 0 67 | Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. 68 | height: contentHeight 69 | verticalAlignment: Text.AlignVCenter 70 | renderType: Text.NativeRendering 71 | elide: Text.ElideRight 72 | } 73 | 74 | Label 75 | { 76 | text: activeQualityDetailText() 77 | font: UM.Theme.getFont("default") 78 | color: UM.Theme.getColor("text_detail") 79 | Layout.margins: 0 80 | Layout.fillWidth: true 81 | 82 | height: contentHeight 83 | verticalAlignment: Text.AlignVCenter 84 | renderType: Text.NativeRendering 85 | elide: Text.ElideRight 86 | 87 | function activeQualityDetailText() 88 | { 89 | var resultMap = Cura.MachineManager.activeQualityDisplayNameMap 90 | var resultSuffix = resultMap["suffix"] 91 | var result = "" 92 | 93 | if (Cura.MachineManager.isActiveQualityExperimental) 94 | { 95 | resultSuffix += " (Experimental)" 96 | } 97 | 98 | if (Cura.MachineManager.isActiveQualitySupported) 99 | { 100 | if (Cura.MachineManager.activeQualityLayerHeight > 0) 101 | { 102 | if (resultSuffix) 103 | { 104 | result += " - " + resultSuffix 105 | } 106 | result += " - " 107 | result += Cura.MachineManager.activeQualityLayerHeight + "mm" 108 | } 109 | } 110 | 111 | return result 112 | } 113 | } 114 | } 115 | 116 | background: Rectangle 117 | { 118 | id: backgroundItem 119 | border.color: intentSelection.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") 120 | border.width: UM.Theme.getSize("default_lining").width 121 | radius: UM.Theme.getSize("default_radius").width 122 | color: UM.Theme.getColor("main_background") 123 | } 124 | 125 | UM.SimpleButton 126 | { 127 | id: customisedSettings 128 | 129 | visible: Cura.MachineManager.hasUserSettings 130 | width: UM.Theme.getSize("print_setup_icon").width 131 | height: UM.Theme.getSize("print_setup_icon").height 132 | 133 | anchors.verticalCenter: parent.verticalCenter 134 | anchors.right: downArrow.left 135 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 136 | 137 | color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); 138 | iconSource: 139 | { 140 | if(isLE410) 141 | { 142 | UM.Theme.getIcon("star") 143 | } 144 | return UM.Theme.getIcon("StarFilled") 145 | } 146 | 147 | onClicked: 148 | { 149 | forceActiveFocus(); 150 | Cura.Actions.manageProfiles.trigger() 151 | } 152 | onEntered: 153 | { 154 | var content = catalog.i18nc("@tooltip", "Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") 155 | base.showTooltip(intent, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content) 156 | } 157 | onExited: base.hideTooltip() 158 | } 159 | UM.RecolorImage 160 | { 161 | id: downArrow 162 | 163 | source: UM.Theme.getIcon("arrow_bottom") 164 | width: UM.Theme.getSize("standard_arrow").width 165 | height: UM.Theme.getSize("standard_arrow").height 166 | 167 | anchors 168 | { 169 | right: parent.right 170 | verticalCenter: parent.verticalCenter 171 | rightMargin: UM.Theme.getSize("default_margin").width 172 | } 173 | 174 | color: UM.Theme.getColor("setting_control_button") 175 | } 176 | } 177 | 178 | Cura.QualitiesWithIntentMenu 179 | { 180 | id: menu 181 | y: intentSelection.y + intentSelection.height 182 | x: intentSelection.x 183 | width: intentSelection.width 184 | } 185 | 186 | ModeToggleSwitch 187 | { 188 | id: modeToggleSwitch 189 | anchors.right: dockButton.left 190 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 191 | } 192 | 193 | UM.SimpleButton 194 | { 195 | id: dockButton 196 | anchors 197 | { 198 | verticalCenter: collapseButton.verticalCenter 199 | 200 | right: collapseButton.left 201 | rightMargin: UM.Theme.getSize("default_margin").width 202 | } 203 | iconSource: settingsDocked ? "../icons/settings_undock.svg" : "../icons/settings_dock.svg" 204 | width: UM.Theme.getSize("default_arrow").width + UM.Theme.getSize("default_lining").width 205 | height: width 206 | color: UM.Theme.getColor("small_button_text") 207 | 208 | onClicked: 209 | { 210 | UM.Preferences.setValue("sidebargui/docked_sidebar", !UM.Preferences.getValue("sidebargui/docked_sidebar")) 211 | stageMenu.settingsDocked = UM.Preferences.getValue("sidebargui/docked_sidebar") 212 | } 213 | } 214 | 215 | UM.SimpleButton 216 | { 217 | id: collapseButton 218 | anchors 219 | { 220 | top: parent.top 221 | topMargin: UM.Theme.getSize("default_margin").width 222 | 223 | right: parent.right 224 | rightMargin: UM.Theme.getSize("default_margin").width 225 | } 226 | iconSource: UM.Theme.getIcon("cross1") 227 | width: UM.Theme.getSize("default_arrow").width + UM.Theme.getSize("default_lining").width 228 | height: width 229 | color: UM.Theme.getColor("small_button_text") 230 | 231 | onClicked: 232 | { 233 | UM.Preferences.setValue("view/settings_visible", false) 234 | stageMenu.settingsVisible = false 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /resources/qml/SidebarContents.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.10 5 | import QtQuick.Controls 2.3 6 | import QtQuick.Layouts 1.3 7 | 8 | import UM 1.3 as UM 9 | import Cura 1.1 as Cura 10 | 11 | Cura.RoundedRectangle 12 | { 13 | border.width: UM.Theme.getSize("default_lining").width 14 | border.color: UM.Theme.getColor("lining") 15 | color: UM.Theme.getColor("main_background") 16 | 17 | cornerSide: Cura.RoundedRectangle.Direction.Left 18 | radius: UM.Theme.getSize("default_radius").width 19 | 20 | width: UM.Theme.getSize("print_setup_widget").width 21 | 22 | Column 23 | { 24 | id: settingsHeader 25 | width: parent.width 26 | 27 | anchors.top: parent.top 28 | anchors.topMargin: UM.Theme.getSize("default_margin").height 29 | 30 | Loader 31 | { 32 | width: parent.width 33 | height: UM.Theme.getSize("setting_control").height + UM.Theme.getSize("default_margin").height 34 | source: 35 | { 36 | if(isLE43) { 37 | return "ProfileSelector40.qml"; 38 | } else if(isLE413) { 39 | return "ProfileSelector44.qml"; 40 | } else if(isLE50) { 41 | return "ProfileSelector50.qml"; 42 | } else if(isLE52) { 43 | return "ProfileSelector51.qml"; 44 | } else { 45 | return "ProfileSelector53.qml"; 46 | } 47 | } 48 | } 49 | 50 | Item 51 | { 52 | width: parent.width 53 | height: extruderSelector.visible ? extruderSelector.height : 0 54 | 55 | Rectangle 56 | { 57 | width: parent.width 58 | anchors.bottom: extruderSelector.bottom 59 | height: UM.Theme.getSize("default_lining").height 60 | color: UM.Theme.getColor("lining") 61 | visible: extruderSelector.visible && extruderSelector.enabled 62 | } 63 | Loader 64 | { 65 | id: extruderSelector 66 | enabled: 67 | { 68 | if (printSetupSelector.contentItem.currentModeIndex == Cura.PrintSetupSelectorContents.Mode.Custom) 69 | { 70 | return true 71 | } 72 | else 73 | { 74 | return extruderConfiguration.visible 75 | } 76 | } 77 | 78 | anchors 79 | { 80 | left: parent.left 81 | leftMargin: UM.Theme.getSize("default_margin").width + 5 * UM.Theme.getSize("default_lining").width 82 | right: showExtruderConfigurationPanel.left 83 | rightMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_lining").width 84 | } 85 | 86 | source: 87 | { 88 | if(isLE410) { 89 | return "ExtruderTabs40.qml"; 90 | } else if (isLE413) { 91 | return "ExtruderTabs411.qml"; 92 | } else { 93 | return "ExtruderTabs50.qml"; 94 | } 95 | } 96 | } 97 | 98 | UM.SimpleButton 99 | { 100 | id: showExtruderConfigurationPanel 101 | anchors 102 | { 103 | right: parent.right 104 | rightMargin: UM.Theme.getSize("default_margin").width 105 | verticalCenter: parent.verticalCenter 106 | } 107 | iconSource: 108 | { 109 | if (isLE410) { 110 | return extruderConfiguration.visible ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") 111 | } 112 | return extruderConfiguration.visible ? UM.Theme.getIcon("ChevronSingleDown") : UM.Theme.getIcon("ChevronSingleLeft") 113 | } 114 | width: UM.Theme.getSize("standard_arrow").width 115 | height: UM.Theme.getSize("standard_arrow").height 116 | color: UM.Theme.getColor("setting_category_text") 117 | 118 | onClicked: 119 | { 120 | extruderConfiguration.visible = !extruderConfiguration.visible 121 | UM.Preferences.setValue("sidebargui/expand_extruder_configuration", extruderConfiguration.visible) 122 | } 123 | visible: extruderSelector.visible 124 | } 125 | } 126 | 127 | Item 128 | { 129 | id: extruderConfiguration 130 | visible: 131 | { 132 | if (extruderSelector.visible) 133 | { 134 | return UM.Preferences.getValue("sidebargui/expand_extruder_configuration") 135 | } 136 | return false 137 | } 138 | width: parent.width 139 | height: visible ? childrenRect.height : 0 140 | children: [ configurationMenu.contentItem ] 141 | 142 | Behavior on height { NumberAnimation { duration: 100 } } 143 | 144 | Cura.ConfigurationMenu 145 | { 146 | id: configurationMenu 147 | visible: false 148 | 149 | property var selectors 150 | property var enabledCheckbox 151 | 152 | Component.onCompleted: 153 | { 154 | configurationMenu.contentItem.children[1].visible = false // separator 155 | if(isLE413) 156 | { 157 | configurationMenu.contentItem.children[0].x = 2 * UM.Theme.getSize("default_margin").width // extruder config 158 | configurationMenu.contentItem.children[2].x = UM.Theme.getSize("default_margin").width // Custom/Configurations 159 | } 160 | 161 | var autoConfiguration = configurationMenu.contentItem.children[0].children[0]; 162 | autoConfiguration.children[0].visible = false // "Configurations" label 163 | autoConfiguration.children[0].height = 0 164 | autoConfiguration.children[1].anchors.topMargin = 0 // configurationListView 165 | autoConfiguration.children[1].children[0].anchors.topMargin = 0 166 | 167 | var customConfiguration = configurationMenu.contentItem.children[0].children[1]; 168 | customConfiguration.children[0].visible = false // "Custom" label 169 | customConfiguration.children[0].height = 0 170 | 171 | var extruderTabs = customConfiguration.children[1] 172 | if (isLE51) 173 | extruderTabs = customConfiguration.children[2] 174 | extruderTabs.visible = false // extruder tabs 175 | extruderTabs.height = 0 176 | extruderTabs.anchors.topMargin = 0 177 | 178 | var customSelectors = customConfiguration.children[2] 179 | if (isLE51) 180 | customSelectors = customConfiguration.children[3] 181 | customSelectors.children[0].visible = false // some spacer rectangle 182 | customSelectors.children[0].height = 0 183 | 184 | selectors = customSelectors.children[1] 185 | selectors.padding = 0 // enabled/material/variant column 186 | selectors.spacing = UM.Theme.getSize("default_lining").height 187 | 188 | enabledCheckbox = selectors.children[0].children[1] 189 | 190 | if (isLE413) 191 | { 192 | return 193 | } 194 | 195 | materialSelectionLoader.source = "MaterialSelection.qml" 196 | } 197 | 198 | Loader 199 | { 200 | id: materialSelectionLoader 201 | property var configurationMenu: parent 202 | onLoaded: 203 | { 204 | configurationMenu.selectors.children[1].children[1] = item 205 | } 206 | } 207 | } 208 | } 209 | } 210 | 211 | // This is a work around to prevent the printSetupSelector from having to be re-loaded every time 212 | // a stage switch is done. 213 | Item 214 | { 215 | id: settingsViewContainer 216 | children: [ printSetupSelector.contentItem ] 217 | anchors 218 | { 219 | top: settingsHeader.bottom 220 | topMargin: UM.Theme.getSize("default_lining").height 221 | bottom: parent.bottom 222 | } 223 | width: parent.width 224 | 225 | Component.onDestruction: 226 | { 227 | // HACK: This is to ensure that the parent never gets set to null, as this wreaks havoc on the focus. 228 | printSetupSelector.contentItem.parent = printSetupSelector; 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /resources/qml/SidebarStageMenu.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | 4 | import QtQuick 2.7 5 | import QtQuick.Controls 2.3 6 | 7 | import UM 1.3 as UM 8 | import Cura 1.1 as Cura 9 | 10 | Item 11 | { 12 | id: stageMenu 13 | 14 | property bool is40 15 | property bool isLE43 16 | property bool isLE44 17 | property bool isLE46 18 | property bool isLE410 19 | property bool isLE413 20 | property bool isLE50 21 | property bool isLE51 22 | property bool isLE52 23 | property bool isLE59 24 | 25 | property bool prepareStageActive: UM.Controller.activeStage.toString().indexOf("PrepareStage") == 0 26 | property bool preSlicedData: PrintInformation !== null && PrintInformation.preSliced 27 | property bool settingsVisible: UM.Preferences.getValue("view/settings_visible") 28 | property bool settingsDocked: UM.Preferences.getValue("sidebargui/docked_sidebar") 29 | property bool sidebarVisible: settingsVisible && (prepareStageActive || !preSlicedData) && settingsDocked 30 | property real sidebarWidth: sidebarVisible ? printSetupSelector.width : 0 31 | 32 | property var printSetupTooltip 33 | 34 | Component.onCompleted: 35 | { 36 | // create a version string that can be easily compared, even with the minor version >= 10 37 | var SortableSDKVersion = parseInt(CuraSDKVersion.replace(/\.(\d)\./g, ".0$1.")) 38 | is40 = (SortableSDKVersion == "6.00.0") 39 | isLE43 = (SortableSDKVersion <= "6.03.0") 40 | isLE44 = (SortableSDKVersion <= "7.00.0") 41 | isLE46 = (SortableSDKVersion <= "7.02.0") 42 | isLE410 = (SortableSDKVersion <= "7.06.0") 43 | isLE413 = (SortableSDKVersion <= "7.09.0") 44 | isLE50 = (SortableSDKVersion <= "8.00.0") 45 | isLE51 = (SortableSDKVersion <= "8.01.0") 46 | isLE52 = (SortableSDKVersion <= "8.02.0") 47 | isLE59 = (SortableSDKVersion <= "8.09.0") && CuraApplication.version != "master" && CuraApplication.version != "dev" 48 | if(is40) 49 | { 50 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 4.0") 51 | } 52 | else if(isLE44) 53 | { 54 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 4.1 - 4.4") 55 | } 56 | else if(isLE46) 57 | { 58 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 4.5 - 4.6") 59 | } 60 | else if(isLE410) 61 | { 62 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 4.7 - 4.9") 63 | } 64 | else if(isLE413) 65 | { 66 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 4.10 - 4.13") 67 | } 68 | else if(isLE51) 69 | { 70 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 5.0 - 5.1") 71 | } 72 | else if(isLE52) 73 | { 74 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 5.2") 75 | } 76 | else if(isLE59) 77 | { 78 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 5.3 - 5.9") 79 | } 80 | else 81 | { 82 | CuraApplication.log("SidebarGUIPlugin patching interface for Cura 5.10 and newer") 83 | } 84 | 85 | // top-align toolbar (defined in Cura.qml) 86 | toolbar.visible = true 87 | toolbar.anchors.verticalCenter = undefined 88 | toolbar.anchors.top = toolbar.parent.top 89 | toolbar.anchors.topMargin = UM.Theme.getSize("stage_menu").height + UM.Theme.getSize("default_margin").height 90 | 91 | // hide view orientation controls (shown them in viewpanel instead) 92 | viewOrientationControls.visible = false 93 | viewOrientationControls.height = 0 94 | viewOrientationControls.anchors.margins = 0 95 | 96 | // adjust message stack position for sidebar 97 | var messageStack 98 | if(is40) 99 | { 100 | messageStack = base.contentItem.children[0].children[3].children[7] 101 | } 102 | else if(isLE44) 103 | { 104 | messageStack = base.contentItem.children[2].children[3].children[7] 105 | } 106 | else if(isLE413) 107 | { 108 | messageStack = base.contentItem.children[2].children[3].children[8] 109 | } 110 | else if(isLE52) 111 | { 112 | messageStack = base.contentItem.children[3].children[3].children[8] 113 | } 114 | else 115 | { 116 | messageStack = base.contentItem.children[4].children[3].children[8] 117 | } 118 | messageStack.anchors.horizontalCenter = undefined 119 | messageStack.anchors.left = messageStack.parent.left 120 | messageStack.anchors.leftMargin = Qt.binding(function() 121 | { 122 | return Math.floor((base.width - printSetupSelector.width) / 2) 123 | }) 124 | 125 | // adjust stages menu position for sidebar 126 | var stagesListContainer = mainWindowHeader.children[1] 127 | stagesListContainer.anchors.horizontalCenter = undefined 128 | stagesListContainer.anchors.left = stagesListContainer.parent.left 129 | stagesListContainer.anchors.leftMargin = Qt.binding(function() 130 | { 131 | return Math.floor((base.width - printSetupSelector.width - stagesListContainer.width) / 2) 132 | }) 133 | 134 | 135 | // hide application logo if there is no room for it 136 | var applicationLogo = mainWindowHeader.children[0] 137 | applicationLogo.visible = Qt.binding(function() 138 | { 139 | return stagesListContainer.anchors.leftMargin > applicationLogo.width + 2 * UM.Theme.getSize("default_margin").width 140 | }) 141 | 142 | // compensate viewport for full-height sidebar 143 | base.viewportRect = Qt.binding(function() 144 | { 145 | return Qt.rect(0, 0, (base.width - sidebarWidth) / base.width, 1.0) 146 | }) 147 | 148 | 149 | // make settingview take up available height 150 | var printSetupContent = printSetupSelector.contentItem 151 | if(is40) 152 | { 153 | printSetupContent.children[1].visible = false // separator line 154 | printSetupContent.children[2].visible = false // recommended/custom button row 155 | } 156 | else 157 | { 158 | printSetupContent.children[2].visible = false // separator line 159 | printSetupContent.children[3].visible = false // recommended/custom button row 160 | } 161 | 162 | printSetupContent.height = undefined 163 | printSetupContent.anchors.fill = printSetupContent.parent 164 | 165 | var printSetupChildren = (is40) ? printSetupContent.children[0] : printSetupContent.children[1] 166 | printSetupChildren.height = undefined // id: contents 167 | printSetupChildren.anchors.fill = printSetupContent 168 | printSetupChildren.anchors.bottomMargin = 2 * UM.Theme.getSize("default_lining").height 169 | 170 | var customPrintSetup = printSetupChildren.children[1] 171 | 172 | customPrintSetup.padding = UM.Theme.getSize("narrow_margin").width - UM.Theme.getSize("default_lining").width 173 | customPrintSetup.height = undefined 174 | customPrintSetup.anchors.fill = customPrintSetup.parent 175 | 176 | customPrintSetup.children[2].height = undefined // rectangle containing settingview 177 | customPrintSetup.children[2].anchors.fill = customPrintSetup 178 | 179 | customPrintSetup.children[1].visible = false // extruder tabs 180 | customPrintSetup.children[0].visible = false // profile selector 181 | customPrintSetup.children[0].height = 0 182 | 183 | var settingView = customPrintSetup.children[2].children[0] 184 | if(isLE413) 185 | { 186 | customPrintSetup.children[2].anchors.rightMargin = 0 187 | } else { 188 | // extend scrollbar to the window right edge 189 | customPrintSetup.children[2].anchors.rightMargin = -UM.Theme.getSize("narrow_margin").width 190 | settingView.children[4].ScrollBar.vertical.rightPadding = UM.Theme.getSize("narrow_margin").width 191 | } 192 | 193 | if(isLE59) 194 | { 195 | var recommendedPrintSetup = printSetupChildren.children[0] 196 | if(!isLE52) 197 | { 198 | recommendedPrintSetup.height = undefined 199 | recommendedPrintSetup.children[0].contentItem.children[0].children[9].children[0].visible = false 200 | } 201 | } 202 | 203 | // tweak header height 204 | headerBackground.height = mainWindowHeader.height + UM.Theme.getSize("default_margin").height 205 | main.anchors.top = main.parent.top 206 | main.anchors.topMargin = UM.Theme.getSize("default_margin").height 207 | 208 | printSetupTooltip = tooltip // defined in Cura.qml 209 | } 210 | 211 | Connections 212 | { 213 | target: tooltip 214 | enabled: !settingsDocked 215 | onOpacityChanged: function() 216 | { 217 | if(tooltip.opacity == 0) 218 | { 219 | sidebarToolWindow.hideTooltip() 220 | } 221 | else if(tooltip.opacity == 1) 222 | { 223 | sidebarToolWindow.showTooltip() 224 | } 225 | } 226 | onTextChanged: function() 227 | { 228 | sidebarToolWindow.toolTipText = tooltip.text 229 | } 230 | onTargetChanged: function() 231 | { 232 | sidebarToolWindow.toolTipY = tooltip.target.y 233 | } 234 | } 235 | 236 | Loader 237 | { 238 | anchors.left: parent.left 239 | anchors.leftMargin: -UM.Theme.getSize("default_lining").width 240 | source: 241 | { 242 | if(isLE410) { 243 | return "OpenFileButton40.qml"; 244 | } else if(isLE413) { 245 | return "OpenFileButton411.qml"; 246 | } else { 247 | return "OpenFileButton50.qml"; 248 | } 249 | } 250 | } 251 | 252 | Loader 253 | { 254 | anchors.left: printSetupSidebar.left 255 | width: UM.Theme.getSize("machine_selector_widget").width 256 | height: 257 | { 258 | if (isLE413) 259 | { 260 | return UM.Theme.getSize("main_window_header_button").height 261 | } else { 262 | return Math.round(0.5 * UM.Theme.getSize("main_window_header").height) 263 | } 264 | } 265 | y: - Math.floor((UM.Theme.getSize("main_window_header").height + height) / 2) 266 | 267 | source: 268 | { 269 | if(isLE52) { 270 | return "MachineSelector40.qml"; 271 | } else { 272 | return "MachineSelector53.qml"; 273 | } 274 | } 275 | } 276 | 277 | Loader 278 | { 279 | id: viewOptionsPanel 280 | 281 | anchors.right: printSetupSidebar.left 282 | anchors.rightMargin: UM.Theme.getSize("default_margin").width 283 | 284 | source: 285 | { 286 | if(isLE413) { 287 | return "ViewOptionsPanel40.qml"; 288 | } else { 289 | return "ViewOptionsPanel50.qml"; 290 | } 291 | } 292 | } 293 | 294 | PrintSetupSummary 295 | { 296 | id: printSetupSummary 297 | visible: !sidebarVisible 298 | 299 | width: printSetupSelector.width 300 | 301 | anchors 302 | { 303 | top: parent.top 304 | right: bottomRight.right 305 | } 306 | } 307 | 308 | Item 309 | { 310 | id: printSetupSidebar 311 | visible: sidebarVisible 312 | 313 | width: UM.Theme.getSize("print_setup_widget").width 314 | anchors 315 | { 316 | top: parent.top 317 | bottom: actionRow.top 318 | bottomMargin: actionRow.height == 0 ? 0 : UM.Theme.getSize("thin_margin").height 319 | right: bottomRight.right 320 | } 321 | 322 | children:[sidebarContents] 323 | } 324 | 325 | SidebarContents 326 | { 327 | id: sidebarContents 328 | anchors.fill: parent 329 | } 330 | 331 | SidebarFooter 332 | { 333 | id: actionRow 334 | 335 | anchors.bottom: bottomRight.bottom 336 | anchors.right: bottomRight.right 337 | } 338 | 339 | Item 340 | { 341 | id: bottomRight 342 | anchors.right: parent.right 343 | y: base.height - stageMenu.mapToItem(base.contentItem, 0, 0).y - height 344 | } 345 | 346 | SidebarToolWindow 347 | { 348 | id: sidebarToolWindow 349 | onClosing: 350 | { 351 | // tool window is closed by window manager (not via our collapse button) 352 | UM.Preferences.setValue("view/settings_visible", false) 353 | stageMenu.settingsVisible = false 354 | 355 | printSetupTooltip.visible = true 356 | } 357 | } 358 | } -------------------------------------------------------------------------------- /resources/qml/MaterialBrandMenu.qml: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Aldo Hoeben / fieldOfView 2 | // SidebarGUIPlugin is released under the terms of the AGPLv3 or higher. 3 | // Copyright (c) 2022 Ultimaker B.V. 4 | // Cura is released under the terms of the LGPLv3 or higher. 5 | 6 | import QtQuick 2.7 7 | import QtQuick.Controls 2.4 8 | import QtQuick.Layouts 2.7 9 | 10 | import UM 1.5 as UM 11 | import Cura 1.7 as Cura 12 | 13 | /* This element is a workaround for MacOS, where it can crash in Qt6 when nested menus are closed. 14 | Instead we'll use a pop-up which doesn't seem to have that problem. */ 15 | 16 | Cura.MenuItem 17 | { 18 | id: materialBrandMenu 19 | overrideShowArrow: true 20 | 21 | property var materialTypesModel 22 | text: materialTypesModel.name 23 | 24 | contentItem: MouseArea 25 | { 26 | hoverEnabled: true 27 | 28 | RowLayout 29 | { 30 | spacing: 0 31 | opacity: materialBrandMenu.enabled ? 1 : 0.5 32 | 33 | Item 34 | { 35 | // Spacer 36 | width: UM.Theme.getSize("default_margin").width 37 | } 38 | 39 | UM.Label 40 | { 41 | text: replaceText(materialBrandMenu.text) 42 | Layout.fillWidth: true 43 | Layout.fillHeight:true 44 | elide: Label.ElideRight 45 | wrapMode: Text.NoWrap 46 | } 47 | 48 | Item 49 | { 50 | Layout.fillWidth: true 51 | } 52 | 53 | Item 54 | { 55 | // Right side margin 56 | width: UM.Theme.getSize("default_margin").width 57 | } 58 | } 59 | 60 | onEntered: showTimer.restartTimer() 61 | onExited: hideTimer.restartTimer() 62 | } 63 | 64 | Timer 65 | { 66 | id: showTimer 67 | interval: 250 68 | function restartTimer() 69 | { 70 | restart(); 71 | running = Qt.binding(function() { return materialBrandMenu.enabled && materialBrandMenu.contentItem.containsMouse; }); 72 | hideTimer.running = false; 73 | } 74 | onTriggered: menuPopup.open() 75 | } 76 | Timer 77 | { 78 | id: hideTimer 79 | interval: 250 80 | function restartTimer() //Restart but re-evaluate the running property then. 81 | { 82 | restart(); 83 | running = Qt.binding(function() { return materialBrandMenu.enabled && !materialBrandMenu.contentItem.containsMouse && !menuPopup.itemHovered > 0; }); 84 | showTimer.running = false; 85 | } 86 | onTriggered: menuPopup.close() 87 | } 88 | 89 | Popup 90 | { 91 | id: menuPopup 92 | width: materialTypesList.width + padding * 2 93 | height: materialTypesList.height + padding * 2 94 | 95 | property var flipped: false 96 | 97 | x: - width + UM.Theme.getSize("thin_margin").width 98 | y: { 99 | // Checks if popup is more than halfway down the screen AND further than 400 down (this avoids popup going off the top of screen) 100 | // If it is then the popup will push up instead of down 101 | // This fixes the popups appearing bellow the bottom of the screen. 102 | 103 | if (materialBrandMenu.parent.height / 2 < parent.y && parent.y > 400) { 104 | flipped = true 105 | return -UM.Theme.getSize("default_lining").width - height + UM.Theme.getSize("menu").height 106 | } 107 | flipped = false 108 | return -UM.Theme.getSize("default_lining").width 109 | } 110 | 111 | padding: background.border.width 112 | // Nasty hack to ensure that we can keep track if the popup contains the mouse. 113 | // Since we also want a hover for the sub items (and these events are sent async) 114 | // We have to keep a count of itemHovered (instead of just a bool) 115 | property int itemHovered: 0 116 | MouseArea 117 | { 118 | id: submenuArea 119 | anchors.fill: parent 120 | 121 | hoverEnabled: true 122 | onEntered: hideTimer.restartTimer() 123 | } 124 | 125 | background: Rectangle 126 | { 127 | color: UM.Theme.getColor("main_background") 128 | border.color: UM.Theme.getColor("lining") 129 | border.width: UM.Theme.getSize("default_lining").width 130 | } 131 | 132 | Column 133 | { 134 | id: materialTypesList 135 | spacing: 0 136 | width: brandMaterialsRepeater.width 137 | 138 | property var brandMaterials: materialTypesModel.material_types 139 | 140 | Repeater 141 | { 142 | id: brandMaterialsRepeater 143 | model: parent.brandMaterials 144 | 145 | property var maxWidth: 0 146 | onItemAdded: updateWidth() 147 | onItemRemoved: updateWidth() 148 | 149 | function updateWidth() 150 | { 151 | var result = 0; 152 | for (var i = 0; i < count; ++i) { 153 | var item = itemAt(i); 154 | if(item != null) 155 | { 156 | result = Math.max(item.contentWidth, result); 157 | } 158 | } 159 | width = result + 2 * UM.Theme.getSize("default_margin").width; 160 | } 161 | 162 | //Use a MouseArea and Rectangle, not a button, because the button grabs mouse events which makes the parent pop-up think it's no longer being hovered. 163 | //With a custom MouseArea, we can prevent the events from being accepted. 164 | delegate: Rectangle 165 | { 166 | id: brandMaterialBase 167 | height: UM.Theme.getSize("menu").height 168 | width: parent.width 169 | 170 | color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1") 171 | 172 | property int contentWidth: brandLabel.width + chevron.width + UM.Theme.getSize("default_margin").width 173 | property bool isFlipped: menuPopup.flipped 174 | 175 | RowLayout 176 | { 177 | spacing: 0 178 | opacity: materialBrandMenu.enabled ? 1 : 0.5 179 | height: parent.height 180 | width: parent.width 181 | 182 | Item 183 | { 184 | // Spacer 185 | width: UM.Theme.getSize("default_margin").width 186 | } 187 | 188 | UM.Label 189 | { 190 | id: brandLabel 191 | text: model.name 192 | Layout.fillHeight: true 193 | elide: Label.ElideRight 194 | wrapMode: Text.NoWrap 195 | } 196 | 197 | Item 198 | { 199 | Layout.fillWidth: true 200 | } 201 | 202 | UM.ColorImage 203 | { 204 | id: chevron 205 | height: UM.Theme.getSize("default_arrow").height 206 | width: UM.Theme.getSize("default_arrow").width 207 | color: UM.Theme.getColor("setting_control_text") 208 | source: UM.Theme.getIcon("ChevronSingleRight") 209 | } 210 | 211 | Item 212 | { 213 | // Right side margin 214 | width: UM.Theme.getSize("default_margin").width 215 | } 216 | } 217 | 218 | MouseArea 219 | { 220 | id: materialTypeButton 221 | anchors.fill: parent 222 | 223 | hoverEnabled: true 224 | acceptedButtons: Qt.NoButton 225 | 226 | onEntered: 227 | { 228 | menuPopup.itemHovered += 1; 229 | showSubTimer.restartTimer(); 230 | } 231 | onExited: 232 | { 233 | menuPopup.itemHovered -= 1; 234 | hideSubTimer.restartTimer(); 235 | } 236 | } 237 | Timer 238 | { 239 | id: showSubTimer 240 | interval: 250 241 | function restartTimer() 242 | { 243 | restart(); 244 | running = Qt.binding(function() { return materialTypeButton.containsMouse; }); 245 | hideSubTimer.running = false; 246 | } 247 | onTriggered: colorPopup.open() 248 | } 249 | Timer 250 | { 251 | id: hideSubTimer 252 | interval: 250 253 | function restartTimer() //Restart but re-evaluate the running property then. 254 | { 255 | restart(); 256 | running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; }); 257 | showSubTimer.running = false; 258 | } 259 | onTriggered: colorPopup.close() 260 | } 261 | 262 | Popup 263 | { 264 | id: colorPopup 265 | width: materialColorsList.width + padding * 2 266 | height: materialColorsList.height + padding * 2 267 | x: parent.width 268 | y: { 269 | // If flipped the popup should push up rather than down from the parent 270 | if (brandMaterialBase.isFlipped) { 271 | return -height + UM.Theme.getSize("menu").height + UM.Theme.getSize("default_lining").width 272 | } 273 | return -UM.Theme.getSize("default_lining").width 274 | } 275 | 276 | property int itemHovered: 0 277 | padding: background.border.width 278 | 279 | background: Rectangle 280 | { 281 | color: UM.Theme.getColor("main_background") 282 | border.color: UM.Theme.getColor("lining") 283 | border.width: UM.Theme.getSize("default_lining").width 284 | } 285 | 286 | Column 287 | { 288 | id: materialColorsList 289 | spacing: 0 290 | width: brandColorsRepeater.width 291 | 292 | property var brandColors: model.colors 293 | 294 | Repeater 295 | { 296 | id: brandColorsRepeater 297 | model: parent.brandColors 298 | 299 | property var maxWidth: 0 300 | onItemAdded: updateColorWidth() 301 | onItemRemoved: updateColorWidth() 302 | 303 | function updateColorWidth() 304 | { 305 | var result = 0; 306 | for (var i = 0; i < count; ++i) { 307 | var item = itemAt(i); 308 | if(item != null) 309 | { 310 | result = Math.max(item.contentWidth, result); 311 | } 312 | } 313 | width = result + 2 * UM.Theme.getSize("default_margin").width; 314 | } 315 | 316 | delegate: Rectangle 317 | { 318 | height: UM.Theme.getSize("menu").height 319 | width: parent.width 320 | 321 | color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("background_1") 322 | 323 | property int contentWidth: checkmark.width + swatch.width + colorLabel.width + 2 * UM.Theme.getSize("default_margin").width 324 | 325 | Item 326 | { 327 | opacity: materialBrandMenu.enabled ? 1 : 0.5 328 | anchors.fill: parent 329 | 330 | //Checkmark, if the material is selected. 331 | UM.ColorImage 332 | { 333 | id: checkmark 334 | visible: model.id === materialMenu.activeMaterialId 335 | height: UM.Theme.getSize("default_arrow").height 336 | width: height 337 | anchors.left: parent.left 338 | anchors.leftMargin: UM.Theme.getSize("default_margin").width 339 | anchors.verticalCenter: parent.verticalCenter 340 | source: UM.Theme.getIcon("Check", "low") 341 | color: UM.Theme.getColor("setting_control_text") 342 | } 343 | 344 | Rectangle 345 | { 346 | id: swatch 347 | color: model.color_code 348 | width: UM.Theme.getSize("icon_indicator").width 349 | height: UM.Theme.getSize("icon_indicator").height 350 | radius: width / 2 351 | anchors.left: parent.left 352 | anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("narrow_margin").width + UM.Theme.getSize("default_arrow").height 353 | anchors.verticalCenter: parent.verticalCenter 354 | } 355 | 356 | UM.Label 357 | { 358 | id: colorLabel 359 | text: model.name 360 | anchors.left: swatch.right 361 | anchors.leftMargin: UM.Theme.getSize("narrow_margin").width 362 | anchors.verticalCenter: parent.verticalCenter 363 | 364 | elide: Label.ElideRight 365 | wrapMode: Text.NoWrap 366 | } 367 | } 368 | 369 | MouseArea 370 | { 371 | id: materialColorButton 372 | anchors.fill: parent 373 | 374 | hoverEnabled: true 375 | onClicked: 376 | { 377 | Cura.MachineManager.setMaterial(extruderIndex, model.container_node); 378 | menuPopup.close(); 379 | colorPopup.close(); 380 | materialMenu.close(); 381 | } 382 | onEntered: 383 | { 384 | menuPopup.itemHovered += 1; 385 | colorPopup.itemHovered += 1; 386 | } 387 | onExited: 388 | { 389 | menuPopup.itemHovered -= 1; 390 | colorPopup.itemHovered -= 1; 391 | } 392 | } 393 | } 394 | } 395 | } 396 | } 397 | } 398 | } 399 | } 400 | } 401 | } 402 | --------------------------------------------------------------------------------