QGIS API Documentation 3.27.0-Master (a46f227e17)
qgsscrollarea.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsscrollarea.cpp
3 -----------------
4 begin : March 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsscrollarea.h"
17
18#include <QEvent>
19#include <QMouseEvent>
20#include <QScrollBar>
21#include <QAbstractItemView>
22
23// milliseconds to swallow child wheel events for after a scroll occurs
24#define TIMEOUT 1000
25
27 : QScrollArea( parent )
28 , mFilter( new ScrollAreaFilter( this, viewport() ) )
29{
30 viewport()->installEventFilter( mFilter );
31 setMouseTracking( true );
32}
33
34void QgsScrollArea::wheelEvent( QWheelEvent *e )
35{
36 //scroll occurred, reset timer
38 QScrollArea::wheelEvent( e );
39}
40
41void QgsScrollArea::resizeEvent( QResizeEvent *event )
42{
43 if ( mVerticalOnly && widget() )
44 widget()->setFixedWidth( event->size().width() );
45 QScrollArea::resizeEvent( event );
46}
47
49{
50 mTimer.setSingleShot( true );
51 mTimer.start( TIMEOUT );
52}
53
55{
56 return mTimer.isActive();
57}
58
60{
61 mTimer.stop();
62}
63
64void QgsScrollArea::setVerticalOnly( bool verticalOnly )
65{
66 mVerticalOnly = verticalOnly;
67 if ( mVerticalOnly )
68 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
69
70 if ( mVerticalOnly && widget() )
71 widget()->setFixedWidth( size().width() );
72}
73
75
76ScrollAreaFilter::ScrollAreaFilter( QgsScrollArea *parent, QWidget *viewPort )
77 : QObject( parent )
78 , mScrollAreaWidget( parent )
79 , mViewPort( viewPort )
80{
81 QFontMetrics fm( parent->font() );
82 mMoveDistanceThreshold = fm.horizontalAdvance( 'X' );
83}
84
85bool ScrollAreaFilter::eventFilter( QObject *obj, QEvent *event )
86{
87 switch ( event->type() )
88 {
89 case QEvent::ChildAdded:
90 {
91 // need to install filter on all child widgets as well
92 QChildEvent *ce = static_cast<QChildEvent *>( event );
93 addChild( ce->child() );
94 break;
95 }
96
97 case QEvent::ChildRemoved:
98 {
99 QChildEvent *ce = static_cast<QChildEvent *>( event );
100 removeChild( ce->child() );
101 break;
102 }
103
104 case QEvent::MouseMove:
105 {
106 if ( obj == mViewPort )
107 {
108 const QPoint mouseDelta = QCursor::pos() - mPreviousViewportCursorPos;
109 if ( mouseDelta.manhattanLength() > mMoveDistanceThreshold )
110 {
111 // release time based child widget constraint -- user moved the mouse over the viewport (and not just an accidental "wiggle")
112 // so we no longer are in the 'possible unwanted mouse wheel event going to child widget mid-scroll' state
113 mScrollAreaWidget->resetHasScrolled();
114 }
115 mPreviousViewportCursorPos = QCursor::pos();
116 }
117 break;
118 }
119
120 case QEvent::Wheel:
121 {
122 if ( obj == mViewPort )
123 {
124 // scrolling scroll area - kick off the timer to block wheel events in children
125 mScrollAreaWidget->scrollOccurred();
126 }
127 else
128 {
129 if ( mScrollAreaWidget->hasScrolled() )
130 {
131 // swallow wheel events for children shortly after scroll occurs
132 return true;
133 }
134 }
135 break;
136 }
137
138 default:
139 break;
140 }
141 return QObject::eventFilter( obj, event );
142}
143
144void ScrollAreaFilter::addChild( QObject *child )
145{
146 if ( child && child->isWidgetType() )
147 {
148 if ( qobject_cast< QScrollArea * >( child ) || qobject_cast< QAbstractItemView * >( child ) )
149 return;
150
151 child->installEventFilter( this );
152 if ( QWidget *w = qobject_cast< QWidget * >( child ) )
153 w->setMouseTracking( true );
154
155 // also install filter on existing children
156 const auto constChildren = child->children();
157 for ( QObject *c : constChildren )
158 {
159 addChild( c );
160 }
161 }
162}
163
164void ScrollAreaFilter::removeChild( QObject *child )
165{
166 if ( child && child->isWidgetType() )
167 {
168 if ( qobject_cast< QScrollArea * >( child ) || qobject_cast< QAbstractItemView * >( child ) )
169 return;
170
171 child->removeEventFilter( this );
172
173 // also remove filter on existing children
174 const auto constChildren = child->children();
175 for ( QObject *c : constChildren )
176 {
177 removeChild( c );
178 }
179 }
180}
181
A QScrollArea subclass with improved scrolling behavior.
Definition: qgsscrollarea.h:42
void setVerticalOnly(bool verticalOnly)
Sets whether the scroll area only applies vertical.
QgsScrollArea(QWidget *parent=nullptr)
Constructor for QgsScrollArea.
void resetHasScrolled()
Resets the hasScrolled() flag.
bool hasScrolled() const
Returns true if a scroll recently occurred within the QScrollArea or its child viewport()
void wheelEvent(QWheelEvent *event) override
void scrollOccurred()
Should be called when a scroll occurs on with the QScrollArea itself or its child viewport().
void resizeEvent(QResizeEvent *event) override
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define TIMEOUT