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 <qscrollarea.h> |
35 | #include <qlayout.h> |
36 | #include <qscrollbar.h> |
37 | |
38 | class tst_QScrollArea : public QObject |
39 | { |
40 | Q_OBJECT |
41 | |
42 | public: |
43 | tst_QScrollArea(); |
44 | virtual ~tst_QScrollArea(); |
45 | |
46 | private slots: |
47 | void getSetCheck(); |
48 | void ensureMicroFocusVisible_Task_167838(); |
49 | void checkHFW_Task_197736(); |
50 | void stableHeightForWidth(); |
51 | }; |
52 | |
53 | tst_QScrollArea::tst_QScrollArea() |
54 | { |
55 | } |
56 | |
57 | tst_QScrollArea::~tst_QScrollArea() |
58 | { |
59 | } |
60 | |
61 | // Testing get/set functions |
62 | void tst_QScrollArea::getSetCheck() |
63 | { |
64 | QScrollArea obj1; |
65 | // QWidget * QScrollArea::widget() |
66 | // void QScrollArea::setWidget(QWidget *) |
67 | QWidget *var1 = new QWidget(); |
68 | obj1.setWidget(var1); |
69 | QCOMPARE(var1, obj1.widget()); |
70 | obj1.setWidget((QWidget *)0); |
71 | QCOMPARE(var1, obj1.widget()); // Cannot set a 0-widget. Old widget returned |
72 | // delete var1; // No delete, since QScrollArea takes ownership |
73 | |
74 | // bool QScrollArea::widgetResizable() |
75 | // void QScrollArea::setWidgetResizable(bool) |
76 | obj1.setWidgetResizable(false); |
77 | QCOMPARE(false, obj1.widgetResizable()); |
78 | obj1.setWidgetResizable(true); |
79 | QCOMPARE(true, obj1.widgetResizable()); |
80 | } |
81 | |
82 | class WidgetWithMicroFocus : public QWidget |
83 | { |
84 | public: |
85 | WidgetWithMicroFocus(QWidget *parent = 0) : QWidget(parent) |
86 | { |
87 | setBackgroundRole(QPalette::Dark); |
88 | } |
89 | protected: |
90 | QVariant inputMethodQuery(Qt::InputMethodQuery query) const |
91 | { |
92 | if (query == Qt::ImCursorRectangle) |
93 | return QRect(width() / 2, height() / 2, 5, 5); |
94 | return QWidget::inputMethodQuery(query); |
95 | } |
96 | // void paintEvent(QPaintEvent *event) |
97 | // { |
98 | // QPainter painter(this); |
99 | // painter.fillRect(rect(), QBrush(Qt::red)); |
100 | // } |
101 | }; |
102 | |
103 | void tst_QScrollArea::ensureMicroFocusVisible_Task_167838() |
104 | { |
105 | QScrollArea scrollArea; |
106 | scrollArea.resize(w: 100, h: 100); |
107 | scrollArea.show(); |
108 | QWidget *parent = new QWidget; |
109 | parent->setLayout(new QVBoxLayout); |
110 | QWidget *child = new WidgetWithMicroFocus; |
111 | parent->layout()->addWidget(w: child); |
112 | parent->resize(w: 300, h: 300); |
113 | scrollArea.setWidget(parent); |
114 | scrollArea.ensureWidgetVisible(childWidget: child, xmargin: 10, ymargin: 10); |
115 | QRect microFocus = child->inputMethodQuery(Qt::ImCursorRectangle).toRect(); |
116 | QPoint p = child->mapTo(scrollArea.viewport(), microFocus.topLeft()); |
117 | microFocus.translate(p: p - microFocus.topLeft()); |
118 | QVERIFY(scrollArea.viewport()->rect().contains(microFocus)); |
119 | } |
120 | |
121 | class HFWWidget : public QWidget |
122 | { |
123 | public: |
124 | HFWWidget(); |
125 | int heightForWidth(int w) const; |
126 | }; |
127 | |
128 | HFWWidget::HFWWidget() |
129 | : QWidget() |
130 | { |
131 | setMinimumSize(QSize(100,50)); |
132 | QSizePolicy s = sizePolicy(); |
133 | s.setHeightForWidth(true); |
134 | setSizePolicy(s); |
135 | } |
136 | |
137 | int HFWWidget::heightForWidth(int w) const |
138 | { |
139 | // Mimic a label - the narrower we are, the taller we have to be |
140 | if (w > 0) |
141 | return 40000 / w; |
142 | else |
143 | return 40000; |
144 | } |
145 | |
146 | void tst_QScrollArea::checkHFW_Task_197736() |
147 | { |
148 | QScrollArea scrollArea; |
149 | HFWWidget *w = new HFWWidget; |
150 | scrollArea.resize(w: 200,h: 100); |
151 | scrollArea.show(); |
152 | scrollArea.setWidgetResizable(true); |
153 | scrollArea.setWidget(w); |
154 | |
155 | // at 200x100px, we expect HFW to be 200px tall, not 100px |
156 | QVERIFY(w->height() >= 200); |
157 | |
158 | // at 200x300px, we expect HFW to be 300px tall (the heightForWidth is a min, not prescribed) |
159 | scrollArea.resize(QSize(200, 300)); |
160 | QVERIFY(w->height() >= 250); // 50px for a fudge factor (size of frame margins/scrollbars etc) |
161 | |
162 | // make sure this only happens with widget resizable |
163 | scrollArea.setWidgetResizable(false); |
164 | scrollArea.resize(QSize(100,100)); |
165 | w->resize(QSize(200,200)); |
166 | QCOMPARE(w->width(), 200); |
167 | QCOMPARE(w->height(), 200); |
168 | } |
169 | |
170 | |
171 | /* |
172 | If the scroll area rides the size where, due to the height-for-width |
173 | implementation of the widget, the vertical scrollbar is needed only |
174 | if the vertical scrollbar is visible, then we don't want it to flip |
175 | back and forth, but rather constrain the width of the widget. |
176 | See QTBUG-92958. |
177 | */ |
178 | void tst_QScrollArea::stableHeightForWidth() |
179 | { |
180 | struct HeightForWidthWidget : public QWidget |
181 | { |
182 | HeightForWidthWidget() |
183 | { |
184 | QSizePolicy policy = sizePolicy(); |
185 | policy.setHeightForWidth(true); |
186 | setSizePolicy(policy); |
187 | } |
188 | // Aspect ratio 1:1 |
189 | int heightForWidth(int width) const override { return width; } |
190 | }; |
191 | |
192 | class HeightForWidthArea : public QScrollArea |
193 | { |
194 | public: |
195 | HeightForWidthArea() |
196 | { |
197 | this->verticalScrollBar()->installEventFilter(filterObj: this); |
198 | } |
199 | protected: |
200 | bool eventFilter(QObject *obj, QEvent *e) override |
201 | { |
202 | if (obj == verticalScrollBar() && e->type() == QEvent::Hide) |
203 | ++m_hideCount; |
204 | return QScrollArea::eventFilter(obj,e); |
205 | } |
206 | public: |
207 | int m_hideCount = 0; |
208 | }; |
209 | |
210 | HeightForWidthArea area; |
211 | HeightForWidthWidget equalWHWidget; |
212 | area.setWidget(&equalWHWidget); |
213 | area.setWidgetResizable(true); |
214 | // at this size, the widget wants to be 501 pixels high, |
215 | // requiring a vertical scrollbar in a 499 pixel high area. |
216 | // but the width resulting from showing the scrollbar would |
217 | // be less than 499, so no scrollbars would be needed anymore. |
218 | area.resize(w: 501, h: 499); |
219 | area.show(); |
220 | QTest::qWait(ms: 500); |
221 | // if the scrollbar got hidden more than once, then the layout |
222 | // isn't stable. |
223 | QVERIFY(area.m_hideCount <= 1); |
224 | } |
225 | |
226 | QTEST_MAIN(tst_QScrollArea) |
227 | #include "tst_qscrollarea.moc" |
228 | |