I have ListViewshowing a Model, in which, at random positions I might add a entry.
Now I want to see my new entry, and try to move the ListView to it, with the
positionViewAtIndex(newIndex, ListView.Visible)
But this change somtimes feeles very hard. Is there a posibility to smooth it with som animation?
A somewhat lengthy example to play with:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: lv.positionViewAtIndex(lastAddedIndex, ListView.Contain)
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}
You can do it by adding addition animation element for contentX (as you have your element horizontally placed), and running it in order to animate changing view position at a given index, it will give you smooth animation:
source
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: gotoIndex(lastAddedIndex)
function gotoIndex(idx) {
anim.running = false;
var pos = lv.contentX;
var destPos;
lv.positionViewAtIndex(idx, ListView.Contain);
destPos = lv.contentX;
anim.from = pos;
anim.to = destPos;
anim.running = true;
}
NumberAnimation { id: anim; target: lv; property: "contentX"; duration: 500 }
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}
Related
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")
}
}
}
}
}
}
I have an model that consist of objects with some properties, e.g.:
ListModel {
id: fruitModel
ListElement {
name: "Apple"
color: "green"
cost: 2.45
}
ListElement {
name: "Orange"
color: "orange"
cost: 3.25
}
ListElement {
name: "Banana"
color: "yellow"
cost: 1.95
}
}
And now I want to display this model using GridLayout. For each property I want to use one element inside GridLayout, e.g:
GridLayout {
columns: 3
Text { text: "Apple" }
Rectangle { color: "green" }
SpinBox { value: 2.45 }
Text { text: "Orange" }
Rectangle { color: "orange" }
SpinBox { value: 3.25 }
Text { text: "Banana" }
Rectangle { color: "yellow" }
SpinBox { value: 1.95 }
}
The point is that I can easily change the columns property of GridLayout and make my layout narrower (e.g. to fit small screens). I can use Repeater to fill GridLayout. However such approach will fill my GridLayout in wrong order:
GridLayout {
columns: 3
Repeater {
model: fruitModel
Text { text: name }
}
Repeater {
model: fruitModel
Rectangle { color: color }
}
Repeater {
model: fruitModel
SpinBox { value: value }
}
}
And it is waste to use Layout.column and Layout.row attached properties since I want to easily change number of columns in GridLayout.
Is there any way to fill GridLayout with data from model row by row?
UPD1:
Behaviour that I want to get:
GridLayout {
columns: parent.width > 235 ? 3 : 1
Text { text: "Apple" }
Rectangle { color: "green"; width: 40; height: 40 }
SpinBox { value: 2 }
Text { text: "Orange" }
Rectangle { color: "orange"; width: 40; height: 40 }
SpinBox { value: 3 }
Text { text: "Banana" }
Rectangle { color: "yellow"; width: 40; height: 40 }
SpinBox { value: 1 }
}
UPD2:
Modified variant from #m7913d:
GridLayout {
id: layout
property int maxColumns: 3
columns: parent.width > 235 ? maxColumns : 1
Repeater {
model: fruitModel
Text {
text: name
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index)
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
Layout.preferredWidth: 30
Layout.preferredHeight: 30
color: col
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 1)
Layout.column: layout.columns == maxColumns ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: val
Layout.row: layout.columns == maxColumns ? index : (maxColumns * index + 2)
Layout.column: layout.columns == maxColumns ? 2 : 0
}
}
}
It's working but not easy-to-modify solution plus sometimes during layout resize there is messages QGridLayoutEngine::addItem: Cell (1, 0) already taken.
I would use a Column filled by either Rows or Columns (or any other arrangement) as delegates.
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: rootCol.width > 300 ? rowDel : colDel
}
Component {
id: rowDel
Row {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost }
}
}
Component {
id: colDel
Column {
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.cost}
}
}
}
Or maybe:
Column {
id: rootCol
anchors.fill: parent
Repeater {
model: fruitModel
delegate: Flow {
anchors {
left: parent.left
right: parent.right
}
Text { width: 100; height: 50; text: model.name }
Rectangle { width: 50; height: 50; color: model.color }
SpinBox { width: 150; height: 50; value: model.value }
}
}
}
Here is yet another solution:
The Idea: add the children in the right order
For that we will need to make sure that all Items for a model entry.
GridLayout {
id: gl
columns: parent.width > 235 ? 3 : 1
}
Instantiator {
model: fruitModel
delegate: QtObject {
property Item text: Text { parent: gl; text: model.name }
property Item rect: Rectangle { parent: gl; color: model.color; width: 50; height: 50; }
property Item spin: SpinBox { parent: gl; value: model.cost; }
}
}
Note: This will fail when entries in the model will be inserted or reordered, as in this solution the entries are always appended.
Other than that, it will automatically support column changes
A possible approach is to extend this method with condition column/row indexes:
GridLayout {
id:gridLayout
columns: parent.width > 235 ? 3 : 1
Repeater {
model: fruitModel
implicitHeight: 50
Text {
text: name
Layout.row: gridLayout.columns == 3 ? index : 3 * index
Layout.column: 0
}
}
Repeater {
model: fruitModel
Rectangle {
color: model.color
implicitWidth: 50
implicitHeight: 50
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 1
Layout.column: gridLayout.columns == 3 ? 1 : 0
}
}
Repeater {
model: fruitModel
SpinBox {
value: value
Layout.row: gridLayout.columns == 3 ? index : 3 * index + 2
Layout.column: gridLayout.columns == 3 ? 2 : 0
}
}
}
I am using a grid view, which can scrolled from left to right. When click on each item in the grid view, new screen launches with an animation from the current grid item position to the screen width and screen height. When click on the 2nd column elements after the grid view scrolled to right, the grid item position is getting wrong. Is there any way to fix this ? Or is there any method to check whether the grid view scrolled to the extreme right ?
Am putting my code below :
GridView {
id: featuresGrid
snapMode: ListView.SnapToItem
clip:true
x: 135
y: 122
width:1650
height:485
cellWidth: 825
cellHeight: 160
flow: GridView.FlowTopToBottom
model:favouriteapp
delegate:featureGridTile
focus: false
}
ListModel {
id:favouriteapp
ListElement {
featureName: "media & radio"
}
ListElement {
featureName: "phone"
}
ListElement {
featureName: "climate"
}
ListElement {
featureName: "navigation"
}
ListElement {
featureName: "ambient lighting"
}
ListElement {
featureName: "settings"
}
ListElement {
featureName: "camera"
}
ListElement {
featureName: "dynamic-i"
}
ListElement {
featureName: "bluetooth"
}
}
Component {
id: featureGridTile
Item {
id:grid_view_rect
width:featuresGrid.cellWidth
height:featuresGrid.cellHeight
Text{
anchors.fill: parent
text : featureName
opacity: 1
}
MouseArea{
anchors.fill: parent
onClicked: {
featuresGrid.currentIndex = index
//Goes to the next screen with the current clicked grid item X and Y position
}
}
}
}
What you are looking for are the mapFromItem and mapToItem functions:
http://doc.qt.io/qt-5/qml-qtquick-item.html#mapFromItem-method-1
Item {
id: rootItem
anchors.fill: parent
Rectangle {
id: myRect
color: 'red'
width: 50
height: 50
// the + 0 * featuresGrid.contentX will force the binding to reevaluate when draged. Maybe you don't want that. Comment it out and try
x: featuresGrid.currentItem.mapToItem(rootItem, 0, 0).x + 0 * featuresGrid.contentX
y: featuresGrid.currentItem.mapToItem(rootItem, 0, 0).y + 0 * featuresGrid.contentY
z: 1
}
GridView {
id: featuresGrid
snapMode: ListView.SnapToItem
clip:true
x: 135
y: 122
width:1650
height:485
cellWidth: 825
cellHeight: 160
flow: GridView.FlowTopToBottom
model:favouriteapp
delegate:featureGridTile
focus: false
}
ListModel {
id:favouriteapp
ListElement {
featureName: "media & radio"
soureIcon:"file:graphics/Icons/MediaIcon.png"
feature:"Media"
}
ListElement {
featureName: "phone"
soureIcon:"file:graphics/Icons/Phoneicon.png"
feature:"Phone"
}
ListElement {
featureName: "climate"
soureIcon:"file:graphics/Icons/ClimateIcon.png"
feature:"Climate"
}
ListElement {
featureName: "navigation"
soureIcon:"file:graphics/Icons/NavIcon.png"
feature:"Navigation"
}
ListElement {
featureName: "ambient lighting"
soureIcon:"file:graphics/Icons/ALIcon.png"
feature:"AL"
}
ListElement {
featureName: "settings"
soureIcon:"file:graphics/Icons/SettingsIcon.png"
feature:"Settings"
}
ListElement {
featureName: "camera"
soureIcon:"file:graphics/Icons/CamIcon.png"
feature:"Camera"
}
ListElement {
featureName: "dynamic-i"
soureIcon:"file:graphics/Icons/dynamicIcon.png"
feature:"Dynamic_I"
}
ListElement {
featureName: "bluetooth"
soureIcon:"file:graphics/Icons/Icon Bluetooth.png"
feature:"Bluetooth"
}
}
}
Component {
id: featureGridTile
Item {
id:grid_view_rect
width:featuresGrid.cellWidth
height:featuresGrid.cellHeight
Rectangle{
anchors.fill: parent
color: "white"
opacity: 1
border.color: 'black'
}
Text{
anchors.fill: parent
text : featureName
opacity: 1
}
MouseArea{
anchors.fill: parent
onClicked: {
featuresGrid.currentIndex = index
//Goes to the next screen with the current clicked grid item X and Y position
}
}
}
}
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)
}
}
I have a component named Tile in Tile.qml, which I want to create by a Repeater. Tile.qml is as follows:
import QtQuick 2.0
Rectangle {
id: tile
property string tileLabel: label.text
property int tileSize: height
width: 50
height: tileSize
color: "green"
border.color: Qt.lighter(color)
anchors.bottom: parent.bottom
Text {
id: label
color: "white";
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.bottom
text: tileLabel
}
}
And my main.qml is as follows:
import QtQuick 2.0
Rectangle {
id: root
width: 552; height: 300
color: "#3C3C3C"
border.color: Qt.lighter(color)
Row {
id: tilesRow
anchors.margins: 8
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: 4
Repeater {
id: repeater
model: 10
delegate: Tile {
tileSize: Math.random() * 100 + 1
tileLabel: tileSize
}
}
}
Rectangle {
id: button
width: 100
height: 30
color: "gray"
focus: true
Text {
anchors.centerIn: parent
color: "white"
text: "Button"
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
onEntered: { button.color = Qt.lighter("blue")}
onExited: { button.color = "gray" }
onPressed: { button.color = "blue" }
onReleased: { button.color = Qt.lighter("blue") }
onClicked: func()
}
}
}
I need to sort the tiles when the button is clicked so that the tiles are in ascending order by their labels. I can access the labels of the tiles using repeater.itemAt(i).tileSize. How can I animate the movement of tiles as they are moved/swapped?
Small example:
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
visible: true
width: 800
height: 600
Row {
anchors.centerIn: parent
property var word: ['H','e','l','l','o','!']
id: row
Repeater {
id: repeater
model: row.word.length
delegate: Rectangle {
id: delegate;
width: 100
height: 100
property int pos
color: Qt.rgba(Math.random(),Math.random(),Math.random(),1);
Text {
anchors.centerIn: parent
font.pixelSize: 36
color: "white"
text: row.word[index]
}
Behavior on x {
ParallelAnimation {
PropertyAnimation {
duration: 500
easing.type: Easing.InOutBack
}
SequentialAnimation {
PropertyAnimation {
target: delegate
property: "y"
from: 0
to: delegate.pos == 1 ? 20 : -20
duration: 250
}
PropertyAnimation {
target: delegate
property: "y"
from: delegate.pos == 1 ? 20 : -20
to: 0
duration: 250
}
}
}
}
Behavior on rotation {
RotationAnimation {
direction: RotationAnimation.Clockwise
duration: 300
}
}
}
}
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var element1 = repeater.itemAt(Math.round(Math.random() * (repeater.count - 1)));
var element2 = repeater.itemAt(Math.round(Math.random() * (repeater.count - 1)));
if(element1 === element2) {
element1.rotation = element1.rotation + 90;
} else {
element1.pos = 1;
element2.pos = 2;
var temp = element1.x;
element1.x = element2.x;
element2.x = temp;
}
}
}
}