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

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