RJVB
RJVB

Reputation: 776

Qt5: filtering out the horizontal component from wheel events

I am trying to filter out any horizontal wheel/touchpad scrolling in a widget (ProjectTreeView) that is based on a QTreeView. The idea is to let the vertical movement pass through, supporting horizontal scrolling only via the scrollbar.

I've tried to implement this via a ProjectTreeView::wheelEvent() event handler (below), but that function somehow has the opposite effect. Am I overlooking something (too) obvious or doing something wrong altogether?

void ProjectTreeView::wheelEvent(QWheelEvent *e)
{
    if ((e->pixelDelta().x() != 0 || e->angleDelta().x() !=0)
#ifdef Q_OS_MACOS
        // allow horizontal scrolling controlled by a physical mouse wheel
        && e->source() != Qt::MouseEventNotSynthesized
#endif
    ){
        QPoint pixelDelta(e->pixelDelta()), angleDelta(e->angleDelta());
        // disable horizontal wheel scrolling
        pixelDelta.setX(0);
        angleDelta.setX(0);
        // discard the original event
        e->accept();
        QWheelEvent filtered(e->posF(), e->globalPosF(), pixelDelta, angleDelta,
            e->delta(), e->orientation(), e->buttons(),
            e->modifiers(), e->phase(), e->source(), e->inverted());
        QCoreApplication::sendEvent(this, &filtered);
    } else {
        QTreeView::wheelEvent(e);
    }
}

When I try to filter via ProjectTreeView::event(QEvent*) I observe that 1) I hardly receive any wheel events at all, at least not when I'm trying to scroll (some come in when I release the touchpad) 2) the events that do come in don't have the required delta information (both components are 0).

This reminds me of remarks I've seen about the Qt4 implementation about how the events are actually treated by a different widget.

thanks!

Upvotes: 0

Views: 1496

Answers (2)

RJVB
RJVB

Reputation: 776

In the end, this works:

void ProjectTreeView::wheelEvent(QWheelEvent *e)
{
    if ((e->pixelDelta().x() !=0 || e->angleDelta().x()!= 0
        || e->orientation() == Qt::Orientation::Horizontal)
#ifdef Q_OS_MACOS
        // Cocoa: allow horizontal scrolling controlled by a physical mouse wheel
        && (!isCocoa || e->source() != Qt::MouseEventNotSynthesized)
#endif
    ){
        QPoint pixelDelta(e->pixelDelta()), angleDelta(e->angleDelta());
        pixelDelta.setX(0);
        angleDelta.setX(0);
        // discard the original event
        e->ignore();
        if (!pixelDelta.isNull() || !angleDelta.isNull()) {
            QWheelEvent filtered(e->posF(), e->globalPosF(), pixelDelta, angleDelta,
                e->delta(), Qt::Orientation::Vertical, e->buttons(),
                e->modifiers(), e->phase(), Qt::MouseEventSynthesizedByApplication, e->inverted());
            QCoreApplication::sendEvent(this, &filtered);
        }
    }
    QTreeView::wheelEvent(e);
}

The key was realising that some events only had the orientation set to horizontal but no non-zero delta information.

The isCocoa member is to detect when I'm running my tweaked XCB QPA plugin (= using Qt under XQuartz).

Upvotes: 1

mohabouje
mohabouje

Reputation: 4050

You can do it easily by installing and event filter function.

bool eventFilter( QObject * o, QEvent * e ) {
   if (e->type() == QEvent::Wheel 
       && qobject_cast<ProjectTreeView*>( o ) ) {
        // Then do what you want, per example: ignore it.
        e->ignore();
        return true;
   }  
   return QWidget::eventFilter( o, e );
}

If you want the user to use the scroll by clicking in the scrollbar then you can modify the focus polizy of the widget. It should fix your problem:

setFocusPolicy(Qt::ClickFocus);

Upvotes: 2

Related Questions