I'm working on a Qt/Qml application.
I'm currently trying to emulate drag behavior through Qt on drag QML elements (Listview, Flickable...) for tests purpose.
I have a specific issue that I wanted to solve with the most generic solution possible, my component is a non-interactive ListView, nested by an interactive ListView, nested itself with a MouseArea :
ListView {
anchors.fill: parent
interactive: false
ListView {
anchors.fill: parent
MouseArea {
...
}
}
}
So. My idea was : take a QML object, local coordinates (x, y) where the movement starts, find it's most nested child at position and apply the movement (dx, dy) to this child. If I'm understanding correctly how QT/QML works, it should send the event to parent if not used by the child, and Flickable components should be able to detect drags and catch them.
void xx::touchAndDrag(QObject *object, const int x, const int y, const int dx, const int dy)
{
timer = new QTimer(this);
timerIteration = 0;
deltaPoint = new QPointF(dx, dy);
parentItem = qobject_cast<QQuickItem *>(object);
item = parentItem;
startPoint = QPointF(x, y);
QPointF tempPoint = startPoint;
for( ;; ) {
//Find the most nested child at coordinate
QQuickItem* child = item->childAt(tempPoint.x(), tempPoint.y());
if(child) {
item = child;
tempPoint = child->mapFromItem(parentItem, tempPoint);
qDebug() << "child found " << item;
} else {
break;
}
}
timer->setInterval(movementDuration / nbIteration);
qDebug() << "interval " << timer->interval();
timer->setSingleShot(false);
connect(timer, SIGNAL(timeout()), this, SLOT(mouseMove()));
// Send press event at starting point
QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, item->mapFromItem(parentItem, startPoint), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
qDebug() << "press " << e;
qApp->postEvent(item, e);
timer->start();
}
void xx::mouseMove()
{
timerIteration++;
QMouseEvent *e;
if(timerIteration < nbIteration) {
int x = startPoint.x() + deltaPoint->x() * timerIteration / nbIteration;
int y = startPoint.y() + deltaPoint->y() * timerIteration / nbIteration;
QPointF point = QPointF(x, y);
// Send moveEvent
e = new QMouseEvent(QEvent::MouseMove, item->mapFromItem(parentItem, point), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
qDebug() << "move! " << e ;
}else {
//End reached, send end event
QPointF point = QPointF(startPoint.x() + deltaPoint->x(), startPoint.y() + deltaPoint->y());
e = new QMouseEvent(QEvent::MouseButtonRelease, item->mapFromItem(parentItem, point), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
timer->stop();
qDebug() << "end! " << e ;
}
qApp->postEvent(item, e);
}
So... It is not working. What happens? From my tests:
-If I remove the nested child part, and give directly the interactive (to drag) QML Component (here the nested ListView), the result is good. But this is not a good solution for me, since I would have exactly to know which component should react. However, this seems to override the "interactive: false" of a component, which is a bad idea when the purpose is... to test the component.
-The mouse area receives all events. The mouseX property is updated. This is an issue. With non simulated event, MouseArea should receive the press event, some move event, then events should captured by the ListView / Flickable. Even worst, even if positions are way different (400px) between mousePress and mouseRelease events, Qt detects a MouseClicked event and triggers it on QML side...
So, not sure where to go from there. I could do some crappy child detection (checks the type of the nested child, and only accepts the good one), but I'm not very happy with it. Any idea?