Search code examples
qtqmlgradientqt6

QML gradient border color issue when trying to center the popup


I'm trying to create a gradient color border for a button on a Popup. It seems good if I don't try to centering the popup. I have no word to call the name or describe the issue. Please take a look at the border of the Cancel button in the attached image and the code. (I'm using Qt 6.3)

enter image description here

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
//import QtQuick3D
import Qt5Compat.GraphicalEffects

ApplicationWindow {
    id: root
    width: 666
    height: 777
    visible: true
    color: "grey"
    Button
    {
        width: 111
        height: 44
        text: "Show"
        onClicked: menu.open()
    }
    Popup {
        id: menu
        width: (559)
        height: (643)

        modal: true
        dim: false
        focus: true
        closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent

        background: Rectangle {
            //        color: "transparent"
            border.color: "transparent"
            LinearGradient {
                anchors.fill: parent
                start: Qt.point(0, 0)
                end: Qt.point(0, parent.height)
                gradient: Gradient {
                    GradientStop { position: 0.0; color: "#29395D" }
                    GradientStop { position: 1.0; color: "#1C2341" }
                }
            }
        }

    //    Component.onCompleted: {
    //    x: (parent.width - width) / 2
    //    y: (parent.height - height) / 2
    ////            anchors.centerIn = parent
    ////                    anchors.centerIn = Overlay.overlay
    //    }
//        onAboutToShow: anchors.centerIn = Overlay.overlay
        anchors.centerIn : Overlay.overlay

        Button {
            id: okBt
            anchors.top: parent.top
            anchors.topMargin: (410)
            anchors.horizontalCenter: parent.horizontalCenter
            width: (276)
            height: (41)
            text: "OK"
//            font.family: FontModel.family
            font.pixelSize: (16)
            font.weight: Font.Normal    //400
            contentItem: Text {
                text: parent.text
                font: parent.font
                color: "#FFFFFF"
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
                elide: Text.ElideRight
            }
            flat: true
            background: Rectangle {
                implicitWidth: (276)
                implicitHeight: (41)
                radius: (6)
                border.width: 0
                gradient: Gradient {
                    orientation: Gradient.Horizontal
                    GradientStop { position: 1.0; color: "#2F3D6D" }
                    GradientStop { position: 0.0; color: "#3C5199" }
                }
            }
        }

        Button {
            id: cancelBt
    //        visible: false
            anchors.top: parent.top
            anchors.topMargin: (471)
            anchors.horizontalCenter: parent.horizontalCenter
            width: (276)
            height: (41)
            text: "Cancel"
//            font.family: FontModel.family
            font.pixelSize: (16)
            font.weight: Font.Normal    //400
            contentItem: Text {
                text: parent.text
                font: parent.font
                color: "#FFFFFF"
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
                elide: Text.ElideRight
            }
            flat: true
        }

        Rectangle {
            id: gradientBorder
            anchors.top: parent.top
            anchors.topMargin: (471)
            anchors.horizontalCenter: parent.horizontalCenter
            width: (276)
            height: (41)
            radius: (6)
            z: cancelBt.z + 11
            color: "transparent"
            Rectangle {
                id: bd
                anchors.fill: parent
                radius: parent.radius
                gradient: Gradient {
                    orientation: Gradient.Horizontal
                    GradientStop { position: 1.0; color: "#2F3D6D" }
                    GradientStop { position: 0.0; color: "#3C5199" }
                }
                layer.enabled: true
                layer.effect: OpacityMask {
                    invert: true
                    maskSource: Item {
                        width: bd.width
                        height: bd.height
                        Rectangle {
                            anchors.centerIn: parent
                            width: parent.width -2
                            height: parent.height -2
                            radius: gradientBorder.radius
                        }
                    }
                }
            }
        }
    }
}

Solution

  • The issue you encountering has definitely something to do with half pixel rounding. This is especially an issue when using texture/layers, because internally the textures size (sourceSize) gets rounded when creating textures from floating point positioned/sized items. I didn't had a proper look at your example when I've created my comment.

    In order to work around the issue you should tweak your sizes in order for the items to be pixel aligned. In my example below I've made all your odd sizes even so the anchoring logic doesn't place items on half pixels.

    Also instead of using OpacityMask to create a cutout, I've used a LinearGradient from the Qt5Compat.GraphicalEffects and an invisible Rectangle that gets assigned as the source.

    screenshot

    import QtQuick
    import QtQuick.Controls
    import QtQuick.Layouts
    import Qt5Compat.GraphicalEffects
    
    ApplicationWindow {
        id: root
        width: 666
        height: 777
        visible: true
        color: "grey"
    
        Button {
            width: 111
            height: 44
            text: qsTr("Show")
            onClicked: popup.open()
            anchors.centerIn: parent
        }
    
        Popup {
            id: popup
            width: 560
            height: 644
            modal: true
            dim: false
            focus: true
            closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
            anchors.centerIn : Overlay.overlay
    
            background: Rectangle {
                border.color: "transparent"
    
                LinearGradient {
                    anchors.fill: parent
                    start: Qt.point(0, 0)
                    end: Qt.point(0, parent.height)
                    gradient: Gradient {
                        GradientStop { position: 0.0; color: "#29395D" }
                        GradientStop { position: 1.0; color: "#1C2341" }
                    }
                }
            }
    
            Button {
                id: okBt
                anchors.top: parent.top
                anchors.topMargin: 410
                anchors.horizontalCenter: parent.horizontalCenter
                width: 276
                height: 42
                text: qsTr("OK")
                font.pixelSize: 16
                font.weight: Font.Normal
                flat: true
    
                contentItem: Text {
                    text: parent.text
                    font: parent.font
                    color: "#FFFFFF"
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    elide: Text.ElideRight
                }
    
                background: Rectangle {
                    implicitWidth: okBt.width
                    implicitHeight: okBt.height
                    radius: 6
    
                    gradient: Gradient {
                        orientation: Gradient.Horizontal
                        GradientStop { position: 1.0; color: "#2F3D6D" }
                        GradientStop { position: 0.0; color: "#3C5199" }
                    }
                }
            }
    
            Button {
                id: cancelBt
                anchors.top: parent.top
                anchors.topMargin: 471
                anchors.horizontalCenter: parent.horizontalCenter
                width: 276
                height: 42
                text: qsTr("Cancel")
                font.pixelSize: 16
                font.weight: Font.Normal
                flat: true
    
                contentItem: Text {
                    text: parent.text
                    font: parent.font
                    color: "#FFFFFF"
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    elide: Text.ElideRight
                }
    
                Rectangle {
                    id: dummy
                    width: cancelBt.width
                    height: cancelBt.height
                    color: "transparent"
                    border.width: 2
                    radius: 6
                    visible: false
                }
    
                background: LinearGradient {
                    anchors.centerIn: parent
                    //anchors.alignWhenCentered: false
                    width: dummy.width
                    height: dummy.height
                    start: Qt.point(0, 0)
                    end: Qt.point(dummy.width, 0)
                    source: dummy
                    gradient: Gradient {
                        GradientStop { position: 1.0; color: "#2F3D6D" }
                        GradientStop { position: 0.0; color: "#3C5199" }
                    }
                }
            }
        }
    }