1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31
32#include <qcoreapplication.h>
33#include <qdebug.h>
34#include <qabstractscrollarea.h>
35#include <qscrollarea.h>
36#include <qscrollbar.h>
37#include <qlabel.h>
38#include <qwidget.h>
39#include <qdialog.h>
40#include <qscroller.h>
41
42class tst_QAbstractScrollArea : public QObject
43{
44Q_OBJECT
45
46public:
47 tst_QAbstractScrollArea();
48 virtual ~tst_QAbstractScrollArea();
49private slots:
50 void scrollBarWidgets();
51 void setScrollBars();
52 void setScrollBars2();
53 void objectNaming();
54 void patternBackground();
55
56 void viewportCrash();
57 void task214488_layoutDirection_data();
58 void task214488_layoutDirection();
59
60 void margins();
61 void resizeWithOvershoot();
62};
63
64tst_QAbstractScrollArea::tst_QAbstractScrollArea()
65{
66}
67
68tst_QAbstractScrollArea::~tst_QAbstractScrollArea()
69{
70}
71
72void tst_QAbstractScrollArea::scrollBarWidgets()
73{
74 QWidget *w1 = new QWidget(0);
75 QWidget *w2 = new QWidget(0);
76 QWidget *w3 = new QWidget(0);
77
78 Qt::Alignment all = Qt::AlignLeft | Qt::AlignRight | Qt::AlignTop | Qt::AlignBottom;
79
80 QWidgetList w1List = QWidgetList() << w1;
81 QWidgetList w2List = QWidgetList() << w2;
82 QWidgetList w3List = QWidgetList() << w3;
83
84 QWidgetList w1w2List = w1List + w2List;
85 QWidgetList allList = w1List + w2List + w3List;
86
87 QAbstractScrollArea area;
88 area.show();
89 QCOMPARE(area.scrollBarWidgets(all), QWidgetList());
90
91 area.addScrollBarWidget(widget: w1, alignment: Qt::AlignLeft);
92 QCOMPARE(area.scrollBarWidgets(all), w1List);
93 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
94 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
95 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
96 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), QWidgetList());
97
98 area.addScrollBarWidget(widget: w2, alignment: Qt::AlignBottom);
99 QCOMPARE(area.scrollBarWidgets(all), w1w2List);
100 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
101 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
102 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
103 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), w2List);
104
105 // duplicate add
106 area.addScrollBarWidget(widget: w2, alignment: Qt::AlignBottom);
107 QCOMPARE(area.scrollBarWidgets(all), w1w2List);
108 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
109 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
110 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
111 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), w2List);
112
113 //reparent
114 w2->setParent(w1);
115 QCOMPARE(area.scrollBarWidgets(all), w1List);
116 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
117 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
118 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
119 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), QWidgetList());
120
121 // add after reparent
122 area.addScrollBarWidget(widget: w2, alignment: Qt::AlignBottom);
123 QCOMPARE(area.scrollBarWidgets(all), w1w2List);
124 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
125 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
126 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
127 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), w2List);
128
129 auto sort = [](const QWidgetList l) {
130 QWidgetList list = l;
131 std::sort(first: list.begin(), last: list.end());
132 return list;
133 };
134
135 // two widgets at Bottom.
136 area.addScrollBarWidget(widget: w3, alignment: Qt::AlignBottom);
137 QCOMPARE(sort(area.scrollBarWidgets(all)), sort(allList));
138 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), w1List);
139 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
140 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
141 QCOMPARE(sort(area.scrollBarWidgets(Qt::AlignBottom)), sort(w2List + w3List));
142
143 //delete
144 delete w1;
145 delete w2;
146 delete w3;
147
148 QCOMPARE(area.scrollBarWidgets(all), QWidgetList());
149 QCOMPARE(area.scrollBarWidgets(Qt::AlignLeft), QWidgetList());
150 QCOMPARE(area.scrollBarWidgets(Qt::AlignRight), QWidgetList());
151 QCOMPARE(area.scrollBarWidgets(Qt::AlignTop), QWidgetList());
152 QCOMPARE(area.scrollBarWidgets(Qt::AlignBottom), QWidgetList());
153}
154
155void tst_QAbstractScrollArea::setScrollBars()
156{
157 QScrollArea scrollArea;
158 scrollArea.resize(w: 300, h: 300);
159 scrollArea.show();
160
161 QPointer<QScrollBar> vbar = scrollArea.verticalScrollBar();
162 QPointer<QScrollBar> hbar = scrollArea.horizontalScrollBar();
163
164 // Now set properties on the scroll bars
165 scrollArea.verticalScrollBar()->setInvertedAppearance(true);
166 scrollArea.verticalScrollBar()->setInvertedControls(true);
167 scrollArea.verticalScrollBar()->setTracking(true);
168 scrollArea.verticalScrollBar()->setRange(min: -100, max: 100);
169 scrollArea.verticalScrollBar()->setPageStep(42);
170 scrollArea.verticalScrollBar()->setSingleStep(3);
171 scrollArea.verticalScrollBar()->setValue(43);
172 scrollArea.horizontalScrollBar()->setInvertedAppearance(true);
173 scrollArea.horizontalScrollBar()->setInvertedControls(true);
174 scrollArea.horizontalScrollBar()->setTracking(true);
175 scrollArea.horizontalScrollBar()->setRange(min: -100, max: 100);
176 scrollArea.horizontalScrollBar()->setPageStep(42);
177 scrollArea.horizontalScrollBar()->setSingleStep(3);
178 scrollArea.horizontalScrollBar()->setValue(43);
179
180 qApp->processEvents();
181
182 // Then replace the scroll bars
183 scrollArea.setVerticalScrollBar(new QScrollBar);
184 scrollArea.setHorizontalScrollBar(new QScrollBar);
185
186 // Check that the old ones were deleted
187 QVERIFY(!vbar);
188 QVERIFY(!hbar);
189
190 qApp->processEvents();
191
192 // Check that all properties have been populated
193 QVERIFY(scrollArea.verticalScrollBar()->invertedAppearance());
194 QVERIFY(scrollArea.verticalScrollBar()->invertedControls());
195 QVERIFY(scrollArea.verticalScrollBar()->hasTracking());
196 QVERIFY(scrollArea.verticalScrollBar()->isVisible());
197 QCOMPARE(scrollArea.verticalScrollBar()->minimum(), -100);
198 QCOMPARE(scrollArea.verticalScrollBar()->maximum(), 100);
199 QCOMPARE(scrollArea.verticalScrollBar()->pageStep(), 42);
200 QCOMPARE(scrollArea.verticalScrollBar()->singleStep(), 3);
201 QCOMPARE(scrollArea.verticalScrollBar()->value(), 43);
202 QVERIFY(scrollArea.horizontalScrollBar()->invertedAppearance());
203 QVERIFY(scrollArea.horizontalScrollBar()->invertedControls());
204 QVERIFY(scrollArea.horizontalScrollBar()->hasTracking());
205 QVERIFY(scrollArea.horizontalScrollBar()->isVisible());
206 QCOMPARE(scrollArea.horizontalScrollBar()->minimum(), -100);
207 QCOMPARE(scrollArea.horizontalScrollBar()->maximum(), 100);
208 QCOMPARE(scrollArea.horizontalScrollBar()->pageStep(), 42);
209 QCOMPARE(scrollArea.horizontalScrollBar()->singleStep(), 3);
210 QCOMPARE(scrollArea.horizontalScrollBar()->value(), 43);
211}
212
213void tst_QAbstractScrollArea::setScrollBars2()
214{
215 QAbstractScrollArea scrollArea;
216 scrollArea.resize(w: 300, h: 300);
217
218 QScrollBar *hbar = new QScrollBar;
219 scrollArea.setHorizontalScrollBar(hbar);
220 qApp->processEvents();
221 QCOMPARE(scrollArea.horizontalScrollBar(), hbar);
222
223 QScrollBar *vbar = new QScrollBar;
224 scrollArea.setVerticalScrollBar(vbar);
225 qApp->processEvents();
226 QCOMPARE(scrollArea.verticalScrollBar(), vbar);
227
228 scrollArea.horizontalScrollBar()->setRange(min: 0, max: 100);
229 scrollArea.verticalScrollBar()->setRange(min: 0, max: 100);
230 scrollArea.show();
231
232 // Make sure scroll bars are not explicitly hidden by QAbstractScrollArea itself.
233 QVERIFY(hbar->isVisible());
234 QVERIFY(vbar->isVisible());
235
236 // Hide the OLD scroll bar and ensure that the NEW one is hidden.
237 hbar->hide();
238 hbar = new QScrollBar(&scrollArea);
239 scrollArea.setHorizontalScrollBar(hbar);
240 QVERIFY(!hbar->isVisibleTo(hbar->parentWidget()));
241
242 vbar->hide();
243 vbar = new QScrollBar(&scrollArea);
244 scrollArea.setVerticalScrollBar(vbar);
245 QVERIFY(!vbar->isVisibleTo(vbar->parentWidget()));
246
247 vbar->show();
248 hbar->show();
249
250 // Hide the NEW scroll bar and ensure that it's visible
251 // (because the OLD one is visible).
252 hbar = new QScrollBar;
253 hbar->hide();
254 scrollArea.setHorizontalScrollBar(hbar);
255 qApp->processEvents();
256 QVERIFY(hbar->isVisible());
257
258 vbar = new QScrollBar;
259 vbar->hide();
260 scrollArea.setVerticalScrollBar(vbar);
261 qApp->processEvents();
262 QVERIFY(vbar->isVisible());
263
264 vbar->setRange(min: 0, max: 0);
265 qApp->processEvents();
266 QVERIFY(!vbar->isVisible());
267
268 hbar->setRange(min: 0, max: 0);
269 qApp->processEvents();
270 QVERIFY(!hbar->isVisible());
271}
272
273// we need to make sure the viewport internal widget is named
274// qt_scrollarea_viewport, otherwise we're going to confuse Squish
275// and friends.
276void tst_QAbstractScrollArea::objectNaming()
277{
278 QScrollArea area;
279 QCOMPARE(area.viewport()->objectName(), QString("qt_scrollarea_viewport"));
280}
281
282class ViewportCrashWidget : public QDialog
283{
284public:
285 ViewportCrashWidget()
286 {
287 // temprorary set PaintOnScreen to set the nativeChildrenForced flag.
288 setAttribute(Qt::WA_PaintOnScreen, on: true);
289 setAttribute(Qt::WA_PaintOnScreen, on: false);
290
291 setAttribute(Qt::WA_DropSiteRegistered, on: true);
292
293 startTimer(interval: 2000);
294 }
295
296 void timerEvent(QTimerEvent * /* event */)
297 {
298 // should not crash.
299 (void)new QScrollArea(this);
300 accept();
301 }
302};
303
304void tst_QAbstractScrollArea::viewportCrash()
305{
306 ViewportCrashWidget w;
307 // should not crash
308 w.exec();
309}
310
311Q_DECLARE_METATYPE(Qt::LayoutDirection);
312Q_DECLARE_METATYPE(Qt::Key);
313
314void tst_QAbstractScrollArea::task214488_layoutDirection_data()
315{
316 QTest::addColumn<Qt::LayoutDirection>(name: "dir");
317 QTest::addColumn<Qt::Key>(name: "key");
318 QTest::addColumn<bool>(name: "lessThan");
319
320 QTest::newRow(dataTag: "LTR left") << Qt::LeftToRight << Qt::Key_Left << true;
321 QTest::newRow(dataTag: "LTR right") << Qt::LeftToRight << Qt::Key_Right << false;
322 QTest::newRow(dataTag: "RTL left") << Qt::RightToLeft << Qt::Key_Left << false;
323 QTest::newRow(dataTag: "RTL right") << Qt::RightToLeft << Qt::Key_Right << true;
324}
325
326void tst_QAbstractScrollArea::task214488_layoutDirection()
327{
328 QScrollArea scrollArea;
329 scrollArea.resize(w: 200, h: 200);
330 QWidget widget;
331 widget.resize(w: 600, h: 600);
332 scrollArea.setWidget(&widget);
333 scrollArea.show();
334 QScrollBar *hbar = scrollArea.horizontalScrollBar();
335 hbar->setValue((hbar->minimum() + hbar->maximum()) / 2);
336
337 QFETCH(Qt::LayoutDirection, dir);
338 QFETCH(Qt::Key, key);
339 QFETCH(bool, lessThan);
340
341 scrollArea.setLayoutDirection(dir);
342
343 int refValue = hbar->value();
344 qApp->sendEvent(receiver: &scrollArea, event: new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier));
345#ifdef Q_OS_WINRT
346 QEXPECT_FAIL("", "WinRT: Scrollbar is not guaranteed to be visible, as QWidget::resize does not"
347 "work", Abort);
348#endif
349 QVERIFY(lessThan ? (hbar->value() < refValue) : (hbar->value() > refValue));
350}
351
352void tst_QAbstractScrollArea::patternBackground()
353{
354 QWidget topLevel;
355 QScrollArea scrollArea(&topLevel);
356 scrollArea.resize(w: 200, h: 200);
357 QWidget widget;
358 widget.resize(w: 600, h: 600);
359 scrollArea.setWidget(&widget);
360 topLevel.show();
361 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
362
363 QLinearGradient linearGrad(QPointF(250, 250), QPointF(300, 300));
364 linearGrad.setColorAt(pos: 0, color: Qt::yellow);
365 linearGrad.setColorAt(pos: 1, color: Qt::red);
366 QBrush bg(linearGrad);
367 scrollArea.viewport()->setPalette(QPalette(Qt::black, bg, bg, bg, bg, bg, bg, bg, bg));
368 widget.setPalette(Qt::transparent);
369
370
371 QImage image(200, 200, QImage::Format_ARGB32);
372 scrollArea.render(target: &image);
373
374 QCOMPARE(image.pixel(QPoint(20,20)) , QColor(Qt::yellow).rgb());
375
376 QScrollBar *hbar = scrollArea.horizontalScrollBar();
377 hbar->setValue(hbar->maximum());
378 QScrollBar *vbar = scrollArea.verticalScrollBar();
379 vbar->setValue(vbar->maximum());
380
381
382 scrollArea.render(target: &image);
383 QCOMPARE(image.pixel(QPoint(20,20)) , QColor(Qt::red).rgb());
384}
385
386class ScrollArea : public QAbstractScrollArea
387{
388public:
389 using QAbstractScrollArea::setViewportMargins;
390 using QAbstractScrollArea::viewportMargins;
391};
392
393void tst_QAbstractScrollArea::margins()
394{
395 ScrollArea area;
396 QCOMPARE(area.viewportMargins(), QMargins());
397
398 QMargins margins(10, 20, 30, 40);
399 area.setViewportMargins(margins);
400 QCOMPARE(area.viewportMargins(), margins);
401}
402
403void tst_QAbstractScrollArea::resizeWithOvershoot()
404{
405 QWidget window;
406
407 QScrollArea scrollArea(&window);
408 scrollArea.setWidget([]{
409 QWidget *widget = new QWidget;
410 widget->setFixedSize(QSize(0, 200));
411 return widget;
412 }());
413 scrollArea.setGeometry(ax: 0, ay: 20, aw: 100, ah: 100);
414
415 QScroller::grabGesture(target: &scrollArea, gestureType: QScroller::LeftMouseButtonGesture);
416
417 window.show();
418 QVERIFY(QTest::qWaitForWindowExposed(&window));
419
420 const QPoint originAtRest = scrollArea.viewport()->pos();
421
422 QPoint center = scrollArea.viewport()->mapToGlobal(scrollArea.viewport()->rect().center());
423 center = window.windowHandle()->mapFromGlobal(pos: center);
424 QTest::mousePress(window: window.windowHandle(), button: Qt::LeftButton, stateKey: {}, pos: center);
425 QTest::mouseMove(window: window.windowHandle(), pos: center + QPoint(0, 50));
426 QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest + QPoint(0, 25));
427 QPoint overshootPosition = scrollArea.viewport()->pos();
428
429 // trigger a layout of the scroll area while there's overshoot
430 scrollArea.setGeometry(ax: 0, ay: 0, aw: 100, ah: 120);
431 QCOMPARE(scrollArea.viewport()->pos(), overshootPosition);
432 QTest::mouseRelease(window: window.windowHandle(), button: Qt::LeftButton, stateKey: {}, pos: center + QPoint(0, 50));
433 QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest);
434 // Process a few more events and verify that the scroll area
435 // doesn't overcompensate for the overshoot.
436 QApplication::processEvents();
437 QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest);
438}
439
440QTEST_MAIN(tst_QAbstractScrollArea)
441#include "tst_qabstractscrollarea.moc"
442

source code of qtbase/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp