Browse Source

Clean up code

Create separate re-usable component for `MaterialBrandSubMenu`

CURA-9522
c.lamboo 2 years ago
parent
commit
e490250d9d

+ 5 - 1
resources/qml/Cura.qml

@@ -17,7 +17,11 @@ UM.MainWindow
 {
     id: base
 
-    readonly property var mainWindow: base
+    Item
+    {
+        id: mainWindow
+        anchors.fill: parent
+    }
 
     // Cura application window title
     title:

+ 157 - 245
resources/qml/Menus/MaterialBrandMenu.qml

@@ -1,4 +1,4 @@
-// Copyright (c) 2022 Ultimaker B.V.
+// Copyright (c) 2022 UltiMaker
 // Cura is released under the terms of the LGPLv3 or higher.
 
 import QtQuick 2.7
@@ -85,35 +85,15 @@ Cura.MenuItem
         onTriggered: menuPopup.close()
     }
 
-    Popup
+    MaterialBrandSubMenu
     {
         id: menuPopup
-        width: materialTypesList.width + padding * 2
-        height: materialTypesList.height + padding * 2
 
-        property var flipped: false
-
-        onOpened:
-        {
-            var popupHeight = materialTypesModel.material_types.count * UM.Theme.getSize("menu").height
-            var parentGlobalY = parent.mapToItem(null, 0, 0).y
-            var overflowY = (parentGlobalY + popupHeight) - mainWindow.height
-            menuPopup.y = overflowY > 0 ? -overflowY : 0
-
-            var defaultX = parent.width - UM.Theme.getSize("default_lining").width
-            var parentGlobalX = parent.mapToItem(null, 0, 0).x
-            var overflowX = (parentGlobalX + defaultX + menuPopup.width) - mainWindow.width
-            menuPopup.x = overflowX > 0 ? overflowX : defaultX
-
-            scrollViewMaterialType.height = popupHeight > mainWindow.height ? mainWindow.height : popupHeight
-            menuPopup.height = popupHeight > mainWindow.height ? mainWindow.heigh : popupHeight 
-        }
-
-        padding: background.border.width
         // Nasty hack to ensure that we can keep track if the popup contains the mouse.
         // Since we also want a hover for the sub items (and these events are sent async)
         // We have to keep a count of itemHovered (instead of just a bool)
         property int itemHovered: 0
+
         MouseArea
         {
             id: submenuArea
@@ -123,263 +103,195 @@ Cura.MenuItem
             onEntered: hideTimer.restartTimer()
         }
 
-        background: Rectangle
+        Column
         {
-            color: UM.Theme.getColor("main_background")
-            border.color: UM.Theme.getColor("lining")
-            border.width: UM.Theme.getSize("default_lining").width
-        }
+            id: materialTypesList
+            width: UM.Theme.getSize("menu").width
+            height: childrenRect.height
+            spacing: 0
 
-        ScrollView
-        {
-            id: scrollViewMaterialType
-            width: UM.Theme.getSize("menu").width + scrollbar.width
-            height: parent.height
-            clip: true
+            property var brandMaterials: materialTypesModel.material_types
 
-            ScrollBar.vertical: UM.ScrollBar
+            Repeater
             {
-                id: scrollbar
-                anchors.right: parent.right
-                anchors.top: parent.top
-                anchors.bottom: parent.bottom
-            }
+                model: parent.brandMaterials
 
-            Column
-            {
-                id: materialTypesList
-                width: UM.Theme.getSize("menu").width
-                height: parent.height
-                spacing: 0
-
-                property var brandMaterials: materialTypesModel.material_types
-
-                Repeater
+                //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.
+                //With a custom MouseArea, we can prevent the events from being accepted.
+                delegate: Rectangle
                 {
-                    model: parent.brandMaterials
+                    id: brandMaterialBase
+                    height: UM.Theme.getSize("menu").height
+                    width: UM.Theme.getSize("menu").width
+
+                    color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : "transparent"
 
-                    //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.
-                    //With a custom MouseArea, we can prevent the events from being accepted.
-                    delegate: Rectangle
+                    RowLayout
                     {
-                        id: brandMaterialBase
-                        height: UM.Theme.getSize("menu").height
-                        width: UM.Theme.getSize("menu").width
+                        spacing: 0
+                        opacity: materialBrandMenu.enabled ? 1 : 0.5
+                        height: parent.height
+                        width: parent.width
 
-                        color: materialTypeButton.containsMouse ? UM.Theme.getColor("background_2") : "transparent"
+                        Item
+                        {
+                            // Spacer
+                            width: UM.Theme.getSize("default_margin").width
+                        }
 
-                        property bool isFlipped:  menuPopup.flipped
+                        UM.Label
+                        {
+                            text: model.name
+                            Layout.fillWidth: true
+                            Layout.fillHeight: true
+                            elide: Label.ElideRight
+                            wrapMode: Text.NoWrap
+                        }
 
-                        RowLayout
+                        Item
                         {
-                            spacing: 0
-                            opacity: materialBrandMenu.enabled ? 1 : 0.5
-                            height: parent.height
-                            width: parent.width
+                            Layout.fillWidth: true
+                        }
 
-                            Item
-                            {
-                                // Spacer
-                                width: UM.Theme.getSize("default_margin").width
-                            }
+                        UM.ColorImage
+                        {
+                            height: UM.Theme.getSize("default_arrow").height
+                            width: UM.Theme.getSize("default_arrow").width
+                            color: UM.Theme.getColor("setting_control_text")
+                            source: UM.Theme.getIcon("ChevronSingleRight")
+                        }
 
-                            UM.Label
-                            {
-                                text: model.name
-                                Layout.fillWidth: true
-                                Layout.fillHeight: true
-                                elide: Label.ElideRight
-                                wrapMode: Text.NoWrap
-                            }
+                        Item
+                        {
+                            // Right side margin
+                            width: UM.Theme.getSize("default_margin").width
+                        }
+                    }
 
-                            Item
-                            {
-                                Layout.fillWidth: true
-                            }
+                    MouseArea
+                    {
+                        id: materialTypeButton
+                        anchors.fill: parent
 
-                            UM.ColorImage
-                            {
-                                height: UM.Theme.getSize("default_arrow").height
-                                width: UM.Theme.getSize("default_arrow").width
-                                color: UM.Theme.getColor("setting_control_text")
-                                source: UM.Theme.getIcon("ChevronSingleRight")
-                            }
+                        hoverEnabled: true
+                        acceptedButtons: Qt.NoButton
 
-                            Item
-                            {
-                                // Right side margin
-                                width: UM.Theme.getSize("default_margin").width
-                            }
+                        onEntered:
+                        {
+                            menuPopup.itemHovered += 1;
+                            showSubTimer.restartTimer();
                         }
-
-                        MouseArea
+                        onExited:
                         {
-                            id: materialTypeButton
-                            anchors.fill: parent
-
-                            hoverEnabled: true
-                            acceptedButtons: Qt.NoButton
-
-                            onEntered:
-                            {
-                                menuPopup.itemHovered += 1;
-                                showSubTimer.restartTimer();
-                            }
-                            onExited:
-                            {
-                                menuPopup.itemHovered -= 1;
-                                hideSubTimer.restartTimer();
-                            }
+                            menuPopup.itemHovered -= 1;
+                            hideSubTimer.restartTimer();
                         }
-                        Timer
+                    }
+                    Timer
+                    {
+                        id: showSubTimer
+                        interval: 250
+                        function restartTimer()
                         {
-                            id: showSubTimer
-                            interval: 250
-                            function restartTimer()
-                            {
-                                restart();
-                                running = Qt.binding(function() { return materialTypeButton.containsMouse; });
-                                hideSubTimer.running = false;
-                            }
-                            onTriggered: colorPopup.open()
+                            restart();
+                            running = Qt.binding(function() { return materialTypeButton.containsMouse; });
+                            hideSubTimer.running = false;
                         }
-                        Timer
+                        onTriggered: colorPopup.open()
+                    }
+                    Timer
+                    {
+                        id: hideSubTimer
+                        interval: 250
+                        function restartTimer() //Restart but re-evaluate the running property then.
                         {
-                            id: hideSubTimer
-                            interval: 250
-                            function restartTimer() //Restart but re-evaluate the running property then.
-                            {
-                                restart();
-                                running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; });
-                                showSubTimer.running = false;
-                            }
-                            onTriggered: colorPopup.close()
+                            restart();
+                            running = Qt.binding(function() { return !materialTypeButton.containsMouse && !colorPopup.itemHovered > 0; });
+                            showSubTimer.running = false;
                         }
+                        onTriggered: colorPopup.close()
+                    }
 
-                        Popup
-                        {
-                            id: colorPopup
-                            width: materialColorsList.width + padding * 2
-                            height: materialColorsList.height + padding * 2
-                            onOpened:
-                            {
-                                // This will be resolved before opening the popup if directly assigned to the properties
-                                // This forces these values to update whenever a popup is opened
-                                var popupHeight = model.colors.count * UM.Theme.getSize("menu").height
-                                var parentGlobalY = parent.mapToItem(null, 0, 0).y
-                                var overflowY = (parentGlobalY + popupHeight) - mainWindow.height
-                                colorPopup.y = overflowY > 0 ? - overflowY - UM.Theme.getSize("default_lining").height : -UM.Theme.getSize("default_lining").height
-
-                                var parentGlobalX = materialTypesList.mapToItem(null, 0, 0).x
-                                var overflowX = (parentGlobalX + parent.width + colorPopup.width) - mainWindow.width
-                                colorPopup.x = overflowX > 0 ? parent.width - overflowX : parent.width
-
-                                scrollView.height =  popupHeight > mainWindow.height ? mainWindow.height : popupHeight
-                                colorPopup.height = popupHeight > mainWindow.height ? mainWindow.height : popupHeight
-                            }
+                    MaterialBrandSubMenu
+                    {
+                        id: colorPopup
+                        property int itemHovered: 0
 
-                            property int itemHovered: 0
-                            padding: background.border.width
+                        Column
+                        {
+                            id: materialColorsList
+                            property var brandColors: model.colors
+                            width: UM.Theme.getSize("menu").width
+                            height: childrenRect.height
+                            spacing: 0
 
-                            background: Rectangle
+                            Repeater
                             {
-                                color: UM.Theme.getColor("main_background")
-                                border.color: UM.Theme.getColor("lining")
-                                border.width: UM.Theme.getSize("default_lining").width
-                            }
-                            ScrollView
-                            {
-                                id: scrollView
-                                width: UM.Theme.getSize("menu").width + scrollbar.width
-                                height: parent.height
-                                clip: true
+                                model: parent.brandColors
 
-                                ScrollBar.vertical: UM.ScrollBar
+                                delegate: Rectangle
                                 {
-                                    id: scrollbar
-                                    anchors.right: parent.right
-                                    anchors.top: parent.top
-                                    anchors.bottom: parent.bottom
-                                }
+                                    height: UM.Theme.getSize("menu").height
+                                    width: parent.width
 
-                                Column
-                                {
-                                    id: materialColorsList
-                                    property var brandColors: model.colors
-                                    width: UM.Theme.getSize("menu").width
-                                    height: parent.height
-                                    spacing: 0
+                                    color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : UM.Theme.getColor("main_background")
+
+                                    MouseArea
+                                    {
+                                        id: materialColorButton
+                                        anchors.fill: parent
+                                        hoverEnabled: true
+                                        onClicked:
+                                        {
+                                            Cura.MachineManager.setMaterial(extruderIndex, model.container_node);
+                                            menuPopup.close();
+                                            colorPopup.close();
+                                            materialMenu.close();
+                                        }
+                                        onEntered:
+                                        {
+                                            menuPopup.itemHovered += 1;
+                                            colorPopup.itemHovered += 1;
+                                        }
+                                        onExited:
+                                        {
+                                            menuPopup.itemHovered -= 1;
+                                            colorPopup.itemHovered -= 1;
+                                        }
+                                    }
 
-                                    Repeater
+                                    Item
                                     {
-                                        model: parent.brandColors
+                                        height: parent.height
+                                        width: parent.width
+                                        opacity: materialBrandMenu.enabled ? 1 : 0.5
+                                        anchors.fill: parent
+
+                                        //Checkmark, if the material is selected.
+                                        UM.ColorImage
+                                        {
+                                            id: checkmark
+                                            visible: model.id === materialMenu.activeMaterialId
+                                            height: UM.Theme.getSize("default_arrow").height
+                                            width: height
+                                            anchors.left: parent.left
+                                            anchors.leftMargin: UM.Theme.getSize("default_margin").width
+                                            anchors.verticalCenter: parent.verticalCenter
+                                            source: UM.Theme.getIcon("Check", "low")
+                                            color: UM.Theme.getColor("setting_control_text")
+                                        }
 
-                                        delegate: Rectangle
+                                        UM.Label
                                         {
-                                            height: UM.Theme.getSize("menu").height
-                                            width: parent.width
-
-                                            color: materialColorButton.containsMouse ? UM.Theme.getColor("background_2") : "transparent"
-
-                                            Item
-                                            {
-                                                height: parent.height
-                                                width: parent.width
-                                                opacity: materialBrandMenu.enabled ? 1 : 0.5
-                                                anchors.fill: parent
-
-                                                //Checkmark, if the material is selected.
-                                                UM.ColorImage
-                                                {
-                                                    id: checkmark
-                                                    visible: model.id === materialMenu.activeMaterialId
-                                                    height: UM.Theme.getSize("default_arrow").height
-                                                    width: height
-                                                    anchors.left: parent.left
-                                                    anchors.leftMargin: UM.Theme.getSize("default_margin").width
-                                                    anchors.verticalCenter: parent.verticalCenter
-                                                    source: UM.Theme.getIcon("Check", "low")
-                                                    color: UM.Theme.getColor("setting_control_text")
-                                                }
-
-                                                UM.Label
-                                                {
-                                                    text: model.name
-                                                    anchors.left: parent.left
-                                                    anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_arrow").height
-                                                    anchors.verticalCenter: parent.verticalCenter
-                                                    anchors.right: parent.right
-                                                    anchors.rightMargin: UM.Theme.getSize("default_margin").width
-
-                                                    elide: Label.ElideRight
-                                                    wrapMode: Text.NoWrap
-                                                }
-                                            }
-
-                                            MouseArea
-                                            {
-                                                id: materialColorButton
-                                                anchors.fill: parent
-
-                                                hoverEnabled: true
-                                                onClicked:
-                                                {
-                                                    Cura.MachineManager.setMaterial(extruderIndex, model.container_node);
-                                                    menuPopup.close();
-                                                    colorPopup.close();
-                                                    materialMenu.close();
-                                                }
-                                                onEntered:
-                                                {
-                                                    menuPopup.itemHovered += 1;
-                                                    colorPopup.itemHovered += 1;
-                                                }
-                                                onExited:
-                                                {
-                                                    menuPopup.itemHovered -= 1;
-                                                    colorPopup.itemHovered -= 1;
-                                                }
-                                            }
+                                            text: model.name
+                                            anchors.left: parent.left
+                                            anchors.leftMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("default_arrow").height
+                                            anchors.verticalCenter: parent.verticalCenter
+                                            anchors.right: parent.right
+                                            anchors.rightMargin: UM.Theme.getSize("default_margin").width
+
+                                            elide: Label.ElideRight
+                                            wrapMode: Text.NoWrap
                                         }
                                     }
                                 }

+ 117 - 0
resources/qml/Menus/MaterialBrandSubMenu.qml

@@ -0,0 +1,117 @@
+// Copyright (c) 2022 UltiMaker
+// Cura is released under the terms of the LGPLv3 or higher.
+
+import QtQuick 2.7
+import QtQuick.Controls 2.4
+import QtQuick.Layouts 2.7
+
+import UM 1.5 as UM
+import Cura 1.7 as Cura
+
+Popup
+{
+    id: materialBrandSubMenu
+
+    bottomPadding: UM.Theme.getSize("thin_margin").height
+    topPadding: UM.Theme.getSize("thin_margin").height
+
+    implicitWidth: scrollViewContent.width + scrollbar.width + leftPadding + rightPadding
+    implicitHeight: scrollViewContent.height + bottomPadding + topPadding
+
+    // offset position relative to the parent
+    property int implicitX: parent.width
+    property int implicitY: -UM.Theme.getSize("thin_margin").height
+
+    default property alias contents: scrollViewContent.children
+
+    x: implicitX
+    y: implicitY
+
+    // needed for the `mapToItem` function to work; apparently a Popup is not an Item
+    Item
+    {
+        id: materialBrandSubMenu
+        anchors.fill: parent
+    }
+
+    onOpened:
+    {
+        // we want to make sure here that the popup never goes out side the window so we adjust the x and y position
+        // based on the width/height of the mainWindow/popup. QML is a bit weird here though, as the globalPosition
+        // is in absolute coordinates relative to the origin of the mainWindow while setting the x and y coordinates
+        // of the popup only changes the position relative to the parent.
+
+        // reset position, the remainder of the function asumes this position and size
+        materialBrandSubMenu.x = implicitX;
+        materialBrandSubMenu.y = implicitY;
+        materialBrandSubMenu.width = implicitWidth;
+        materialBrandSubMenu.height = implicitHeight;
+
+        const globalPosition = materialBrandSubMenu.mapToItem(null, 0, 0);
+
+        if (globalPosition.y > mainWindow.height - materialBrandSubMenu.height)
+        {
+            if (mainWindow.height > materialBrandSubMenu.height)
+            {
+                const targetY = mainWindow.height - materialBrandSubMenu.height;
+                const deltaY = globalPosition.y - targetY;
+                materialBrandSubMenu.y = implicitY - deltaY;
+            }
+            else
+            {
+                // if popup is taller then the the component, limit
+                // the components height and set the position to
+                // y = 0 (in absolute coordinates)
+                materialBrandSubMenu.y = implicitY - globalPosition.y;
+                materialBrandSubMenu.height = mainWindow.height;
+            }
+        }
+
+        if (globalPosition.x > mainWindow.width - materialBrandSubMenu.width)
+        {
+            if (mainWindow.width > materialBrandSubMenu.width)
+            {
+                const targetY = mainWindow.width - materialBrandSubMenu.width;
+                const deltaX = globalPosition.x - targetY;
+                materialBrandSubMenu.x = implicitX - deltaX;
+            }
+            else
+            {
+                materialBrandSubMenu.x = implicitX - globalPosition.x;
+                materialBrandSubMenu.width = mainWindow.width;
+            }
+        }
+    }
+
+    padding: background.border.width
+
+    background: Rectangle
+    {
+        color: UM.Theme.getColor("main_background")
+        border.color: UM.Theme.getColor("lining")
+        border.width: UM.Theme.getSize("default_lining").width
+    }
+
+    ScrollView
+    {
+        id: scrollView
+        anchors.fill: parent
+        contentHeight: scrollViewContent.height
+        clip: true
+
+        ScrollBar.vertical: UM.ScrollBar
+        {
+            id: scrollbar
+            anchors.right: parent.right
+            anchors.top: parent.top
+            anchors.bottom: parent.bottom
+        }
+
+        Rectangle
+        {
+            id: scrollViewContent
+            width: childrenRect.width
+            height: childrenRect.height
+        }
+    }
+}