├── .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 |
11 |
--------------------------------------------------------------------------------
/resources/icons/settings_undock.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/resources/icons/view_orthographic.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
--------------------------------------------------------------------------------