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
69QT_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
163QAbstractScrollAreaPrivate::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
171QAbstractScrollAreaPrivate::~QAbstractScrollAreaPrivate()
172{
173}
174
175QAbstractScrollAreaScrollBarContainer::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*/
190void 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(index: 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*/
208QWidgetList QAbstractScrollAreaScrollBarContainer::widgets(LogicalPosition position)
209{
210 QWidgetList list;
211 const int scrollBarIndex = scrollBarLayoutIndex();
212 if (position == LogicalLeft) {
213 list.reserve(alloc: scrollBarIndex);
214 for (int i = 0; i < scrollBarIndex; ++i)
215 list.append(t: layout->itemAt(i)->widget());
216 } else if (position == LogicalRight) {
217 const int layoutItemCount = layout->count();
218 list.reserve(alloc: layoutItemCount - (scrollBarIndex + 1));
219 for (int i = scrollBarIndex + 1; i < layoutItemCount; ++i)
220 list.append(t: 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*/
231int QAbstractScrollAreaScrollBarContainer::scrollBarLayoutIndex() const
232{
233 const int layoutItemCount = layout->count();
234 for (int i = 0; i < layoutItemCount; ++i) {
235 if (qobject_cast<QScrollBar *>(object: layout->itemAt(i)->widget()))
236 return i;
237 }
238 return -1;
239}
240
241/*! \internal
242*/
243void 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(w: oldBar);
258 container->layout->insertWidget(index: 0, widget: scrollBar);
259 scrollBar->setVisible(oldBar->isVisibleTo(container));
260 scrollBar->setInvertedAppearance(oldBar->invertedAppearance());
261 scrollBar->setInvertedControls(oldBar->invertedControls());
262 scrollBar->setRange(min: oldBar->minimum(), max: 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(filterObj: q);
272 oldBar->removeEventFilter(obj: q);
273 delete oldBar;
274
275 QObject::connect(sender: scrollBar, SIGNAL(valueChanged(int)),
276 receiver: q, member: horizontal ? SLOT(_q_hslide(int)) : SLOT(_q_vslide(int)));
277 QObject::connect(sender: scrollBar, SIGNAL(rangeChanged(int,int)),
278 receiver: q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
279}
280
281void 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(min: 0,max: 0);
292 scrollBarContainers[Qt::Horizontal]->setVisible(false);
293 hbar->installEventFilter(filterObj: q);
294 QObject::connect(sender: hbar, SIGNAL(valueChanged(int)), receiver: q, SLOT(_q_hslide(int)));
295 QObject::connect(sender: hbar, SIGNAL(rangeChanged(int,int)), receiver: 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(min: 0,max: 0);
300 scrollBarContainers[Qt::Vertical]->setVisible(false);
301 vbar->installEventFilter(filterObj: q);
302 QObject::connect(sender: vbar, SIGNAL(valueChanged(int)), receiver: q, SLOT(_q_vslide(int)));
303 QObject::connect(sender: vbar, SIGNAL(rangeChanged(int,int)), receiver: q, SLOT(_q_showOrHideScrollBars()), Qt::QueuedConnection);
304 viewportFilter.reset(other: new QAbstractScrollAreaFilter(this));
305 viewport->installEventFilter(filterObj: viewportFilter.data());
306 viewport->setFocusProxy(q);
307 q->setFocusPolicy(Qt::StrongFocus);
308 q->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
309 q->setSizePolicy(hor: QSizePolicy::Expanding, ver: QSizePolicy::Expanding);
310 layoutChildren();
311#ifndef Q_OS_MACOS
312# ifndef QT_NO_GESTURES
313 viewport->grabGesture(type: Qt::PanGesture);
314# endif
315#endif
316}
317
318void QAbstractScrollAreaPrivate::layoutChildren()
319{
320 bool needH = false;
321 bool needV = false;
322 layoutChildren_helper(needHorizontalScrollbar: &needH, needVerticalScrollbar: &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(needHorizontalScrollbar: &needH, needVerticalScrollbar: &needV);
327}
328
329void QAbstractScrollAreaPrivate::layoutChildren_helper(bool *needHorizontalScrollbar, bool *needVerticalScrollbar)
330{
331 Q_Q(QAbstractScrollArea);
332 bool htransient = hbar->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: 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(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: 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(w: q);
344
345 const int hscrollOverlap = hbar->style()->pixelMetric(metric: QStyle::PM_ScrollView_ScrollBarOverlap, option: &opt, widget: hbar);
346 const int vscrollOverlap = vbar->style()->pixelMetric(metric: QStyle::PM_ScrollView_ScrollBarOverlap, option: &opt, widget: 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(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: &opt, widget: q)) {
365 controlsRect = widgetRect;
366 const int spacing = q->style()->pixelMetric(metric: QStyle::PM_ScrollView_ScrollBarSpacing, option: &opt, widget: q);
367 const QPoint cornerExtra(needv ? spacing + vscrollOverlap : 0, needh ? spacing + hscrollOverlap : 0);
368 QRect frameRect = widgetRect;
369 frameRect.adjust(dx1: 0, dy1: 0, dx2: -cornerOffset.x() - cornerExtra.x(), dy2: -cornerOffset.y() - cornerExtra.y());
370 q->setFrameRect(QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: 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(direction: opt.direction, boundingRect: opt.rect, logicalRect: q->contentsRect());
375 } else {
376 q->setFrameRect(QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: 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(direction: opt.direction, boundingRect: opt.rect, logicalRect: 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(direction: opt.direction, boundingRect: opt.rect, logicalRect: geo).left() <= opt.rect.width() / 2)
409 vHeaderRight = QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: 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(dx1: 0, dy1: 0, dx2: cornerOffset.x(), dy2: 0);
421 scrollBarContainers[Qt::Horizontal]->setGeometry(QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: 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(dx1: 0, dy1: 0, dx2: 0, dy2: cornerOffset.y());
429 scrollBarContainers[Qt::Vertical]->setGeometry(QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: verticalScrollBarRect));
430 scrollBarContainers[Qt::Vertical]->raise();
431 }
432
433 if (cornerWidget) {
434 const QRect cornerWidgetRect(cornerPoint, controlsRect.bottomRight());
435 cornerWidget->setGeometry(QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: cornerWidgetRect));
436 }
437
438 scrollBarContainers[Qt::Horizontal]->setVisible(needh);
439 scrollBarContainers[Qt::Vertical]->setVisible(needv);
440
441 if (q->isRightToLeft())
442 viewportRect.adjust(dx1: right, dy1: top, dx2: -left, dy2: -bottom);
443 else
444 viewportRect.adjust(dx1: left, dy1: top, dx2: -right, dy2: -bottom);
445 viewportRect = QStyle::visualRect(direction: opt.direction, boundingRect: opt.rect, logicalRect: viewportRect);
446 viewportRect.translate(p: -overshoot);
447 viewport->setGeometry(viewportRect); // resize the viewport last
448
449 *needHorizontalScrollbar = needh;
450 *needVerticalScrollbar = needv;
451}
452
453/*!
454 \enum QAbstractScrollArea::SizeAdjustPolicy
455 \since 5.2
456
457 This enum specifies how the size hint of the QAbstractScrollArea should
458 adjust when the size of the viewport changes.
459
460 \value AdjustIgnored The scroll area will behave like before - and not do any adjust.
461 \value AdjustToContents The scroll area will always adjust to the viewport
462 \value AdjustToContentsOnFirstShow The scroll area will adjust to its viewport the first time it is shown.
463*/
464
465
466/*!
467 \internal
468
469 Creates a new QAbstractScrollAreaPrivate, \a dd with the given \a parent.
470*/
471QAbstractScrollArea::QAbstractScrollArea(QAbstractScrollAreaPrivate &dd, QWidget *parent)
472 :QFrame(dd, parent)
473{
474 Q_D(QAbstractScrollArea);
475 QT_TRY {
476 d->init();
477 } QT_CATCH(...) {
478 d->viewportFilter.reset();
479 QT_RETHROW;
480 }
481}
482
483/*!
484 Constructs a viewport.
485
486 The \a parent argument is sent to the QWidget constructor.
487*/
488QAbstractScrollArea::QAbstractScrollArea(QWidget *parent)
489 :QFrame(*new QAbstractScrollAreaPrivate, parent)
490{
491 Q_D(QAbstractScrollArea);
492 QT_TRY {
493 d->init();
494 } QT_CATCH(...) {
495 d->viewportFilter.reset();
496 QT_RETHROW;
497 }
498}
499
500
501/*!
502 Destroys the viewport.
503 */
504QAbstractScrollArea::~QAbstractScrollArea()
505{
506 Q_D(QAbstractScrollArea);
507 // reset it here, otherwise we'll have a dangling pointer in ~QWidget
508 d->viewportFilter.reset();
509}
510
511
512/*!
513 \since 4.2
514 Sets the viewport to be the given \a widget.
515 The QAbstractScrollArea will take ownership of the given \a widget.
516
517 If \a widget is \nullptr, QAbstractScrollArea will assign a new QWidget
518 instance for the viewport.
519
520 \sa viewport()
521*/
522void QAbstractScrollArea::setViewport(QWidget *widget)
523{
524 Q_D(QAbstractScrollArea);
525 if (widget != d->viewport) {
526 QWidget *oldViewport = d->viewport;
527 if (!widget)
528 widget = new QWidget;
529 d->viewport = widget;
530 d->viewport->setParent(this);
531 d->viewport->setFocusProxy(this);
532 d->viewport->installEventFilter(filterObj: d->viewportFilter.data());
533#ifndef QT_NO_GESTURES
534 d->viewport->grabGesture(type: Qt::PanGesture);
535#endif
536 d->layoutChildren();
537#ifndef QT_NO_OPENGL
538 QWidgetPrivate::get(w: d->viewport)->initializeViewportFramebuffer();
539#endif
540 if (isVisible())
541 d->viewport->show();
542 setupViewport(widget);
543 delete oldViewport;
544 }
545}
546
547/*!
548 Returns the viewport widget.
549
550 Use the QScrollArea::widget() function to retrieve the contents of
551 the viewport widget.
552
553 \sa QScrollArea::widget()
554*/
555QWidget *QAbstractScrollArea::viewport() const
556{
557 Q_D(const QAbstractScrollArea);
558 return d->viewport;
559}
560
561
562/*!
563Returns the size of the viewport as if the scroll bars had no valid
564scrolling range.
565*/
566QSize QAbstractScrollArea::maximumViewportSize() const
567{
568 Q_D(const QAbstractScrollArea);
569 int hsbExt = d->hbar->sizeHint().height();
570 int vsbExt = d->vbar->sizeHint().width();
571
572 int f = 2 * d->frameWidth;
573 QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom);
574 if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
575 max.rwidth() -= vsbExt;
576 if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
577 max.rheight() -= hsbExt;
578 return max;
579}
580
581/*!
582 \property QAbstractScrollArea::verticalScrollBarPolicy
583 \brief the policy for the vertical scroll bar
584
585 The default policy is Qt::ScrollBarAsNeeded.
586
587 \sa horizontalScrollBarPolicy
588*/
589
590Qt::ScrollBarPolicy QAbstractScrollArea::verticalScrollBarPolicy() const
591{
592 Q_D(const QAbstractScrollArea);
593 return d->vbarpolicy;
594}
595
596void QAbstractScrollArea::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
597{
598 Q_D(QAbstractScrollArea);
599 const Qt::ScrollBarPolicy oldPolicy = d->vbarpolicy;
600 d->vbarpolicy = policy;
601 if (isVisible())
602 d->layoutChildren();
603 if (oldPolicy != d->vbarpolicy)
604 d->scrollBarPolicyChanged(Qt::Vertical, d->vbarpolicy);
605}
606
607
608/*!
609 Returns the vertical scroll bar.
610
611 \sa verticalScrollBarPolicy, horizontalScrollBar()
612 */
613QScrollBar *QAbstractScrollArea::verticalScrollBar() const
614{
615 Q_D(const QAbstractScrollArea);
616 return d->vbar;
617}
618
619/*!
620 \since 4.2
621 Replaces the existing vertical scroll bar with \a scrollBar, and sets all
622 the former scroll bar's slider properties on the new scroll bar. The former
623 scroll bar is then deleted.
624
625 QAbstractScrollArea already provides vertical and horizontal scroll bars by
626 default. You can call this function to replace the default vertical
627 scroll bar with your own custom scroll bar.
628
629 \sa verticalScrollBar(), setHorizontalScrollBar()
630*/
631void QAbstractScrollArea::setVerticalScrollBar(QScrollBar *scrollBar)
632{
633 Q_D(QAbstractScrollArea);
634 if (Q_UNLIKELY(!scrollBar)) {
635 qWarning(msg: "QAbstractScrollArea::setVerticalScrollBar: Cannot set a null scroll bar");
636 return;
637 }
638
639 d->replaceScrollBar(scrollBar, orientation: Qt::Vertical);
640}
641
642/*!
643 \property QAbstractScrollArea::horizontalScrollBarPolicy
644 \brief the policy for the horizontal scroll bar
645
646 The default policy is Qt::ScrollBarAsNeeded.
647
648 \sa verticalScrollBarPolicy
649*/
650
651Qt::ScrollBarPolicy QAbstractScrollArea::horizontalScrollBarPolicy() const
652{
653 Q_D(const QAbstractScrollArea);
654 return d->hbarpolicy;
655}
656
657void QAbstractScrollArea::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
658{
659 Q_D(QAbstractScrollArea);
660 const Qt::ScrollBarPolicy oldPolicy = d->hbarpolicy;
661 d->hbarpolicy = policy;
662 if (isVisible())
663 d->layoutChildren();
664 if (oldPolicy != d->hbarpolicy)
665 d->scrollBarPolicyChanged(Qt::Horizontal, d->hbarpolicy);
666}
667
668/*!
669 Returns the horizontal scroll bar.
670
671 \sa horizontalScrollBarPolicy, verticalScrollBar()
672 */
673QScrollBar *QAbstractScrollArea::horizontalScrollBar() const
674{
675 Q_D(const QAbstractScrollArea);
676 return d->hbar;
677}
678
679/*!
680 \since 4.2
681
682 Replaces the existing horizontal scroll bar with \a scrollBar, and sets all
683 the former scroll bar's slider properties on the new scroll bar. The former
684 scroll bar is then deleted.
685
686 QAbstractScrollArea already provides horizontal and vertical scroll bars by
687 default. You can call this function to replace the default horizontal
688 scroll bar with your own custom scroll bar.
689
690 \sa horizontalScrollBar(), setVerticalScrollBar()
691*/
692void QAbstractScrollArea::setHorizontalScrollBar(QScrollBar *scrollBar)
693{
694 Q_D(QAbstractScrollArea);
695 if (Q_UNLIKELY(!scrollBar)) {
696 qWarning(msg: "QAbstractScrollArea::setHorizontalScrollBar: Cannot set a null scroll bar");
697 return;
698 }
699
700 d->replaceScrollBar(scrollBar, orientation: Qt::Horizontal);
701}
702
703/*!
704 \since 4.2
705
706 Returns the widget in the corner between the two scroll bars.
707
708 By default, no corner widget is present.
709*/
710QWidget *QAbstractScrollArea::cornerWidget() const
711{
712 Q_D(const QAbstractScrollArea);
713 return d->cornerWidget;
714}
715
716/*!
717 \since 4.2
718
719 Sets the widget in the corner between the two scroll bars to be
720 \a widget.
721
722 You will probably also want to set at least one of the scroll bar
723 modes to \c AlwaysOn.
724
725 Passing \nullptr shows no widget in the corner.
726
727 Any previous corner widget is hidden.
728
729 You may call setCornerWidget() with the same widget at different
730 times.
731
732 All widgets set here will be deleted by the scroll area when it is
733 destroyed unless you separately reparent the widget after setting
734 some other corner widget (or \nullptr).
735
736 Any \e newly set widget should have no current parent.
737
738 By default, no corner widget is present.
739
740 \sa horizontalScrollBarPolicy, horizontalScrollBarPolicy
741*/
742void QAbstractScrollArea::setCornerWidget(QWidget *widget)
743{
744 Q_D(QAbstractScrollArea);
745 QWidget* oldWidget = d->cornerWidget;
746 if (oldWidget != widget) {
747 if (oldWidget)
748 oldWidget->hide();
749 d->cornerWidget = widget;
750
751 if (widget && widget->parentWidget() != this)
752 widget->setParent(this);
753
754 d->layoutChildren();
755 if (widget)
756 widget->show();
757 } else {
758 d->cornerWidget = widget;
759 d->layoutChildren();
760 }
761}
762
763/*!
764 \since 4.2
765 Adds \a widget as a scroll bar widget in the location specified
766 by \a alignment.
767
768 Scroll bar widgets are shown next to the horizontal or vertical
769 scroll bar, and can be placed on either side of it. If you want
770 the scroll bar widgets to be always visible, set the
771 scrollBarPolicy for the corresponding scroll bar to \c AlwaysOn.
772
773 \a alignment must be one of Qt::Alignleft and Qt::AlignRight,
774 which maps to the horizontal scroll bar, or Qt::AlignTop and
775 Qt::AlignBottom, which maps to the vertical scroll bar.
776
777 A scroll bar widget can be removed by either re-parenting the
778 widget or deleting it. It's also possible to hide a widget with
779 QWidget::hide()
780
781 The scroll bar widget will be resized to fit the scroll bar
782 geometry for the current style. The following describes the case
783 for scroll bar widgets on the horizontal scroll bar:
784
785 The height of the widget will be set to match the height of the
786 scroll bar. To control the width of the widget, use
787 QWidget::setMinimumWidth and QWidget::setMaximumWidth, or
788 implement QWidget::sizeHint() and set a horizontal size policy.
789 If you want a square widget, call
790 QStyle::pixelMetric(QStyle::PM_ScrollBarExtent) and set the
791 width to this value.
792
793 \sa scrollBarWidgets()
794*/
795void QAbstractScrollArea::addScrollBarWidget(QWidget *widget, Qt::Alignment alignment)
796{
797 Q_D(QAbstractScrollArea);
798
799 if (widget == nullptr)
800 return;
801
802 const Qt::Orientation scrollBarOrientation
803 = ((alignment & Qt::AlignLeft) || (alignment & Qt::AlignRight)) ? Qt::Horizontal : Qt::Vertical;
804 const QAbstractScrollAreaScrollBarContainer::LogicalPosition position
805 = ((alignment & Qt::AlignRight) || (alignment & Qt::AlignBottom))
806 ? QAbstractScrollAreaScrollBarContainer::LogicalRight : QAbstractScrollAreaScrollBarContainer::LogicalLeft;
807 d->scrollBarContainers[scrollBarOrientation]->addWidget(widget, position);
808 d->layoutChildren();
809 if (isHidden() == false)
810 widget->show();
811}
812
813/*!
814 \since 4.2
815 Returns a list of the currently set scroll bar widgets. \a alignment
816 can be any combination of the four location flags.
817
818 \sa addScrollBarWidget()
819*/
820QWidgetList QAbstractScrollArea::scrollBarWidgets(Qt::Alignment alignment)
821{
822 Q_D(QAbstractScrollArea);
823
824 QWidgetList list;
825
826 if (alignment & Qt::AlignLeft)
827 list += d->scrollBarContainers[Qt::Horizontal]->widgets(position: QAbstractScrollAreaScrollBarContainer::LogicalLeft);
828 if (alignment & Qt::AlignRight)
829 list += d->scrollBarContainers[Qt::Horizontal]->widgets(position: QAbstractScrollAreaScrollBarContainer::LogicalRight);
830 if (alignment & Qt::AlignTop)
831 list += d->scrollBarContainers[Qt::Vertical]->widgets(position: QAbstractScrollAreaScrollBarContainer::LogicalLeft);
832 if (alignment & Qt::AlignBottom)
833 list += d->scrollBarContainers[Qt::Vertical]->widgets(position: QAbstractScrollAreaScrollBarContainer::LogicalRight);
834
835 return list;
836}
837
838/*!
839 Sets the margins around the scrolling area to \a left, \a top, \a
840 right and \a bottom. This is useful for applications such as
841 spreadsheets with "locked" rows and columns. The marginal space is
842 is left blank; put widgets in the unused area.
843
844 Note that this function is frequently called by QTreeView and
845 QTableView, so margins must be implemented by QAbstractScrollArea
846 subclasses. Also, if the subclasses are to be used in item views,
847 they should not call this function.
848
849 By default all margins are zero.
850 \sa viewportMargins()
851*/
852void QAbstractScrollArea::setViewportMargins(int left, int top, int right, int bottom)
853{
854 Q_D(QAbstractScrollArea);
855 d->left = left;
856 d->top = top;
857 d->right = right;
858 d->bottom = bottom;
859 d->layoutChildren();
860}
861
862/*!
863 \since 4.6
864 Sets \a margins around the scrolling area. This is useful for
865 applications such as spreadsheets with "locked" rows and columns.
866 The marginal space is is left blank; put widgets in the unused
867 area.
868
869 By default all margins are zero.
870 \sa viewportMargins()
871*/
872void QAbstractScrollArea::setViewportMargins(const QMargins &margins)
873{
874 setViewportMargins(left: margins.left(), top: margins.top(),
875 right: margins.right(), bottom: margins.bottom());
876}
877
878/*!
879 \since 5.5
880 Returns the margins around the scrolling area.
881 By default all the margins are zero.
882
883 \sa setViewportMargins()
884*/
885QMargins QAbstractScrollArea::viewportMargins() const
886{
887 Q_D(const QAbstractScrollArea);
888 return QMargins(d->left, d->top, d->right, d->bottom);
889}
890
891/*! \internal */
892bool QAbstractScrollArea::eventFilter(QObject *o, QEvent *e)
893{
894 Q_D(QAbstractScrollArea);
895 if ((o == d->hbar || o == d->vbar) && (e->type() == QEvent::HoverEnter || e->type() == QEvent::HoverLeave)) {
896 if (d->hbarpolicy == Qt::ScrollBarAsNeeded && d->vbarpolicy == Qt::ScrollBarAsNeeded) {
897 QScrollBar *sbar = static_cast<QScrollBar*>(o);
898 QScrollBar *sibling = sbar == d->hbar ? d->vbar : d->hbar;
899 if (sbar->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: sbar) &&
900 sibling->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: sibling))
901 d->setScrollBarTransient(scrollBar: sibling, transient: e->type() == QEvent::HoverLeave);
902 }
903 }
904 return QFrame::eventFilter(watched: o, event: e);
905}
906
907/*!
908 \fn bool QAbstractScrollArea::event(QEvent *event)
909
910 \reimp
911
912 This is the main event handler for the QAbstractScrollArea widget (\e not
913 the scrolling area viewport()). The specified \a event is a general event
914 object that may need to be cast to the appropriate class depending on its
915 type.
916
917 \sa QEvent::type()
918*/
919bool QAbstractScrollArea::event(QEvent *e)
920{
921 Q_D(QAbstractScrollArea);
922 switch (e->type()) {
923 case QEvent::AcceptDropsChange:
924 // There was a chance that with accessibility client we get an
925 // event before the viewport was created.
926 // Also, in some cases we might get here from QWidget::event() virtual function which is (indirectly) called
927 // from the viewport constructor at the time when the d->viewport is not yet initialized even without any
928 // accessibility client. See qabstractscrollarea autotest for a test case.
929 if (d->viewport)
930 d->viewport->setAcceptDrops(acceptDrops());
931 break;
932 case QEvent::MouseTrackingChange:
933 d->viewport->setMouseTracking(hasMouseTracking());
934 break;
935 case QEvent::Resize:
936 if (!d->inResize) {
937 d->inResize = true;
938 d->layoutChildren();
939 d->inResize = false;
940 }
941 break;
942 case QEvent::Show:
943 if (!d->shownOnce && d->sizeAdjustPolicy == QAbstractScrollArea::AdjustToContentsOnFirstShow) {
944 d->sizeHint = QSize();
945 updateGeometry();
946 }
947 d->shownOnce = true;
948 return QFrame::event(e);
949 case QEvent::Paint: {
950 QStyleOption option;
951 option.initFrom(w: this);
952 if (d->cornerPaintingRect.isValid()) {
953 option.rect = d->cornerPaintingRect;
954 QPainter p(this);
955 style()->drawPrimitive(pe: QStyle::PE_PanelScrollAreaCorner, opt: &option, p: &p, w: this);
956 }
957 }
958 QFrame::paintEvent((QPaintEvent*)e);
959 break;
960#ifndef QT_NO_CONTEXTMENU
961 case QEvent::ContextMenu:
962 if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard)
963 return QFrame::event(e);
964 e->ignore();
965 break;
966#endif // QT_NO_CONTEXTMENU
967 case QEvent::MouseButtonPress:
968 case QEvent::MouseButtonRelease:
969 case QEvent::MouseButtonDblClick:
970 case QEvent::MouseMove:
971 case QEvent::Wheel:
972#if QT_CONFIG(draganddrop)
973 case QEvent::Drop:
974 case QEvent::DragEnter:
975 case QEvent::DragMove:
976 case QEvent::DragLeave:
977#endif
978 // ignore touch events in case they have been propagated from the viewport
979 case QEvent::TouchBegin:
980 case QEvent::TouchUpdate:
981 case QEvent::TouchEnd:
982 return false;
983#ifndef QT_NO_GESTURES
984 case QEvent::Gesture:
985 {
986 QGestureEvent *ge = static_cast<QGestureEvent *>(e);
987 QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(type: Qt::PanGesture));
988 if (g) {
989 QScrollBar *hBar = horizontalScrollBar();
990 QScrollBar *vBar = verticalScrollBar();
991 QPointF delta = g->delta();
992 if (!delta.isNull()) {
993 if (QGuiApplication::isRightToLeft())
994 delta.rx() *= -1;
995 int newX = hBar->value() - delta.x();
996 int newY = vBar->value() - delta.y();
997 hBar->setValue(newX);
998 vBar->setValue(newY);
999 }
1000 return true;
1001 }
1002 return false;
1003 }
1004#endif // QT_NO_GESTURES
1005 case QEvent::ScrollPrepare:
1006 {
1007 QScrollPrepareEvent *se = static_cast<QScrollPrepareEvent *>(e);
1008 if (d->canStartScrollingAt(startPos: se->startPos().toPoint())) {
1009 QScrollBar *hBar = horizontalScrollBar();
1010 QScrollBar *vBar = verticalScrollBar();
1011
1012 se->setViewportSize(QSizeF(viewport()->size()));
1013 se->setContentPosRange(QRectF(0, 0, hBar->maximum(), vBar->maximum()));
1014 se->setContentPos(QPointF(hBar->value(), vBar->value()));
1015 se->accept();
1016 return true;
1017 }
1018 return false;
1019 }
1020 case QEvent::Scroll:
1021 {
1022 QScrollEvent *se = static_cast<QScrollEvent *>(e);
1023
1024 QScrollBar *hBar = horizontalScrollBar();
1025 QScrollBar *vBar = verticalScrollBar();
1026 hBar->setValue(se->contentPos().x());
1027 vBar->setValue(se->contentPos().y());
1028
1029 QPoint delta = d->overshoot - se->overshootDistance().toPoint();
1030 if (!delta.isNull())
1031 viewport()->move(viewport()->pos() + delta);
1032
1033 d->overshoot = se->overshootDistance().toPoint();
1034
1035 return true;
1036 }
1037 case QEvent::StyleChange:
1038 case QEvent::LayoutDirectionChange:
1039 case QEvent::ApplicationLayoutDirectionChange:
1040 case QEvent::LayoutRequest:
1041 d->layoutChildren();
1042 Q_FALLTHROUGH();
1043 default:
1044 return QFrame::event(e);
1045 }
1046 return true;
1047}
1048
1049/*!
1050 \fn bool QAbstractScrollArea::viewportEvent(QEvent *event)
1051
1052 The main event handler for the scrolling area (the viewport() widget).
1053 It handles the \a event specified, and can be called by subclasses to
1054 provide reasonable default behavior.
1055
1056 Returns \c true to indicate to the event system that the event has been
1057 handled, and needs no further processing; otherwise returns \c false to
1058 indicate that the event should be propagated further.
1059
1060 You can reimplement this function in a subclass, but we recommend
1061 using one of the specialized event handlers instead.
1062
1063 Specialized handlers for viewport events are: paintEvent(),
1064 mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(),
1065 mouseMoveEvent(), wheelEvent(), dragEnterEvent(), dragMoveEvent(),
1066 dragLeaveEvent(), dropEvent(), contextMenuEvent(), and
1067 resizeEvent().
1068*/
1069bool QAbstractScrollArea::viewportEvent(QEvent *e)
1070{
1071 switch (e->type()) {
1072 case QEvent::Resize:
1073 case QEvent::Paint:
1074 case QEvent::MouseButtonPress:
1075 case QEvent::MouseButtonRelease:
1076 case QEvent::MouseButtonDblClick:
1077 case QEvent::TouchBegin:
1078 case QEvent::TouchUpdate:
1079 case QEvent::TouchEnd:
1080 case QEvent::MouseMove:
1081 case QEvent::ContextMenu:
1082#if QT_CONFIG(wheelevent)
1083 case QEvent::Wheel:
1084#endif
1085#if QT_CONFIG(draganddrop)
1086 case QEvent::Drop:
1087 case QEvent::DragEnter:
1088 case QEvent::DragMove:
1089 case QEvent::DragLeave:
1090#endif
1091#ifndef QT_NO_OPENGL
1092 // QOpenGLWidget needs special support because it has to know
1093 // its size has changed, so that it can resize its fbo.
1094 if (e->type() == QEvent::Resize)
1095 QWidgetPrivate::get(w: viewport())->resizeViewportFramebuffer();
1096#endif
1097 return QFrame::event(e);
1098 case QEvent::LayoutRequest:
1099#ifndef QT_NO_GESTURES
1100 case QEvent::Gesture:
1101 case QEvent::GestureOverride:
1102 return event(e);
1103#endif
1104 case QEvent::ScrollPrepare:
1105 case QEvent::Scroll:
1106 return event(e);
1107 default:
1108 break;
1109 }
1110 return false; // let the viewport widget handle the event
1111}
1112
1113/*!
1114 \fn void QAbstractScrollArea::resizeEvent(QResizeEvent *event)
1115
1116 This event handler can be reimplemented in a subclass to receive
1117 resize events (passed in \a event), for the viewport() widget.
1118
1119 When resizeEvent() is called, the viewport already has its new
1120 geometry: Its new size is accessible through the
1121 QResizeEvent::size() function, and the old size through
1122 QResizeEvent::oldSize().
1123
1124 \sa QWidget::resizeEvent()
1125 */
1126void QAbstractScrollArea::resizeEvent(QResizeEvent *)
1127{
1128}
1129
1130/*!
1131 \fn void QAbstractScrollArea::paintEvent(QPaintEvent *event)
1132
1133 This event handler can be reimplemented in a subclass to receive
1134 paint events (passed in \a event), for the viewport() widget.
1135
1136 \note If you open a painter, make sure to open it on the viewport().
1137
1138 \sa QWidget::paintEvent()
1139*/
1140void QAbstractScrollArea::paintEvent(QPaintEvent*)
1141{
1142}
1143
1144/*!
1145 This event handler can be reimplemented in a subclass to receive
1146 mouse press events for the viewport() widget. The event is passed
1147 in \a e.
1148
1149 The default implementation calls QWidget::mousePressEvent() for
1150 default popup handling.
1151
1152 \sa QWidget::mousePressEvent()
1153*/
1154void QAbstractScrollArea::mousePressEvent(QMouseEvent *e)
1155{
1156 QWidget::mousePressEvent(event: e);
1157}
1158
1159/*!
1160 This event handler can be reimplemented in a subclass to receive
1161 mouse release events for the viewport() widget. The event is
1162 passed in \a e.
1163
1164 \sa QWidget::mouseReleaseEvent()
1165*/
1166void QAbstractScrollArea::mouseReleaseEvent(QMouseEvent *e)
1167{
1168 e->ignore();
1169}
1170
1171/*!
1172 This event handler can be reimplemented in a subclass to receive
1173 mouse double click events for the viewport() widget. The event is
1174 passed in \a e.
1175
1176 \sa QWidget::mouseDoubleClickEvent()
1177*/
1178void QAbstractScrollArea::mouseDoubleClickEvent(QMouseEvent *e)
1179{
1180 e->ignore();
1181}
1182
1183/*!
1184 This event handler can be reimplemented in a subclass to receive
1185 mouse move events for the viewport() widget. The event is passed
1186 in \a e.
1187
1188 \sa QWidget::mouseMoveEvent()
1189*/
1190void QAbstractScrollArea::mouseMoveEvent(QMouseEvent *e)
1191{
1192 e->ignore();
1193}
1194
1195/*!
1196 This event handler can be reimplemented in a subclass to receive
1197 wheel events for the viewport() widget. The event is passed in \a
1198 e.
1199
1200 \sa QWidget::wheelEvent()
1201*/
1202#if QT_CONFIG(wheelevent)
1203void QAbstractScrollArea::wheelEvent(QWheelEvent *e)
1204{
1205 Q_D(QAbstractScrollArea);
1206 if (qAbs(t: e->angleDelta().x()) > qAbs(t: e->angleDelta().y()))
1207 QCoreApplication::sendEvent(receiver: d->hbar, event: e);
1208 else
1209 QCoreApplication::sendEvent(receiver: d->vbar, event: e);
1210}
1211#endif
1212
1213#ifndef QT_NO_CONTEXTMENU
1214/*!
1215 This event handler can be reimplemented in a subclass to receive
1216 context menu events for the viewport() widget. The event is passed
1217 in \a e.
1218
1219 \sa QWidget::contextMenuEvent()
1220*/
1221void QAbstractScrollArea::contextMenuEvent(QContextMenuEvent *e)
1222{
1223 e->ignore();
1224}
1225#endif // QT_NO_CONTEXTMENU
1226
1227/*!
1228 This function is called with key event \a e when key presses
1229 occur. It handles PageUp, PageDown, Up, Down, Left, and Right, and
1230 ignores all other key presses.
1231*/
1232void QAbstractScrollArea::keyPressEvent(QKeyEvent * e)
1233{
1234 Q_D(QAbstractScrollArea);
1235 if (false){
1236#ifndef QT_NO_SHORTCUT
1237 } else if (e == QKeySequence::MoveToPreviousPage) {
1238 d->vbar->triggerAction(action: QScrollBar::SliderPageStepSub);
1239 } else if (e == QKeySequence::MoveToNextPage) {
1240 d->vbar->triggerAction(action: QScrollBar::SliderPageStepAdd);
1241#endif
1242 } else {
1243#ifdef QT_KEYPAD_NAVIGATION
1244 if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1245 e->ignore();
1246 return;
1247 }
1248#endif
1249 switch (e->key()) {
1250 case Qt::Key_Up:
1251 d->vbar->triggerAction(action: QScrollBar::SliderSingleStepSub);
1252 break;
1253 case Qt::Key_Down:
1254 d->vbar->triggerAction(action: QScrollBar::SliderSingleStepAdd);
1255 break;
1256 case Qt::Key_Left:
1257#ifdef QT_KEYPAD_NAVIGATION
1258 if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()
1259 && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->minimum())) {
1260 //if we aren't using the hbar or we are already at the leftmost point ignore
1261 e->ignore();
1262 return;
1263 }
1264#endif
1265 d->hbar->triggerAction(
1266 action: layoutDirection() == Qt::LeftToRight
1267 ? QScrollBar::SliderSingleStepSub : QScrollBar::SliderSingleStepAdd);
1268 break;
1269 case Qt::Key_Right:
1270#ifdef QT_KEYPAD_NAVIGATION
1271 if (QApplicationPrivate::keypadNavigationEnabled() && hasEditFocus()
1272 && (!d->hbar->isVisible() || d->hbar->value() == d->hbar->maximum())) {
1273 //if we aren't using the hbar or we are already at the rightmost point ignore
1274 e->ignore();
1275 return;
1276 }
1277#endif
1278 d->hbar->triggerAction(
1279 action: layoutDirection() == Qt::LeftToRight
1280 ? QScrollBar::SliderSingleStepAdd : QScrollBar::SliderSingleStepSub);
1281 break;
1282 default:
1283 e->ignore();
1284 return;
1285 }
1286 }
1287 e->accept();
1288}
1289
1290
1291#if QT_CONFIG(draganddrop)
1292/*!
1293 \fn void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *event)
1294
1295 This event handler can be reimplemented in a subclass to receive
1296 drag enter events (passed in \a event), for the viewport() widget.
1297
1298 \sa QWidget::dragEnterEvent()
1299*/
1300void QAbstractScrollArea::dragEnterEvent(QDragEnterEvent *)
1301{
1302}
1303
1304/*!
1305 \fn void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *event)
1306
1307 This event handler can be reimplemented in a subclass to receive
1308 drag move events (passed in \a event), for the viewport() widget.
1309
1310 \sa QWidget::dragMoveEvent()
1311*/
1312void QAbstractScrollArea::dragMoveEvent(QDragMoveEvent *)
1313{
1314}
1315
1316/*!
1317 \fn void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *event)
1318
1319 This event handler can be reimplemented in a subclass to receive
1320 drag leave events (passed in \a event), for the viewport() widget.
1321
1322 \sa QWidget::dragLeaveEvent()
1323*/
1324void QAbstractScrollArea::dragLeaveEvent(QDragLeaveEvent *)
1325{
1326}
1327
1328/*!
1329 \fn void QAbstractScrollArea::dropEvent(QDropEvent *event)
1330
1331 This event handler can be reimplemented in a subclass to receive
1332 drop events (passed in \a event), for the viewport() widget.
1333
1334 \sa QWidget::dropEvent()
1335*/
1336void QAbstractScrollArea::dropEvent(QDropEvent *)
1337{
1338}
1339
1340
1341#endif
1342
1343/*!
1344 This virtual handler is called when the scroll bars are moved by
1345 \a dx, \a dy, and consequently the viewport's contents should be
1346 scrolled accordingly.
1347
1348 The default implementation simply calls update() on the entire
1349 viewport(), subclasses can reimplement this handler for
1350 optimization purposes, or - like QScrollArea - to move a contents
1351 widget. The parameters \a dx and \a dy are there for convenience,
1352 so that the class knows how much should be scrolled (useful
1353 e.g. when doing pixel-shifts). You may just as well ignore these
1354 values and scroll directly to the position the scroll bars
1355 indicate.
1356
1357 Calling this function in order to scroll programmatically is an
1358 error, use the scroll bars instead (e.g. by calling
1359 QScrollBar::setValue() directly).
1360*/
1361void QAbstractScrollArea::scrollContentsBy(int, int)
1362{
1363 viewport()->update();
1364}
1365
1366bool QAbstractScrollAreaPrivate::canStartScrollingAt( const QPoint &startPos )
1367{
1368 Q_Q(QAbstractScrollArea);
1369
1370#if QT_CONFIG(graphicsview)
1371 // don't start scrolling when a drag mode has been set.
1372 // don't start scrolling on a movable item.
1373 if (QGraphicsView *view = qobject_cast<QGraphicsView *>(object: q)) {
1374 if (view->dragMode() != QGraphicsView::NoDrag)
1375 return false;
1376
1377 QGraphicsItem *childItem = view->itemAt(pos: startPos);
1378
1379 if (childItem && (childItem->flags() & QGraphicsItem::ItemIsMovable))
1380 return false;
1381 }
1382#endif
1383
1384 // don't start scrolling on a QAbstractSlider
1385 if (qobject_cast<QAbstractSlider *>(object: q->viewport()->childAt(p: startPos))) {
1386 return false;
1387 }
1388
1389 return true;
1390}
1391
1392void QAbstractScrollAreaPrivate::flashScrollBars()
1393{
1394 bool htransient = hbar->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: hbar);
1395 if ((hbarpolicy != Qt::ScrollBarAlwaysOff) && (hbarpolicy == Qt::ScrollBarAsNeeded || htransient))
1396 hbar->d_func()->flash();
1397 bool vtransient = vbar->style()->styleHint(stylehint: QStyle::SH_ScrollBar_Transient, opt: nullptr, widget: vbar);
1398 if ((vbarpolicy != Qt::ScrollBarAlwaysOff) && (vbarpolicy == Qt::ScrollBarAsNeeded || vtransient))
1399 vbar->d_func()->flash();
1400}
1401
1402void QAbstractScrollAreaPrivate::setScrollBarTransient(QScrollBar *scrollBar, bool transient)
1403{
1404 scrollBar->d_func()->setTransient(transient);
1405}
1406
1407void QAbstractScrollAreaPrivate::_q_hslide(int x)
1408{
1409 Q_Q(QAbstractScrollArea);
1410 int dx = xoffset - x;
1411 xoffset = x;
1412 q->scrollContentsBy(dx, 0);
1413 flashScrollBars();
1414}
1415
1416void QAbstractScrollAreaPrivate::_q_vslide(int y)
1417{
1418 Q_Q(QAbstractScrollArea);
1419 int dy = yoffset - y;
1420 yoffset = y;
1421 q->scrollContentsBy(0, dy);
1422 flashScrollBars();
1423}
1424
1425void QAbstractScrollAreaPrivate::_q_showOrHideScrollBars()
1426{
1427 layoutChildren();
1428}
1429
1430QPoint QAbstractScrollAreaPrivate::contentsOffset() const
1431{
1432 Q_Q(const QAbstractScrollArea);
1433 QPoint offset;
1434 if (vbar->isVisible())
1435 offset.setY(vbar->value());
1436 if (hbar->isVisible()) {
1437 if (q->isRightToLeft())
1438 offset.setX(hbar->maximum() - hbar->value());
1439 else
1440 offset.setX(hbar->value());
1441 }
1442 return offset;
1443}
1444
1445/*!
1446 \reimp
1447
1448*/
1449QSize QAbstractScrollArea::minimumSizeHint() const
1450{
1451 Q_D(const QAbstractScrollArea);
1452 int hsbExt = d->hbar->sizeHint().height();
1453 int vsbExt = d->vbar->sizeHint().width();
1454 int extra = 2 * d->frameWidth;
1455 QStyleOption opt;
1456 opt.initFrom(w: this);
1457 if ((d->frameStyle != QFrame::NoFrame)
1458 && style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: &opt, widget: this)) {
1459 extra += style()->pixelMetric(metric: QStyle::PM_ScrollView_ScrollBarSpacing, option: &opt, widget: this);
1460 }
1461 return QSize(d->scrollBarContainers[Qt::Horizontal]->sizeHint().width() + vsbExt + extra,
1462 d->scrollBarContainers[Qt::Vertical]->sizeHint().height() + hsbExt + extra);
1463}
1464
1465/*!
1466 Returns the sizeHint property of the scroll area. The size is determined by using
1467 viewportSizeHint() plus some extra space for scroll bars, if needed.
1468 \reimp
1469*/
1470QSize QAbstractScrollArea::sizeHint() const
1471{
1472 Q_D(const QAbstractScrollArea);
1473 if (d->sizeAdjustPolicy == QAbstractScrollArea::AdjustIgnored)
1474 return QSize(256, 192);
1475
1476 if (!d->sizeHint.isValid() || d->sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents) {
1477 const int f = 2 * d->frameWidth;
1478 const QSize frame( f, f );
1479 const bool vbarHidden = d->vbar->isHidden() || d->vbarpolicy == Qt::ScrollBarAlwaysOff;
1480 const bool hbarHidden = d->hbar->isHidden() || d->hbarpolicy == Qt::ScrollBarAlwaysOff;
1481 const QSize scrollbars(vbarHidden ? 0 : d->vbar->sizeHint().width(),
1482 hbarHidden ? 0 : d->hbar->sizeHint().height());
1483 d->sizeHint = frame + scrollbars + viewportSizeHint();
1484 }
1485 return d->sizeHint;
1486}
1487
1488/*!
1489 \since 5.2
1490 Returns the recommended size for the viewport.
1491 The default implementation returns viewport()->sizeHint().
1492 Note that the size is just the viewport's size, without any scroll bars visible.
1493 */
1494QSize QAbstractScrollArea::viewportSizeHint() const
1495{
1496 Q_D(const QAbstractScrollArea);
1497 if (d->viewport) {
1498 const QSize sh = d->viewport->sizeHint();
1499 if (sh.isValid()) {
1500 return sh;
1501 }
1502 }
1503 const int h = qMax(a: 10, b: fontMetrics().height());
1504 return QSize(6 * h, 4 * h);
1505}
1506
1507/*!
1508 \since 5.2
1509 \property QAbstractScrollArea::sizeAdjustPolicy
1510 This property holds the policy describing how the size of the scroll area changes when the
1511 size of the viewport changes.
1512
1513 The default policy is QAbstractScrollArea::AdjustIgnored.
1514 Changing this property might actually resize the scrollarea.
1515*/
1516
1517QAbstractScrollArea::SizeAdjustPolicy QAbstractScrollArea::sizeAdjustPolicy() const
1518{
1519 Q_D(const QAbstractScrollArea);
1520 return d->sizeAdjustPolicy;
1521}
1522
1523void QAbstractScrollArea::setSizeAdjustPolicy(SizeAdjustPolicy policy)
1524{
1525 Q_D(QAbstractScrollArea);
1526 if (d->sizeAdjustPolicy == policy)
1527 return;
1528
1529 d->sizeAdjustPolicy = policy;
1530 d->sizeHint = QSize();
1531 updateGeometry();
1532}
1533
1534/*!
1535 This slot is called by QAbstractScrollArea after setViewport(\a
1536 viewport) has been called. Reimplement this function in a
1537 subclass of QAbstractScrollArea to initialize the new \a viewport
1538 before it is used.
1539
1540 \sa setViewport()
1541*/
1542void QAbstractScrollArea::setupViewport(QWidget *viewport)
1543{
1544 Q_UNUSED(viewport);
1545}
1546
1547QT_END_NAMESPACE
1548
1549#include "moc_qabstractscrollarea.cpp"
1550#include "moc_qabstractscrollarea_p.cpp"
1551
1552#endif // QT_CONFIG(scrollarea)
1553

source code of qtbase/src/widgets/widgets/qabstractscrollarea.cpp