3
votes

I got quite an experience with mixing QML and C++, however recently run into trouble: I have GridLayout item inside QML code. Initially I populate it with QQuickItems (Rectangles containing icons in fact) from C++. This works and renders properly, including sizing, wrap on QML defined columns: property etc.

Now I need based on user interaction (mouse clicks on Rectangles) insert new rectangles in exact positions (in fact just before the clicked Rectangle). I have no problem with MouseEvent propagation, I am even able to pass any ammount of parameters (be it values or pointers to items) into my method to process. I just can not found "c++ wording" to make rectangles really rearrange.

I use the following code:

QQuickItem *icon = place(parent);
icon->stackBefore(insertBefore);
parent->update();   //(1)
GUI::programWindow->update(); //(2)
qDebug() << "\tCHILDS:" << parent->childItems(); //(3)

parent is pointer to GridLayout item passed from QML code (verified that it points where expected). place(parent) is my method, which loads proper icon based on this object, generates surrounding rectangle and returns pointer to it (i.e. *icon is Rectangle in fact). insertBefore is pointer to other rectangle inside the same GridLayout (passed through custom signal from my onClick handler). Double checked that it is child of proper GridLayout item.

Now I would expect, that Rectangle, that firstly got generated as the last of GridLayout children, gets "lighting fast" teleported before intended other Rect and GridLayout (knowing, that visual children order changed) will redraw new arrangement to screen. In fact I face to the newly created Rect visible as the last of GridLayout items.

Funny thing is, that output on line (3) shows proper, i.e. reordered order of children. So order of output from the parent->childItems() differs from what can be seen on screen. The lines commented (1) and(2) were my desperate attempts to make GridLayout to redraw, but no visible change appeared.

Does anybody have some experience with reordering GridLayout children? Is there some other, more proper, method to call instead of stackBefore()? Or am I missing some stupid, immediately visible thing :) ?

Thanks in advance for any clues.

1

1 Answers

0
votes

You're triggering QQuickItem::update(), but for children reordering just calling of QQuickItem::polish() should be enough. According to Scene Graph and Rendering doc, QQuickItem::updatePolish() is scheduled by call of QQuickItem::polish() does final touch-up of items before they are rendered. But polish event should be scheduled for the top most QQuickItem in your scene and not just for parent item.

Additionally, when using QQuickItem::stackBefore or QQuickItem::stackAfter you need to aware that both reordering items should be children of the same parent QQuickItem instance.

If you're going to insert QQuickItem into the end of children list, since it's default position for new added items, you can use QQuickItem::setParentItem.

To summaries above, final implementation might look as following:

bool insertChild(QQuickItem* item, int position, QQuickItem* parent, QQuickItem* topmostItem) {
    if (!item || !parent || !topmostItem)
        return false;
    QList<QQuickItem*> children = parent->childItems();
    if (children.size() && children.size() > position) {
        QQuickItem* nextItem = children.at(position);
        item->setParentItem(parent);
        item->stackBefore(nextItem);
    } else {
        item->setParentItem(parent);
    }
    topmostItem->polish();
    return true;
}

As alternative to QQuickItem::stackBefore and QQuickItem::stackAfter it's possible to use QQuickItem::setParentItem solely, but complexity is linear in this case:

...
QList<QQuickItem*> children = parent->childItems();
// Remove all children from parent from insert position and to the end
for (int index = position; index < children.size(); ++index) {
    children[index]->setParentItem(nullptr);
}
// Push back new QQuickItem into parent
item->setParentItem(parent);
// Push back into parent all previously temporary removed children
for (int index = position; index < children.size(); ++index) {
    children[index]->setParentItem(parent);
}
topmostItem->polish();
...