QML: How to move items within a grid
Solution 1
You'd better use a GridView
Item instead of your Grid
approach.
This way you can use it's currentIndex
property to choose which item to move like this:
import QtQuick 1.1
Rectangle {
id: main;
width: 500; height: 500;
color: "darkgreen";
property int emptyBlock: 16;
GridView {
id: grid16;
x: 5; y: 5;
width: 490; height: 490;
model: gridModel
delegate: Component{
Rectangle {
width: 118; height: 118; color: "darkblue";
Text {
anchors.centerIn: parent
font.pixelSize: 20
text: value
}
}
}
}
ListModel {
id: gridModel
ListElement {value: 1}
ListElement {value: 2}
ListElement {value: 3}
ListElement {value: 4}
}
Keys.onRightPressed: {
gridModel.move(grid16.currentIndex, grid16.currentIndex+1, 1)
}
Keys.onLeftPressed: {
gridModel.move(grid16.currentIndex, grid16.currentIndex-1, 1)
}
focus: true;
}
Solution 2
Grids give you no way to manipulate the position of the contained items directly. Instead their position is directly derived from the physically order of the child items of the grid. There is no easy way to to manipulate child items in QML dynamically, so I think you should abandon the Grid
item and specify the position of the child items explicitly with the x
and y
properties. Applied to your code this could look like:
Rectangle {
id: main;
width: 500; height: 500;
color: "darkgreen";
Item {
x: 5; y: 5;
width: 490; height: 490;
Repeater {
id: pieces
model: 1;
Rectangle {
property int column: 0
property int row: 0
x: column * 123
y: row * 123
width: 118; height: 118; color: "darkblue";
}
}
}
Keys.onRightPressed: pressRight();
function pressRight() {
console.log("Left key pressed");
pieces.itemAt(0).column++
}
focus: true;
}
Update 1:
Grids (in combination with a Repeater) can be used to visualize models, e.g., a XmlListModel
item or an QAbstractItemModel
descendent.
With move
property it's easy to react to layout changes in the model (if an entry is removed/added) in an animated way. Still, the items in the Grid
are laid out strictly in the order of the entries of the model.
So if you want have manual control over the position of your items, even in cellular layout, use of a Grid
is not advisable.
Solution 3
You can change the number of items before the item you want to move to change its position:
import QtQuick 1.1
Rectangle {
id: main;
width: 500; height: 500;
color: "darkgreen";
property int emptyBlock: 16;
property int posX: 0;
property int posY: 0;
Grid {
id: grid;
x: 5; y: 5;
width: 490; height: 490;
rows: 4; columns: 4; spacing: 5;
Repeater {
id: beforeTheItem
model: main.posX + parent.columns * main.posY
Rectangle {
width: 118; height: 118; color: "transparent";
}
}
Rectangle {
id:theItem
width: 118; height: 118; color: "darkblue";
}
}
Keys.onPressed: {
// To avoid flickering, the item is hidden before the change
// and made visible again after
theItem.visible = false;
switch(event.key) {
case Qt.Key_Left: if(posX > 0) posX--;
break;
case Qt.Key_Right: if(posX < grid.columns - 1) posX++;
break;
case Qt.Key_Up: if(posY > 0) posY--;
break;
case Qt.Key_Down: if(posY < grid.rows - 1) posY++;
break;
}
theItem.visible = true;
}
focus: true;
}
Solution 4
Now, by using Qt 5.1 or above and GridLayout you can move your items without hassle:
import QtQuick 2.4
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
Window
{
visible: true
MainForm
{
GridLayout {
id: gridLayout
columns: 3
property int oneRow: 0
property int oneCol: 0
Text { id: one; Layout.row :grid.oneRow; Layout.column: grid.oneCol; text: "My"; font.bold: true; }
Text { text: "name"; color: "red" }
Text { text: "is"; font.underline: true }
Text { text: "not"; font.pixelSize: 20 }
Text { text: "Ravan"; font.strikeout: true }
}
Component.onCompleted:
{
gridLayout.oneRow = 1
gridLayout.oneCol = 2
}
}
}
Solution 5
The GridView is a very confusing monster. It just populates one row from a given model, which leads to confusion since it is called GRID. But it can still be used as a fixed size grid, as I show in the example below. A single square can be moved with arrow keys on a 4x4 sized grid.
GridView {
id: grid16;
anchors.fill: parent
cellWidth: parent.width / 2
cellHeight: parent.height / 2
model: gridModel
delegate:
Rectangle {
Component.onCompleted: if( index >= 1 ) visible = false
width: grid16.cellWidth ; height: grid16.cellHeight ; color: "yellow";
Text {
anchors.centerIn: parent
font.pixelSize: 20
text: value
}
}
move: Transition {
NumberAnimation { properties: "x,y"; duration: 1000 }
}
}
ListModel {
id: gridModel
ListElement {value: 1}
//Necessary, otherwise the grid will have the dimension 1x1
ListElement {value: 2}
ListElement {value: 3}
ListElement {value: 4}
}
Keys.onRightPressed: { gridModel.move(grid16.currentIndex, grid16.currentIndex+1, 1) }
Keys.onLeftPressed: { gridModel.move(grid16.currentIndex, grid16.currentIndex-1, 1) }
Keys.onUpPressed: { gridModel.move(grid16.currentIndex, grid16.currentIndex-2, 1) }
Keys.onDownPressed: { gridModel.move(grid16.currentIndex, grid16.currentIndex+2, 1) }
focus: true;
}
Xolve
Updated on June 04, 2022Comments
-
Xolve about 2 years
I have a 4x4 grid and I want to associate arrow key presses with the movement of items within the grid. How does one do that?
Here is a sample QML:
import QtQuick 1.1 Rectangle { id: main; width: 500; height: 500; color: "darkgreen"; property int emptyBlock: 16; Grid { id: grid16; x: 5; y: 5; width: 490; height: 490; rows: 4; columns: 4; spacing: 5; Repeater { model: 1; Rectangle { width: 118; height: 118; color: "darkblue"; } } } Keys.onRightPressed: pressRight(); function pressRight() { console.log("Left key pressed"); } focus: true; }
Update 1: Thanks to sebasgo and alexisdm for the answers. If moving within a grid is not that easy why we have the
move
transition property [http://qt-project.org/doc/qt-4.8/qml-grid.html#move-prop]