QML: ListView items expansion - qt

I have a ListView and its delagates. My intention is to make expandable delegates (eg. I have three parent delegates, if the parent delegate is clicked, it will expand its children). I made this demonstration:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
id: root
width: 1406; height: 536
ListModel {
id: animalsModel
ListElement { name: "Cats"; isExpanded: false; type: "parent"}
ListElement { name: "Puss in Boots 1"; isExpanded: false; type: "Cats"}
ListElement { name: "Puss in Boots 2"; isExpanded: false; type: "Cats"}
ListElement { name: "Puss in Boots 3"; isExpanded: false; type: "Cats"}
ListElement { name: "Dogs"; isExpanded: false; type: "parent"}
ListElement { name: "Pug 1"; isExpanded: false; type: "Dogs" }
ListElement { name: "Birds"; isExpanded: false; type: "parent"}
ListElement { name: "Parrot 1"; isExpanded: false; type: "Birds" }
ListElement { name: "Parrot 2"; isExpanded: false; type: "Birds" }
ListElement { name: "Parrot 3"; isExpanded: false; type: "Birds" }
}
ListView {
id: listing
width: 181
height: parent.height
model: animalsModel
delegate: listdelegate
}
Component {
id: listdelegate
Rectangle {
id: menuItem
width: 181
height: type === "parent" ? 55 : 0
color: type === "parent" ? "lightblue" : "white"
border.width: 1
states: State {
name: "expanded"
when: isExpanded
PropertyChanges {
target: menuItem
height: 55
}
}
transitions:[
Transition {
from: ""
to: "expanded"
reversible: true
SequentialAnimation {
PropertyAnimation { property: "height"; duration: 1000 }
}
}
]
Text {
id: text
text: name
clip: true
anchors.fill: parent
}
MouseArea {
function expand(isExpanded) {
for(var i = 0; i < animalsModel.count; ++i) {
var animal = animalsModel.get(i)
if(animal.type === name) {
animal.isExpanded = isExpanded
}
}
}
anchors.fill: parent
onClicked: {
if (type == "parent") {
if (isExpanded == false) {
// expand
expand(true)
}
else {
// collapse
expand(false)
}
isExpanded = !isExpanded
}
}
}
}
}
}
It works perfectly except one small detail: the height animation is very expensive in my case (I have more complex delagate). How can I make the expanding animation height independent. My idea is to not resize the delagetes, only hide them behind the parent. Like a train going into the tunnel. But I cannot manipulate the x property of delagates in case of vertical ListView.
Any idea how to achive this? In the best scenario how to make the animation I described?

Related

QML List View Drag and Drop

I would like to create two qml list views that can perform two functions:
Drag and drop items within one list to change order of items
Drag and drop items cross lists. Items will remove from one list and add to another
Based on the Drag and Drop example in Qt Documentation, I decided to create two list views that accessing the same list model. Each item in the list model has a key as a bool value. Using DelegateModel with a filter, I can display items with key equals to true in one list and with false in the second list. When dragging an item from first list to second list, the key value will be flipped.
Now, I am facing two problems:
onDropped is not executed
DelegateModel is not refreshed after key value changed
Can anybody help me solve these problems or maybe have a better solution?
import QtQuick 2.5
import QtQuick.Window 2.0
import QtQml.Models 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 300
height: 400
title: qsTr("List View Test")
property bool dropAreaOneEntered: false
property bool dropAreaTwoEntered: false
property int listNumer: 0
Rectangle {
id: root
width: parent.width
height: parent.height
ListView {
id: displayListOne
width: 100; height: parent.height
model: displayListOneModel
property int dragItemIndex: -1
moveDisplaced: Transition {
NumberAnimation{
properties: "x,y"
duration: 200
}
}
}
ListView {
id: displayListTwo
width: 100; height: parent.height
anchors.right: parent.right
model: displayListTwoModel
property int dragItemIndex: -1
moveDisplaced: Transition {
NumberAnimation{
properties: "x,y"
duration: 200
}
}
}
// ..........................................................
ListModel {
id: itemsListModel
// #disable-check M16
ListElement { colore: "blue"; key: true }
// #disable-check M16
ListElement { colore: "orange"; key: false }
// #disable-check M16
ListElement { colore: "blue"; key: true }
// #disable-check M16
ListElement { colore: "orange"; key: false }
// #disable-check M16
ListElement { colore: "blue"; key: true }
// #disable-check M16
ListElement { colore: "orange"; key: false }
// #disable-check M16
ListElement { colore: "blue"; key: true }
// #disable-check M16
ListElement { colore: "orange"; key: false }
}
// ..........................................................
DelegateModel {
id: displayListOneModel
model: itemsListModel
delegate: MouseArea {
id: mouseOneArea
width: 100; height: 100
property bool held: false
drag.target: held ? itemOneBlock : undefined
drag.axis: Drag.XAndYAxis
property var draggedItem: null
onPressAndHold: {
held = true
listNumer = 1
displayListOne.dragItemIndex = index
}
onReleased: {
listNumer = 0
held = false
}
Rectangle {
id: itemOneBlock
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
width: 100
height: 100
color: colore
opacity: mouseOneArea.held ? 0.8 : 1.0
Drag.active: mouseOneArea.held
Drag.source: mouseOneArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states: State{
when: mouseOneArea.held
ParentChange { target: itemOneBlock; parent: root }
AnchorChanges {
target: itemOneBlock
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
}
DropArea {
id: listOneDropArea
anchors.fill: parent
property bool flag: false
onExited: dropAreaOneEntered = false
onEntered: {
dropAreaOneEntered = true
if (dropAreaOneEntered & ! dropAreaTwoEntered){
if (listNumer == 1){
displayListOneModel.items.move(drag.source.DelegateModel.itemsIndex,
mouseOneArea.DelegateModel.itemsIndex)
}
}
}
onDropped: {
console.log("onDropped has been executed!")
}
}
}
groups: [
DelegateModelGroup {
includeByDefault: true
name: "listOne"
}
]
filterOnGroup: "listOne"
Component.onCompleted: {
var rowCount = itemsListModel.count;
for( var i = 0; i < items.count; i++ ) {
var entry = items.get(i).model;
if((entry.key != true)) {
items.removeGroups(i,1,"listOne")
}
}
}
}
DelegateModel {
id: displayListTwoModel
model: itemsListModel
delegate: MouseArea {
id: mouseTwoArea
width: 100; height: 100
property bool held: false
drag.target: held ? itemTwoBlock : undefined
drag.axis: Drag.XAndYAxis
onPressAndHold: {
held = true
listNumer = 2
displayListTwo.dragItemIndex = index
console.log("current index is "+index)
}
onReleased: {
listNumer = 0
held = false
}
Rectangle {
id: itemTwoBlock
anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
width: 100
height: 100
color: colore
opacity: mouseTwoArea.held ? 0.8 : 1.0
Drag.active: mouseTwoArea.held
Drag.source: mouseTwoArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states: State{
when: mouseTwoArea.held
ParentChange { target: itemTwoBlock; parent: root }
AnchorChanges {
target: itemTwoBlock
anchors { horizontalCenter: undefined; verticalCenter: undefined }
}
}
}
DropArea {
id: listTwoDropArea
anchors.fill: parent
onExited: dropAreaTwoEntered = false
onEntered: {
dropAreaTwoEntered = true
if (dropAreaTwoEntered & ! dropAreaOneEntered){
if (listNumer == 2){
displayListTwoModel.items.move(drag.source.DelegateModel.itemsIndex,
mouseTwoArea.DelegateModel.itemsIndex)
}
}
}
onDropped: {
console.log("Flip the key value here!")
}
}
}
groups: [
DelegateModelGroup {
includeByDefault: true
name: "listTwo"
}
]
filterOnGroup: "listTwo"
Component.onCompleted: {
var rowCount = itemsListModel.count;
for( var i = 0; i < items.count; i++ ) {
var entry = items.get(i).model;
if((entry.key != false)) {
items.removeGroups(i,1,"listTwo")
}
}
}
}
}
}

Creating QML States dynamically

I want to make an icon component that changes it picture and color depending on it state:
StateIcon.qml:
import QtQuick 2.0
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Item {
Layout.preferredWidth: appLayout.icon.prefWidth
Layout.preferredHeight: appLayout.icon.prefHeight
property variant stateImage: stateImageInstance
Image {
id: stateImageInstance
width: appLayout.icon.prefWidth
height: appLayout.icon.prefWidth
sourceSize.width: width
sourceSize.height: height
}
property variant imageOverlay: imageOverlayInstance
ColorOverlay {
id: imageOverlayInstance
anchors.fill: stateImage
source: stateImage
}
transitions: Transition {
SequentialAnimation {
NumberAnimation {
target: stateImage; property: "scale"
to: 0; duration: 100
}
PropertyAction {
target: stateImage; property: "source"
}
PropertyAction {
target: imageOverlay; property: "color"
}
NumberAnimation {
target: stateImage; property: "scale"
to: 1; duration: 100
}
}
}
}
The problem is that I have to define states in the component instance:
main.qml:
StateIcon {
id: stateIcon
states: [
State {
name: "state1";
PropertyChanges {
target: stateIcon.stateImage
source: "qrc:/resources/icons/icon1.svg"
}
PropertyChanges {
target: stateIcon.imageOverlay; color: "gray"
}
},
State {
name: "state2";
PropertyChanges {
target: stateIcon.stateImage
source: "qrc:/resources/icons/icon2.svg"
}
PropertyChanges {
target: stateIcon.imageOverlay; color: "green"
}
}
...
]
state: "state1"
}
And now I want to know is it possible to define only state names, color and source in some array:
main.qml:
StateIcon {
id: stateIcon
rawStates: [
{
name: "state1",
iconSource: "qrc:/resources/icons/state1.svg",
color: "green"
},
{
name: "state2",
iconSource: "qrc:/resources/icons/state2.svg",
color: "green"
},
...
]
state: "state1"
}
And in the StateIcon.qml define states property dynamically using rawStates property?
Maybe something like that:
StateIcon.qml:
import QtQuick 2.0
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Item {
property variant rawStates
Layout.preferredWidth: appLayout.icon.prefWidth
Layout.preferredHeight: appLayout.icon.prefHeight
Image {
id: stateImage
width: appLayout.icon.prefWidth
height: appLayout.icon.prefWidth
sourceSize.width: width
sourceSize.height: height
}
ColorOverlay {
id: imageOverlay
anchors.fill: stateImage
source: stateImage
}
states: [
for(var i=0; i<rawStates.length; ++i) {
?
}
]
transitions: Transition {
SequentialAnimation {
NumberAnimation {
target: stateImage; property: "scale"
to: 0; duration: 100
}
PropertyAction {
target: stateImage; property: "source"
}
PropertyAction {
target: imageOverlay; property: "color"
}
NumberAnimation {
target: stateImage; property: "scale"
to: 1; duration: 100
}
}
}
}
Instead of using States I would use a plain javascript associative arrays.
You can't use transitions but you could use Behavior instead. Not anything can be done with behavior but it's enough most of the time.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
property var stateDescriptors: {
'state0': {color: 'green'},
'state1': {color: 'red'},
'state2': {color: 'blue'},
'state3': {color: 'purple'},
'state4': {color: 'orange'}
}
property string iconState: "state0"
Text {
anchors.fill: parent
text: parent.iconState
}
color: stateDescriptors[iconState].color
Behavior on iconState {
SequentialAnimation {
NumberAnimation {
target: rect; property: "scale"
to: 0; duration: 100
}
PropertyAction { } //actually change the iconState here, since the color is binded to it, it will also change between the 2 scale animations
NumberAnimation {
target: rect; property: "scale"
to: 1; duration: 100
}
}
}
}
Button {
text: 'change state'
property int count: 0
onClicked: {
count = (count + 1) % Object.keys(rect.stateDescriptors).length
rect.iconState = 'state' + count
}
}
}
}
Maybe this helps you:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
Text {
anchors.fill: parent
text: parent.state
}
property var myStates: []
states: myStates
onStateChanged: console.log(Object.keys(rect.states))
}
Button {
text: 'add state'
onClicked: {
rect.myStates.push(statePrototype.createObject(rect,
{
name: 'state' + count,
color: Qt.rgba(Math.random(count),
Math.random(count),
Math.random(count),
Math.random(count))
}))
rect.myStatesChanged()
count++
}
}
Button {
text: 'change state'
onClicked: {
rect.state = 'state' + (count1 % count)
count1++
}
}
}
property int count: 0
property int count1: 0
Component {
id: statePrototype
State {
id: st
property color color
PropertyChanges {
target: rect
color: st.color
}
}
}
}
It seems to be not so easily possible to add States to states directly. With the extra mile going over a custom property var myStates it suddenly works. Don't forget to tell everyone, that myStatesChanged() after adding something!
EDIT Once more, with the list of JS Objects, and a Instantiator. The method is the same
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
Text {
anchors.fill: parent
text: parent.state
}
property var myStates: []
states: myStates
onStateChanged: console.log(Object.keys(rect.states))
}
Button {
text: 'change state'
property int count: 0
onClicked: {
rect.state = 'state' + count % rect.myStates.length
count ++
}
}
Button {
text: 'add states'
onClicked: {
stateDescriptors.push( { name: 'state' + stateDescriptors.length, color: Qt.rgba(Math.random(1),
Math.random(2),
Math.random(3),
Math.random(4)) })
stateDescriptorsChanged()
}
}
}
Instantiator {
model: stateDescriptors
delegate: State {
name: modelData.name
PropertyChanges {
target: rect
color: modelData.color
}
Component.onCompleted: {
console.log('created', modelData.name)
rect.myStates.push(this)
rect.myStatesChanged()
}
Component.onDestruction: {
console.log('destroy', modelData.name)
rect.myStates.pop()
}
}
}
property var stateDescriptors: [
{
name: 'state0',
color: 'green'
},
{
name: 'state1',
color: 'red'
},
{
name: 'state2',
color: 'blue'
},
{
name: 'state3',
color: 'purple'
},
{
name: 'state4',
color: 'orange'
}
]
}

Stop the Pathview movement at the last element

How can I stop the movement of the Pathview element in QML when it reaches the last element in the model(like the list view and grid view default behaviour).ie,I don't want a cyclic movement.
in the below code if we swipe right after the first("first") item last("last") will be shown (then "Jane Doe") .what can be done to stop the further swipe in right when first is reached and further swipe in left when the last item is reached.it should stop when first or last is reached
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ListModel {
id:contactModel
ListElement {
name: "first"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "John Smith"
icon: "pic.png"
}
ListElement {
name: "Bill Jones"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "John Smith"
icon: "pic.png"
}
ListElement {
name: "Bill Jones"
icon: "pic.png"
}
ListElement {
name: "Jane Doe"
icon: "pic.png"
}
ListElement {
name: "last"
icon: "pic.png"
}
}
Component {
id: delegate
Column {
id: wrapper
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
source: icon
}
Text {
id: nameText
text: name
font.pointSize: 5
color: wrapper.PathView.isCurrentItem ? "red" : "black"
}
}
}
PathView {
anchors.fill: parent
model: contactModel
delegate: delegate
snapMode:PathView.SnapOneItem
path: Path {
startX: 0; startY: 100
PathLine { x: 640; y: 400; }
}
}
}
Here it's how implementation might look (I was not able to find available properties to do that)
PathView {
property int previousCurrentIndex: 0
currentIndex: 0
...
onCurrentIndexChanged: {
var lastIndex = contactModel.count - 1
if (currentIndex == 0 && previousCurrentIndex == lastIndex) {
pathViewId.positionViewAtIndex(lastIndex, PathView.Beginning)
} else if (currentIndex == lastIndex && previousCurrentIndex == 0) {
pathViewId.positionViewAtIndex(0, PathView.Beginning)
}
previousCurrentIndex = currentIndex
}
}
Custom property previousCurrentIndex is introduced and calculation of actual currentIndex is done on emitting of onCurrentIndexChanged signal
You can add the following code in your PathView. It'll stop the view moving from the end to the start and the other way around.
property real previousOffsetVal: 0.
onOffsetChanged: {
/* 10 is a threshhold, should be smaller than the total elements count*/
if (Math.abs( previousOffsetVal - offset) > 10 ) {
offset = previousOffsetVal
}
previousOffsetVal = offset
}

Horizontal listview navigation - visible item

I have more horizontal listview. For example let's take two of them. Items of each list are not the same length, when one list is scrolling - the other one does, too. I've solved that with contentX property. But, when I want to navigate between each other (when I press key "down" I need to force focus second listview). The problem is that focus is on horizontalna2.currentIndex that is remembered, and I want to go on first visible item in listview.
In android is that very simple, but to solve that here, huh..
Here is sketch of the code:
Rectangle {
width: 500
height: 200
ListModel {
id: model1
ListElement { itemwidth: 100 }
ListElement { itemwidth: 200 }
ListElement { itemwidth: 50 }
ListElement { itemwidth: 70 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
}
ListModel {
id: model2
ListElement { itemwidth: 300 }
ListElement { itemwidth: 50 }
ListElement { itemwidth: 70 }
ListElement { itemwidth: 100 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 30 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
ListElement { itemwidth: 90 }
}
ListView {
clip: true
id: horizontalna
boundsBehavior: Flickable.StopAtBounds
width: 500
height: 60;
focus: true
model: model1
orientation: ListView.Horizontal
KeyNavigation.down: horizontalna2
onContentXChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.contentX = horizontalna.contentX
}
}
delegate: Item {
id: containerHorizontal
width: itemwidth; height: 60;
Rectangle {
id: contentHorizontal
anchors.centerIn: parent; width: containerHorizontal.width; height: containerHorizontal.height - 10
color: "transparent"
antialiasing: true
Rectangle { id: insideConHorizontal; anchors.fill: parent; anchors.margins: 3; color: "grey"; antialiasing: true; radius: 5
Text {
id: labelHorizontal
text: "name"
color: "white"
}
}
}
states: State {
name: "active"; when: containerHorizontal.activeFocus
PropertyChanges { target: contentHorizontal; color: "#FFFF00"; scale: 1}
PropertyChanges { target: insideConHorizontal; color: "#F98F06" }
PropertyChanges { target: labelHorizontal; color: "#0E2687"}
}
}
}
ListView {
id: horizontalna2
anchors.top: horizontalna.bottom
boundsBehavior: Flickable.StopAtBounds
width: 500
height: 60;
focus: true
model: model2
orientation: ListView.Horizontal
onContentXChanged: {
if (horizontalna2.activeFocus === true)
{
horizontalna.contentX = horizontalna2.contentX
}
}
delegate: Item {
id: containerHorizontal2
width: itemwidth; height: 60;
Rectangle {
id: contentHorizontal2
anchors.centerIn: parent; width: containerHorizontal2.width; height: containerHorizontal2.height - 10
color: "transparent"
antialiasing: true
Rectangle { id: insideConHorizontal2; anchors.fill: parent; anchors.margins: 3; color: "grey"; antialiasing: true; radius: 5
Text {
id:labelHorizontal2
color: "white"
text: "name"
}
}
}
states: State {
name: "active"; when: containerHorizontal2.activeFocus
PropertyChanges { target: contentHorizontal2; color: "#FFFF00"; scale: 1}
PropertyChanges { target: insideConHorizontal2; color: "#F98F06" }
PropertyChanges { target: labelHorizontal2; color: "#0E2687"}
}
}
}
}
EDIT[SOLVED] :
Get index of first visible item of second list depending on contentX --> function indexAt()
onCurrentIndexChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.currentIndex = horizontalna2.indexAt((horizontalna2.contentX),0)
}
}
Get index of first visible item of second list depending on contentX --> function indexAt()
onCurrentIndexChanged: {
if (horizontalna.activeFocus === true)
{
horizontalna2.currentIndex = horizontalna2.indexAt((horizontalna2.contentX),0)
}
}

Dynamically reparenting objects in QML fails on initial load

I am trying to dynamically reparent QML objects generated in a repeater according to data they inherit from their model.
This works like a charm - with one catch. When the object is generated for the first time, it is automatically reparented to the Repeater's parent after the state's ParentChange object makes its changes. Run the following QML file in a QML viewer, paying attention to the order of the console messages to see what I'm describing.
After you've clicked on each of the objects, they behave as expected.
import QtQuick 1.1
Rectangle {
id: container
height: 300
width: 300
signal completed
ListModel {
id: fooModel
ListElement { data: "red" }
ListElement { data: "red" }
ListElement { data: "blue" }
}
Component.onCompleted: {
console.log("Rect Completed!")
container.completed()
}
// The object I want to dynamically move
Component {
id: delg
Rectangle {
id: moveable
height: 40; width: 100
border.width: 1; border.color: "black"
state: model.data
color: state
// The following code makes it work, but feels very hackish
/*Connections {
target: container
onCompleted: {
moveable.parent = moveable.state == "red" ? red_col : blue_col
}
}*/
onStateChanged: { console.log("New state: " + state) }
onParentChanged: { console.log("New parent: " + parent) }
Component.onCompleted: { console.log("Delegate Completed!") }
MouseArea {
anchors.fill: parent
onClicked: {
// I know this is bad to do, but in my REAL application,
// the change is triggered through the model, not the qml
// object
moveable.state = (moveable.state == "red" ? "blue" : "red")
}
}
states: [
State {
name: 'red'
ParentChange { target: moveable; parent: red_col; x: 0 }
},
State {
name: 'blue'
ParentChange { target: moveable; parent: blue_col; x: 0 }
}
]
transitions: [ Transition {
ParentAnimation {
NumberAnimation { properties: 'x,y,height,width' }
}
}]
}
}
// Generates the Objects
Repeater {
id: repeat
model: fooModel
delegate: delg
}
// Display
Row {
spacing: 100
Column {
id: red_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
Column {
id: blue_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
}
}
I figured out a way to fix the behavior, but it's not pretty. (See the commented out "Connections" code above for that fix).
Is there a cleaner/less-hacky way to accomplish the same thing I'm trying here?
Easy way of doing it is placing extra Item under your delegate. This will cause Repeater to reparent Item and your own code would set new parent of its child, your Rectangle element. Like this:
import QtQuick 1.1
Rectangle {
id: container
height: 300
width: 300
signal completed
ListModel {
id: fooModel
ListElement { data: "red" }
ListElement { data: "red" }
ListElement { data: "blue" }
}
Component.onCompleted: {
console.log("Rect Completed!")
container.completed()
}
// The object I want to dynamically move
Component {
id: delg
Item {
Rectangle {
id: moveable
height: 40; width: 100
border.width: 1; border.color: "black"
state: model.data
color: state
// The following code makes it work, but feels very hackish
/*Connections {
target: container
onCompleted: {
moveable.parent = moveable.state == "red" ? red_col : blue_col
}
}*/
onStateChanged: { console.log("New state: " + state) }
onParentChanged: { console.log("New parent: " + parent) }
Component.onCompleted: { console.log("Delegate Completed!") }
MouseArea {
anchors.fill: parent
onClicked: {
// I know this is bad to do, but in my REAL application,
// the change is triggered through the model, not the qml
// object
moveable.state = (moveable.state == "red" ? "blue" : "red")
}
}
states: [
State {
name: 'red'
ParentChange { target: moveable; parent: red_col; x: 0 }
},
State {
name: 'blue'
ParentChange { target: moveable; parent: blue_col; x: 0 }
}
]
transitions: [ Transition {
ParentAnimation {
NumberAnimation { properties: 'x,y,height,width' }
}
}]
}
}
}
// Generates the Objects
Repeater {
id: repeat
model: fooModel
delegate: delg
}
// Display
Row {
spacing: 100
Column {
id: red_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
Column {
id: blue_col
spacing: 10
width: 100; height: 300
move: Transition { NumberAnimation { properties: "y" } }
add: Transition { NumberAnimation { properties: "y" } }
}
}
}

Resources