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 <qapplication.h>
33#include <qtoolbar.h>
34#include <qcombobox.h>
35#include <qwidgetaction.h>
36#include <qlabel.h>
37#include <qmenu.h>
38#include <qmainwindow.h>
39#include <qmenubar.h>
40
41#include <QtTest/private/qtesthelpers_p.h>
42
43using namespace QTestPrivate;
44
45class tst_QWidgetAction : public QObject
46{
47 Q_OBJECT
48private slots:
49 void initTestCase();
50 void cleanup();
51 void defaultWidget();
52 void visibilityUpdate();
53 void customWidget();
54 void keepOwnership();
55 void visibility();
56 void setEnabled();
57 void popup();
58 void releaseWidgetCrash();
59};
60
61void tst_QWidgetAction::initTestCase()
62{
63 // Disable menu/combo animations to prevent the alpha widgets from getting in the
64 // way in popup(), failing the top level leak check in cleanup().
65 QApplication::setEffectEnabled(Qt::UI_AnimateMenu, enable: false);
66 QApplication::setEffectEnabled(Qt::UI_AnimateCombo, enable: false);
67}
68
69void tst_QWidgetAction::cleanup()
70{
71 QVERIFY(QApplication::topLevelWidgets().isEmpty());
72}
73
74void tst_QWidgetAction::defaultWidget()
75{
76 {
77 QToolBar tb1;
78
79 QPointer<QComboBox> combo = new QComboBox(&tb1);
80
81 QWidgetAction *action = new QWidgetAction(0);
82 action->setDefaultWidget(combo);
83
84 QVERIFY(!combo->isVisible());
85 QVERIFY(!combo->parent());
86 QVERIFY(action->isVisible());
87
88 delete action;
89 QVERIFY(!combo);
90 }
91 {
92 QToolBar tb1;
93
94 QPointer<QComboBox> combo = new QComboBox(&tb1);
95 combo->hide();
96
97 QWidgetAction *action = new QWidgetAction(0);
98 action->setDefaultWidget(combo);
99
100 // explicitly hidden widgets should also set the action invisible
101 QVERIFY(!action->isVisible());
102
103 delete action;
104 }
105 {
106 QPointer<QComboBox> combo = new QComboBox(0);
107 setFrameless(combo.data());
108 combo->show();
109
110 QWidgetAction *action = new QWidgetAction(0);
111 action->setDefaultWidget(combo);
112
113 QVERIFY(action->isVisible());
114 QVERIFY(!combo->isVisible());
115
116 delete action;
117 }
118 {
119 QToolBar tb1;
120 setFrameless(&tb1);
121 tb1.show();
122 QToolBar tb2;
123 setFrameless(&tb2);
124 tb2.show();
125
126 QPointer<QComboBox> combo = new QComboBox(0);
127
128 QWidgetAction *action = new QWidgetAction(0);
129 action->setDefaultWidget(combo);
130
131 tb1.addAction(action);
132 QCOMPARE(combo->parent(), &tb1);
133 qApp->processEvents();
134 qApp->processEvents();
135 QVERIFY(combo->isVisible());
136
137 // not supported, not supposed to work, hence the parent() check
138 tb2.addAction(action);
139 QCOMPARE(combo->parent(), &tb1);
140
141 tb2.removeAction(action);
142 tb1.removeAction(action);
143
144 qApp->processEvents(); //the call to hide is delayd by the toolbar layout
145 QVERIFY(!combo->isVisible());
146
147 tb2.addAction(action);
148 qApp->processEvents(); //the call to hide is delayd by the toolbar layout
149 qApp->processEvents();
150 QCOMPARE(combo->parent(), &tb2);
151 QVERIFY(combo->isVisible());
152
153 tb1.addAction(action);
154 QCOMPARE(combo->parent(), &tb2);
155
156 delete action;
157 QVERIFY(!combo);
158 }
159 {
160 QWidgetAction *a = new QWidgetAction(0);
161 QVERIFY(!a->defaultWidget());
162
163 QPointer<QComboBox> combo1 = new QComboBox;
164 a->setDefaultWidget(combo1);
165 QCOMPARE(a->defaultWidget(), combo1.data());
166 a->setDefaultWidget(combo1);
167 QVERIFY(combo1);
168 QCOMPARE(a->defaultWidget(), combo1.data());
169
170 QPointer<QComboBox> combo2 = new QComboBox;
171 QVERIFY(combo1 != combo2);
172
173 a->setDefaultWidget(combo2);
174 QVERIFY(!combo1);
175 QCOMPARE(a->defaultWidget(), combo2.data());
176
177 delete a;
178 QVERIFY(!combo2);
179 }
180}
181
182void tst_QWidgetAction::visibilityUpdate()
183{
184 // actually keeping the widget's state in sync with the
185 // action in terms of visibility is QToolBar's responsibility.
186 QToolBar tb;
187 setFrameless(&tb);
188 tb.show();
189
190 QComboBox *combo = new QComboBox(0);
191 QWidgetAction *action = new QWidgetAction(0);
192 action->setDefaultWidget(combo);
193
194 tb.addAction(action);
195 //the call to show is delayed by the toolbar layout
196 QTRY_VERIFY(combo->isVisible());
197 QVERIFY(action->isVisible());
198
199 action->setVisible(false);
200 //the call to hide is delayed by the toolbar layout
201 QTRY_VERIFY(!combo->isVisible());
202
203 delete action;
204 // action also deletes combo
205}
206
207class ComboAction : public QWidgetAction
208{
209public:
210 inline ComboAction(QObject *parent) : QWidgetAction(parent) {}
211
212 QList<QWidget *> createdWidgets() const { return QWidgetAction::createdWidgets(); }
213
214protected:
215 virtual QWidget *createWidget(QWidget *parent);
216};
217
218QWidget *ComboAction::createWidget(QWidget *parent)
219{
220 return new QComboBox(parent);
221}
222
223void tst_QWidgetAction::customWidget()
224{
225 QToolBar tb1;
226 setFrameless(&tb1);
227 tb1.show();
228 QToolBar tb2;
229 setFrameless(&tb2);
230 tb2.show();
231
232 ComboAction *action = new ComboAction(0);
233
234 tb1.addAction(action);
235
236 QList<QWidget *> combos = action->createdWidgets();
237 QCOMPARE(combos.count(), 1);
238
239 QPointer<QComboBox> combo1 = qobject_cast<QComboBox *>(object: combos.at(i: 0));
240 QVERIFY(combo1);
241
242 tb2.addAction(action);
243
244 combos = action->createdWidgets();
245 QCOMPARE(combos.count(), 2);
246
247 QCOMPARE(combos.at(0), combo1.data());
248 QPointer<QComboBox> combo2 = qobject_cast<QComboBox *>(object: combos.at(i: 1));
249 QVERIFY(combo2);
250
251 tb2.removeAction(action);
252 QVERIFY(combo2);
253 // widget is deleted using deleteLater(), so process that posted event
254 QCoreApplication::sendPostedEvents(receiver: combo2, event_type: QEvent::DeferredDelete);
255 QVERIFY(!combo2);
256
257 delete action;
258 QVERIFY(!combo1);
259 QVERIFY(!combo2);
260}
261
262void tst_QWidgetAction::keepOwnership()
263{
264 QPointer<QComboBox> combo = new QComboBox;
265 QWidgetAction *action = new QWidgetAction(0);
266 action->setDefaultWidget(combo);
267
268 {
269 QToolBar *tb = new QToolBar;
270 tb->addAction(action);
271 QCOMPARE(combo->parent(), tb);
272 delete tb;
273 }
274
275 QVERIFY(combo);
276 delete action;
277 QVERIFY(!combo);
278}
279
280void tst_QWidgetAction::visibility()
281{
282 {
283 QWidgetAction *a = new QWidgetAction(0);
284 QComboBox *combo = new QComboBox;
285 a->setDefaultWidget(combo);
286
287 QToolBar *tb = new QToolBar;
288 setFrameless(tb);
289 tb->addAction(action: a);
290 QVERIFY(!combo->isVisible());
291 tb->show();
292 QVERIFY(combo->isVisible());
293
294 delete tb;
295
296 delete a;
297 }
298 {
299 QWidgetAction *a = new QWidgetAction(0);
300 QComboBox *combo = new QComboBox;
301 a->setDefaultWidget(combo);
302
303 QToolBar *tb = new QToolBar;
304 tb->addAction(action: a);
305 QVERIFY(!combo->isVisible());
306
307 QToolBar *tb2 = new QToolBar;
308 setFrameless(tb2);
309 tb->removeAction(action: a);
310 tb2->addAction(action: a);
311 QVERIFY(!combo->isVisible());
312 tb2->show();
313 QVERIFY(combo->isVisible());
314
315 delete tb;
316 delete tb2;
317
318 delete a;
319 }
320}
321
322void tst_QWidgetAction::setEnabled()
323{
324 QToolBar toolbar;
325 setFrameless(&toolbar);
326 QComboBox *combobox = new QComboBox;
327 QAction *action = toolbar.addWidget(widget: combobox);
328 toolbar.show();
329
330 QVERIFY(action->isEnabled());
331 QVERIFY(combobox->isEnabled());
332
333 action->setEnabled(false);
334 QVERIFY(!action->isEnabled());
335 QVERIFY(!combobox->isEnabled());
336
337 action->setEnabled(true);
338 QVERIFY(action->isEnabled());
339 QVERIFY(combobox->isEnabled());
340
341 combobox->setEnabled(false);
342 QVERIFY(!combobox->isEnabled());
343
344 combobox->setEnabled(true);
345 QVERIFY(action->isEnabled());
346 QVERIFY(combobox->isEnabled());
347
348
349 QWidgetAction aw(0);
350 aw.setEnabled(false);
351 QVERIFY(!aw.isEnabled());
352
353 combobox = new QComboBox;
354 aw.setDefaultWidget(combobox);
355 QVERIFY(!aw.isEnabled());
356 QVERIFY(!combobox->isEnabled());
357
358 // Make sure we don't change the default widget's Qt::WA_ForceDisabled attribute
359 // during a normal disable/enable operation (task 207433).
360 {
361 QToolBar toolBar;
362 QWidget widget;
363 toolBar.addWidget(widget: &widget); // creates a QWidgetAction and sets 'widget' as the default widget.
364 QVERIFY(!widget.testAttribute(Qt::WA_ForceDisabled));
365
366 toolBar.setEnabled(false);
367 QVERIFY(toolBar.testAttribute(Qt::WA_ForceDisabled));
368 QVERIFY(!widget.isEnabled());
369 QVERIFY(!widget.testAttribute(Qt::WA_ForceDisabled));
370
371 toolBar.setEnabled(true);
372 QVERIFY(widget.isEnabled());
373 QVERIFY(!widget.testAttribute(Qt::WA_ForceDisabled));
374 }
375}
376
377void tst_QWidgetAction::popup()
378{
379 QPointer<QLabel> l = new QLabel("test");
380 QWidgetAction action(0);
381 action.setDefaultWidget(l);
382
383 {
384 QMenu menu;
385 menu.addAction(action: &action);
386 QTimer::singleShot(msec: 100, receiver: &menu, SLOT(close()));
387 menu.exec();
388 }
389
390 QVERIFY(!l.isNull());
391 delete l;
392}
393
394class CrashedAction : public QWidgetAction
395{
396public:
397 inline CrashedAction(QObject *parent) : QWidgetAction(parent) { }
398
399 virtual QWidget *createWidget(QWidget *parent) {
400 return new QWidget(parent);
401 }
402};
403
404void tst_QWidgetAction::releaseWidgetCrash()
405{
406 // this should not crash!
407 QMainWindow *w = new QMainWindow;
408 QAction *a = new CrashedAction(w);
409 QMenu *menu = w->menuBar()->addMenu(title: "Test");
410 menu->addAction(text: "foo");
411 menu->addAction(action: a);
412 menu->addAction(text: "bar");
413 delete w;
414}
415
416QTEST_MAIN(tst_QWidgetAction)
417#include "tst_qwidgetaction.moc"
418

source code of qtbase/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp