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

Transition in child component binded to parent's animated property



  • Hello,

    I have a problem in applying some animated state change for both parent and child elements.

    Basically, the idea is to have some content that changes it's layout based on whether it is in one of the two states - "expanded" and "" (not expanded, default one).

    The idea is to have both those states defined in parent and child elements with some custom (and different) transitions. Those transitions are used to rearrange the contents of the child element based on the state of the parent.

    The problem is that a transition in the child element is not aware of the parent's transition, and when state change is triggered, it uses the parent's properties that was in an old state, thus causing wrong and abrupt animations in child element.

    Consider following example. The main.qml file instantiates some big black rectangle that will serve in this example as the parent with two states ("rectParent")

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.2
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
        Button{
            anchors.bottom: parent.bottom
            anchors.right: parent.right
            text: qsTr("Animation")
            font.family: "Ubuntu"
            font.pixelSize: 24
            onClicked: rectParent.state = rectParent.state === "expanded"? "" : "expanded"
        }
        Rectangle {
            // Parent rectangle
            id: rectParent
            anchors.top: parent.top
            anchors.left: parent.left
            height: parent.height/2
            width: parent.width/2
            color: "black"
            // States for parent rectangle
            states: [
                State {
                    name: "expanded"
                    PropertyChanges {
                        target: rectParent;
                        height: parent.height;
                    }
                }
            ]
            // Transitions for parent Rectangle
            transitions: [
                Transition {
                    from: "*"; to: "expanded"
                    PropertyAnimation { target: rectParent; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                },
                Transition {
                    from: "expanded"; to: "*"
                    PropertyAnimation { target: rectParent; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                }
            ]
            // Inner content
            Rectangle {
                id: cont1
                color: "yellow"
                anchors.left: parent.left
                anchors.top: parent.top
                anchors.margins: 25
                height: parent.height*2/3
                width: parent.width*2/3
                CustomComponent {
                    expanded: rectParent.state
                }
            }
        }
    }
    

    The code for some custom child component (CustomComponent.qml) is as follows:

    import QtQuick 2.9
    import QtQuick.Window 2.2
    import QtQuick.Controls 2.2
    
    Item {
        id: root
        anchors.fill: parent
        property bool expanded: false
        state: expanded? "expanded" : ""    
        // Contents of the component
        Rectangle {
            id: rec1
            color: "red"
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.margins: 25
            width: parent.width/5
            height: parent.height/4
        }
        Rectangle {
            id: rec2
            color: "green"
            anchors.left: rec1.right
            anchors.top: parent.top
            anchors.margins: 25
            width: parent.width/5
            height: parent.height/4
        }
        Rectangle {
            id: rec3
            color: "blue"
            anchors.left: rec2.right
            anchors.top: parent.top
            anchors.margins: 25
            width: parent.width/5
            height: parent.height/4
        }
        // States for parent rectangle
        states: [
            State {
                name: "expanded"
                PropertyChanges { target: rec1; height: parent.height; }
                PropertyChanges { target: rec2; height: parent.height; }
                PropertyChanges { target: rec3; height: parent.height; }
            }
        ]
        // Transitions for parent Rectangle
        transitions: [
            Transition {
                from: "*"; to: "expanded"
                PropertyAnimation { target: rec1; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                PropertyAnimation { target: rec2; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                PropertyAnimation { target: rec3; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
            },
            Transition {
                from: "expanded"; to: "*"
                PropertyAnimation { target: rec1; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                PropertyAnimation { target: rec2; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
                PropertyAnimation { target: rec3; property: "height"; duration: 2000; easing.type: Easing.InOutCirc; }
            }
        ]
    }
    

    In this particular example, the problem will be that transition in the CustomComponent element is launched at the moment when the parent height property is still from the old state, and this value is used as target value for animation. Then, when animation is finished, the value is abruptly changed to the new parent.height from the new state. This is a working example so the problem can be easily visualized.

    I think that it would be really nice to have such a nested components that can rearrange their contents based on some state flag, while also following any animations in the parent element.

    Am I doing something wrong with the general pattern, or there is some workaround on this?