animation (time) isn't working
-
Hi all -
I'm implementing a rectangle that will collapse and expand on a button click. I'd like the size change to be animated, but despite my efforts, it occurs immediately. I'm probably doing something wrong with the animation code. Here's what I've got:
// Main.qml ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 CollapsibleBar { contents: Rectangle { height: 200; width: 200; color: 'lightgreen' } } } // CollapsibleBar.qml Rectangle { id: bar default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 RoundButton { onClicked: { bar.collapsed = !bar.collapsed; } } } Column { id: contentItem visible: !bar.collapsed Behavior on height { NumberAnimation { duration: 500 } } } } }The collapsing and expanding works, but it doesn't take 500 ms. Can someone explain to me why this is?
Thanks...
-
I love it when I come back to something after a couple weeks, and the problem goes away.
I still had some layout issues to deal with, and this solution isn't perfect; the caller has to manually reposition anything below the collapsible bar using y coordinates, but it does work nicely, and it's fairly easy to understand.
For anyone who wants it, here's the complete code (with a few extras thrown in for the example);
main.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 color: 'ivory' ColumnLayout { anchors.fill: parent spacing: 0 Rectangle { Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightblue' Text { anchors.centerIn: parent text: "this bar should appear above everything else." } } CollapsibleBar { id: collapsibleBar Layout.alignment: Qt.AlignVCenter | Qt.AlignTop contents: Rectangle { implicitHeight: 200 // this is causing the problem. width: mainWindow.width color: 'transparent' Text { anchors.centerIn: parent text: 'this rectangle should appear between the blue and green bars.' } } } Rectangle { y: collapsibleBar.height + collapsibleBar.y // THIS IS IMPORTANT Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightgreen' Text { anchors.centerIn: parent text: "this bar should appear below everything else." } } Item { Layout.fillHeight: true Layout.fillWidth: true } } }CollapsibleBar.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts Rectangle { id: bar default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width gradient: Gradient { GradientStop { position: 0.0; color: 'white' } GradientStop { position: 1.0; color: '#ded7f6' } } Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 Label { Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.leftMargin: 32 text: "IQ RECOMMENDED" color: 'white' font.family: "Barlow" font.pixelSize: 12 font.weight: Font.DemiBold background: Rectangle { anchors.centerIn: parent width: parent.width + (8 * 2) height: parent.height + (2 * 2) radius: height / 2 color: '#29292A' } } RoundButton { id: expanderButton Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.rightMargin: 32 Layout.maximumHeight: 24 // note: must use maximumHeight/Weight when within Layout. Layout.maximumWidth: 24 icon { source: "qrc:/Collapse.svg" } Behavior on rotation { NumberAnimation { duration: 500 easing.type: Easing.InOutQuad } } onClicked: { bar.collapsed = !bar.collapsed; expanderButton.rotation = (expanderButton.rotation + 180) % 360 } } } Column { id: contentItem clip: true height: bar.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } } } }As always, comments and questions are welcome.
-
Hi all -
I'm implementing a rectangle that will collapse and expand on a button click. I'd like the size change to be animated, but despite my efforts, it occurs immediately. I'm probably doing something wrong with the animation code. Here's what I've got:
// Main.qml ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 CollapsibleBar { contents: Rectangle { height: 200; width: 200; color: 'lightgreen' } } } // CollapsibleBar.qml Rectangle { id: bar default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 RoundButton { onClicked: { bar.collapsed = !bar.collapsed; } } } Column { id: contentItem visible: !bar.collapsed Behavior on height { NumberAnimation { duration: 500 } } } } }The collapsing and expanding works, but it doesn't take 500 ms. Can someone explain to me why this is?
Thanks...
@mzimmers the only height that is changing is on contentsArea. The height of that Item changes because contentItem is no longer visible. So contentItem is not included in the height calculation. It cannot animate an invisible Item. You will probably need a different approach for this if you want this animated. Perhaps you will need a sequence of changes to make this work properly. I have a similar side drawer and I slide the drawer offscreen (embedded system). So you might have to make and Item of a certain size slides the contentItem underneath, then turns it invisible.
-
@mzimmers the only height that is changing is on contentsArea. The height of that Item changes because contentItem is no longer visible. So contentItem is not included in the height calculation. It cannot animate an invisible Item. You will probably need a different approach for this if you want this animated. Perhaps you will need a sequence of changes to make this work properly. I have a similar side drawer and I slide the drawer offscreen (embedded system). So you might have to make and Item of a certain size slides the contentItem underneath, then turns it invisible.
-
@fcarney interesting. I suppose I could try using a drawer instead of my collapsing rectangle. I'll do some experimenting and report back. Thanks.
@mzimmers
I tried using height of contentItem:Rectangle { id: bar height: areaLayout.height width: 300 default property alias contents: contentItem.children property bool collapsed: true MouseArea { anchors.fill: parent onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout Text { text: "normal bar content" } Column { id: contentItem clip: true height: bar.collapsed ? 0 : children.height Behavior on height { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } } } }But it only animates the contraction.
-
@fcarney interesting. I suppose I could try using a drawer instead of my collapsing rectangle. I'll do some experimenting and report back. Thanks.
@mzimmers there is a calculated rectangle as the children move around. I used clipping (not sure what this does to performance) to hide things inside the rect if the items exceed the height of the default content:
Rectangle { id: bar height: childrenRect.height width: 300 clip: true default property alias contents: contentItem.children property bool collapsed: true color: "lightblue" Rectangle { anchors.fill: contentItem color: "salmon" } Column { id: contentItem clip: true y: bar.collapsed ? -(contentItem.height - areaLayout.height) : areaLayout.height Behavior on y { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } } Rectangle { anchors.fill: areaLayout color: "pink" } MouseArea { anchors.fill: areaLayout onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout width: bar.width Text { text: "normal bar content" } } } -
@fcarney interesting. I suppose I could try using a drawer instead of my collapsing rectangle. I'll do some experimenting and report back. Thanks.
@mzimmers Okay, this is flawed if the content you add is bigger than the default:
Rectangle { id: bar height: childrenRect.height width: 300 y: 200 clip: true default property alias contents: contentItem.children property bool collapsed: true color: "lightblue" Rectangle { anchors.fill: contentItem color: "salmon" } Column { id: contentItem clip: true y: bar.collapsed ? -(contentItem.height - areaLayout.height) : areaLayout.height Behavior on y { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } Text { text: "more text" } Text { text: "even more" } } Rectangle { anchors.fill: areaLayout color: "pink" } MouseArea { anchors.fill: areaLayout onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout width: bar.width Text { text: "normal bar content" } } }Still doesn't work.
-
@mzimmers there is a calculated rectangle as the children move around. I used clipping (not sure what this does to performance) to hide things inside the rect if the items exceed the height of the default content:
Rectangle { id: bar height: childrenRect.height width: 300 clip: true default property alias contents: contentItem.children property bool collapsed: true color: "lightblue" Rectangle { anchors.fill: contentItem color: "salmon" } Column { id: contentItem clip: true y: bar.collapsed ? -(contentItem.height - areaLayout.height) : areaLayout.height Behavior on y { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } } Rectangle { anchors.fill: areaLayout color: "pink" } MouseArea { anchors.fill: areaLayout onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout width: bar.width Text { text: "normal bar content" } } } -
@mzimmers
This has gotten kind of messy, but it works:Rectangle { id: bar //height: areaLayout.height property real distancePastBar: { contentItem.y // creates dependency on value change, forces update whenever value changes contentItem.mapToItem(bar, 0, 0).y + contentItem.height } onDistancePastBarChanged: { console.log("distancePastBar:", distancePastBar) } Component.onCompleted: { console.log("distancePastBar:", distancePastBar) } height: distancePastBar width: 300 y: 100 clip: true default property alias contents: contentItem.children property bool collapsed: true color: "lightblue" Rectangle { anchors.fill: contentItem color: "salmon" } Column { id: contentItem clip: true y: bar.collapsed ? -(contentItem.height - areaLayout.height) : areaLayout.height Behavior on y { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } Text { text: "more text" } Text { text: "even more" } } Rectangle { anchors.fill: areaLayout color: "pink" } MouseArea { anchors.fill: areaLayout onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout width: bar.width Text { text: "normal bar content" } Text { text: "normal bar content" } } } -
@mzimmers
This has gotten kind of messy, but it works:Rectangle { id: bar //height: areaLayout.height property real distancePastBar: { contentItem.y // creates dependency on value change, forces update whenever value changes contentItem.mapToItem(bar, 0, 0).y + contentItem.height } onDistancePastBarChanged: { console.log("distancePastBar:", distancePastBar) } Component.onCompleted: { console.log("distancePastBar:", distancePastBar) } height: distancePastBar width: 300 y: 100 clip: true default property alias contents: contentItem.children property bool collapsed: true color: "lightblue" Rectangle { anchors.fill: contentItem color: "salmon" } Column { id: contentItem clip: true y: bar.collapsed ? -(contentItem.height - areaLayout.height) : areaLayout.height Behavior on y { NumberAnimation { duration: 500 } } // simulate content used in "contents" Text { text: "some text" } Text { text: "more text" } Text { text: "even more" } } Rectangle { anchors.fill: areaLayout color: "pink" } MouseArea { anchors.fill: areaLayout onClicked: { bar.collapsed = !bar.collapsed } } // columns expand contract based upon content Column { id: areaLayout width: bar.width Text { text: "normal bar content" } Text { text: "normal bar content" } } }Doesn't just binding the content colum height on its implicit height when non collapsed works?
-
Doesn't just binding the content colum height on its implicit height when non collapsed works?
@GrecKo
Yes, this does work animate the height:Rectangle { id: bar2 y: 250 color: "red" default property alias contents: contentItem2.children property bool collapsed: true height: contentsArea2.height width: 300 Column { id: contentsArea2 RowLayout { id: barHeader width: 300 height: 32 RoundButton { implicitWidth: 30 implicitHeight: 30 onClicked: { bar2.collapsed = !bar2.collapsed; } } } Column { id: contentItem2 clip: true height: bar2.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } Text { text: "some text" } } } } -
@GrecKo
Yes, this does work animate the height:Rectangle { id: bar2 y: 250 color: "red" default property alias contents: contentItem2.children property bool collapsed: true height: contentsArea2.height width: 300 Column { id: contentsArea2 RowLayout { id: barHeader width: 300 height: 32 RoundButton { implicitWidth: 30 implicitHeight: 30 onClicked: { bar2.collapsed = !bar2.collapsed; } } } Column { id: contentItem2 clip: true height: bar2.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } Text { text: "some text" } } } }@fcarney this is very close to working as desired. I've added a couple things to Main.qml to represent the desired usage. It almost works perfectly; the only problem is the initial height of the collapsing area -- it's taking the value from the contents setting in Main.qml.
Any ideas on what could be done about this?
Here's the complete code. Thanks...
// Main.qml import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 ColumnLayout { anchors.fill: parent spacing: 0 CollapsibleBar { id: collapsibleBar Layout.alignment: Qt.AlignVCenter | Qt.AlignTop contents: Rectangle { implicitHeight: 300 // this is causing the problem. width: mainWindow.width color: 'lightgray' Text { anchors.centerIn: parent text: 'this light gray bar should appear below the light blue bar.' } } } Rectangle { y: collapsibleBar.height Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightgreen' Text { anchors.centerIn: parent text: "this light green bar should appear below everything else." } } } } // CollapsibleBar.qml import QtQuick import QtQuick.Controls import QtQuick.Layouts Rectangle { id: bar color: "lightblue" default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 RoundButton { implicitWidth: 24 implicitHeight: 24 onClicked: { bar.collapsed = !bar.collapsed; } } Text { // Layout.Alignment: Qt.AlignHCenter // BTW: this line doesn't compile. text: "this light blue bar should appear above everything." } } Column { id: contentItem clip: true height: bar.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } } } } -
@fcarney this is very close to working as desired. I've added a couple things to Main.qml to represent the desired usage. It almost works perfectly; the only problem is the initial height of the collapsing area -- it's taking the value from the contents setting in Main.qml.
Any ideas on what could be done about this?
Here's the complete code. Thanks...
// Main.qml import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 ColumnLayout { anchors.fill: parent spacing: 0 CollapsibleBar { id: collapsibleBar Layout.alignment: Qt.AlignVCenter | Qt.AlignTop contents: Rectangle { implicitHeight: 300 // this is causing the problem. width: mainWindow.width color: 'lightgray' Text { anchors.centerIn: parent text: 'this light gray bar should appear below the light blue bar.' } } } Rectangle { y: collapsibleBar.height Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightgreen' Text { anchors.centerIn: parent text: "this light green bar should appear below everything else." } } } } // CollapsibleBar.qml import QtQuick import QtQuick.Controls import QtQuick.Layouts Rectangle { id: bar color: "lightblue" default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 RoundButton { implicitWidth: 24 implicitHeight: 24 onClicked: { bar.collapsed = !bar.collapsed; } } Text { // Layout.Alignment: Qt.AlignHCenter // BTW: this line doesn't compile. text: "this light blue bar should appear above everything." } } Column { id: contentItem clip: true height: bar.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } } } } -
I love it when I come back to something after a couple weeks, and the problem goes away.
I still had some layout issues to deal with, and this solution isn't perfect; the caller has to manually reposition anything below the collapsible bar using y coordinates, but it does work nicely, and it's fairly easy to understand.
For anyone who wants it, here's the complete code (with a few extras thrown in for the example);
main.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 color: 'ivory' ColumnLayout { anchors.fill: parent spacing: 0 Rectangle { Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightblue' Text { anchors.centerIn: parent text: "this bar should appear above everything else." } } CollapsibleBar { id: collapsibleBar Layout.alignment: Qt.AlignVCenter | Qt.AlignTop contents: Rectangle { implicitHeight: 200 // this is causing the problem. width: mainWindow.width color: 'transparent' Text { anchors.centerIn: parent text: 'this rectangle should appear between the blue and green bars.' } } } Rectangle { y: collapsibleBar.height + collapsibleBar.y // THIS IS IMPORTANT Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightgreen' Text { anchors.centerIn: parent text: "this bar should appear below everything else." } } Item { Layout.fillHeight: true Layout.fillWidth: true } } }CollapsibleBar.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts Rectangle { id: bar default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width gradient: Gradient { GradientStop { position: 0.0; color: 'white' } GradientStop { position: 1.0; color: '#ded7f6' } } Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 Label { Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.leftMargin: 32 text: "IQ RECOMMENDED" color: 'white' font.family: "Barlow" font.pixelSize: 12 font.weight: Font.DemiBold background: Rectangle { anchors.centerIn: parent width: parent.width + (8 * 2) height: parent.height + (2 * 2) radius: height / 2 color: '#29292A' } } RoundButton { id: expanderButton Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.rightMargin: 32 Layout.maximumHeight: 24 // note: must use maximumHeight/Weight when within Layout. Layout.maximumWidth: 24 icon { source: "qrc:/Collapse.svg" } Behavior on rotation { NumberAnimation { duration: 500 easing.type: Easing.InOutQuad } } onClicked: { bar.collapsed = !bar.collapsed; expanderButton.rotation = (expanderButton.rotation + 180) % 360 } } } Column { id: contentItem clip: true height: bar.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } } } }As always, comments and questions are welcome.
-
M mzimmers has marked this topic as solved on
-
I love it when I come back to something after a couple weeks, and the problem goes away.
I still had some layout issues to deal with, and this solution isn't perfect; the caller has to manually reposition anything below the collapsible bar using y coordinates, but it does work nicely, and it's fairly easy to understand.
For anyone who wants it, here's the complete code (with a few extras thrown in for the example);
main.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts ApplicationWindow { id: mainWindow visible: true width: 800 height: 480 color: 'ivory' ColumnLayout { anchors.fill: parent spacing: 0 Rectangle { Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightblue' Text { anchors.centerIn: parent text: "this bar should appear above everything else." } } CollapsibleBar { id: collapsibleBar Layout.alignment: Qt.AlignVCenter | Qt.AlignTop contents: Rectangle { implicitHeight: 200 // this is causing the problem. width: mainWindow.width color: 'transparent' Text { anchors.centerIn: parent text: 'this rectangle should appear between the blue and green bars.' } } } Rectangle { y: collapsibleBar.height + collapsibleBar.y // THIS IS IMPORTANT Layout.preferredHeight: 100 Layout.fillWidth: true color: 'lightgreen' Text { anchors.centerIn: parent text: "this bar should appear below everything else." } } Item { Layout.fillHeight: true Layout.fillWidth: true } } }CollapsibleBar.qml
import QtQuick import QtQuick.Controls import QtQuick.Layouts Rectangle { id: bar default property alias contents: contentItem.children property bool collapsed: true height: contentsArea.height width: mainWindow.width gradient: Gradient { GradientStop { position: 0.0; color: 'white' } GradientStop { position: 1.0; color: '#ded7f6' } } Column { id: contentsArea RowLayout { id: barHeader width: mainWindow.width height: 32 Label { Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.leftMargin: 32 text: "IQ RECOMMENDED" color: 'white' font.family: "Barlow" font.pixelSize: 12 font.weight: Font.DemiBold background: Rectangle { anchors.centerIn: parent width: parent.width + (8 * 2) height: parent.height + (2 * 2) radius: height / 2 color: '#29292A' } } RoundButton { id: expanderButton Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.rightMargin: 32 Layout.maximumHeight: 24 // note: must use maximumHeight/Weight when within Layout. Layout.maximumWidth: 24 icon { source: "qrc:/Collapse.svg" } Behavior on rotation { NumberAnimation { duration: 500 easing.type: Easing.InOutQuad } } onClicked: { bar.collapsed = !bar.collapsed; expanderButton.rotation = (expanderButton.rotation + 180) % 360 } } } Column { id: contentItem clip: true height: bar.collapsed ? 0 : implicitHeight Behavior on height { NumberAnimation { duration: 500 } } } } }As always, comments and questions are welcome.
hey mr.zimmers, I often read your questions but can rarely help out, but I still like to run your code sometimes to see what I get.
In this case, I get a white screen with a lightblue bar containing text at the top and nothing else. I assume there should be text in the centre of the lightblue bar and a lightgreen bar, but the lightgreen bar does not show at all. How does your app look and function when running?
The only thing I cannot use is RoundButton and 'background' for Label/Text, these are missing in SailfishSDK for some reason, licensing I guess.
I'm building this on Sailfish SDK with/for my Sony Xperia 10II device.
-
hey mr.zimmers, I often read your questions but can rarely help out, but I still like to run your code sometimes to see what I get.
In this case, I get a white screen with a lightblue bar containing text at the top and nothing else. I assume there should be text in the centre of the lightblue bar and a lightgreen bar, but the lightgreen bar does not show at all. How does your app look and function when running?
The only thing I cannot use is RoundButton and 'background' for Label/Text, these are missing in SailfishSDK for some reason, licensing I guess.
I'm building this on Sailfish SDK with/for my Sony Xperia 10II device.
Qt 6.5.1, running on Windows 10, Fusion style.I know nothing about Sailfish, so I can't speak to that. I would recommend, though, that you experiment with changing styles, to see if that does anything for you.