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