timday
timday

Reputation: 24892

How can I reorder the items in a QML GridLayout?

How can I reorder the items in a GridLayout (or other QML item containers generally)? I have one with some number of rows and columns and populated with child items. Until a few minutes ago I was hoping I could simply operate on the children list member of the item to reorder them: I have a MouseArea button in the UI from which I was hoping could simply invoke (from onClicked:) a

function reorder() {children.unshift(children.pop());}

method of the layout item (with the intention of the last item in the layout becoming the first), but when triggered that generates the error message:

TypeError: Property 'pop' of object [object Object] is not a function

and in any case I note the documentation says children is read only.

Is there an easy way of doing this? (The documentation on dynamic object management doesn't really cover more than creation and destruction of child objects.)

Upvotes: 3

Views: 4009

Answers (3)

fallerd
fallerd

Reputation: 1738

You can do pop/push-like operations as initially described by changing the parent of the items in the GridLayout. Pop any item from any index in the GridLayout by un-parenting it from the GridLayout. Push to the end of the GridLayout by re-parenting it to the GridLayout again. Between these two operations, you could insert items at any location by pushing and popping all of the children in a specific order and not otherwise have to manage row/column on each item, leaving automatic layout to the GridLayout.

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.9
import QtQuick.Layouts 1.3

Window {
    id: window
    visible: true
    height: 600
    width: 800

    GridLayout {
        id: grid
        columns: 4
        Rectangle {
            id: rect1
            color: Qt.rgba(Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, 1)
            height: 20
            width: 20
        }
        Rectangle {
            id: rect2
            color: Qt.rgba(Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, 1)
            height: 20
            width: 20
        }
        Rectangle {
            id: rect3
            color: Qt.rgba(Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, 1)
            height: 20
            width: 20
        }
        Rectangle {
            id: rect4
            color: Qt.rgba(Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, 1)
            height: 20
            width: 20
        }
        Rectangle {
            id: rect5
            color: Qt.rgba(Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, Math.random()*0.5 + 0.25, 1)
            height: 20
            width: 20
        }
    }

    Item {
        id: item
        visible: false
    }

    Button {
        text: "push first item to rear of gridview"
        anchors.bottom: parent.bottom
        onClicked: {
                var object = grid.children[0] 
                object.parent = item // pop from grid
                object.parent = grid // push to grid
        }
    }
}

Upvotes: 2

timday
timday

Reputation: 24892

Following Mitch's answer, I came up with this:

  function reorder() {
    for (var i=0;i<children.length;i++) {
      var index=columns*children[i].Layout.row + children[i].Layout.column;
      var newIndex=(index+1)%(rows*columns);
      children[i].Layout.row=Math.floor(newIndex/columns)%rows;
      children[i].Layout.column=newIndex%columns;
    }
  }

There are a few caveats:

  • For this to work you have to have explicitly set a Layout.row and Layout.column on the contained items already. If you rely on the GridLayout's cell assignment, the attached properties remain zero. This was a bit surprising as I initially read the documentation as implying the GridLayout actually set these itself if you didn't.
  • You also need to have set rows and columns on the layout explicitly (for left to right layouts you can get away with just setting columns for layout wrapping purposes... but then the rows property remains -1 regardless of how many rows of child items are added, and the above code will fail).
  • It spits out some console warnings like QGridLayoutEngine::addItem: Cell (1, 1) already taken as the items are shuffled along. Seems to be harmess enough though.
  • Obviously the above is only really expecting to deal with a fully populated grid of single cell items.

Upvotes: 2

Mitch
Mitch

Reputation: 24416

You can use the row and column attached properties.

Upvotes: 4

Related Questions