Expandible/Collapsible Pane with smooth Animation in QML



  • Hey devs, it is possible to expand/collapse a panel with an smooth animation when it becomes visible.

    Example:

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.3
    import QtQuick.Controls.Material 2.2
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Example")
    
        property var settings1Model: [
            {"settingsName": qsTr("Settings 1.1")},
            {"settingsName": qsTr("Settings 1.2")},
            {"settingsName": qsTr("Settings 1.3")}
        ]
    
        property var settings2Model: [
            {"settingsName": qsTr("Settings 2.1")},
            {"settingsName": qsTr("Settings 2.2")},
            {"settingsName": qsTr("Settings 2.3")},
            {"settingsName": qsTr("Settings 2.4")},
            {"settingsName": qsTr("Settings 2.5")}
        ]
    
        property var settings3Model: [
            {"settingsName": qsTr("Settings 3.1")},
            {"settingsName": qsTr("Settings 3.2")},
            {"settingsName": qsTr("Settings 3.3")}
        ]
    
        Flickable {
            id: flickable
            anchors.fill: parent
            contentHeight: root.implicitHeight
            boundsBehavior: Flickable.OvershootBounds
    
            Pane {
                id: root
                anchors.fill: parent
    
                ColumnLayout {
                    anchors.right: parent.right
                    anchors.left: parent.left
                    anchors.verticalCenter: parent.verticalCenter
    
                    Label {
                        topPadding: 10
                        bottomPadding: 10
                        font.pixelSize: 20
                        Layout.fillWidth: true
                        Layout.preferredWidth: 1
                        horizontalAlignment: Qt.AlignHCenter
                        text: qsTr("Example")
                    }
    
                    Button {
                        id: buttonListSettings1
                        Layout.fillWidth: true
                        text: qsTr("Settings 1")
                        onClicked: {
                            paneSettings1List.visible ? paneSettings1List.visible = false : paneSettings1List.visible = true
                        }
                    }
    
                    Pane {
                        id: paneSettings1List
                        visible: false
                        Material.background: "lightblue"
                        padding: 0
                        anchors.left: parent.left
                        anchors.right: parent.right
                        ColumnLayout {
                            anchors.right: parent.right
                            anchors.left: parent.left
    
                            ListView {
                                id: listSettings1
                                visible: true
                                boundsBehavior: Flickable.OvershootBounds
                                interactive: false
                                focus: true
                                clip: true
                                Layout.fillWidth: true
                                height: contentHeight
    
                                model: settings1Model
    
                                delegate: ItemDelegate {
                                    id: itemDelegateSettings1
                                    text: " "
                                    height: settings1DataColumn.implicitHeight
                                    padding: 0
                                    width: parent.width
                                    onClicked: {
    
                                    }
    
                                    ColumnLayout {
                                        id: settings1DataColumn
                                        parent: itemDelegateSettings1.contentItem
                                        anchors.left: parent.left
                                        anchors.right: parent.right
                                        anchors.leftMargin: 20
                                        anchors.rightMargin: 20
                                        RowLayout {
                                            spacing: 20
                                            Layout.fillWidth: true
                                            Item {
                                                height: 30
                                            }
    
                                            Label {
                                                text: modelData.settingsName
                                                wrapMode: Label.WordWrap
                                            }
                                        }
                                    }
                                }
    
                                ScrollIndicator.vertical: ScrollIndicator {}
                            }
                        }
                    }
    
                    Button {
                        id: buttonListSettings2
                        Layout.fillWidth: true
                        text: qsTr("Settings 2")
                        onClicked: {
                            paneSettings2List.visible ? paneSettings2List.visible = false : paneSettings2List.visible = true
                        }
                    }
    
                    Pane {
                        id: paneSettings2List
                        visible: false
                        Material.background: "lightblue"
                        padding: 0
                        anchors.left: parent.left
                        anchors.right: parent.right
                        ColumnLayout {
                            anchors.right: parent.right
                            anchors.left: parent.left
    
                            ListView {
                                id: listSettings2
                                visible: true
                                boundsBehavior: Flickable.OvershootBounds
                                interactive: false
                                focus: true
                                clip: true
                                Layout.fillWidth: true
                                height: contentHeight
    
                                model: settings2Model
    
                                delegate: ItemDelegate {
                                    id: itemDelegateSettings2
                                    text: " "
                                    height: settings2DataColumn.implicitHeight
                                    padding: 0
                                    width: parent.width
                                    onClicked: {
    
                                    }
    
                                    ColumnLayout {
                                        id: settings2DataColumn
                                        parent: itemDelegateSettings2.contentItem
                                        anchors.left: parent.left
                                        anchors.right: parent.right
                                        anchors.leftMargin: 20
                                        anchors.rightMargin: 20
                                        RowLayout {
                                            spacing: 20
                                            Layout.fillWidth: true
                                            Item {
                                                height: 30
                                            }
                                            Label {
                                                text: modelData.settingsName
                                                wrapMode: Label.WordWrap
                                            }
                                        }
                                    }
                                }
                                ScrollIndicator.vertical: ScrollIndicator {}
                            }
                        }
                    }
    
                    Button {
                        id: buttonListSettings3
                        Layout.fillWidth: true
                        text: qsTr("Settings 3")
                        onClicked: {
                            paneSettings3List.visible ? paneSettings3List.visible = false : paneSettings3List.visible = true
                        }
                    }
    
                    Pane {
                        id: paneSettings3List
                        visible: false
                        Material.background: "lightblue"
                        padding: 0
                        anchors.left: parent.left
                        anchors.right: parent.right
                        ColumnLayout {
                            anchors.right: parent.right
                            anchors.left: parent.left
    
                            ListView {
                                id: listSettings3
                                visible: true
                                boundsBehavior: Flickable.OvershootBounds
                                interactive: false
                                focus: true
                                clip: true
                                Layout.fillWidth: true
                                height: contentHeight
    
                                model: settings3Model
    
                                delegate: ItemDelegate {
                                    id: itemDelegateSettings3
                                    text: " "
                                    height: settings3DataColumn.implicitHeight
                                    padding: 0
                                    width: parent.width
                                    onClicked: {
    
                                    }
    
                                    ColumnLayout {
                                        id: settings3DataColumn
                                        parent: itemDelegateSettings3.contentItem
                                        anchors.left: parent.left
                                        anchors.right: parent.right
                                        anchors.leftMargin: 20
                                        anchors.rightMargin: 20
                                        RowLayout {
                                            spacing: 20
                                            Layout.fillWidth: true
                                            Item {
                                                height: 30
                                            }
                                            Label {
                                                text: modelData.settingsName
                                                wrapMode: Label.WordWrap
                                            }
                                        }
                                    }
                                }
                                ScrollIndicator.vertical: ScrollIndicator {}
                            }
                        }
                    }
                }
            }
    
            ScrollIndicator.vertical: ScrollIndicator { }
    
        }
    }
    

    At the moment, I clicked a Button and the list becomes visible fast and hard. Is there a way to do this a little bit smoother, maybe with a animation?



  • @Manu19 Can use states to handle item properties and perform animations
    states:[
    State{
    name:"hidecontainer"
    PropertyChanges {
    target: rect
    y:-500
    opacity:0
    }
    },
    State{
    name:"showcontainer"
    PropertyChanges {
    target: rect
    y:0
    opacity:1
    }
    }
    ]

        transitions: [
            Transition {
                to:"showcontainer"
                NumberAnimation
                {
                    duration: 500
                    properties:"y,opacity"
                    easing.type: Easing.OutCubic
                }
            }
            ,Transition {
                to:"hidecontainer"
                NumberAnimation
                {
                    duration: 500
                    properties:"y,opacity"
                    easing.type: Easing.InCubic
                }
            }
        ]
    

    In the above code opacity and y has been modified from 0 to 1 and -500 to 0 which makes slide down from top.
    Like wise can handle for any properties of the rectangle(500x500).

    In your use case display the list at y= -list height and then perform animation to y=0 which will make the list visible with a slide down animation.



  • @Arvindhan-Ponnusamy Thank you for your answer. Your example helps a little bit.
    The animation works fine, but the problem is the Column Layout. When I click Settings 2, then the List becomes visible with animation, but the button "Settings 3" change the y-position very hard. It is possible to make this also smooth with animations?



  • @Manu19 Yes this also can be performed with same approach.
    Move settings 3 along with the list using states.



  • I simplified your code a bit to make it more concise ( a single model instead of 3, and Column+ Repeater instead of ColumnLayout + ListView)

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.3
    import QtQuick.Controls.Material 2.2
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("Example")
    
        property var settingsModel: [
            {
                "title": "Settings 1",
                "settings": [
                    qsTr("Settings 1.1"),
                    qsTr("Settings 1.2"),
                    qsTr("Settings 1.3")
                ]
            },
            {
                "title": "Settings 2",
                "settings": [
                    qsTr("Settings 2.1"),
                    qsTr("Settings 2.2"),
                    qsTr("Settings 2.3"),
                    qsTr("Settings 2.4"),
                    qsTr("Settings 2.5")
                ]
            },
            {
                "title": "Settings 3",
                "settings": [
                    qsTr("Settings 3.1"),
                    qsTr("Settings 3.2"),
                    qsTr("Settings 3.3"),
                ]
            }
        ]
    
        Flickable {
            id: flickable
            anchors.fill: parent
            contentHeight: root.implicitHeight
            boundsBehavior: Flickable.OvershootBounds
    
            Pane {
                id: root
                anchors.fill: parent
    
                Column {
                    anchors.right: parent.right
                    anchors.left: parent.left
                    anchors.verticalCenter: parent.verticalCenter
    
                    Label {
                        anchors { left: parent.left; right: parent.right }
                        topPadding: 10
                        bottomPadding: 10
                        font.pixelSize: 20
                        horizontalAlignment: Qt.AlignHCenter
                        text: qsTr("Example")
                    }
    
                    Repeater {
                        model: settingsModel
                        delegate: Column {
                            property bool showList: false
                            anchors.left: parent.left
                            anchors.right: parent.right
                            Button {
                                anchors.left: parent.left
                                anchors.right: parent.right
                                text: modelData.title
                                onClicked: paneSettingsList.shown = !paneSettingsList.shown
                            }
                            Pane {
                                id: paneSettingsList
    
                                // ## relevant part ##
                                property bool shown: false
                                visible: height > 0
                                height: shown ? implicitHeight : 0
                                Behavior on height {
                                    NumberAnimation {
                                        easing.type: Easing.InOutQuad
                                    }
                                }
                                clip: true
                                // ## relevant part ##
    
                                Material.background: "lightblue"
                                padding: 0
                                anchors.left: parent.left
                                anchors.right: parent.right
                                Column {
                                    anchors.right: parent.right
                                    anchors.left: parent.left
    
                                    Repeater {
                                        id: listSettings1
                                        model: modelData.settings
    
                                        delegate: ItemDelegate {
                                            id: itemDelegateSettings1
                                            text: modelData
                                            padding: 0
                                            width: parent.width
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
    
            ScrollIndicator.vertical: ScrollIndicator { }
    
        }
    }
    

    The relevant part is the one with the Behavior, where the height of a Pane is animated.



  • @GrecKo Thank you very much, this is exactly what I want.



  • This post is deleted!

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.