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

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