Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Button with gradient, custom radius and double borders



  • Hello everyone!

    I am quite new to QtQuick and Qml, but doing fine so far with the documentation.

    However, I am facing some problems with recreating a button and I like to ask for your help.

    What I am trying to do is create this in Qml (w/o using any images):

    spec.png

    It is a button that has:

    • a linear gradient as background
    • two borders
      • each has a width of 1px
      • the colors have an alpha, so the background will effect the actual visible color of each pixel
      • the outer border is above the general background of the application
      • the inner border is above the gradient
      • the colors of the inner and outer border of the left side is interchanged compared to the bottom and right border (i.e. color of inner border left = color of outer border bottom, etc.)
    • on the bottom left and bottom right (but not on the top!) there is a 3px radius

    In CSS (the reference implementation is a browser single-page application) it is done with this code:

    border-bottom-left-radius: 3px;
    border-bottom-right-radius: 3px;
    box-shadow: rgba(152, 182, 219, 0.15) 0px 0px inset, rgba(2, 2, 4, 0.4) -1px 0px inset, rgba(2, 2, 4, 0.4) 0px -1px inset, rgba(152, 182, 219, 0.15) 1px 0px inset;
    background: linear-gradient(rgb(47, 72, 99) 30%, rgb(36, 51, 71) 100%) padding-box;
    border-bottom: 1px solid rgba(152, 182, 219, 0.15);
    border-left: 1px solid rgba(2, 2, 4, 0.4);
    border-right: 1px solid rgba(152, 182, 219, 0.15)
    

    I am having trouble recreating this in Qml. As the radius property of the rectangle can only be set equal for all corners, I followed the idea mentioned in https://stackoverflow.com/a/39971599 . The result without using borders looks like this:

    no-borders.png

    And this is the code I use for it:

    Button {
        id: headerConfigButton
        // ...
    
        background: Rectangle {
            color: "transparent"
    
            Rectangle {
                height: headerConfigButton.height - headerConfigButtonLowerBackground.radius
                width: headerConfigButton.width
            }
    
            Rectangle {
                id: headerConfigButtonLowerBackground
                radius: 3
                height: headerConfigButton.height
                width: headerConfigButton.width
                color: "#242e3a"
            }
    
            LinearGradient {
                source: parent
                anchors.fill: parent
                start: Qt.point(0, 0)
                end: Qt.point(0, parent.height)
                gradient: Gradient {
                    GradientStop {
                        position: 0.0
                        color: "#344a62"
                    }
                    GradientStop {
                        position: 1.0
                        color: "#293749"
                    }
                }
            }
        }
    }
    

    Applying the borders was a little clumsy - I made a rectangle ("1px line") for each side and each border. Without the radius it looks like as in this picture:

    no-radius.png

    Please note that the alpha values of the borders are not respected in this imgage; I found it easier to see what happens without using them.

    The code for this:

    Button {
        id: headerConfigButton
        // ...
    
        background: Rectangle {
            color: "transparent"
    
            Rectangle {  // left outer border
                width: 1
                height: headerConfigButton.height
                color: "#181e26"
            }
    
            Rectangle {  // left inner border
                x: 1
                width: 1
                height: headerConfigButton.height - 1
                color: "#4d637d"
            }
    
            Rectangle {  // bottom outer border
                x: 1
                y: headerConfigButton.height - 1
                width: headerConfigButton.width - 2
                height: 1
                color: "#344151"
            }
    
            Rectangle {  // bottom inner border
                x: 2
                y: headerConfigButton.height - 2
                width: headerConfigButton.width - 4
                height: 1
                color: "#181e26"
            }
    
            Rectangle {  // right inner border
                x: headerConfigButton.width - 2
                width: 1
                height: headerConfigButton.height - 1
                color: "#181e26"
            }
    
            Rectangle {  // right outer border
                x: headerConfigButton.width - 1
                width: 1
                height: headerConfigButton.height
                color: "#344151"
            }
    
            Rectangle {  // gradient fill
                id: headerConfigButtonGradientFill
                x: 2
                width: headerConfigButton.width - 4
                height: headerConfigButton.height - 2
    
                LinearGradient {
                    source: parent
                    anchors.fill: parent
                    start: Qt.point(0, 0)
                    end: Qt.point(0, parent.height)
                    gradient: Gradient {
                        GradientStop {
                            position: 0.0
                            color: "#344a62"
                        }
                        GradientStop {
                            position: 1.0
                            color: "#293749"
                        }
                    }
                }
            }
        }
    }
    

    But now I struggle to create the round corners in conjunction with the borders. One issue is that the color of the outer and inner borders are not the same on each side. In conjunction with the opacity of the border colors, I have no idea how to get the colors in the corners right. Additionally, the gradient needs to be cropped as well.

    The colors of the gradient and borders shall be themeable later on. Thus, using a background image or recreating the corners pixel by pixel with fixed colors taken from the spec is not an option.

    Has anyone got an idea how to solve this? Maybe I am completely off and there is a much better and easier way to do this?

    Thanks in advance!


Log in to reply