1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qabstractscrollarea.h"
41
42 #if QT_CONFIG(scrollarea)
43
44 #include "qscrollbar.h"
45 #include "qapplication.h"
46 #include "qstyle.h"
47 #include "qstyleoption.h"
48 #include "qevent.h"
49 #include "qdebug.h"
50 #include "qboxlayout.h"
51 #include "qpainter.h"
52 #include "qmargins.h"
53 #if QT_CONFIG(itemviews)
54 #include "qheaderview.h"
55 #endif
56
57 #include <QDebug>
58
59 #include "qabstractscrollarea_p.h"
60 #include "qscrollbar_p.h"
61 #include <qwidget.h>
62
63 #include <private/qapplication_p.h>
64
65 #ifdef Q_OS_WIN
66 # include <qt_windows.h>
67 #endif
68
69 QT_BEGIN_NAMESPACE
70
71 /*!
72 \class QAbstractScrollArea
73 \brief The QAbstractScrollArea widget provides a scrolling area with
74 on-demand scroll bars.
75
76 \ingroup abstractwidgets
77 \inmodule QtWidgets
78
79 QAbstractScrollArea is a low-level abstraction of a scrolling
80 area. The area provides a central widget called the viewport, in
81 which the contents of the area is to be scrolled (i.e, the
82 visible parts of the contents are rendered in the viewport).
83
84 Next to the viewport is a vertical scroll bar, and below is a
85 horizontal scroll bar. When all of the area contents fits in the
86 viewport, each scroll bar can be either visible or hidden
87 depending on the scroll bar's Qt::ScrollBarPolicy. When a scroll
88 bar is hidden, the viewport expands in order to cover all
89 available space. When a scroll bar becomes visible again, the
90 viewport shrinks in order to make room for the scroll bar.
91
92 It is possible to reserve a margin area around the viewport, see
93 setViewportMargins(). The feature is mostly used to place a
94 QHeaderView widget above or beside the scrolling area. Subclasses
95 of QAbstractScrollArea should implement margins.
96
97 When inheriting QAbstractScrollArea, you need to do the
98 following:
99
100 \list
101 \li Control the scroll bars by setting their
102 range, value, page step, and tracking their
103 movements.
104 \li Draw the contents of the area in the viewport according
105 to the values of the scroll bars.
106 \li Handle events received by the viewport in
107 viewportEvent() - notably resize events.
108 \li Use \c{viewport->update()} to update the contents of the
109 viewport instead of \l{QWidget::update()}{update()}
110 as all painting operations take place on the viewport.
111 \endlist
112
113 With a scroll bar policy of Qt::ScrollBarAsNeeded (the default),
114 QAbstractScrollArea shows scroll bars when they provide a non-zero
115 scrolling range, and hides them otherwise.
116
117 The scroll bars and viewport should be updated whenever the viewport
118 receives a resize event or the size of the contents changes.
119 The viewport also needs to be updated when the scroll bars
120 values change. The initial values of the scroll bars are often
121 set when the area receives new contents.
122
123 We give a simple example, in which we have implemented a scroll area
124 that can scroll any QWidget. We make the widget a child of the
125 viewport; this way, we do not have to calculate which part of
126 the widget to draw but can simply move the widget with
127 QWidget::move(). When the area contents or the viewport size
128 changes, we do the following:
129
130 \snippet myscrollarea/myscrollarea.cpp 1
131
132 When the scroll bars change value, we need to update the widget
133 position, i.e., find the part of the widget that is to be drawn in
134 the viewport:
135
136 \snippet myscrollarea/myscrollarea.cpp 0
137
138 In order to track scroll bar movements, reimplement the virtual
139 function scrollContentsBy(). In order to fine-tune scrolling
140 behavior, connect to a scroll bar's
141 QAbstractSlider::actionTriggered() signal and adjust the \l
142 QAbstractSlider::sliderPosition as you wish.
143
144 For convenience, QAbstractScrollArea makes all viewport events
145 available in the virtual viewportEvent() handler. QWidget's
146 specialized handlers are remapped to viewport events in the cases
147 where this makes sense. The remapped specialized handlers are:
148 paintEvent(), mousePressEvent(), mouseReleaseEvent(),
149 mouseDoubleClickEvent(), mouseMoveEvent(), wheelEvent(),
150 dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent(),
151 contextMenuEvent(), and resizeEvent().
152
153 QScrollArea, which inherits QAbstractScrollArea, provides smooth
154 scrolling for any QWidget (i.e., the widget is scrolled pixel by
155 pixel). You only need to subclass QAbstractScrollArea if you need
156 more specialized behavior. This is, for instance, true if the
157 entire contents of the area is not suitable for being drawn on a
158 QWidget or if you do not want smooth scrolling.
159
160 \sa QScrollArea
161 */
162
QAbstractScrollAreaPrivate()163 QAbstractScrollAreaPrivate::QAbstractScrollAreaPrivate()
164 :hbar(nullptr), vbar(nullptr), vbarpolicy(Qt::ScrollBarAsNeeded), hbarpolicy(Qt::ScrollBarAsNeeded),
165 shownOnce(false), inResize(false), sizeAdjustPolicy(QAbstractScrollArea::AdjustIgnored),
166 viewport(nullptr), cornerWidget(nullptr), left(0), top(0), right(0), bottom(0),
167 xoffset(0), yoffset(0), viewportFilter(nullptr)
168 {
169 }
170
~QAbstractScrollAreaPrivate()171 QAbstractScrollAreaPrivate::~QAbstractScrollAreaPrivate()
172 {
173 }
174
QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation,QWidget * parent)175 QAbstractScrollAreaScrollBarContainer::QAbstractScrollAreaScrollBarContainer(Qt::Orientation orientation, QWidget *parent)
176 :QWidget(parent), scrollBar(new QScrollBar(orientation, this)),
177 layout(new QBoxLayout(orientation == Qt::Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom)),
178 orientation(orientation)
179 {
180 setLayout(layout);
181 layout->setContentsMargins(QMargins());
182 layout->setSpacing(0);
183 layout->addWidget(scrollBar);
184 layout->setSizeConstraint(QLayout::SetMaximumSize);
185 }
186
187 /*! \internal
188 Adds a widget to the scroll bar container.
189 */
addWidget(QWidget * widget,LogicalPosition position)190 void QAbstractScrollAreaScrollBarContainer::addWidget(QWidget *widget, LogicalPosition position)
191 {
192 QSizePolicy policy = widget->sizePolicy();
193 if (orientation == Qt::Vertical)
194 policy.setHorizontalPolicy(QSizePolicy::Ignored);
195 else
196 policy.setVerticalPolicy(QSizePolicy::Ignored);
197 widget->setSizePolicy(policy);
198 widget->setParent(this);
199
200 const int insertIndex = (position & LogicalLeft) ? 0 : scrollBarLayoutIndex() + 1;
201 layout->insertWidget(insertIndex, widget);
202 }
203
204 /*! \internal
205 Retuns a list of scroll bar widgets for the given position. The scroll bar
206 itself is not returned.
207 */
widgets(LogicalPosition position)208 QWidgetList QAbstractScrollAreaScrollBarContainer::widgets(LogicalPosition position)
209 {
210 QWidgetList list;
211 const int scrollBarIndex = scrollBarLayoutIndex();
212 if (position == LogicalLeft) {
213 list.reserve(scrollBarIndex);
214 for (int i = 0; i < scrollBarIndex; ++i)
215 list.append(layout->itemAt(i)->widget());
216 } else if (position == LogicalRight) {
217 const int layoutItemCount = layout->count();
218 list.reserve(layoutItemCount - (scrollBarIndex + 1));
219 for (int i = scrollBarIndex + 1; i < layoutItemCount; ++i)
220 list.append(layout->itemAt(i)->widget());
221 }
222 return list;
223 }
224
225 /*! \internal
226 Returns the layout index for the scroll bar. This needs to be
227 recalculated by a linear search for each use, since items in
228 the layout can be removed at any time (i.e. when a widget is
229 deleted or re-parented).
230 */
scrollBarLayoutIndex() const231 int QAbstractScrollAreaScrollBarContainer::scrollBarLayoutIndex() const
232 {
233 const int layoutItemCount = layout->count();
234 for (int i = 0; i < layoutItemCount; ++i) {
235 if (qobject_cast<QScrollBar *>(layout->itemAt(i)->widget()))
236 return i;
237 }
238 return -1;
239 }
240
241 /*! \internal
242 */
replaceScrollBar(QScrollBar * scrollBar,Qt::Orientation orientation)243 void QAbstractScrollAreaPrivate::replaceScrollBar(QScrollBar *scrollBar,
244 Qt::Orientation orientation)
245 {
246 Q_Q(QAbstractScrollArea);
247
248 QAbstractScrollAreaScrollBarContainer *container = scrollBarContainers[orientation];
249 bool horizontal = (orientation == Qt::Horizontal);
250 QScrollBar *oldBar = horizontal ? hbar : vbar;
251 if (horizontal)
252 hbar = scrollBar;
253 else
254 vbar = scrollBar;
255 scrollBar->setParent(container);
256 container->scrollBar = scrollBar;
257 container->layout->removeWidget(oldBar);
258 container->layout->insertWidget(0, scrollBar);
259 scrollBar->setVisible(oldBar->isVisibleTo(container));
260 scrollBar->setInvertedAppearance(oldBar->invertedAppearance());
261 scrollBar->setInvertedControls(oldBar->invertedControls());
262 scrollBar->setRange(oldBar->minimum(), oldBar->maximum());
263 scrollBar->setOrientation(oldBar->orientation());
264 scrollBar->setPageStep(oldBar->pageStep());
265 scrollBar->setSingleStep(oldBar->singleStep());
266 scrollBar->d_func()->viewMayChangeSingleStep = oldBar->d_func()->viewMayChangeSingleStep;
267 scrollBar->setSliderDown(oldBar->isSliderDown());
268 scrollBar->setSliderPosition(oldBar->sliderPosition());
269 scrollBar->setTracking(oldBar->hasTracking());
270 scrollBar->setValue(oldBar->value());
271 scrollBar->installEventFilter(q);
272 oldBar->removeEventFilter(q);
273 delete oldBar;
274
275 QObject::connect(scrollBar, SIGNAL(valueChanged(int)),
276 q, horizontal ? SLOT(_q_hslide(int)) : SLOT(_q_vslide(int)));
277 QObject::connect(scrollBar, SIGNAL(rangeChanged(int,int)),
278 q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
279 }
280
init()281 void QAbstractScrollAreaPrivate::init()
282 {
283 Q_Q(QAbstractScrollArea);
284 viewport = new QWidget(q);
285 viewport->setObjectName(QLatin1String("qt_scrollarea_viewport"));
286 viewport->setBackgroundRole(QPalette::Base);
287 viewport->setAutoFillBackground(true);
288 scrollBarContainers[Qt::Horizontal] = new QAbstractScrollAreaScrollBarContainer(Qt::Horizontal, q);
289 scrollBarContainers[Qt::Horizontal]->setObjectName(QLatin1String("qt_scrollarea_hcontainer"));
290 hbar = scrollBarContainers[Qt::Horizontal]->scrollBar;
291 hbar->setRange(0,0);
292 scrollBarContainers[Qt::Horizontal]->setVisible(false);
293 hbar->installEventFilter(q);
294 QObject::connect(hbar, SIGNAL(valueChanged(int)), q, SLOT(_q_hslide(int)));
295 QObject::connect(hbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
296 scrollBarContainers[Qt::Vertical] = new QAbstractScrollAreaScrollBarContainer(Qt::Vertical, q);
297 scrollBarContainers[Qt::Vertical]->setObjectName(QLatin1String("qt_scrollarea_vcontainer"));
298 vbar = scrollBarContainers[Qt::Vertical]->scrollBar;
299 vbar->setRange(0,0);
300 scrollBarContainers[Qt::Vertical]->setVisible(false);
301 vbar->installEventFilter(q);
302 QObject::connect(vbar, SIGNAL(valueChanged(int)), q, SLOT(_q_vslide(int)));
303 QObject::connect(vbar, SIGNAL(rangeChanged(int,int)), q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
304 viewportFilter.reset(new QAbstractScrollAreaFilter(this));
305 viewport->installEventFilter(viewportFilter.data());
306 viewport->setFocusProxy(q);
307 q->setFocusPolicy(Qt::StrongFocus);
308 q->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
309 q->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
310 layoutChildren();
311 #ifndef Q_OS_MACOS
312 # ifndef QT_NO_GESTURES
313 viewport->grabGesture(Qt::PanGesture);
314 # endif
315 #endif
316 }
317
layoutChildren()318 void QAbstractScrollAreaPrivate::layoutChildren()
319 {
320 bool needH = false;
321 bool needV = false;
322 layoutChildren_helper(&needH, &needV);
323 // Call a second time if one scrollbar was needed and not the other to
324 // check if it needs to readjust accordingly
325 if (needH != needV)
326 layoutChildren_helper(&needH, &needV);
327 }
328
layoutChildren_helper(bool * needHorizontalScrollbar,bool * needVerticalScrollbar)329 void QAbstractScrollAreaPrivate::layoutChildren_helper(bool *needHorizontalScrollbar, bool *needVerticalScrollbar)
330 {
331 Q_Q(QAbstractScrollArea);
332 bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, hbar);
333 bool needh = *needHorizontalScrollbar || ((hbarpolicy != Qt::ScrollBarAlwaysOff) && ((hbarpolicy == Qt::ScrollBarAlwaysOn && !htransient)
334 || ((hbarpolicy == Qt::ScrollBarAsNeeded || htransient)
335 && hbar->minimum() < hbar->maximum() && !hbar->sizeHint().isEmpty())));
336
337 bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, vbar);
338 bool needv = *needVerticalScrollbar || ((vbarpolicy != Qt::ScrollBarAlwaysOff) && ((vbarpolicy == Qt::ScrollBarAlwaysOn && !vtransient)
339 || ((vbarpolicy == Qt::ScrollBarAsNeeded || vtransient)
340 && vbar->minimum() < vbar->maximum() && !vbar->sizeHint().isEmpty())));
341
342 QStyleOption opt(0);
343 opt.init(q);
344
345 const int hscrollOverlap = hbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, hbar);
346 const int vscrollOverlap = vbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, vbar);
347
348 const int hsbExt = hbar->sizeHint().height();
349 const int vsbExt = vbar->sizeHint().width();
350 const QPoint extPoint(vsbExt, hsbExt);
351 const QSize extSize(vsbExt, hsbExt);
352
353 const QRect widgetRect = q->rect();
354
355 const bool hasCornerWidget = (cornerWidget != nullptr);
356
357 QPoint cornerOffset((needv && vscrollOverlap == 0) ? vsbExt : 0, (needh && hscrollOverlap == 0) ? hsbExt : 0);
358 QRect controlsRect;
359 QRect viewportRect;
360
361 // In FrameOnlyAroundContents mode the frame is drawn between the controls and
362 // the viewport, else the frame rect is equal to the widget rect.
363 if ((frameStyle != QFrame::NoFrame) &&
364 q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, q)) {
365 controlsRect = widgetRect;
366 const int spacing = q->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, q);
367 const QPoint cornerExtra(needv ? spacing + vscrollOverlap : 0, needh ? spacing + hscrollOverlap : 0);
368 QRect frameRect = widgetRect;
369 frameRect.adjust(0, 0, -cornerOffset.x() - cornerExtra.x(), -cornerOffset.y() - cornerExtra.y());
370 q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, frameRect));
371 // The frame rect needs to be in logical coords, however we need to flip
372 // the contentsRect back before passing it on to the viewportRect
373 // since the viewportRect has its logical coords calculated later.
374 viewportRect = QStyle::visualRect(opt.direction, opt.rect, q->contentsRect());
375 } else {
376 q->setFrameRect(QStyle::visualRect(opt.direction, opt.rect, widgetRect));
377 controlsRect = q->contentsRect();
378 viewportRect = QRect(controlsRect.topLeft(), controlsRect.bottomRight() - cornerOffset);
379 }
380
381 cornerOffset = QPoint(needv ? vsbExt : 0, needh ? hsbExt : 0);
382
383 // If we have a corner widget and are only showing one scroll bar, we need to move it
384 // to make room for the corner widget.
385 if (hasCornerWidget && ((needv && vscrollOverlap == 0) || (needh && hscrollOverlap == 0)))
386 cornerOffset = extPoint;
387
388 // The corner point is where the scroll bar rects, the corner widget rect and the
389 // viewport rect meets.
390 const QPoint cornerPoint(controlsRect.bottomRight() + QPoint(1, 1) - cornerOffset);
391
392 // Some styles paints the corner if both scorllbars are showing and there is
393 // no corner widget.
394 if (needv && needh && !hasCornerWidget && hscrollOverlap == 0 && vscrollOverlap == 0)
395 cornerPaintingRect = QStyle::visualRect(opt.direction, opt.rect, QRect(cornerPoint, extSize));
396 else
397 cornerPaintingRect = QRect();
398
399 // move the scrollbars away from top/left headers
400 int vHeaderRight = 0;
401 int hHeaderBottom = 0;
402 #if QT_CONFIG(itemviews)
403 if ((vscrollOverlap > 0 && needv) || (hscrollOverlap > 0 && needh)) {
404 const QList<QHeaderView *> headers = q->findChildren<QHeaderView*>();
405 if (headers.count() <= 2) {
406 for (const QHeaderView *header : headers) {
407 const QRect geo = header->geometry();
408 if (header->orientation() == Qt::Vertical && header->isVisible() && QStyle::visualRect(opt.direction, opt.rect, geo).left() <= opt.rect.width() / 2)
409 vHeaderRight = QStyle::visualRect(opt.direction, opt.rect, geo).right();
410 else if (header->orientation() == Qt::Horizontal && header->isVisible() && geo.top() <= q->frameWidth())
411 hHeaderBottom = geo.bottom();
412 }
413 }
414 }
415 #endif // QT_CONFIG(itemviews)
416 if (needh) {
417 QRect horizontalScrollBarRect(QPoint(controlsRect.left() + vHeaderRight, cornerPoint.y()), QPoint(cornerPoint.x() - 1, controlsRect.bottom()));
418
419 if (!hasCornerWidget && htransient)
420 horizontalScrollBarRect.adjust(0, 0, cornerOffset.x(), 0);
421 scrollBarContainers[Qt::Horizontal]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, horizontalScrollBarRect));
422 scrollBarContainers[Qt::Horizontal]->raise();
423 }
424
425 if (needv) {
426 QRect verticalScrollBarRect (QPoint(cornerPoint.x(), controlsRect.top() + hHeaderBottom), QPoint(controlsRect.right(), cornerPoint.y() - 1));
427 if (!hasCornerWidget && vtransient)
428 verticalScrollBarRect.adjust(0, 0, 0, cornerOffset.y());
429 scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(opt.direction, opt.rect, verticalScrollBarRect));
430 scrollBarContainers[Qt::Vertical]->raise();
431 }
432
433 if (cornerWidget) {
434 const QRect cornerWidgetRect(cornerPoint, controlsRect.bottomRight());
435 cornerWidget->setGeometry(QStyle::visualRect(opt.direction, opt.rect, cornerWidgetRect));
436 }
437
438 scrollBarContainers[Qt::Horizontal]->setVisible(needh);
439 scrollBarContainers[Qt::Vertical]->setVisible(needv);
440
441 if (q->isRightToLeft())
442 viewportRect.adjust(right, top, -left, -bottom);
443 else
444 viewportRect.adjust(left, top, -right, -bottom);
445
446 viewport->setGeometry(QStyle::visualRect(opt.direction, opt.rect, viewportRect)); // resize the viewport last
447 *needHorizontalScrollbar = needh;
448 *needVerticalScrollbar = needv;
449 }
450
451 /*!
452 \enum QAbstractScrollArea::SizeAdjustPolicy
453 \since 5.2
454
455 This enum specifies how the size hint of the QAbstractScrollArea should
456 adjust when the size of the viewport changes.
457
458 \value AdjustIgnored The scroll area will behave like before - and not do any adjust.
459 \value AdjustToContents The scroll area will always adjust to the viewport
460 \value AdjustToContentsOnFirstShow The scroll area will adjust to its viewport the first time it is shown.
461 */
462
463
464 /*!
465 \internal
466
467 Creates a new QAbstractScrollAreaPrivate, \a dd with the given \a parent.
468 */
QAbstractScrollArea(QAbstractScrollAreaPrivate & dd,QWidget * parent)469 QAbstractScrollArea::QAbstractScrollArea(QAbstractScrollAreaPrivate &dd, QWidget *parent)
470 :QFrame(dd, parent)
471 {
472 Q_D(QAbstractScrollArea);
473 QT_TRY {
474 d->init();
475 } QT_CATCH(...) {
476 d->viewportFilter.reset();
477 QT_RETHROW;
478 }
479 }
480
481 /*!
482 Constructs a viewport.
483
484 The \a parent argument is sent to the QWidget constructor.
485 */
QAbstractScrollArea(QWidget * parent)486 QAbstractScrollArea::QAbstractScrollArea(QWidget *parent)
487 :QFrame(*new QAbstractScrollAreaPrivate, parent)
488 {
489 Q_D(QAbstractScrollArea);
490 QT_TRY {
491 d->init();
492 } QT_CATCH(...) {
493 d->viewportFilter.reset();
494 QT_RETHROW;
495 }
496 }
497
498
499 /*!
500 Destroys the viewport.
501 */
~QAbstractScrollArea()502 QAbstractScrollArea::~QAbstractScrollArea()
503 {
504 Q_D(QAbstractScrollArea);
505 // reset it here, otherwise we'll have a dangling pointer in ~QWidget
506 d->viewportFilter.reset();
507 }
508
509
510 /*!
511 \since 4.2
512 Sets the viewport to be the given \a widget.
513 The QAbstractScrollArea will take ownership of the given \a widget.
514
515 If \a widget is \nullptr, QAbstractScrollArea will assign a new QWidget
516 instance for the viewport.
517
518 \sa viewport()
519 */
setViewport(QWidget * widget)520 void QAbstractScrollArea::setViewport(QWidget *widget)
521 {
522 Q_D(QAbstractScrollArea);
523 if (widget != d->viewport) {
524 QWidget *oldViewport = d->viewport;
525 if (!widget)
526 widget = new QWidget;
527 d->viewport = widget;
528 d->viewport->setParent(this);
529 d->viewport->setFocusProxy(this);
530 d->viewport->installEventFilter(d->viewportFilter.data());
531 #ifndef QT_NO_GESTURES
532 d->viewport->grabGesture(Qt::PanGesture);
533 #endif
534 d->layoutChildren();
535 #ifndef QT_NO_OPENGL
536 QWidgetPrivate::get(d->viewport)->initializeViewportFramebuffer();
537 #endif
538 if (isVisible())
539 d->viewport->show();
540 setupViewport(widget);
541 delete oldViewport;
542 }
543 }
544
545 /*!
546 Returns the viewport widget.
547
548 Use the QScrollArea::widget() function to retrieve the contents of
549 the viewport widget.
550
551 \sa QScrollArea::widget()
552 */
viewport() const553 QWidget *QAbstractScrollArea::viewport() const
554 {
555 Q_D(const QAbstractScrollArea);
556 return d->viewport;
557 }
558
559
560 /*!
561 Returns the size of the viewport as if the scroll bars had no valid
562 scrolling range.
563 */
maximumViewportSize() const564 QSize QAbstractScrollArea::maximumViewportSize() const
565 {
566 Q_D(const QAbstractScrollArea);
567 int hsbExt = d->hbar->sizeHint().height();
568 int vsbExt = d->vbar->sizeHint().width();
569
570 int f = 2 * d->frameWidth;
571 QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom);
572 if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
573 max.rwidth() -= vsbExt;
574 if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
575 max.rheight() -= hsbExt;
576 return max;
577 }
578
579 /*!
580 \property QAbstractScrollArea::verticalScrollBarPolicy
581 \brief the policy for the vertical scroll bar
582
583 The default policy is Qt::ScrollBarAsNeeded.
584
585 \sa horizontalScrollBarPolicy
586 */
587
verticalScrollBarPolicy() const588 Qt::ScrollBarPolicy QAbstractScrollArea::verticalScrollBarPolicy() const
589 {
590 Q_D(const QAbstractScrollArea);
591 return d->vbarpolicy;
592 }
593
setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)594 void QAbstractScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
595 {
596 Q_D(QAbstractScrollArea);
597 const Qt::ScrollBarPolicy oldPolicy = d->vbarpolicy;
598 d->vbarpolicy = policy;
599 if (isVisible())
600 d->layoutChildren();
601 if (oldPolicy != d->vbarpolicy)
602 d->scrollBarPolicyChanged(Qt::Vertical, d->vbarpolicy);
603 }
604
605
606 /*!
607 Returns the vertical scroll bar.
608
609 \sa verticalScrollBarPolicy, horizontalScrollBar()
610 */
verticalScrollBar() const611 QScrollBar *QAbstractScrollArea::verticalScrollBar() const
612 {
613 Q_D(const QAbstractScrollArea);
614 return d->vbar;
615 }
616
617 /*!
618 \since 4.2
619 Replaces the existing vertical scroll bar with \a scrollBar, and sets all
620 the former scroll bar's slider properties on the new scroll bar. The former
621 scroll bar is then deleted.
622
623 QAbstractScrollArea already provides vertical and horizontal scroll bars by
624 default. You can call this function to replace the default vertical
625 scroll bar with your own custom scroll bar.
626
627 \sa verticalScrollBar(), setHorizontalScrollBar()
628 */
setVerticalScrollBar(QScrollBar * scrollBar)629 void QAbstractScrollArea::setVerticalScrollBar(QScrollBar *scrollBar)
630 {
631 Q_D(QAbstractScrollArea);
632 if (Q_UNLIKELY(!scrollBar)) {
633 qWarning("QAbstractScrollArea::setVerticalScrollBar: Cannot set a null scroll bar");
634 return;
635 }
636
637 d->replaceScrollBar(scrollBar, Qt::Vertical);
638 }
639
640 /*!
641 \property QAbstractScrollArea::horizontalScrollBarPolicy
642 \brief the policy for the horizontal scroll bar
643
644 The default policy is Qt::ScrollBarAsNeeded.
645
646 \sa verticalScrollBarPolicy
647 */
648
horizontalScrollBarPolicy() const649 Qt::ScrollBarPolicy QAbstractScrollArea::horizontalScrollBarPolicy() const
650 {
651 Q_D(const QAbstractScrollArea);
652 return d->hbarpolicy;
653 }
654
setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)655 void QAbstractScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
656 {
657 Q_D(QAbstractScrollArea);
658 const Qt::ScrollBarPolicy oldPolicy = d->hbarpolicy;
659 d->hbarpolicy = policy;
660 if (isVisible())
661 d->layoutChildren();
662 if (oldPolicy != d->hbarpolicy)
663 d->scrollBarPolicyChanged(Qt::Horizontal, d->hbarpolicy);
664 }
665
666 /*!
667 Returns the horizontal scroll bar.
668
669 \sa horizontalScrollBarPolicy, verticalScrollBar()
670 */
horizontalScrollBar() const671 QScrollBar *QAbstractScrollArea::horizontalScrollBar() const
672 {
673 Q_D(const QAbstractScrollArea);
674 return d->hbar;
675 }
676
677 /*!
678 \since 4.2
679
680 Replaces the existing horizontal scroll bar with \a scrollBar, and sets all
681 the former scroll bar's slider properties on the new scroll bar. The former
682 scroll bar is then deleted.
683
684 QAbstractScrollArea already provides horizontal and vertical scroll bars by
685 default. You can call this function to replace the default horizontal
686 scroll bar with your own custom scroll bar.
687
688 \sa horizontalScrollBar(), setVerticalScrollBar()
689 */
setHorizontalScrollBar(QScrollBar * scrollBar)690 void QAbstractScrollArea::setHorizontalScrollBar(QScrollBar *scrollBar)
691 {
692 Q_D(QAbstractScrollArea);
693 if (Q_UNLIKELY(!scrollBar)) {
694 qWarning("QAbstractScrollArea::setHorizontalScrollBar: Cannot set a null scroll bar");
695 return;
696 }
697
698 d->replaceScrollBar(scrollBar, Qt::Horizontal);
699 }
700
701 /*!
702 \since 4.2
703
704 Returns the widget in the corner between the two scroll bars.
705
706 By default, no corner widget is present.
707 */
cornerWidget() const708 QWidget *QAbstractScrollArea::cornerWidget() const
709 {
710 Q_D(const QAbstractScrollArea);
711 return d->cornerWidget;
712 }
713
714 /*!
715 \since 4.2
716
717 Sets the widget in the corner between the two scroll bars to be
718 \a widget.
719
720 You will probably also want to set at least one of the scroll bar
721 modes to \c AlwaysOn.
722
723 Passing \nullptr shows no widget in the corner.
724
725 Any previous corner widget is hidden.
726
727 You may call setCornerWidget() with the same widget at different
728 times.
729
730 All widgets set here will be deleted by the scroll area when it is
731 destroyed unless you separately reparent the widget after setting
732 some other corner widget (or \nullptr).
733
734 Any \e newly set widget should have no current parent.
735
736 By default, no corner widget is present.
737
738 \sa horizontalScrollBarPolicy, horizontalScrollBarPolicy
739 */
setCornerWidget(QWidget * widget)740 void QAbstractScrollArea::setCornerWidget(QWidget *widget)
741 {
742 Q_D(QAbstractScrollArea);
743 QWidget* oldWidget = d->cornerWidget;
744 if (oldWidget != widget) {
745 if (oldWidget)
746 oldWidget->hide();
747 d->cornerWidget = widget;
748
749 if (widget && widget->parentWidget() != this)
750 widget->setParent(this);
751
752 d->layoutChildren();
753 if (widget)
754 widget->show();
755 } else {
756 d->cornerWidget = widget;
757 d->layoutChildren();
758 }
759 }
760
761 /*!
762 \since 4.2
763 Adds \a widget as a scroll bar widget in the location specified
764 by \a alignment.
765
766 Scroll bar widgets are shown next to the horizontal or vertical
767 scroll bar, and can be placed on either side of it. If you want
768 the scroll bar widgets to be always visible, set the
769 scrollBarPolicy for the corresponding scroll bar to \c AlwaysOn.
770
771 \a alignment must be one of Qt::Alignleft and Qt::AlignRight,
772 which maps to the horizontal scroll bar, or Qt::AlignTop and
773 Qt::AlignBottom, which maps to the vertical scroll bar.
774
775 A scroll bar widget can be removed by either re-parenting the
776 widget or deleting it. It's also possible to hide a widget with
777 QWidget::hide()
778
779 The scroll bar widget will be resized to fit the scroll bar
780 geometry for the current style. The following describes the case
781 for scroll bar widgets on the horizontal scroll bar:
782
783 The height of the widget will be set to match the height of the
784 scroll bar. To control the width of the widget, use
785 QWidget::setMinimumWidth and QWidget::setMaximumWidth, or
786 implement QWidget::sizeHint() and set a horizontal size policy.
787 If you want a square widget, call
788 QStyle::pixelMetric(QStyle::PM_ScrollBarExtent) and set the
789 width to this value.
790
791 \sa scrollBarWidgets()
792 */
addScrollBarWidget(QWidget * widget,Qt::Alignment alignment)793 void QAbstractScrollArea::addScrollBarWidget(QWidget *widget, Qt::Alignment alignment)
794 {
795 Q_D(QAbstractScrollArea);
796
797 if (widget == nullptr)
798 return;
799
800 const Qt::Orientation scrollBarOrientation
801 = ((alignment & Qt::AlignLeft) || (alignment & Qt::AlignRight)) ? Qt::Horizontal : Qt::Vertical;
802 const QAbstractScrollAreaScrollBarContainer::LogicalPosition position
803 = ((alignment & Qt::AlignRight) || (alignment & Qt::AlignBottom))
804 ? QAbstractScrollAreaScrollBarContainer::LogicalRight : QAbstractScrollAreaScrollBarContainer::LogicalLeft;
805 d->scrollBarContainers[scrollBarOrientation]->addWidget(widget, position);
806 d->layoutChildren();
807 if (isHidden() == false)
808 widget->show();
809 }
810
811 /*!
812 \since 4.2
813 Returns a list of the currently set scroll bar widgets. \a alignment
814 can be any combination of the four location flags.
815
816 \sa addScrollBarWidget()
817 */
scrollBarWidgets(Qt::Alignment alignment)818 QWidgetList QAbstractScrollArea::scrollBarWidgets(Qt::Alignment alignment)
819 {
820 Q_D(QAbstractScrollArea);
821
822 QWidgetList list;
823
824 if (alignment & Qt::AlignLeft)
825 list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft);
826 if (alignment & Qt::AlignRight)
827 list += d->scrollBarContainers[Qt::Horizontal]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight);
828 if (alignment & Qt::AlignTop)
829 list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalLeft);
830 if (alignment & Qt::AlignBottom)
831 list += d->scrollBarContainers[Qt::Vertical]->widgets(QAbstractScrollAreaScrollBarContainer::LogicalRight);
832
833 return list;
834 }
835
836 /*!
837 Sets the margins around the scrolling area to \a left, \a top, \a
838 right and \a bottom. This is useful for applications such as
839 spreadsheets with "locked" rows and columns. The marginal space is
840 is left blank; put widgets in the unused area.
841
842 Note that this function is frequently called by QTreeView and
843 QTableView, so margins must be implemented by QAbstractScrollArea
844 subclasses. Also, if the subclasses are to be used in item views,
845 they should not call this function.
846
847 By default all margins are zero.
848 \sa viewportMargins()
849 */
setViewportMargins(int left,int top,int right,int bottom)850 void QAbstractScrollArea::setViewportMargins(int left, int top, int right, int bottom)
851 {
852 Q_D(QAbstractScrollArea);
853 d->left = left;
854 d->top = top;
855 d->right = right;
856 d->bottom = bottom;
857 d->layoutChildren();
858 }
859
860 /*!
861 \since 4.6
862 Sets \a margins around the scrolling area. This is useful for
863 applications such as spreadsheets with "locked" rows and columns.
864 The marginal space is is left blank; put widgets in the unused
865 area.
866
867 By default all margins are zero.
868 \sa viewportMargins()
869 */
setViewportMargins(const QMargins & margins)870 void QAbstractScrollArea::setViewportMargins(const QMargins &margins)
871 {
872 setViewportMargins(margins.left(), margins.top(),
873 margins.right(), margins.bottom());
874 }
875
876 /*!
877 \since 5.5
878 Returns the margins around the scrolling area.
879 By default all the margins are zero.
880
881 \sa setViewportMargins()
882 */
viewportMargins() const883 QMargins QAbstractScrollArea::viewportMargins() const
884 {
885 Q_D(const QAbstractScrollArea);
886 return QMargins(d->left, d->top, d->right, d->bottom);
887 }
888
889 /*! \internal */
eventFilter(QObject * o,QEvent * e)890 bool QAbstractScrollArea::eventFilter(QObject *o, QEvent *e)
891 {
892 Q_D(QAbstractScrollArea);
893 if ((o == d->hbar || o == d->vbar) && (e->type() == QEvent::HoverEnter || e->type() == QEvent::HoverLeave)) {
894 if (d->hbarpolicy == Qt::ScrollBarAsNeeded && d->vbarpolicy == Qt::ScrollBarAsNeeded) {
895 QScrollBar *sbar = static_cast<QScrollBar*>(o);
896 QScrollBar *sibling = sbar == d->hbar ? d->vbar : d->hbar;
897 if (sbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, sbar) &&
898 sibling->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, sibling))
899 d->setScrollBarTransient(sibling, e->type() == QEvent::HoverLeave);
900 }
901 }
902 return QFrame::eventFilter(o, e);
903 }
904
905 /*!
906 \fn bool QAbstractScrollArea::event(QEvent *event)
907
908 \reimp
909
910 This is the main event handler for the QAbstractScrollArea widget (\e not
911 the scrolling area viewport()). The specified \a event is a general event
912 object that may need to be cast to the appropriate class depending on its
913 type.
914
915 \sa QEvent::type()
916 */
event(QEvent * e)917 bool QAbstractScrollArea::event(QEvent *e)
918 {
919 Q_D(QAbstractScrollArea);
920 switch (e->type()) {
921 case QEvent::AcceptDropsChange:
922 // There was a chance that with accessibility client we get an
923 // event before the viewport was created.
924 // Also, in some cases we might get here from QWidget::event() virtual function which is (indirectly) called
925 // from the viewport constructor at the time when the d->viewport is not yet initialized even without any
926 // accessibility client. See qabstractscrollarea autotest for a test case.
927 if (d->viewport)
928 d->viewport->setAcceptDrops(acceptDrops());
929 break;
930 case QEvent::MouseTrackingChange:
931 d->viewport->setMouseTracking(hasMouseTracking());
932 break;
933 case QEvent::Resize:
934 if (!d->inResize) {
935 d->inResize = true;
936 d->layoutChildren();
937 d->inResize = false;
938 }
939 break;
940 case QEvent::Show:
941 if (!d->shownOnce && d->sizeAdjustPolicy == QAbstractScrollArea::AdjustToContentsOnFirstShow) {
942 d->sizeHint = QSize();
943 updateGeometry();
944 }
945 d->shownOnce = true;
946 return QFrame::event(e);
947 case QEvent::Paint: {
948 QStyleOption option;
949 option.initFrom(this);
950 if (d->cornerPaintingRect.isValid()) {
951 option.rect = d->cornerPaintingRect;
952 QPainter p(this);
953 style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this);
954 }
955 }
956 QFrame::paintEvent((QPaintEvent*)e);
957 break;
958 #ifndef QT_NO_CONTEXTMENU
959 case QEvent::ContextMenu:
960 if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard)
961 return QFrame::event(e);
962 e->ignore();
963 break;
964 #endif // QT_NO_CONTEXTMENU
965 case QEvent::MouseButtonPress:
966 case QEvent::MouseButtonRelease:
967 case QEvent::MouseButtonDblClick:
968 case QEvent::MouseMove:
969 case QEvent::Wheel:
970 #if QT_CONFIG(draganddrop)
971 case QEvent::Drop:
972 case QEvent::DragEnter:
973 case QEvent::DragMove:
974 case QEvent::DragLeave:
975 #endif
976 // ignore touch events in case they have been propagated from the viewport
977 case QEvent::TouchBegin:
978 case QEvent::TouchUpdate:
979 case QEvent::TouchEnd:
980 return false;
981 #ifndef QT_NO_GESTURES
982 case QEvent::Gesture:
983 {
984 QGestureEvent *ge = static_cast<QGestureEvent *>(e);
985 QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(Qt::PanGesture));
986 if (g) {
987 QScrollBar *hBar = horizontalScrollBar();
988 QScrollBar *vBar = verticalScrollBar();
989 QPointF delta = g->delta();
990 if (!delta.isNull()) {
991 if (QGuiApplication::isRightToLeft())
992 delta.rx() *= -1;
993 int newX = hBar->value() - delta.x();
994 int newY = vBar->value() - delta.y();
995 hBar->setValue(newX);
996 vBar->setValue(newY);
997 }
998 return true;
999 }
1000 return false;
1001 }
1002 #endif // QT_NO_GESTURES
1003 case QEvent::ScrollPrepare:
1004 {
1005 QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
1006 if (d->canStartScrollingAt(se->startPos().toPoint())) {
1007 QScrollBar *hBar = horizontalScrollBar();
1008 QScrollBar *vBar = verticalScrollBar();
1009
1010 se->setViewportSize(QSizeF(viewport()->size()));
1011 se->setContentPosRange(QRectF(0, 0, hBar->maximum(), vBar->maximum()));
1012 se->setContentPos(QPointF(hBar->value(), vBar->value()));
1013 se->accept();
1014 return true;
1015 }
1016 return false;
1017 }
1018 case QEvent::Scroll:
1019 {
1020 QScrollEvent *se = static_cast<QScrollEvent *>(e);
1021
1022 QScrollBar *hBar = horizontalScrollBar();
1023 QScrollBar *vBar = verticalScrollBar();
1024 hBar->setValue(se->contentPos().x());
1025 vBar->setValue(se->contentPos().y());
1026
1027 QPoint delta = d->overshoot - se->overshootDistance().toPoint();
1028 if (!delta.isNull())
1029 viewport()->move(viewport()->pos() + delta);
1030
1031 d->overshoot = se->overshootDistance().toPoint();
1032
1033 return true;
1034 }
1035 case QEvent::StyleChange:
1036 case QEvent::LayoutDirectionChange:
1037 case QEvent::ApplicationLayoutDirectionChange:
1038 case QEvent::LayoutRequest:
1039 d->layoutChildren();
1040 Q_FALLTHROUGH();
1041 default:
1042 return QFrame::event(e);
1043 }
1044 return true;
1045 }
1046
1047 /*!
1048 \fn bool QAbstractScrollArea::viewportEvent(QEvent *event)
1049
1050 The main event handler for the scrolling area (the viewport() widget).
1051 It handles the \a event specified, and can be called by subclasses to
1052 provide reasonable default behavior.
1053
1054 Returns \c true to indicate to the event system that the event has been
1055 handled, and needs no further processing; otherwise returns \c false to
1056 indicate that the event should be propagated further.
1057
1058 You can reimplement this function in a subclass, but we recommend
1059 using one of the specialized event handlers instead.
1060
1061 Specialized handlers for viewport events are: paintEvent(),
1062 mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(),
1063 mouseMoveEvent(), wheelEvent(), dragEnterEvent(), dragMoveEvent(),
1064 dragLeaveEvent(), dropEvent(), contextMenuEvent(), and
1065 resizeEvent().
1066 */
viewportEvent(QEvent * e)1067 bool QAbstractScrollArea::viewportEvent(QEvent *e)
1068 {
1069 switch (e->type()) {
1070 case QEvent::Resize:
1071 case QEvent::Paint:
1072 case QEvent::MouseButtonPress:
1073 case QEvent::MouseButtonRelease:
1074 case QEvent::MouseButtonDblClick:
1075 case QEvent::TouchBegin:
1076 case QEvent::TouchUpdate:
1077 case QEvent::TouchEnd:
1078 case QEvent::MouseMove:
1079 case QEvent::ContextMenu:
1080 #if QT_CONFIG(wheelevent)
1081 case QEvent::Wheel:
1082 #endif
1083 #if QT_CONFIG(draganddrop)
1084 case QEvent::Drop:
1085 case QEvent::DragEnter:
1086 case QEvent::DragMove:
1087 case QEvent::DragLeave:
1088 #endif
1089 #ifndef QT_NO_OPENGL
1090 // QOpenGLWidget needs special support because it has to know
1091 // its size has changed, so that it can resize its fbo.
1092 if (e->type() == QEvent::Resize)
1093 QWidgetPrivate::get(viewport())->resizeViewportFramebuffer();
1094 #endif
1095 return QFrame::event(e);
1096 case QEvent::LayoutRequest:
1097 #ifndef QT_NO_GESTURES
1098 case QEvent::Gesture:
1099 case QEvent::GestureOverride:
1100 return event(e);
1101 #endif
1102 case QEvent::ScrollPrepare:
1103 case QEvent::Scroll:
1104 return event(e);
1105 default:
1106 break;
1107 }
1108 return false; // let the viewport widget handle the event
1109 }
1110
1111 /*!
1112 \fn void QAbstractScrollArea::resizeEvent(QResizeEvent *event)
1113
1114 This event handler can be reimplemented in a subclass to receive
1115 resize events (passed in \a event), for the viewport() widget.
1116
1117 When resizeEvent() is called, the viewport already has its new
1118 geometry: Its new size is accessible through the
1119 QResizeEvent::size() function, and the old size through
1120 QResizeEvent::oldSize().
1121
1122 \sa QWidget::resizeEvent()
1123 */
resizeEvent(QResizeEvent *)1124 void QAbstractScrollArea::resizeEvent(QResizeEvent *)
1125 {
1126 }
1127
1128 /*!
1129 \fn void QAbstractScrollArea::paintEvent(QPaintEvent *event)
1130
1131 This event handler can be reimplemented in a subclass to receive
1132 paint events (passed in \a event), for the viewport() widget.
1133
1134 \note If you open a painter, make sure to open it on the viewport().
1135
1136 \sa QWidget::paintEvent()
1137 */
paintEvent(QPaintEvent *)1138 void QAbstractScrollArea::paintEvent(QPaintEvent*)
1139 {
1140 }
1141
1142 /*!
1143 This event handler can be reimplemented in a subclass to receive
1144 mouse press events for the viewport() widget. The event is passed
1145 in \a e.
1146
1147 The default implementation calls QWidget::mousePressEvent() for
1148 default popup handling.
1149
1150 \sa QWidget::mousePressEvent()
1151 */
mousePressEvent(QMouseEvent * e)1152 void QAbstractScrollArea::mousePressEvent(QMouseEvent *e)
1153 {
1154 QWidget::mousePressEvent(e);
1155 }
1156
1157 /*!
1158 This event handler can be reimplemented in a subclass to receive
1159 mouse release events for the viewport() widget. The event is
1160 passed in \a e.
1161
1162 \sa QWidget::mouseReleaseEvent()
1163 */
mouseReleaseEvent(QMouseEvent * e)1164 void QAbstractScrollArea::mouseReleaseEvent(QMouseEvent *e)
1165 {
1166 e->ignore();
1167 }
1168
1169 /*!
1170 This event handler can be reimplemented in a subclass to receive
1171 mouse double click events for the viewport() widget. The event is
1172 passed in \a e.
1173
1174 \sa QWidget::mouseDoubleClickEvent()
1175 */
mouseDoubleClickEvent(QMouseEvent * e)1176 void QAbstractScrollArea::mouseDoubleClickEvent(QMouseEvent *e)
1177 {
1178 e->ignore();
1179 }
1180
1181 /*!
1182 This event handler can be reimplemented in a subclass to receive
1183 mouse move events for the viewport() widget. The event is passed
1184 in \a e.
1185
1186 \sa QWidget::mouseMoveEvent()
1187 */
mouseMoveEvent(QMouseEvent * e)1188 void QAbstractScrollArea::mouseMoveEvent(QMouseEvent *e)
1189 {
1190 e->ignore();
1191 }
1192
1193 /*!
1194 This event handler can be reimplemented in a subclass to receive
1195 wheel events for the viewport() widget. The event is passed in \a
1196 e.
1197
1198 \sa QWidget::wheelEvent()
1199 */
1200 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * e)1201 void QAbstractScrollArea::wheelEvent(QWheelEvent *e)
1202 {
1203 Q_D(QAbstractScrollArea);
1204 if (qAbs(e->angleDelta().x()) > qAbs(e->angleDelta().y()))
1205 QCoreApplication::sendEvent(d->hbar, e);
1206 else
1207 QCoreApplication::sendEvent(d->vbar, e);
1208 }
1209 #endif
1210
1211 #ifndef QT_NO_CONTEXTMENU
1212 /*!
1213 This event handler can be reimplemented in a subclass to receive
1214 context menu events for the viewport() widget. The event is passed
1215 in \a e.
1216
1217 \sa QWidget::contextMenuEvent()
1218 */
contextMenuEvent(QContextMenuEvent * e)1219 void QAbstractScrollArea::contextMenuEvent(QContextMenuEvent *e)
1220 {
1221 e->ignore();
1222 }
1223 #endif // QT_NO_CONTEXTMENU
1224
1225 /*!
1226 This function is called with key event \a e when key presses
1227 occur. It handles PageUp, PageDown, Up, Down, Left, and Right, and
1228 ignores all other key presses.
1229 */
keyPressEvent(QKeyEvent * e)1230 void QAbstractScrollArea::keyPressEvent(QKeyEvent * e)
1231 {
1232 Q_D(QAbstractScrollArea);
1233 if (false){
1234 #ifndef QT_NO_SHORTCUT
1235 } else if (e == QKeySequence::MoveToPreviousPage) {
1236 d->vbar->triggerAction(QScrollBar::SliderPageStepSub);
1237 } else if (e == QKeySequence::MoveToNextPage) {
1238 d->vbar->triggerAction(QScrollBar::SliderPageStepAdd);
1239 #endif
1240 } else {
1241 #ifdef QT_KEYPAD_NAVIGATION
1242 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1243 e->ignore();
1244 return;
1245 }
1246 #endif
1247 switch (e->key()) {
1248 case Qt::Key_Up:
1249 d->vbar->triggerAction(QScrollBar::SliderSingleStepSub);
1250 break;
1251 case Qt::Key_Down:
1252 d->vbar->triggerAction(QScrollBar::SliderSingleStepAdd);
1253 break;
1254 case Qt::Key_Left:
1255 #ifdef QT_KEYPAD_NAVIGATION
1256 if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()
1257 && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->minimum())) {
1258 //if we aren't using the hbar or we are already at the leftmost point ignore
1259 e->ignore();
1260 return;
1261 }
1262 #endif
1263 d->hbar->triggerAction(
1264 layoutDirection() == Qt::LeftToRight
1265 ? QScrollBar::SliderSingleStepSub : QScrollBar::SliderSingleStepAdd);
1266 break;
1267 case Qt::Key_Right:
1268 #ifdef QT_KEYPAD_NAVIGATION
1269 if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()
1270 && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->maximum())) {
1271 //if we aren't using the hbar or we are already at the rightmost point ignore
1272 e->ignore();
1273 return;
1274 }
1275 #endif
1276 d->hbar->triggerAction(
1277 layoutDirection() == Qt::LeftToRight
1278 ? QScrollBar::SliderSingleStepAdd : QScrollBar::SliderSingleStepSub);
1279 break;
1280 default:
1281 e->ignore();
1282 return;
1283 }
1284 }
1285 e->accept();
1286 }
1287
1288
1289 #if QT_CONFIG(draganddrop)
1290 /*!
1291 \fn void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *event)
1292
1293 This event handler can be reimplemented in a subclass to receive
1294 drag enter events (passed in \a event), for the viewport() widget.
1295
1296 \sa QWidget::dragEnterEvent()
1297 */
dragEnterEvent(QDragEnterEvent *)1298 void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *)
1299 {
1300 }
1301
1302 /*!
1303 \fn void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *event)
1304
1305 This event handler can be reimplemented in a subclass to receive
1306 drag move events (passed in \a event), for the viewport() widget.
1307
1308 \sa QWidget::dragMoveEvent()
1309 */
dragMoveEvent(QDragMoveEvent *)1310 void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *)
1311 {
1312 }
1313
1314 /*!
1315 \fn void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *event)
1316
1317 This event handler can be reimplemented in a subclass to receive
1318 drag leave events (passed in \a event), for the viewport() widget.
1319
1320 \sa QWidget::dragLeaveEvent()
1321 */
dragLeaveEvent(QDragLeaveEvent *)1322 void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *)
1323 {
1324 }
1325
1326 /*!
1327 \fn void QAbstractScrollArea::dropEvent(QDropEvent *event)
1328
1329 This event handler can be reimplemented in a subclass to receive
1330 drop events (passed in \a event), for the viewport() widget.
1331
1332 \sa QWidget::dropEvent()
1333 */
dropEvent(QDropEvent *)1334 void QAbstractScrollArea::dropEvent(QDropEvent *)
1335 {
1336 }
1337
1338
1339 #endif
1340
1341 /*!
1342 This virtual handler is called when the scroll bars are moved by
1343 \a dx, \a dy, and consequently the viewport's contents should be
1344 scrolled accordingly.
1345
1346 The default implementation simply calls update() on the entire
1347 viewport(), subclasses can reimplement this handler for
1348 optimization purposes, or - like QScrollArea - to move a contents
1349 widget. The parameters \a dx and \a dy are there for convenience,
1350 so that the class knows how much should be scrolled (useful
1351 e.g. when doing pixel-shifts). You may just as well ignore these
1352 values and scroll directly to the position the scroll bars
1353 indicate.
1354
1355 Calling this function in order to scroll programmatically is an
1356 error, use the scroll bars instead (e.g. by calling
1357 QScrollBar::setValue() directly).
1358 */
scrollContentsBy(int,int)1359 void QAbstractScrollArea::scrollContentsBy(int, int)
1360 {
1361 viewport()->update();
1362 }
1363
canStartScrollingAt(const QPoint & startPos)1364 bool QAbstractScrollAreaPrivate::canStartScrollingAt( const QPoint &startPos )
1365 {
1366 Q_Q(QAbstractScrollArea);
1367
1368 #if QT_CONFIG(graphicsview)
1369 // don't start scrolling when a drag mode has been set.
1370 // don't start scrolling on a movable item.
1371 if (QGraphicsView *view = qobject_cast<QGraphicsView *>(q)) {
1372 if (view->dragMode() != QGraphicsView::NoDrag)
1373 return false;
1374
1375 QGraphicsItem *childItem = view->itemAt(startPos);
1376
1377 if (childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable))
1378 return false;
1379 }
1380 #endif
1381
1382 // don't start scrolling on a QAbstractSlider
1383 if (qobject_cast<QAbstractSlider *>(q->viewport()->childAt(startPos))) {
1384 return false;
1385 }
1386
1387 return true;
1388 }
1389
flashScrollBars()1390 void QAbstractScrollAreaPrivate::flashScrollBars()
1391 {
1392 bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, hbar);
1393 if ((hbarpolicy != Qt::ScrollBarAlwaysOff) && (hbarpolicy == Qt::ScrollBarAsNeeded || htransient))
1394 hbar->d_func()->flash();
1395 bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, vbar);
1396 if ((vbarpolicy != Qt::ScrollBarAlwaysOff) && (vbarpolicy == Qt::ScrollBarAsNeeded || vtransient))
1397 vbar->d_func()->flash();
1398 }
1399
setScrollBarTransient(QScrollBar * scrollBar,bool transient)1400 void QAbstractScrollAreaPrivate::setScrollBarTransient(QScrollBar *scrollBar, bool transient)
1401 {
1402 scrollBar->d_func()->setTransient(transient);
1403 }
1404
_q_hslide(int x)1405 void QAbstractScrollAreaPrivate::_q_hslide(int x)
1406 {
1407 Q_Q(QAbstractScrollArea);
1408 int dx = xoffset - x;
1409 xoffset = x;
1410 q->scrollContentsBy(dx, 0);
1411 flashScrollBars();
1412 }
1413
_q_vslide(int y)1414 void QAbstractScrollAreaPrivate::_q_vslide(int y)
1415 {
1416 Q_Q(QAbstractScrollArea);
1417 int dy = yoffset - y;
1418 yoffset = y;
1419 q->scrollContentsBy(0, dy);
1420 flashScrollBars();
1421 }
1422
_q_showOrHideScrollBars()1423 void QAbstractScrollAreaPrivate::_q_showOrHideScrollBars()
1424 {
1425 layoutChildren();
1426 }
1427
contentsOffset() const1428 QPoint QAbstractScrollAreaPrivate::contentsOffset() const
1429 {
1430 Q_Q(const QAbstractScrollArea);
1431 QPoint offset;
1432 if (vbar->isVisible())
1433 offset.setY(vbar->value());
1434 if (hbar->isVisible()) {
1435 if (q->isRightToLeft())
1436 offset.setX(hbar->maximum() - hbar->value());
1437 else
1438 offset.setX(hbar->value());
1439 }
1440 return offset;
1441 }
1442
1443 /*!
1444 \reimp
1445
1446 */
minimumSizeHint() const1447 QSize QAbstractScrollArea::minimumSizeHint() const
1448 {
1449 Q_D(const QAbstractScrollArea);
1450 int hsbExt = d->hbar->sizeHint().height();
1451 int vsbExt = d->vbar->sizeHint().width();
1452 int extra = 2 * d->frameWidth;
1453 QStyleOption opt;
1454 opt.initFrom(this);
1455 if ((d->frameStyle != QFrame::NoFrame)
1456 && style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, this)) {
1457 extra += style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, this);
1458 }
1459 return QSize(d->scrollBarContainers[Qt::Horizontal]->sizeHint().width() + vsbExt + extra,
1460 d->scrollBarContainers[Qt::Vertical]->sizeHint().height() + hsbExt + extra);
1461 }
1462
1463 /*!
1464 Returns the sizeHint property of the scroll area. The size is determined by using
1465 viewportSizeHint() plus some extra space for scroll bars, if needed.
1466 \reimp
1467 */
sizeHint() const1468 QSize QAbstractScrollArea::sizeHint() const
1469 {
1470 Q_D(const QAbstractScrollArea);
1471 if (d->sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored)
1472 return QSize(256, 192);
1473
1474 if (!d->sizeHint.isValid() || d->sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents) {
1475 const int f = 2 * d->frameWidth;
1476 const QSize frame( f, f );
1477 const bool vbarHidden = d->vbar->isHidden() || d->vbarpolicy == Qt::ScrollBarAlwaysOff;
1478 const bool hbarHidden = d->hbar->isHidden() || d->hbarpolicy == Qt::ScrollBarAlwaysOff;
1479 const QSize scrollbars(vbarHidden ? 0 : d->vbar->sizeHint().width(),
1480 hbarHidden ? 0 : d->hbar->sizeHint().height());
1481 d->sizeHint = frame + scrollbars + viewportSizeHint();
1482 }
1483 return d->sizeHint;
1484 }
1485
1486 /*!
1487 \since 5.2
1488 Returns the recommended size for the viewport.
1489 The default implementation returns viewport()->sizeHint().
1490 Note that the size is just the viewport's size, without any scroll bars visible.
1491 */
viewportSizeHint() const1492 QSize QAbstractScrollArea::viewportSizeHint() const
1493 {
1494 Q_D(const QAbstractScrollArea);
1495 if (d->viewport) {
1496 const QSize sh = d->viewport->sizeHint();
1497 if (sh.isValid()) {
1498 return sh;
1499 }
1500 }
1501 const int h = qMax(10, fontMetrics().height());
1502 return QSize(6 * h, 4 * h);
1503 }
1504
1505 /*!
1506 \since 5.2
1507 \property QAbstractScrollArea::sizeAdjustPolicy
1508 This property holds the policy describing how the size of the scroll area changes when the
1509 size of the viewport changes.
1510
1511 The default policy is QAbstractScrollArea::AdjustIgnored.
1512 Changing this property might actually resize the scrollarea.
1513 */
1514
sizeAdjustPolicy() const1515 QAbstractScrollArea::SizeAdjustPolicy QAbstractScrollArea::sizeAdjustPolicy() const
1516 {
1517 Q_D(const QAbstractScrollArea);
1518 return d->sizeAdjustPolicy;
1519 }
1520
setSizeAdjustPolicy(SizeAdjustPolicy policy)1521 void QAbstractScrollArea::setSizeAdjustPolicy(SizeAdjustPolicy policy)
1522 {
1523 Q_D(QAbstractScrollArea);
1524 if (d->sizeAdjustPolicy == policy)
1525 return;
1526
1527 d->sizeAdjustPolicy = policy;
1528 d->sizeHint = QSize();
1529 updateGeometry();
1530 }
1531
1532 /*!
1533 This slot is called by QAbstractScrollArea after setViewport(\a
1534 viewport) has been called. Reimplement this function in a
1535 subclass of QAbstractScrollArea to initialize the new \a viewport
1536 before it is used.
1537
1538 \sa setViewport()
1539 */
setupViewport(QWidget * viewport)1540 void QAbstractScrollArea::setupViewport(QWidget *viewport)
1541 {
1542 Q_UNUSED(viewport);
1543 }
1544
1545 QT_END_NAMESPACE
1546
1547 #include "moc_qabstractscrollarea.cpp"
1548 #include "moc_qabstractscrollarea_p.cpp"
1549
1550 #endif // QT_CONFIG(scrollarea)
1551