Set correct size for custom QML slider

Viewed 124

I'm trying to create a reusable slider. I'm having trouble to set implicit sizes correctly so that the CustomSlider includes the Slider and the Label. I would like to have a implicit size specified, but let the user set a width for the slider itself.

I tried using childrenRect but that gives me a binding loop error. How can I have the yellow background span across all the components: the slider and the green label?

Currently: enter image description here

Would like: enter image description here

CustomSlider.qml

Item {
    id: root
    property int startval: 0
    property int endval: 20
    property int sliderWidth: 200
   // This results in binding loop
   //implicitHeight: childrenRect.height

   implicitHeight: control.height + label.height
   implicitWidth: sliderWidth

   Rectangle {
       color: "yellow"
       width: root.width
       height: root.height
   }
   Slider {
       id: control
       stepSize: 1
       anchors.centerIn: parent
       snapMode: Slider.SnapOnRelease
       width: root.sliderWidth
       from: root.startval
       to: root.endval
       handle: Rectangle {
           id: handleId
           x: control.visualPosition * (control.width - width)
           y: (control.height - height) / 2
           width: 20
           height: 20
           radius: 20
           color: "gray"
       }

       background: Rectangle {
           y: (control.height - height) / 2
           height: 4
           radius: 2
           color: "green"

           Rectangle {
               width: control.visualPosition * parent.width
               height: parent.height
               color: "red"
               radius: 2
           }
       }
   }

   Label {
       id: label
        width: 20
        height: 20
       text: control.value
       font.pixelSize: 15
       color: "black"
       x: handleId.x + control.x
       y: handleId.y - 20
       Rectangle {
           color: "green"
           anchors.fill: parent
           opacity: 0.3
       }
   }
}

Main.qml

CustomSlider {
     anchors.centerIn: parent
     startval: 0
     endval: 10
     //sliderWidth: 100
}
1 Answers

I usually set the Width and height based on the parent.

I set sliderWidth: parent.width/2.0 and for your green label I add TextMetrics and calculate its width based on the text that it wants to show.

import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
    id: root
    property int startval: 0
    property int endval: 0
    property int sliderWidth: parent.width/2.0
    // This results in binding loop
    //implicitHeight: childrenRect.height

    implicitHeight: control.height + label.height
    implicitWidth: sliderWidth

    Rectangle {
        color: "yellow"
        width: root.width
        height: root.height
    }
    Slider {
        id: control
        stepSize: 1
        anchors.centerIn: parent
        snapMode: Slider.SnapOnRelease
        width: root.sliderWidth
        from: root.startval
        to: root.endval
        handle: Rectangle {
            id: handleId
            x: control.visualPosition * (control.width - width)
            y: (control.height - height) / 2
            width: 20
            height: 20
            radius: 20
            color: "gray"
        }

        background: Rectangle {
            y: (control.height - height) / 2
            height: 4
            radius: 2
            color: "green"

            Rectangle {
                width: control.visualPosition * parent.width
                height: parent.height
                color: "red"
                radius: 2
            }
        }
    }

    Label {
        id: label
        width: t_metrics.tightBoundingRect.width +4
        height: t_metrics.tightBoundingRect.height +7
        text: control.value
        font.pixelSize: 15
        color: "black"
        x: handleId.x + control.x
        y: handleId.y - 20
        Rectangle {
            color: "green"
            anchors.fill: parent
            opacity: 0.3
        }
    }

    TextMetrics {
            id:     t_metrics
            text:   control.value.toString()
        }
}


enter image description here

Updated:

in CustomSlider.qml , changed it to this code:

import QtQuick 2.15
import QtQuick.Controls 2.15
Item {
    id: root
    property int startval: 0
    property int endval: 0
    property int sliderWidth: parent.width/2.0

    implicitHeight: control.height + label.height
    implicitWidth: sliderWidth

    Rectangle {
        color: "yellow"
        width: root.width +10
        height: root.height +control.height + label.height + t_metrics.tightBoundingRect.height +7

        Slider {
            id: control
            stepSize: 1
            anchors.centerIn: parent
            snapMode: Slider.SnapOnRelease
            width: root.sliderWidth
            from: root.startval
            to: root.endval
            handle: Rectangle {
                id: handleId
                x: control.visualPosition * (control.width - width)
                y: (control.height - height) / 2
                width: 20
                height: 20
                radius: 20
                color: "gray"
            }

            background: Rectangle {
                y: (control.height - height) / 2
                height: 4
                radius: 2
                color: "green"

                Rectangle {
                    width: control.visualPosition * parent.width
                    height: parent.height
                    color: "red"
                    radius: 2
                }
            }
        }

        Label {
            id: label
            width: t_metrics.tightBoundingRect.width +4
            height: t_metrics.tightBoundingRect.height +7
            text: control.value
            font.pixelSize: 15
            color: "black"
            x: handleId.x + control.x
            y: handleId.y /2 + 4
            Rectangle {
                color: "green"
                anchors.fill: parent
                opacity: 0.3
            }
        }

        TextMetrics {
            id:     t_metrics
            text:   control.value.toString()
        }

    }

}


This makes your text label shows inside the yellow rectangle and in main.qml I add one Row with labels and spinboxes for the test. If you want that user to set sliderWidth or startval and endval and then show him Slider you need to create that object dynamically. and if you want to be displayed in the Column and don't overlap you need ColumnLayout.

If you want to scroll them then you need to use ScrollView.

import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

Window {
    width: 660
    height: 480
    visible: true
    title: qsTr("Hello World")



   ScrollView {
        width: parent.width
        height : parent.height
        contentWidth: slidersColumn.width
        contentHeight: slidersColumn.height
        clip : true

        ColumnLayout {
            id: slidersColumn
            anchors.fill: parent
            spacing:50

        }
    }

    function addSlider(sliderWidth,startval,endval) {

        var obj = Qt.createComponent("CustomSlider.qml");
        var slider = obj.createObject(slidersColumn);
        slider.startval=startval;
        slider.endval=endval;
        slider.sliderWidth=sliderWidth;


    }

    Row
    {
        x: 0
        y: 5
        width: parent.width
        height: 30

        Label {
            id: lbl_slider_width
            text: qsTr(" Slider width ")
        }
        SpinBox {
            id: spinBox_slider_width
            editable: true
            from:0
            to:parent.width
        }

        Label {
            id: lbl_startval
            text: qsTr(" Startval ")
        }

        SpinBox {
            id: spinBox_startval
            editable: true
            from:0
            to:parent.width

        }

        Label {
            id: lbl_endval
            text: qsTr(" Endval ")
        }

        SpinBox {
            id: spinBox_endval
            editable: true
            from:0
            to:parent.width

        }

        Button {
            id: button

            text: qsTr(" Create Slider")

            onClicked:
            {
                addSlider(spinBox_slider_width.value,spinBox_startval.value,spinBox_endval.value)

            }
        }
    }


}

The result is:

enter image description here

Related