QScollArea doesn't autoscroll when dragging inside it
-
I have some widgets inside a QScrollArea and I'm dragging between these widgets, the problem is that the QScrollArea doesn't scroll when I'm dragging inside it, so if I want to drag between a widget A to the widget B and the widget B is not visible on the viewport, the QScrollArea doesn't automatically scroll when the mouse moves to the edge of the viewport. Is there any way to implement such auto scroll into the QScrollArea ? I tried to subclass QScrollArea but it isn't receiving the drag events from the widgets inside it.
-
you don't necessarily need to subclass the scrollarea, but use an event filter on the QScrollArea's viewport:
@
enum { UndefinedDirection, UpDirection, DownDirection, LeftDirection, RightDirection } mDragScrollDirection;...
mDragScrollTimer.setSingleShot(false); //QTimer
mDragScrollTimer.setInterval(0);
connect(&mDragScrollTimer, SIGNAL(timeout()), this, SLOT(doDragScroll()));...
bool MyClass::eventFilter(QObject* watched, QEvent* event)
{
if( mScrollArea->viewport() )
{
switch( event->type() )
{
case QEvent::DragMove:
{
QDragMoveEvent* de = static_cast<QDragMoveEvent*>(event);//determine the position in the viewport and set mDragScrollDirection accordingly //if pos is near the edges start the mDragScrollTimer } break; case QEvent::Drop: case QEvent::DragLeave: { mDragScrollTimer.stop(); mDragScrollDirection = Undefined; } break; } } return false;
}
void MyClass::doDragScroll()
{
QScrollBar* vsb = mScrollArea->verticalScrollBar();
QScrollBar* hsb = mScrollArea->horizontalScrollBar();switch( mDragScrollDirection ) { case UpDirection: if( vsb->isVisible() ) vsb->setValue( vsb->value() - 5 ); break; case DownDirection: if( vsb->isVisible() ) vsb->setValue( vsb->value() + 5 ); break; case LeftDirection: if( hsb->isVisible() ) hsb->setValue( hsb->value() - 5 ); break; case RightDirection: if( hsb->isVisible() ) hsb->setValue( hsb->value() + 5 ); break; case UndefinedDirection: default: break; }
}
@
This should give you an idea how to achieve this... ;) -
I tried to use an event filter on the viewport but it isn't receiving the drag'n drop events from the widgets inside the QScrollArea.
-
i see...seems i shot too quickly into the dark.
There a multiple problems preventing this:you need to accept the dragenter event in the viewport to receive consecutive dragmove events
viewport is the parent for the content of the scrollarea, thus it's not guaranteed to receive all dragmove events at a specific point by event propagation
you could do the following (ugly) solution:
@
MyClass::MyClass()
{
qApp->installEventFilter(this);
this->viewport()->setAcceptDrops(true);
}MyClass::~MyClass()
{
qApp->removeEventFilter(this);
}bool MyClass::eventFilter(QObject* watched, QEvent* event)
{
if( watched->isWidgetType() )
{
QWidget* w = static_cast<QWidget*>(watched);if( ! this->isAncestorOf(w) ) return false; switch( event->type() ) { case QEvent::DragMove: { QDragMoveEvent* de = static_cast<QDragMoveEvent*>(event); //determine the position in the viewport and set mDragScrollDirection accordingly if( calculatedDirection == mDragScrollDirection ) return false; mDragScrollDirection = calculatedDirection; if( mDragScrollDirection == UndefinedDirection && mDragScrollTimer.isActive() ) mDragScrollTimer.stop(); else if( ! mDragScrollTimer.isActive() ) mDragScrollTimer.start(); } break; case QEvent::Drop: case QEvent::DragLeave: { mDragScrollTimer.stop(); mDragScrollDirection = Undefined; } break; } } return false;
}
@you may have to listen (eventFilter) the viewports dragEnter event and simply accept() it, to ensure you will receive dragMove events.
-
Thanks for the reply, the problem you pointed on the item 2 is very problematic on my case because a have a lots of widgets inside the viewport, and since they are handling the drag events, these events aren't propagated to the viewport event filter, so if I'm dragging from a widget to the viewport there is no problem because it receives the event, but when I'm dragging from the widget to outside of the QScrollArea the event isn't received, I'm starting to wonder that the only way to solve this is to use the notify() event from the QApplication, which captures all events from the application. I'm very surprised that this simple UI behavior used by lots of UIs is so hard to implement in Qt.
-
[quote author="tarantulae" date="1368197356"]Thanks for the reply, the problem you pointed on the item 2 is very problematic on my case because a have a lots of widgets inside the viewport, and since they are handling the drag events, these events aren't propagated to the viewport event filter[/quote]
thats why i installed the eventFilter on the QApplication instance (see the code).[quote author="tarantulae" date="1368197356"]so if I'm dragging from a widget to the viewport there is no problem because it receives the event, but when I'm dragging from the widget to outside of the QScrollArea the event isn't received[/quote]
why do you wanna receive an event outside the qscrollarea (and it's viewport) at all?! Its the only purpose to auto scroll right?!