c++qtqscrollareaqspinbox

QSpinBox inside a QScrollArea: How to prevent Spin Box from stealing focus when scrolling?


I have a control with several QSpinBox objects inside a QScrollArea. All works fine when scrolling in the scroll area unless the mouse happens to be over one of the QSpinBoxes. Then the QSpinBox steals focus and the wheel events manipulate the spin box value rather than scrolling the scroll area.

I don't want to completely disable using the mouse wheel to manipulate the QSpinBox, but I only want it to happen if the user explicitly clicks or tabs into the QSpinBox. Is there a way to prevent the QSpinBox from stealing the focus from the QScrollArea?

As said in a comment to an answer below, setting Qt::StrongFocus does prevent the focus rect from appearing on the control, however it still steals the mouse wheel and adjusts the value in the spin box and stops the QScrollArea from scrolling. Same with Qt::ClickFocus.


Solution

  • Try removing Qt::WheelFocus from the spinbox' QWidget::focusPolicy:

    spin->setFocusPolicy( Qt::StrongFocus );
    

    In addition, you need to prevent the wheel event from reaching the spinboxes. You can do that with an event filter:

    explicit Widget( QWidget * parent=0 )
        : QWidget( parent )
    {
        // setup ...
        Q_FOREACH( QSpinBox * sp, findChildren<QSpinBox*>() ) {
            sp->installEventFilter( this );
            sp->setFocusPolicy( Qt::StrongFocus );
        }
    
    }
    
    /* reimp */ bool eventFilter( QObject * o, QEvent * e ) {
        if ( e->type() == QEvent::Wheel &&
             qobject_cast<QAbstractSpinBox*>( o ) )
        {
            e->ignore();
            return true;
        }
        return QWidget::eventFilter( o, e );
    }
    

    edit from Grant Limberg for completeness as this got me 90% of the way there:

    In addition to what mmutz said above, I needed to do a few other things. I had to create a subclass of QSpinBox and implement focusInEvent(QFocusEvent*) and focusOutEvent(QFocusEvent*). Basically, on a focusInEvent, I change the Focus Policy to Qt::WheelFocus and on the focusOutEvent I change it back to Qt::StrongFocus.

    void MySpinBox::focusInEvent(QFocusEvent*)
    {
         setFocusPolicy(Qt::WheelFocus);
    }
    
    void MySpinBox::focusOutEvent(QFocusEvent*)
    {
         setFocusPolicy(Qt::StrongFocus);
    }
    

    Additionally, the eventFilter method implementation in the event filter class changes its behavior based on the current focus policy of the spinbox subclass:

    bool eventFilter(QObject *o, QEvent *e)
    {
        if(e->type() == QEvent::Wheel &&
           qobject_cast<QAbstractSpinBox*>(o))
        {
            if(qobject_cast<QAbstractSpinBox*>(o)->focusPolicy() == Qt::WheelFocus)
            {
                e->accept();
                return false;
            }
            else
            {
                e->ignore();
                return true;
            }
        }
        return QWidget::eventFilter(o, e);
    }