QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
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#include "moc_qgsscrollarea.cpp"
18
19#include <QEvent>
20#include <QMouseEvent>
21#include <QScrollBar>
22#include <QAbstractItemView>
23
24// milliseconds to swallow child wheel events for after a scroll occurs
25#define TIMEOUT 1000
26
28 : QScrollArea( parent )
29 , mFilter( new ScrollAreaFilter( this, viewport() ) )
30{
31 viewport()->installEventFilter( mFilter );
32 setMouseTracking( true );
33}
34
35void QgsScrollArea::wheelEvent( QWheelEvent *e )
36{
37 //scroll occurred, reset timer
39 QScrollArea::wheelEvent( e );
40}
41
42void QgsScrollArea::resizeEvent( QResizeEvent *event )
43{
44 if ( mVerticalOnly && widget() )
45 widget()->setFixedWidth( event->size().width() );
46 QScrollArea::resizeEvent( event );
47}
48
50{
51 mTimer.setSingleShot( true );
52 mTimer.start( TIMEOUT );
53}
54
56{
57 return mTimer.isActive();
58}
59
61{
62 mTimer.stop();
63}
64
65void QgsScrollArea::setVerticalOnly( bool verticalOnly )
66{
67 mVerticalOnly = verticalOnly;
68 if ( mVerticalOnly )
69 setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
70
71 if ( mVerticalOnly && widget() )
72 widget()->setFixedWidth( size().width() );
73}
74
76
77ScrollAreaFilter::ScrollAreaFilter( QgsScrollArea *parent, QWidget *viewPort )
78 : QObject( parent )
79 , mScrollAreaWidget( parent )
80 , mViewPort( viewPort )
81{
82 QFontMetrics fm( parent->font() );
83 mMoveDistanceThreshold = fm.horizontalAdvance( 'X' );
84}
85
86bool ScrollAreaFilter::eventFilter( QObject *obj, QEvent *event )
87{
88 switch ( event->type() )
89 {
90 case QEvent::ChildAdded:
91 {
92 // need to install filter on all child widgets as well
93 QChildEvent *ce = static_cast<QChildEvent *>( event );
94 addChild( ce->child() );
95 break;
96 }
97
98 case QEvent::ChildRemoved:
99 {
100 QChildEvent *ce = static_cast<QChildEvent *>( event );
101 removeChild( ce->child() );
102 break;
103 }
104
105 case QEvent::MouseMove:
106 {
107 if ( obj == mViewPort )
108 {
109 const QPoint mouseDelta = QCursor::pos() - mPreviousViewportCursorPos;
110 if ( mouseDelta.manhattanLength() > mMoveDistanceThreshold )
111 {
112 // release time based child widget constraint -- user moved the mouse over the viewport (and not just an accidental "wiggle")
113 // so we no longer are in the 'possible unwanted mouse wheel event going to child widget mid-scroll' state
114 mScrollAreaWidget->resetHasScrolled();
115 }
116 mPreviousViewportCursorPos = QCursor::pos();
117 }
118 break;
119 }
120
121 case QEvent::Wheel:
122 {
123 if ( obj == mViewPort )
124 {
125 // scrolling scroll area - kick off the timer to block wheel events in children
126 mScrollAreaWidget->scrollOccurred();
127 }
128 else
129 {
130 if ( mScrollAreaWidget->hasScrolled() )
131 {
132 // swallow wheel events for children shortly after scroll occurs
133 return true;
134 }
135 }
136 break;
137 }
138
139 default:
140 break;
141 }
142 return QObject::eventFilter( obj, event );
143}
144
145void ScrollAreaFilter::addChild( QObject *child )
146{
147 if ( child && child->isWidgetType() )
148 {
149 if ( qobject_cast< QScrollArea * >( child ) || qobject_cast< QAbstractItemView * >( child ) )
150 return;
151
152 child->installEventFilter( this );
153 if ( QWidget *w = qobject_cast< QWidget * >( child ) )
154 w->setMouseTracking( true );
155
156 // also install filter on existing children
157 const auto constChildren = child->children();
158 for ( QObject *c : constChildren )
159 {
160 addChild( c );
161 }
162 }
163}
164
165void ScrollAreaFilter::removeChild( QObject *child )
166{
167 if ( child && child->isWidgetType() )
168 {
169 if ( qobject_cast< QScrollArea * >( child ) || qobject_cast< QAbstractItemView * >( child ) )
170 return;
171
172 child->removeEventFilter( this );
173
174 // also remove filter on existing children
175 const auto constChildren = child->children();
176 for ( QObject *c : constChildren )
177 {
178 removeChild( c );
179 }
180 }
181}
182
A QScrollArea subclass with improved scrolling behavior.
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