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#include <qapplication.h>
32#include <qmainwindow.h>
33#include <qmenubar.h>
34#include <qstyle.h>
35#include <qproxystyle.h>
36#include <qstylefactory.h>
37#include <qaction.h>
38#include <qstyleoption.h>
39#include <QVBoxLayout>
40#include <QLabel>
41#include <QPlainTextEdit>
42#include <qscreen.h>
43
44#include <qobject.h>
45
46QT_FORWARD_DECLARE_CLASS(QMainWindow)
47
48#include <qmenubar.h>
49
50#include <QtTest/private/qtesthelpers_p.h>
51
52using namespace QTestPrivate;
53
54// Helper to calculate the action position in window coordinates
55static inline QPoint widgetToWindowPos(const QWidget *w, const QPoint &pos)
56{
57 const QWindow *window = w->window()->windowHandle();
58 Q_ASSERT(window);
59 return window->mapFromGlobal(pos: w->mapToGlobal(pos));
60}
61
62static QPoint menuBarActionWindowPos(const QMenuBar *mb, QAction *a)
63{
64 return widgetToWindowPos(w: mb, pos: mb->actionGeometry(a).center());
65}
66
67class Menu : public QMenu
68{
69 Q_OBJECT
70 public slots:
71 void addActions()
72 {
73 //this will change the geometry of the menu
74 addAction(text: "action1");
75 addAction(text: "action2");
76 }
77};
78
79struct TestMenu
80{
81 QList<QMenu *> menus;
82 QList<QAction *> actions;
83};
84
85class tst_QMenuBar : public QObject
86{
87 Q_OBJECT
88public:
89 tst_QMenuBar();
90
91private slots:
92 void getSetCheck();
93 void cleanup();
94
95 void clear();
96 void removeItemAt();
97 void removeItemAt_data();
98 void removeItem_data();
99 void removeItem();
100 void count();
101 void insertItem_QString_QObject();
102
103#if !defined(Q_OS_DARWIN)
104 void accel();
105 void activatedCount();
106 void activatedCount_data();
107
108 void check_accelKeys();
109 void check_cursorKeys1();
110 void check_cursorKeys2();
111 void check_cursorKeys3();
112
113 void check_escKey();
114#endif
115 void allowActiveAndDisabled();
116 void taskQTBUG56860_focus();
117 void check_endKey();
118 void check_homeKey();
119
120// void check_mouse1_data();
121// void check_mouse1();
122// void check_mouse2_data();
123// void check_mouse2();
124
125 void check_altPress();
126 void check_altClosePress();
127#if !defined(Q_OS_DARWIN)
128 void check_shortcutPress();
129 void check_menuPosition();
130 void taskQTBUG46812_doNotLeaveMenubarHighlighted();
131#endif
132 void task223138_triggered();
133 void task256322_highlight();
134 void menubarSizeHint();
135#ifndef Q_OS_MACOS
136 void taskQTBUG4965_escapeEaten();
137#endif
138 void taskQTBUG11823_crashwithInvisibleActions();
139 void closeOnSecondClickAndOpenOnThirdClick();
140 void cornerWidgets_data();
141 void cornerWidgets();
142 void taskQTBUG53205_crashReparentNested();
143#ifdef Q_OS_MACOS
144 void taskQTBUG56275_reinsertMenuInParentlessQMenuBar();
145 void QTBUG_57404_existingMenuItemException();
146#endif
147 void QTBUG_25669_menubarActionDoubleTriggered();
148 void taskQTBUG55966_subMenuRemoved();
149 void QTBUG_58344_invalidIcon();
150 void platformMenu();
151 void addActionQt5connect();
152 void QTBUG_65488_hiddenActionTriggered();
153protected slots:
154 void onSimpleActivated( QAction*);
155 void onComplexActionTriggered();
156 void slotForTaskQTBUG53205();
157
158private:
159 TestMenu initSimpleMenuBar(QMenuBar *mb, bool forceNonNative = true);
160 TestMenu initWindowWithSimpleMenuBar(QMainWindow &w, bool forceNonNative = true);
161 QAction *createCharacterAction(QMenu *menu, char lowerAscii);
162 QMenu *addNumberedMenu(QMenuBar *mb, int n);
163 TestMenu initComplexMenuBar(QMenuBar *mb);
164 TestMenu initWindowWithComplexMenuBar(QMainWindow &w);
165
166 QAction* m_lastSimpleAcceleratorId;
167 int m_simpleActivatedCount;
168 int m_complexTriggerCount[int('k')];
169 QMenuBar* taskQTBUG53205MenuBar;
170};
171
172// Testing get/set functions
173void tst_QMenuBar::getSetCheck()
174{
175 QMenuBar obj1;
176 // QAction * QMenuBar::activeAction()
177 // void QMenuBar::setActiveAction(QAction *)
178 QAction *var1 = new QAction(0);
179 obj1.setActiveAction(var1);
180 QCOMPARE(var1, obj1.activeAction());
181 obj1.setActiveAction((QAction *)0);
182 QCOMPARE((QAction *)0, obj1.activeAction());
183 delete var1;
184}
185
186#include <qcursor.h>
187
188/*!
189 Test plan:
190 insertItem (all flavors and combinations)
191 removing menu items
192 clearing the menu
193
194 check the common behaviour + emitted signals for:
195 accelerator keys
196 navigating tru the menu and then pressing ENTER
197 mouse clicks
198 mouse drags
199 combinations of key + mouse (if possible)
200 checked / unckecked state of menu options
201 active / inactive state
202
203 Can't test these without pixmap comparison...
204 show and hide
205 icons in a menu
206 pixmaps in a menu
207
208*/
209
210tst_QMenuBar::tst_QMenuBar() : m_lastSimpleAcceleratorId(0), m_simpleActivatedCount(0)
211{
212 QApplication::setEffectEnabled(Qt::UI_AnimateMenu, enable: false);
213}
214
215void tst_QMenuBar::onSimpleActivated( QAction* action )
216{
217 m_lastSimpleAcceleratorId = action;
218 m_simpleActivatedCount++;
219}
220
221void tst_QMenuBar::cleanup()
222{
223 QVERIFY(QApplication::topLevelWidgets().isEmpty());
224}
225
226// Create a simple menu bar and connect its actions to onSimpleActivated().
227
228TestMenu tst_QMenuBar::initSimpleMenuBar(QMenuBar *mb, bool forceNonNative) {
229 TestMenu result;
230 if (forceNonNative)
231 mb->setNativeMenuBar(false);
232 connect(sender: mb, SIGNAL(triggered(QAction*)), receiver: this, SLOT(onSimpleActivated(QAction*)));
233 QMenu *menu = mb->addMenu(QStringLiteral("&accel"));
234 QAction *action = menu->addAction(QStringLiteral("menu1") );
235 action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_A));
236 action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A));
237 connect(sender: menu, SIGNAL(triggered(QAction*)), receiver: this, SLOT(onSimpleActivated(QAction*)));
238 result.menus << menu;
239 result.actions << action;
240
241 menu = mb->addMenu(QStringLiteral("accel1"));
242 action = menu->addAction(QStringLiteral("&Open...") );
243 action->setShortcut(Qt::Key_O);
244 result.actions << action;
245
246 action = menu->addAction(QStringLiteral("action"));
247 action->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Z));
248 result.actions << action;
249
250 result.menus << menu;
251 connect(sender: menu, SIGNAL(triggered(QAction*)), receiver: this, SLOT(onSimpleActivated(QAction*)));
252
253 m_lastSimpleAcceleratorId = 0;
254 m_simpleActivatedCount = 0;
255
256 return result;
257}
258
259inline TestMenu tst_QMenuBar::initWindowWithSimpleMenuBar(QMainWindow &w, bool forceNonNative)
260{
261 w.resize(w: 200, h: 200);
262 centerOnScreen(w: &w);
263 return initSimpleMenuBar(mb: w.menuBar(), forceNonNative);
264}
265
266// add a menu with number n, set number as data.
267QMenu *tst_QMenuBar::addNumberedMenu(QMenuBar *mb, int n)
268{
269 const QString text = QStringLiteral("Menu &") + QString::number(n);
270 QMenu *menu = mb->addMenu(title: text);
271 menu->setObjectName(text);
272 QAction *action = menu->menuAction();
273 action->setObjectName(text + QStringLiteral("Action"));
274 action->setData(QVariant(n));
275 connect(sender: action, SIGNAL(triggered()), receiver: this, SLOT(onComplexActionTriggered()));
276 return menu;
277}
278
279// Create an action triggering on Ctrl+character, set number as data.
280QAction *tst_QMenuBar::createCharacterAction(QMenu *menu, char lowerAscii)
281{
282 const QString text = QStringLiteral("Item ") + QChar(QLatin1Char(lowerAscii)).toUpper();
283 QAction *action = menu->addAction(text);
284 action->setObjectName(text);
285 action->setData(QVariant(int(lowerAscii)));
286 action->setShortcut(Qt::CTRL + (lowerAscii - 'a' + Qt::Key_A));
287 connect(sender: action, SIGNAL(triggered()), receiver: this, SLOT(onComplexActionTriggered()));
288 return action;
289}
290
291void tst_QMenuBar::onComplexActionTriggered()
292{
293 if (QAction *a = qobject_cast<QAction *>(object: sender()))
294 m_complexTriggerCount[a->data().toInt()]++;
295}
296
297// Create a complex menu bar and connect its actions to onComplexActionTriggered()
298// for their invocations to be counted in m_complexTriggerCount. The index is the
299// menu number (1..n) for the menu bar actions and the ASCII-code of the shortcut
300// character for the actions in the menus.
301TestMenu tst_QMenuBar::initComplexMenuBar(QMenuBar *mb)
302{
303 TestMenu result;
304 mb->setNativeMenuBar(false);
305 QMenu *menu = addNumberedMenu(mb, n: 1);
306 result.menus << menu;
307 for (char c = 'a'; c < 'c'; ++c)
308 result.actions << createCharacterAction(menu, lowerAscii: c);
309
310 menu = addNumberedMenu(mb, n: 2);
311 menu->menuAction()->setData(QVariant(2));
312 result.menus << menu;
313 for (char c = 'c'; c < 'g'; ++c)
314 result.actions << createCharacterAction(menu, lowerAscii: c);
315 menu->addSeparator();
316 for (char c = 'g'; c < 'i'; ++c)
317 result.actions << createCharacterAction(menu, lowerAscii: c);
318
319 QAction *action = mb->addAction(QStringLiteral("M&enu 3"));
320 action->setData(QVariant(3));
321 action->setShortcut(Qt::ALT + Qt::Key_J);
322 connect(sender: action, SIGNAL(triggered()), receiver: this, SLOT(onComplexActionTriggered()));
323 result.actions << action;
324
325 std::fill(m_complexTriggerCount, m_complexTriggerCount + sizeof(m_complexTriggerCount) / sizeof(int), 0);
326
327 return result;
328}
329
330inline TestMenu tst_QMenuBar::initWindowWithComplexMenuBar(QMainWindow &w)
331{
332 w.resize(w: 400, h: 200);
333 centerOnScreen(w: &w);
334 return initComplexMenuBar(mb: w.menuBar());
335}
336
337// On Mac native key events are needed to test menu action activation
338#if !defined(Q_OS_DARWIN)
339void tst_QMenuBar::accel()
340{
341 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
342 QSKIP("Wayland: This fails. Figure out why.");
343
344 // create a popup menu with menu items set the accelerators later...
345 QMainWindow w;
346 const TestMenu menu = initWindowWithSimpleMenuBar(w);
347 w.show();
348 QApplication::setActiveWindow(&w);
349 QVERIFY(QTest::qWaitForWindowActive(&w));
350 // shortcuts won't work unless the window is active
351 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_A, modifier: Qt::ControlModifier );
352 QTest::qWait(ms: 300);
353
354 QCOMPARE( m_lastSimpleAcceleratorId, menu.actions.front() );
355}
356#endif
357
358// On Mac native key events are needed to test menu action activation
359#if !defined(Q_OS_DARWIN)
360void tst_QMenuBar::activatedCount()
361{
362 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
363 QSKIP("Wayland: This fails. Figure out why.");
364
365 // create a popup menu with menu items set the accelerators later...
366 QMainWindow w;
367 QFETCH( bool, forceNonNative );
368 initWindowWithSimpleMenuBar(w, forceNonNative);
369 w.show();
370 QApplication::setActiveWindow(&w);
371 QVERIFY(QTest::qWaitForWindowActive(&w));
372
373 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_A, modifier: Qt::ControlModifier );
374//wait(5000);
375 QCOMPARE( m_simpleActivatedCount, 2 ); //1 from the popupmenu and 1 from the menubar
376}
377
378void tst_QMenuBar::activatedCount_data()
379{
380 QTest::addColumn<bool>(name: "forceNonNative");
381 QTest::newRow( dataTag: "forcing non-native menubar" ) << true;
382 QTest::newRow( dataTag: "not forcing non-native menubar" ) << false;
383}
384#endif
385
386void tst_QMenuBar::clear()
387{
388 QMenuBar menuBar;
389 initSimpleMenuBar(mb: &menuBar);
390 menuBar.clear();
391 QCOMPARE( menuBar.actions().size(), 0 );
392
393 menuBar.clear();
394 for (int i = 0; i < 10; i++) {
395 QMenu *menu = menuBar.addMenu( QStringLiteral("Menu ") + QString::number(i));
396 for (int k = 0; k < i; k++)
397 menu->addAction( QStringLiteral("Item ") + QString::number(k));
398 QCOMPARE( menuBar.actions().size(), i + 1 );
399 }
400 QCOMPARE( menuBar.actions().size(), 10 );
401 menuBar.clear();
402 QCOMPARE( menuBar.actions().size(), 0 );
403}
404
405void tst_QMenuBar::count()
406{
407 QMenuBar menuBar;
408 QCOMPARE( menuBar.actions().size(), 0 );
409
410 for (int i = 0; i < 10; i++) {
411 menuBar.addAction( QStringLiteral("Menu ") + QString::number(i));
412 QCOMPARE( menuBar.actions().size(), i + 1 );
413 }
414}
415
416void tst_QMenuBar::removeItem_data()
417{
418 QTest::addColumn<int>(name: "removeIndex");
419 QTest::newRow( dataTag: "first" ) << 0;
420 QTest::newRow( dataTag: "middle" ) << 1;
421 QTest::newRow( dataTag: "last" ) << 2;
422}
423
424// Basically the same test as removeItemAt, except that we remember and remove id's.
425void tst_QMenuBar::removeItem()
426{
427 QMenuBar menuBar;
428
429 QMenu *pm = new QMenu( "stuff", &menuBar );
430 pm->setTitle("Menu 1");
431 pm->addAction( text: QString("Item 10") );
432 QAction* action1 = menuBar.addMenu( menu: pm );
433
434 pm = new QMenu( &menuBar );
435 pm->setTitle("Menu 2");
436 pm->addAction( text: QString("Item 20") );
437 pm->addAction( text: QString("Item 21") );
438 QAction *action2 = menuBar.addMenu( menu: pm );
439
440 pm = new QMenu( "Menu 3", &menuBar );
441 pm->addAction( text: QString("Item 30") );
442 pm->addAction( text: QString("Item 31") );
443 pm->addAction( text: QString("Item 32") );
444 QAction *action3 = menuBar.addMenu( menu: pm );
445
446 const QList<QAction *> menuBarActions = menuBar.actions();
447
448 QCOMPARE( action1->text(), QString("Menu 1") );
449 QCOMPARE( action2->text(), QString("Menu 2") );
450 QCOMPARE( action3->text(), QString("Menu 3") );
451
452 QVERIFY( menuBarActions.at(0) == action1 );
453 QVERIFY( menuBarActions.at(1) == action2 );
454 QVERIFY( menuBarActions.at(2) == action3 );
455
456 // Ok, now that we know we have created the menu we expect, lets remove an item...
457 QFETCH( int, removeIndex );
458 switch (removeIndex )
459 {
460 case 0: {
461 menuBar.removeAction(action: action1);
462 const QList<QAction *> menuBarActions2 = menuBar.actions();
463 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 2") );
464 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 3") );
465 }
466 break;
467 case 1: {
468 menuBar.removeAction(action: action2);
469 const QList<QAction *> menuBarActions2 = menuBar.actions();
470 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 1") );
471 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 3") );
472 }
473 break;
474 case 2: {
475 menuBar.removeAction(action: action3);
476 const QList<QAction *> menuBarActions2 = menuBar.actions();
477 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 1") );
478 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 2") );
479 }
480 break;
481 }
482 QList<QAction *> menuBarActions2 = menuBar.actions();
483 QVERIFY( menuBarActions2.size() == 2 );
484}
485
486void tst_QMenuBar::removeItemAt_data()
487{
488 QTest::addColumn<int>(name: "removeIndex");
489 QTest::newRow( dataTag: "first" ) << 0;
490 QTest::newRow( dataTag: "middle" ) << 1;
491 QTest::newRow( dataTag: "last" ) << 2;
492}
493
494void tst_QMenuBar::removeItemAt()
495{
496 QMenuBar menuBar;
497 QMenu *pm = new QMenu("Menu 1", &menuBar);
498 pm->addAction( text: QString("Item 10") );
499 menuBar.addMenu( menu: pm );
500
501 pm = new QMenu( "Menu 2", &menuBar);
502 pm->addAction( text: QString("Item 20") );
503 pm->addAction( text: QString("Item 21") );
504 menuBar.addMenu( menu: pm );
505
506 pm = new QMenu( "Menu 3", &menuBar);
507 pm->addAction( text: QString("Item 30") );
508 pm->addAction( text: QString("Item 31") );
509 pm->addAction( text: QString("Item 32") );
510 menuBar.addMenu( menu: pm );
511
512 QList<QAction *> menuBarActions = menuBar.actions();
513
514 QCOMPARE( menuBarActions.at(0)->text(), QString("Menu 1") );
515 QCOMPARE( menuBarActions.at(1)->text(), QString("Menu 2") );
516 QCOMPARE( menuBarActions.at(2)->text(), QString("Menu 3") );
517
518 // Ok, now that we know we have created the menu we expect, lets remove an item...
519 QFETCH( int, removeIndex );
520 menuBar.removeAction(action: menuBarActions.at(i: removeIndex));
521 const QList<QAction *> menuBarActions2 = menuBar.actions();
522 switch (removeIndex )
523 {
524 case 0:
525 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 2") );
526 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 3") );
527 break;
528 case 1:
529 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 1") );
530 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 3") );
531 break;
532 case 2:
533 QCOMPARE( menuBarActions2.at(0)->text(), QString("Menu 1") );
534 QCOMPARE( menuBarActions2.at(1)->text(), QString("Menu 2") );
535 break;
536 }
537
538 QVERIFY( menuBarActions2.size() == 2 );
539}
540
541/*
542 Check the insert functions that create menu items.
543 For the moment i only check the strings and pixmaps. The rest are special cases which are
544 used less frequently.
545*/
546
547void tst_QMenuBar::insertItem_QString_QObject()
548{
549 QMenuBar menuBar;
550 initComplexMenuBar(mb: &menuBar);
551
552 const QList<QAction *> actions = menuBar.actions();
553
554 QCOMPARE(actions.at(0)->text(), QString("Menu &1") );
555 QCOMPARE(actions.at(1)->text(), QString("Menu &2") );
556 QCOMPARE(actions.at(2)->text(), QString("M&enu 3") );
557 QVERIFY(actions.size() < 4); // there is no menu 4!
558}
559
560// On Mac native key events are needed to test menu action activation
561#if !defined(Q_OS_DARWIN)
562void tst_QMenuBar::check_accelKeys()
563{
564 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
565 QSKIP("Wayland: This fails. Figure out why.");
566
567 QMainWindow w;
568 initWindowWithComplexMenuBar(w);
569 w.show();
570 QApplication::setActiveWindow(&w);
571 QVERIFY(QTest::qWaitForWindowActive(&w));
572
573 // start with a bogus key that shouldn't trigger anything
574 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_I, modifier: Qt::ControlModifier);
575 QCOMPARE(m_complexTriggerCount[1], 0);
576 QCOMPARE(m_complexTriggerCount[2], 0);
577 QCOMPARE(m_complexTriggerCount[3], 0);
578 QCOMPARE(m_complexTriggerCount[4], 0);
579 QCOMPARE(m_complexTriggerCount[int('a')], 0);
580 QCOMPARE(m_complexTriggerCount[int('b')], 0);
581 QCOMPARE(m_complexTriggerCount[int('c')], 0);
582 QCOMPARE(m_complexTriggerCount[int('d')], 0);
583
584 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_A, modifier: Qt::ControlModifier);
585 QCOMPARE(m_complexTriggerCount[1], 0);
586 QCOMPARE(m_complexTriggerCount[2], 0);
587 QCOMPARE(m_complexTriggerCount[3], 0);
588 QCOMPARE(m_complexTriggerCount[4], 0);
589 QCOMPARE(m_complexTriggerCount[int('a')], 1);
590 QCOMPARE(m_complexTriggerCount[int('b')], 0);
591 QCOMPARE(m_complexTriggerCount[int('c')], 0);
592 QCOMPARE(m_complexTriggerCount[int('d')], 0);
593
594 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_C, modifier: Qt::ControlModifier);
595 QCOMPARE(m_complexTriggerCount[1], 0);
596 QCOMPARE(m_complexTriggerCount[2], 0);
597 QCOMPARE(m_complexTriggerCount[3], 0);
598 QCOMPARE(m_complexTriggerCount[4], 0);
599 QCOMPARE(m_complexTriggerCount[int('a')], 1);
600 QCOMPARE(m_complexTriggerCount[int('b')], 0);
601 QCOMPARE(m_complexTriggerCount[int('c')], 1);
602 QCOMPARE(m_complexTriggerCount[int('d')], 0);
603
604 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_B, modifier: Qt::ControlModifier);
605 QCOMPARE(m_complexTriggerCount[1], 0);
606 QCOMPARE(m_complexTriggerCount[2], 0);
607 QCOMPARE(m_complexTriggerCount[3], 0);
608 QCOMPARE(m_complexTriggerCount[4], 0);
609 QCOMPARE(m_complexTriggerCount[int('a')], 1);
610 QCOMPARE(m_complexTriggerCount[int('b')], 1);
611 QCOMPARE(m_complexTriggerCount[int('c')], 1);
612 QCOMPARE(m_complexTriggerCount[int('d')], 0);
613
614 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_D, modifier: Qt::ControlModifier);
615 QCOMPARE(m_complexTriggerCount[1], 0);
616 QCOMPARE(m_complexTriggerCount[2], 0);
617 QCOMPARE(m_complexTriggerCount[3], 0);
618 QCOMPARE(m_complexTriggerCount[4], 0);
619 QCOMPARE(m_complexTriggerCount[int('a')], 1);
620 QCOMPARE(m_complexTriggerCount[int('b')], 1);
621 QCOMPARE(m_complexTriggerCount[int('c')], 1);
622 QCOMPARE(m_complexTriggerCount[int('d')], 1);
623
624 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_J, modifier: Qt::AltModifier);
625 QCOMPARE(m_complexTriggerCount[1], 0);
626 QCOMPARE(m_complexTriggerCount[2], 0);
627 QCOMPARE(m_complexTriggerCount[3], 1);
628 QCOMPARE(m_complexTriggerCount[4], 0);
629 QCOMPARE(m_complexTriggerCount[int('a')], 1);
630 QCOMPARE(m_complexTriggerCount[int('b')], 1);
631 QCOMPARE(m_complexTriggerCount[int('c')], 1);
632 QCOMPARE(m_complexTriggerCount[int('d')], 1);
633}
634#endif
635
636// On Mac native key events are needed to test menu action activation
637#if !defined(Q_OS_DARWIN)
638void tst_QMenuBar::check_cursorKeys1()
639{
640 if (qgetenv(varName: "XDG_CURRENT_DESKTOP") == "Unity")
641 QSKIP("This test is flaky on Ubuntu/Unity due to regression introduced by QTBUG-39362");
642
643 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
644 QSKIP("Wayland: This fails. Figure out why.");
645
646 QMainWindow w;
647 initWindowWithComplexMenuBar(w);
648 w.show();
649 QApplication::setActiveWindow(&w);
650 QVERIFY(QTest::qWaitForWindowActive(&w));
651
652 // start with a ALT + 1 that activates the first popupmenu
653 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_1, modifier: Qt::AltModifier );
654 // the Popupmenu should be visible now
655 QCOMPARE(m_complexTriggerCount[3], 0);
656 QCOMPARE(m_complexTriggerCount[4], 0);
657 QCOMPARE(m_complexTriggerCount[int('a')], 0);
658 QCOMPARE(m_complexTriggerCount[int('b')], 0);
659 QCOMPARE(m_complexTriggerCount[int('c')], 0);
660 QCOMPARE(m_complexTriggerCount[int('d')], 0);
661
662 // Simulate a cursor key down click
663 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
664 // and an Enter key
665 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
666 // Let's see if the correct slot is called...
667 QCOMPARE(m_complexTriggerCount[3], 0);
668 QCOMPARE(m_complexTriggerCount[4], 0);
669 QCOMPARE(m_complexTriggerCount[int('a')], 0); // this shouldn't have been called
670 QCOMPARE(m_complexTriggerCount[int('b')], 1); // and this should have been called by a signal now
671 QCOMPARE(m_complexTriggerCount[int('c')], 0);
672 QCOMPARE(m_complexTriggerCount[int('d')], 0);
673}
674#endif
675
676// Qt/Mac does not use the native popups/menubar
677#if !defined(Q_OS_DARWIN)
678void tst_QMenuBar::check_cursorKeys2()
679{
680 if (qgetenv(varName: "XDG_CURRENT_DESKTOP") == "Unity")
681 QSKIP("This test is flaky on Ubuntu/Unity due to regression introduced by QTBUG-39362");
682
683 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
684 QSKIP("Wayland: This fails. Figure out why.");
685
686 QMainWindow w;
687 initWindowWithComplexMenuBar(w);
688 w.show();
689 QApplication::setActiveWindow(&w);
690 QVERIFY(QTest::qWaitForWindowActive(&w));
691
692 // select popupmenu2
693 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_2, modifier: Qt::AltModifier );
694
695 // Simulate some cursor keys
696 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Left );
697 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
698 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Right );
699 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
700 // and an Enter key
701 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
702 // Let's see if the correct slot is called...
703 QCOMPARE(m_complexTriggerCount[3], 0);
704 QCOMPARE(m_complexTriggerCount[4], 0);
705 QCOMPARE(m_complexTriggerCount[int('a')], 0); // this shouldn't have been caled
706 QCOMPARE(m_complexTriggerCount[int('b')], 0); // and this should have been called by a signal ow
707 QCOMPARE(m_complexTriggerCount[int('c')], 0);
708 QCOMPARE(m_complexTriggerCount[int('d')], 1);
709}
710#endif
711
712/*!
713 If a popupmenu is active you can use Left to move to the menu to the left of it.
714*/
715// Qt/Mac does not use the native popups/menubar
716#if !defined(Q_OS_DARWIN)
717void tst_QMenuBar::check_cursorKeys3()
718{
719 if (qgetenv(varName: "XDG_CURRENT_DESKTOP") == "Unity")
720 QSKIP("This test is flaky on Ubuntu/Unity due to regression introduced by QTBUG-39362");
721
722 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
723 QSKIP("Wayland: This fails. Figure out why.");
724
725 QMainWindow w;
726 initWindowWithComplexMenuBar(w);
727 w.show();
728 QApplication::setActiveWindow(&w);
729 QVERIFY(QTest::qWaitForWindowActive(&w));
730
731 // select Popupmenu 2
732 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_2, modifier: Qt::AltModifier );
733
734 // Simulate some keys
735 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Left );
736 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
737 // and press ENTER
738 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
739 // Let's see if the correct slot is called...
740 QCOMPARE(m_complexTriggerCount[3], 0);
741 QCOMPARE(m_complexTriggerCount[4], 0);
742 QCOMPARE(m_complexTriggerCount[int('a')], 0); // this shouldn't have been called
743 QCOMPARE(m_complexTriggerCount[int('b')], 1); // and this should have been called by a signal now
744 QCOMPARE(m_complexTriggerCount[int('c')], 0);
745 QCOMPARE(m_complexTriggerCount[int('d')], 0);
746}
747#endif
748
749void tst_QMenuBar::taskQTBUG56860_focus()
750{
751#if defined(Q_OS_DARWIN)
752 QSKIP("Native key events are needed to test menu action activation on macOS.");
753#endif
754 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
755 QSKIP("Wayland: This fails. Figure out why.");
756
757 QMainWindow w;
758 QMenuBar *mb = w.menuBar();
759 mb->setNativeMenuBar(false);
760
761 QMenu *em = mb->addMenu(title: "&Edit");
762 em->setObjectName("EditMenu");
763 em->addAction(text: "&Cut");
764 em->addAction(text: "C&opy");
765 QPlainTextEdit *e = new QPlainTextEdit;
766 e->setObjectName("edit");
767
768 w.setCentralWidget(e);
769 w.show();
770 QApplication::setActiveWindow(&w);
771 QVERIFY(QTest::qWaitForWindowActive(&w));
772
773 QTRY_COMPARE(QApplication::focusWidget(), e);
774
775 // Open menu
776 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_E, modifier: Qt::AltModifier );
777 QTRY_COMPARE(QApplication::activePopupWidget(), em);
778 // key down to trigger focus
779 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
780 // and press ENTER to close
781 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
782 QTRY_COMPARE(QApplication::activePopupWidget(), nullptr);
783 // focus should have returned to the editor by now
784 QTRY_COMPARE(QApplication::focusWidget(), e);
785
786 // Now do it all over again...
787 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_E, modifier: Qt::AltModifier );
788 QTRY_COMPARE(QApplication::activePopupWidget(), em);
789 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
790 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
791 QTRY_COMPARE(QApplication::activePopupWidget(), nullptr);
792 QTRY_COMPARE(QApplication::focusWidget(), e);
793
794}
795
796/*!
797 If a popupmenu is active you can use home to go quickly to the first item in the menu.
798*/
799void tst_QMenuBar::check_homeKey()
800{
801 // I'm temporarily shutting up this testcase.
802 // Seems like the behaviour i'm expecting isn't ok.
803 QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)");
804
805 QEXPECT_FAIL( "0", "Popupmenu should respond to a Home key", Abort );
806
807 QMainWindow w;
808 initWindowWithComplexMenuBar(w);
809 w.show();
810 QApplication::setActiveWindow(&w);
811 QVERIFY(QTest::qWaitForWindowActive(&w));
812
813 // select Popupmenu 2
814 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_2, modifier: Qt::AltModifier );
815
816 // Simulate some keys
817 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
818 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
819 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
820 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Home );
821 // and press ENTER
822 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
823 // Let's see if the correct slot is called...
824// QVERIFY2( m_complexActionTriggerCount[int('c')] == 1, "Popupmenu should respond to a Home key" );
825 QCOMPARE(m_complexTriggerCount[int('c')], 1);
826 QCOMPARE(m_complexTriggerCount[3], 0);
827 QCOMPARE(m_complexTriggerCount[4], 0);
828 QCOMPARE(m_complexTriggerCount[int('a')], 0);
829 QCOMPARE(m_complexTriggerCount[int('b')], 0);
830 QCOMPARE(m_complexTriggerCount[int('d')], 0);
831 QCOMPARE(m_complexTriggerCount[int('e')], 0);
832 QCOMPARE(m_complexTriggerCount[int('f')], 0);
833 QCOMPARE(m_complexTriggerCount[int('g')], 0);
834 QCOMPARE(m_complexTriggerCount[int('h')], 0);
835}
836
837/*!
838 If a popupmenu is active you can use end to go quickly to the last item in the menu.
839*/
840void tst_QMenuBar::check_endKey()
841{
842 // I'm temporarily silenting this testcase.
843 // Seems like the behaviour i'm expecting isn't ok.
844 QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)");
845
846 QEXPECT_FAIL( "0", "Popupmenu should respond to an End key", Abort );
847
848 QMainWindow w;
849 initWindowWithComplexMenuBar(w);
850 w.show();
851 QApplication::setActiveWindow(&w);
852 QVERIFY(QTest::qWaitForWindowActive(&w));
853
854 // select Popupmenu 2
855 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_2, modifier: Qt::AltModifier );
856
857 // Simulate some keys
858 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_End );
859 // and press ENTER
860 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Enter );
861 // Let's see if the correct slot is called...
862// QVERIFY2( m_complexActionTriggerCount[int('h')] == 1, "Popupmenu should respond to an End key" );
863 QCOMPARE(m_complexTriggerCount[int('h')], 1);//, "Popupmenu should respond to an End key");
864 QCOMPARE(m_complexTriggerCount[3], 0);
865 QCOMPARE(m_complexTriggerCount[4], 0);
866 QCOMPARE(m_complexTriggerCount[int('a')], 0);
867 QCOMPARE(m_complexTriggerCount[int('b')], 0);
868 QCOMPARE(m_complexTriggerCount[int('c')], 0);
869 QCOMPARE(m_complexTriggerCount[int('d')], 0);
870 QCOMPARE(m_complexTriggerCount[int('e')], 0);
871 QCOMPARE(m_complexTriggerCount[int('f')], 0);
872 QCOMPARE(m_complexTriggerCount[int('g')], 0);
873}
874
875/*!
876 If a popupmenu is active you can use esc to hide the menu and then the
877 menubar should become active.
878 If Down is pressed next the popup is activated again.
879*/
880
881// Qt/Mac does not use the native popups/menubar
882#if !defined(Q_OS_DARWIN)
883void tst_QMenuBar::check_escKey()
884{
885 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
886 QSKIP("Wayland: This fails. Figure out why.");
887
888 QMainWindow w;
889 const TestMenu menu = initWindowWithComplexMenuBar(w);
890 w.show();
891 w.setFocus();
892 QApplication::setActiveWindow(&w);
893 QVERIFY(QTest::qWaitForWindowActive(&w));
894
895 QVERIFY( !menu.menus.at(0)->isActiveWindow() );
896 QVERIFY( !menu.menus.at(1)->isActiveWindow() );
897
898 // select Popupmenu 2
899 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_2, modifier: Qt::AltModifier );
900 QVERIFY( !menu.menus.at(0)->isActiveWindow() );
901 QVERIFY( menu.menus.at(1)->isActiveWindow() );
902
903 // If we press ESC, the popup should disappear
904 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Escape );
905 QVERIFY( !menu.menus.at(0)->isActiveWindow() );
906 QVERIFY( !menu.menus.at(1)->isActiveWindow() );
907
908 if (!QApplication::style()->inherits(classname: "QWindowsStyle"))
909 return;
910
911 if (!QGuiApplication::platformName().compare(other: QLatin1String("minimal"), cs: Qt::CaseInsensitive)
912 || !QGuiApplication::platformName().compare(other: QLatin1String("offscreen"), cs: Qt::CaseInsensitive)) {
913 QWARN("Skipping menu button test on minimal/offscreen platforms");
914 return;
915 }
916
917 // If we press Down the popupmenu should be active again
918 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Down );
919 QVERIFY( !menu.menus.at(0)->isActiveWindow() );
920 QVERIFY( menu.menus.at(1)->isActiveWindow() );
921
922 // and press ENTER
923 QTest::keyClick( widget: menu.menus.at(i: 1), key: Qt::Key_Enter );
924 // Let's see if the correct slot is called...
925 QVERIFY2(m_complexTriggerCount[int('c')] == 1, "Expected item 2C to be selected");
926}
927#endif
928
929
930// void tst_QMenuBar::check_mouse1_data()
931// {
932// QTest::addColumn<QString>("popup_item");
933// QTest::addColumn<int>("itemA_count");
934// QTest::addColumn<int>("itemB_count");
935
936// QTest::newRow( "A" ) << QString( "Item A Ctrl+A" ) << 1 << 0;
937// QTest::newRow( "B" ) << QString( "Item B Ctrl+B" ) << 0 << 1;
938// }
939
940// /*!
941// Check if the correct signals are emitted if we select a popupmenu.
942// */
943// void tst_QMenuBar::check_mouse1()
944// {
945// if (QSystem::curStyle() == "Motif")
946// QSKIP("This fails in Motif due to a bug in the testing framework");
947// QFETCH( QString, popup_item );
948// QFETCH( int, itemA_count );
949// QFETCH( int, itemB_count );
950
951// // initComplexMenubar();
952// QVERIFY( !pm1->isActiveWindow() );
953// QVERIFY( !pm2->isActiveWindow() );
954
955// QTest::qWait(1000);
956// QtTestMouse mouse;
957// mouse.mouseEvent( QtTestMouse::MouseClick, mb, "Menu &1", Qt::LeftButton );
958
959// QVERIFY( pm1->isActiveWindow() );
960// QVERIFY( !pm2->isActiveWindow() );
961
962// QTest::qWait(1000);
963// mouse.mouseEvent( QtTestMouse::MouseClick, pm1, popup_item, Qt::LeftButton );
964
965// QCOMPARE(m_complexActionTriggerCount[3], 0);
966// QCOMPARE(m_complexActionTriggerCount[4], 0);
967// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count); // this option should have fired
968// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count);
969// QCOMPARE(m_complexActionTriggerCount['c'], 0);
970// QCOMPARE(m_complexActionTriggerCount['d'], 0);
971// QCOMPARE(m_complexActionTriggerCount['e'], 0);
972// QCOMPARE(m_complexActionTriggerCount['f'], 0);
973// QCOMPARE(m_complexActionTriggerCount['g'], 0);
974// }
975
976// void tst_QMenuBar::check_mouse2_data()
977// {
978// QTest::addColumn<QString>("label");
979// QTest::addColumn<int>("itemA_count");
980// QTest::addColumn<int>("itemB_count");
981// QTest::addColumn<int>("itemC_count");
982// QTest::addColumn<int>("itemD_count");
983// QTest::addColumn<int>("itemE_count");
984// QTest::addColumn<int>("itemF_count");
985// QTest::addColumn<int>("itemG_count");
986// QTest::addColumn<int>("itemH_count");
987// QTest::addColumn<int>("menu3_count");
988
989// QTest::newRow( "A" ) << QString( "Menu &1/Item A Ctrl+A" ) << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0;
990// QTest::newRow( "B" ) << QString( "Menu &1/Item B Ctrl+B" ) << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0;
991// QTest::newRow( "C" ) << QString( "Menu &2/Item C Ctrl+C" ) << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0;
992// QTest::newRow( "D" ) << QString( "Menu &2/Item D Ctrl+D" ) << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0;
993// QTest::newRow( "E" ) << QString( "Menu &2/Item E Ctrl+E" ) << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0;
994// QTest::newRow( "F" ) << QString( "Menu &2/Item F Ctrl+F" ) << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0;
995// QTest::newRow( "G" ) << QString( "Menu &2/Item G Ctrl+G" ) << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0;
996// QTest::newRow( "H" ) << QString( "Menu &2/Item H Ctrl+H" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0;
997// QTest::newRow( "menu 3" ) << QString( "M&enu 3" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1;
998// }
999
1000// /*!
1001// Check if the correct signals are emitted if we select a popupmenu.
1002// This time, we use a little bit more magic from the testframework.
1003// */
1004// void tst_QMenuBar::check_mouse2()
1005// {
1006// if (QSystem::curStyle() == "Motif")
1007// QSKIP("This fails in Motif due to a bug in the testing framework");
1008// QFETCH( QString, label );
1009// QFETCH( int, itemA_count );
1010// QFETCH( int, itemB_count );
1011// QFETCH( int, itemC_count );
1012// QFETCH( int, itemD_count );
1013// QFETCH( int, itemE_count );
1014// QFETCH( int, itemF_count );
1015// QFETCH( int, itemG_count );
1016// QFETCH( int, itemH_count );
1017// QFETCH( int, menu3_count );
1018
1019// // initComplexMenubar();
1020// QtTestMouse mouse;
1021// mouse.click( QtTestMouse::Menu, label, Qt::LeftButton );
1022
1023// // check if the correct signals have fired
1024// QCOMPARE(m_complexActionTriggerCount[3], (uint)menu3_count);
1025// QCOMPARE(m_complexActionTriggerCount[4], 0);
1026// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count);
1027// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count);
1028// QCOMPARE(m_complexActionTriggerCount['c'], (uint)itemC_count);
1029// QCOMPARE(m_complexActionTriggerCount['d'], (uint)itemD_count);
1030// QCOMPARE(m_complexActionTriggerCount['e'], (uint)itemE_count);
1031// QCOMPARE(m_complexActionTriggerCount['f'], (uint)itemF_count);
1032// QCOMPARE(m_complexActionTriggerCount['g'], (uint)itemG_count);
1033// QCOMPARE(m_complexActionTriggerCount['h'], (uint)itemH_count);
1034// }
1035
1036void tst_QMenuBar::allowActiveAndDisabled()
1037{
1038 QMenuBar menuBar;
1039 menuBar.setNativeMenuBar(false);
1040
1041 // Task 241043 : check that second menu is activated if only
1042 // disabled menu items are added
1043
1044 QMenu fileMenu("&File");
1045 // Task 241043 : check that second menu is activated
1046 // if all items are disabled
1047 QAction *act = fileMenu.addAction(text: "Disabled");
1048 act->setEnabled(false);
1049
1050 menuBar.addMenu(menu: &fileMenu);
1051 QMenu disabledMenu("Disabled");
1052 disabledMenu.setEnabled(false);
1053 QMenu activeMenu("Active");
1054 menuBar.addMenu(menu: &disabledMenu);
1055 menuBar.addMenu(menu: &activeMenu);
1056 centerOnScreen(w: &menuBar);
1057 menuBar.show();
1058 QVERIFY(QTest::qWaitForWindowExposed(&menuBar));
1059
1060 // Here we verify that AllowActiveAndDisabled correctly skips
1061 // the disabled menu entry
1062 QTest::keyClick(widget: &menuBar, key: Qt::Key_F, modifier: Qt::AltModifier );
1063 QTest::keyClick(widget: &fileMenu, key: (Qt::Key_Right));
1064 if (qApp->style()->styleHint(stylehint: QStyle::SH_Menu_AllowActiveAndDisabled))
1065 QCOMPARE(menuBar.activeAction()->text(), disabledMenu.title());
1066 else
1067 QCOMPARE(menuBar.activeAction()->text(), activeMenu.title());
1068
1069 QTest::keyClick(widget: &menuBar, key: (Qt::Key_Left));
1070 if (qApp->style()->styleHint(stylehint: QStyle::SH_Menu_AllowActiveAndDisabled))
1071 QCOMPARE(menuBar.activeAction()->text(), fileMenu.title());
1072 else
1073 QCOMPARE(menuBar.activeAction()->text(), fileMenu.title());
1074}
1075
1076void tst_QMenuBar::check_altPress()
1077{
1078 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1079 QSKIP("Wayland: This fails. Figure out why.");
1080
1081 if ( !qApp->style()->styleHint(stylehint: QStyle::SH_MenuBar_AltKeyNavigation) ) {
1082 QSKIP(QString( "this is not supposed to work in the %1 style. Skipping." ).
1083 arg(qApp->style()->objectName()).toLatin1());
1084 }
1085
1086 QMainWindow w;
1087 initWindowWithSimpleMenuBar(w);
1088 w.show();
1089 w.setFocus();
1090 QApplication::setActiveWindow(&w);
1091 QVERIFY(QTest::qWaitForWindowActive(&w));
1092
1093 QTest::keyClick( widget: &w, key: Qt::Key_Alt );
1094 QTRY_VERIFY( ::qobject_cast<QMenuBar *>(qApp->focusWidget()) );
1095}
1096
1097// QTBUG-47377: Pressing 'Alt' after opening a menu by pressing 'Alt+Accelerator'
1098// should close it and QMenuBar::activeAction() should be 0.
1099void tst_QMenuBar::check_altClosePress()
1100{
1101 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1102 QSKIP("Wayland: This fails. Figure out why.");
1103
1104 const QStyle *style = QApplication::style();
1105 if (!style->styleHint(stylehint: QStyle::SH_MenuBar_AltKeyNavigation) ) {
1106 QSKIP(("This test is not supposed to work in the " + style->objectName().toLatin1()
1107 + " style. Skipping.").constData());
1108 }
1109
1110 QMainWindow w;
1111 w.setWindowTitle(QTest::currentTestFunction());
1112 w.menuBar()->setNativeMenuBar(false);
1113 QMenu *menuFile = w.menuBar()->addMenu(title: tr(s: "&File"));
1114 menuFile->addAction(text: "Quit");
1115 QMenu *menuEdit = w.menuBar()->addMenu(title: tr(s: "&Edit"));
1116 menuEdit->addAction(text: "Copy");
1117
1118 w.show();
1119 w.move(QGuiApplication::primaryScreen()->availableGeometry().center());
1120 QApplication::setActiveWindow(&w);
1121 QVERIFY(QTest::qWaitForWindowActive(&w));
1122
1123 QTest::keyClick(widget: &w, key: Qt::Key_F, modifier: Qt::AltModifier);
1124 QTRY_VERIFY(menuFile->isVisible());
1125 QTest::keyClick(widget: menuFile, key: Qt::Key_Alt, modifier: Qt::AltModifier);
1126 QTRY_VERIFY(!menuFile->isVisible());
1127 QTRY_VERIFY(!w.menuBar()->activeAction());
1128}
1129
1130// Qt/Mac does not use the native popups/menubar
1131#if !defined(Q_OS_DARWIN)
1132void tst_QMenuBar::check_shortcutPress()
1133{
1134 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1135 QSKIP("Wayland: This fails. Figure out why.");
1136
1137 QMainWindow w;
1138 const TestMenu menu = initWindowWithComplexMenuBar(w);
1139 w.show();
1140 w.setFocus();
1141 QApplication::setActiveWindow(&w);
1142 QVERIFY(QTest::qWaitForWindowActive(&w));
1143
1144 QCOMPARE(m_complexTriggerCount[3], 0);
1145 QTest::keyClick(widget: &w, key: Qt::Key_E, modifier: Qt::AltModifier);
1146 QTest::qWait(ms: 200);
1147 QCOMPARE(m_complexTriggerCount[3], 1);
1148 QVERIFY(!w.menuBar()->activeAction());
1149
1150 QTest::keyClick(widget: &w, key: Qt::Key_1, modifier: Qt::AltModifier );
1151 QVERIFY(menu.menus.at(0)->isActiveWindow());
1152 QTest::keyClick(widget: &w, key: Qt::Key_2);
1153 QVERIFY(menu.menus.at(0)->isActiveWindow());
1154}
1155#endif
1156
1157class LayoutDirectionSaver
1158{
1159 Q_DISABLE_COPY(LayoutDirectionSaver)
1160public:
1161 explicit LayoutDirectionSaver(Qt::LayoutDirection direction)
1162 : m_oldDirection(qApp->layoutDirection())
1163 {
1164 qApp->setLayoutDirection(direction);
1165 }
1166
1167 ~LayoutDirectionSaver()
1168 {
1169 qApp->setLayoutDirection(m_oldDirection);
1170 }
1171
1172private:
1173 const Qt::LayoutDirection m_oldDirection;
1174};
1175
1176// Qt/Mac does not use the native popups/menubar
1177#if !defined(Q_OS_DARWIN)
1178void tst_QMenuBar::check_menuPosition()
1179{
1180 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1181 QSKIP("Wayland: This fails. Figure out why.");
1182
1183 QMainWindow w;
1184
1185 Menu menu;
1186 menu.setTitle("&menu");
1187 QRect availRect = w.screen()->availableGeometry();
1188 QRect screenRect = w.screen()->geometry();
1189
1190 while(menu.sizeHint().height() < (screenRect.height()*2/3)) {
1191 menu.addAction(text: "item");
1192 }
1193
1194 w.menuBar()->setNativeMenuBar(false);
1195 QAction *menu_action = w.menuBar()->addMenu(menu: &menu);
1196 centerOnScreen(w: &w);
1197 w.show();
1198 qApp->setActiveWindow(&w);
1199 QVERIFY(QTest::qWaitForWindowActive(&w));
1200
1201 //the menu should be below the menubar item
1202 {
1203 w.move(availRect.topLeft());
1204 QRect mbItemRect = w.menuBar()->actionGeometry(menu_action);
1205 mbItemRect.moveTo(p: w.menuBar()->mapToGlobal(mbItemRect.topLeft()));
1206 QTest::keyClick(widget: &w, key: Qt::Key_M, modifier: Qt::AltModifier );
1207 QVERIFY(menu.isActiveWindow());
1208 QCOMPARE(menu.pos(), QPoint(mbItemRect.x(), mbItemRect.bottom() + 1));
1209 menu.close();
1210 }
1211
1212 //the menu should be above the menubar item
1213 {
1214 w.move(ax: 0,ay: screenRect.bottom() - screenRect.height()/4); //just leave some place for the menubar
1215 QRect mbItemRect = w.menuBar()->actionGeometry(menu_action);
1216 mbItemRect.moveTo(p: w.menuBar()->mapToGlobal(mbItemRect.topLeft()));
1217 QTest::keyClick(widget: &w, key: Qt::Key_M, modifier: Qt::AltModifier );
1218 QVERIFY(menu.isActiveWindow());
1219#ifdef Q_OS_WINRT
1220 QEXPECT_FAIL("", "QTest::keyClick does not work on WinRT.", Abort);
1221#endif
1222 QCOMPARE(menu.pos(), QPoint(mbItemRect.x(), mbItemRect.top() - menu.height()));
1223 menu.close();
1224 }
1225
1226 //the menu should be on the side of the menubar item and should be "stuck" to the bottom of the screen
1227 {
1228 w.move(ax: 0,ay: screenRect.y() + screenRect.height()/2); //put it in the middle
1229 QRect mbItemRect = w.menuBar()->actionGeometry(menu_action);
1230 mbItemRect.moveTo(p: w.menuBar()->mapToGlobal(mbItemRect.topLeft()));
1231 QTest::keyClick(widget: &w, key: Qt::Key_M, modifier: Qt::AltModifier );
1232 QVERIFY(menu.isActiveWindow());
1233 QPoint firstPoint = QPoint(mbItemRect.right()+1, screenRect.bottom() - menu.height() + 1);
1234 QPoint secondPoint = QPoint(mbItemRect.right()+1, availRect.bottom() - menu.height() + 1);
1235 QVERIFY(menu.pos() == firstPoint || menu.pos() == secondPoint);
1236 menu.close();
1237 }
1238
1239 // QTBUG-2596: in RTL, the menu should be stuck at the right of the action geometry
1240 {
1241 LayoutDirectionSaver directionSaver(Qt::RightToLeft);
1242 menu.clear();
1243 QObject::connect(sender: &menu, SIGNAL(aboutToShow()), receiver: &menu, SLOT(addActions()));
1244 QRect mbItemRect = w.menuBar()->actionGeometry(menu_action);
1245 mbItemRect.moveTo(p: w.menuBar()->mapToGlobal(mbItemRect.topLeft()));
1246 QTest::keyClick(widget: &w, key: Qt::Key_M, modifier: Qt::AltModifier );
1247 QVERIFY(menu.isActiveWindow());
1248 QCOMPARE(menu.geometry().right(), mbItemRect.right());
1249 menu.close();
1250 }
1251
1252 // QTBUG-28031: Click at bottom-right corner.
1253 {
1254 w.move(ax: 400, ay: 200);
1255 LayoutDirectionSaver directionSaver(Qt::RightToLeft);
1256 QMenuBar *mb = w.menuBar();
1257 const QPoint bottomRight = mb->actionGeometry(menu.menuAction()).bottomRight() - QPoint(1, 1);
1258 const QPoint localPos = widgetToWindowPos(w: mb, pos: bottomRight);
1259 const QPoint globalPos = w.mapToGlobal(localPos);
1260 QTest::mouseClick(window: w.windowHandle(), button: Qt::LeftButton, stateKey: {}, pos: localPos);
1261 QTRY_VERIFY(menu.isActiveWindow());
1262 QCOMPARE(menu.geometry().right() - 1, globalPos.x());
1263 menu.close();
1264 }
1265}
1266#endif
1267
1268void tst_QMenuBar::task223138_triggered()
1269{
1270 //we create a window with submenus and we check that both menubar and menus get the triggered signal
1271 QMainWindow win;
1272 centerOnScreen(w: &win);
1273 QMenu *menu = win.menuBar()->addMenu(title: "test");
1274 QAction *top = menu->addAction(text: "toplevelaction");
1275 QMenu *submenu = menu->addMenu(title: "nested menu");
1276 QAction *action = submenu->addAction(text: "nested action");
1277
1278 QSignalSpy menubarSpy(win.menuBar(), SIGNAL(triggered(QAction*)));
1279 QSignalSpy menuSpy(menu, SIGNAL(triggered(QAction*)));
1280 QSignalSpy submenuSpy(submenu, SIGNAL(triggered(QAction*)));
1281
1282 //let's trigger the first action
1283 top->trigger();
1284
1285 QCOMPARE(menubarSpy.count(), 1);
1286 QCOMPARE(menuSpy.count(), 1);
1287 QCOMPARE(submenuSpy.count(), 0);
1288
1289 menubarSpy.clear();
1290 menuSpy.clear();
1291 submenuSpy.clear();
1292
1293 //let's trigger the sub action
1294 action->trigger();
1295 QCOMPARE(menubarSpy.count(), 1);
1296 QCOMPARE(menuSpy.count(), 1);
1297 QCOMPARE(submenuSpy.count(), 1);
1298}
1299
1300void tst_QMenuBar::task256322_highlight()
1301{
1302 if (!QGuiApplication::platformName().compare(other: QLatin1String("minimal"), cs: Qt::CaseInsensitive))
1303 QSKIP("Highlighting does not work correctly for minimal platform");
1304
1305 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1306 QSKIP("Wayland: This fails. Figure out why.");
1307
1308 QMainWindow win;
1309 win.menuBar()->setNativeMenuBar(false); //we can't check the geometry of native menubars
1310 QMenu menu;
1311 QAction *file = win.menuBar()->addMenu(menu: &menu);
1312 file->setText("file");
1313 QMenu menu2;
1314 QAction *file2 = win.menuBar()->addMenu(menu: &menu2);
1315 file2->setText("file2");
1316 QAction *nothing = win.menuBar()->addAction(text: "nothing");
1317
1318 centerOnScreen(w: &win);
1319 win.show();
1320 QApplication::setActiveWindow(&win);
1321 QVERIFY(QTest::qWaitForWindowActive(&win));
1322
1323 const QPoint filePos = menuBarActionWindowPos(mb: win.menuBar(), a: file);
1324 QWindow *window = win.windowHandle();
1325 QTest::mousePress(window, button: Qt::LeftButton, stateKey: {}, pos: filePos);
1326 QTest::mouseMove(window, pos: filePos);
1327 QTest::mouseRelease(window, button: Qt::LeftButton, stateKey: {}, pos: filePos);
1328 QTRY_VERIFY(menu.isVisible());
1329 QVERIFY(!menu2.isVisible());
1330 QCOMPARE(win.menuBar()->activeAction(), file);
1331
1332 const QPoint file2Pos = menuBarActionWindowPos(mb: win.menuBar(), a: file2);
1333 QTest::mouseMove(window, pos: file2Pos);
1334 QTRY_VERIFY(!menu.isVisible());
1335 QTRY_VERIFY(menu2.isVisible());
1336 QCOMPARE(win.menuBar()->activeAction(), file2);
1337
1338 QPoint nothingCenter = menuBarActionWindowPos(mb: win.menuBar(), a: nothing);
1339 QTest::mouseMove(window, pos: nothingCenter);
1340 QTRY_VERIFY(!menu2.isVisible());
1341 QVERIFY(!menu.isVisible());
1342 QTRY_COMPARE(win.menuBar()->activeAction(), nothing);
1343}
1344
1345void tst_QMenuBar::menubarSizeHint()
1346{
1347 struct MyStyle : public QProxyStyle
1348 {
1349 MyStyle() : QProxyStyle(QStyleFactory::create("windows")) { }
1350
1351 virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const
1352 {
1353 // I chose strange values (prime numbers to be more sure that the size of the menubar is correct)
1354 switch (metric)
1355 {
1356 case QStyle::PM_MenuBarItemSpacing:
1357 return 7;
1358 case PM_MenuBarHMargin:
1359 return 13;
1360 case PM_MenuBarVMargin:
1361 return 11;
1362 case PM_MenuBarPanelWidth:
1363 return 1;
1364 default:
1365 return QProxyStyle::pixelMetric(metric, option, widget);
1366 }
1367 }
1368 } style;
1369
1370 QMenuBar mb;
1371 mb.setNativeMenuBar(false); //we can't check the geometry of native menubars
1372
1373 mb.setStyle(&style);
1374 //this is a list of arbitrary strings so that we check the geometry
1375 QStringList list = QStringList() << "trer" << "ezrfgtgvqd" << "sdgzgzerzerzer" << "eerzertz" << "er";
1376 foreach(QString str, list)
1377 mb.addAction(text: str);
1378
1379 const int panelWidth = style.pixelMetric(metric: QStyle::PM_MenuBarPanelWidth);
1380 const int hmargin = style.pixelMetric(metric: QStyle::PM_MenuBarHMargin);
1381 const int vmargin = style.pixelMetric(metric: QStyle::PM_MenuBarVMargin);
1382 const int spacing = style.pixelMetric(metric: QStyle::PM_MenuBarItemSpacing);
1383
1384 centerOnScreen(w: &mb);
1385 mb.show();
1386 QRect result;
1387 foreach(QAction *action, mb.actions()) {
1388 const QRect actionRect = mb.actionGeometry(action);
1389 if (!result.isNull()) //this is the first item
1390 QCOMPARE(actionRect.left() - result.right() - 1, spacing);
1391 result |= actionRect;
1392 QCOMPARE(result.x(), panelWidth + hmargin + spacing);
1393 QCOMPARE(result.y(), panelWidth + vmargin);
1394 }
1395
1396 //this code is copied from QMenuBar
1397 //there is no public member that allows to initialize a styleoption instance
1398 QStyleOptionMenuItem opt;
1399 opt.rect = mb.rect();
1400 opt.menuRect = mb.rect();
1401 opt.state = QStyle::State_None;
1402 opt.menuItemType = QStyleOptionMenuItem::Normal;
1403 opt.checkType = QStyleOptionMenuItem::NotCheckable;
1404 opt.palette = mb.palette();
1405
1406 QSize resSize = QSize(result.x(), result.y()) + result.size()
1407 + QSize(panelWidth + hmargin, panelWidth + vmargin);
1408
1409
1410 resSize = style.sizeFromContents(type: QStyle::CT_MenuBar, option: &opt,
1411 size: resSize.expandedTo(otherSize: QApplication::globalStrut()),
1412 widget: &mb);
1413
1414 QCOMPARE(resSize, mb.sizeHint());
1415}
1416
1417// On Mac, do not test the menubar with escape key
1418#ifndef Q_OS_MACOS
1419void tst_QMenuBar::taskQTBUG4965_escapeEaten()
1420{
1421 QMenuBar menubar;
1422 menubar.setNativeMenuBar(false);
1423 QMenu menu("menu1");
1424 QAction *first = menubar.addMenu(menu: &menu);
1425 menu.addAction(text: "quit", receiver: &menubar, SLOT(close()), shortcut: QKeySequence("ESC"));
1426 centerOnScreen(w: &menubar);
1427 menubar.show();
1428 QApplication::setActiveWindow(&menubar);
1429 QVERIFY(QTest::qWaitForWindowExposed(&menubar));
1430 menubar.setActiveAction(first);
1431 QTRY_VERIFY(menu.isVisible());
1432 QCOMPARE(menubar.activeAction(), first);
1433 QVERIFY(QTest::qWaitForWindowExposed(&menu));
1434 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Escape);
1435 QVERIFY(!menu.isVisible());
1436 QTRY_VERIFY(menubar.hasFocus());
1437 QCOMPARE(menubar.activeAction(), first);
1438 QTest::qWait(ms: 200);
1439 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Escape);
1440 QVERIFY(!menubar.activeAction());
1441 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Escape); //now the action should be triggered
1442 QTRY_VERIFY(!menubar.isVisible());
1443}
1444#endif
1445
1446void tst_QMenuBar::taskQTBUG11823_crashwithInvisibleActions()
1447{
1448 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1449 QSKIP("Wayland: This fails. Figure out why.");
1450
1451 QMenuBar menubar;
1452 menubar.setNativeMenuBar(false); //we can't check the geometry of native menubars
1453
1454 QAction * m = menubar.addAction( text: "&m" );
1455 QAction * a = menubar.addAction( text: "&a" );
1456
1457 centerOnScreen(w: &menubar);
1458 menubar.show();
1459 QApplication::setActiveWindow(&menubar);
1460 QVERIFY(QTest::qWaitForWindowActive(&menubar));
1461 menubar.setActiveAction(m);
1462 QCOMPARE(menubar.activeAction(), m);
1463 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Right);
1464 QCOMPARE(menubar.activeAction(), a);
1465 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Right);
1466 QCOMPARE(menubar.activeAction(), m);
1467 a->setVisible(false);
1468
1469 menubar.setActiveAction(m);
1470 QCOMPARE(menubar.activeAction(), m); //the active action shouldn't have changed
1471
1472 //it used to crash here because the action is invisible
1473 QTest::keyClick(widget: static_cast<QWidget *>(0), key: Qt::Key_Right);
1474 QCOMPARE(menubar.activeAction(), m); //the active action shouldn't have changed
1475}
1476
1477void tst_QMenuBar::closeOnSecondClickAndOpenOnThirdClick() // QTBUG-32807, menu should close on 2nd click.
1478{
1479 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1480 QSKIP("Wayland: This fails. Figure out why.");
1481
1482 QMainWindow mainWindow;
1483 mainWindow.resize(w: 300, h: 200);
1484 centerOnScreen(w: &mainWindow);
1485 QMenuBar *menuBar = mainWindow.menuBar();
1486 menuBar->setNativeMenuBar(false);
1487 QMenu *fileMenu = menuBar->addMenu(QStringLiteral("OpenCloseOpen"));
1488 fileMenu->addAction(QStringLiteral("Quit"));
1489 mainWindow.show();
1490 QApplication::setActiveWindow(&mainWindow);
1491 QVERIFY(QTest::qWaitForWindowActive(&mainWindow));
1492
1493 const QPoint center = menuBarActionWindowPos(mb: mainWindow.menuBar(), a: fileMenu->menuAction());
1494 const QPoint globalPos = mainWindow.mapToGlobal(center);
1495
1496 QWindow *window = mainWindow.windowHandle();
1497 QTest::mouseMove(window, pos: center);
1498 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: {}, pos: center);
1499 QTRY_VERIFY(fileMenu->isVisible());
1500 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: {}, pos: fileMenu->mapFromGlobal(globalPos));
1501 QTRY_VERIFY(!fileMenu->isVisible());
1502 QTest::mouseClick(window, button: Qt::LeftButton, stateKey: {}, pos: center);
1503 QTRY_VERIFY(fileMenu->isVisible());
1504}
1505
1506Q_DECLARE_METATYPE(Qt::Corner)
1507
1508void tst_QMenuBar::cornerWidgets_data()
1509{
1510 QTest::addColumn<Qt::Corner>(name: "corner");
1511 QTest::newRow(dataTag: "left") << Qt::TopLeftCorner;
1512 QTest::newRow(dataTag: "right") << Qt::TopRightCorner;
1513}
1514
1515static QByteArray msgComparison(int v1, const char *op, int v2)
1516{
1517 QString result;
1518 QDebug(&result) << v1 << op << v2 << "failed";
1519 return result.toLocal8Bit();
1520}
1521
1522void tst_QMenuBar::cornerWidgets()
1523{
1524 enum { cornerWidgetWidth = 100 };
1525
1526 QFETCH(Qt::Corner, corner);
1527
1528#if defined(Q_OS_MACOS)
1529 QSKIP("Test interferes with native menu bars on this platform");
1530#endif
1531
1532 QWidget widget;
1533 const QString dataTag = QLatin1String(QTest::currentDataTag());
1534 widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + dataTag);
1535 QVBoxLayout *layout = new QVBoxLayout(&widget);
1536 QMenuBar *menuBar = new QMenuBar(&widget);
1537 menuBar->setNativeMenuBar(false);
1538 layout->addWidget(menuBar);
1539 QMenu *fileMenu = menuBar->addMenu(title: "File");
1540 fileMenu->addAction(text: "Quit");
1541 QMenu *editMenu =menuBar->addMenu(title: "Edit");
1542 editMenu->addAction(text: "Copy");
1543 centerOnScreen(w: &widget);
1544
1545 QLabel *cornerLabel = new QLabel(dataTag);
1546 cornerLabel->setFixedWidth(cornerWidgetWidth);
1547 menuBar->setCornerWidget(w: cornerLabel, corner);
1548 QCOMPARE(menuBar->cornerWidget(corner), cornerLabel);
1549 widget.show();
1550 QVERIFY(QTest::qWaitForWindowExposed(&widget));
1551
1552 const QRect fileMenuGeometry = menuBar->actionGeometry(fileMenu->menuAction());
1553 const QRect editMenuGeometry = menuBar->actionGeometry(editMenu->menuAction());
1554 const int menuBarWidth = menuBar->width();
1555 switch (corner) { // QTBUG-36010 , verify corner widget geometry is correct
1556 case Qt::TopLeftCorner:
1557 QVERIFY2(fileMenuGeometry.left() >= cornerWidgetWidth,
1558 msgComparison(fileMenuGeometry.left(), ">=", cornerWidgetWidth));
1559#ifdef Q_OS_WINRT
1560 QEXPECT_FAIL("", "Broken on WinRT - QTBUG-68297", Abort);
1561#endif
1562 QVERIFY2(menuBarWidth - editMenuGeometry.right() < cornerWidgetWidth,
1563 msgComparison(menuBarWidth - editMenuGeometry.right(), "<", cornerWidgetWidth));
1564 break;
1565 case Qt::TopRightCorner:
1566 QVERIFY2(fileMenuGeometry.left() < cornerWidgetWidth,
1567 msgComparison(fileMenuGeometry.left(), "<", cornerWidgetWidth));
1568 QVERIFY2(menuBarWidth - editMenuGeometry.right() >= cornerWidgetWidth,
1569 msgComparison(menuBarWidth - editMenuGeometry.right(), ">=", cornerWidgetWidth));
1570 break;
1571 default:
1572 break;
1573 }
1574
1575 menuBar->setCornerWidget(w: 0, corner); // Don't crash.
1576 QVERIFY(!menuBar->cornerWidget(corner));
1577 delete cornerLabel;
1578}
1579
1580
1581void tst_QMenuBar::taskQTBUG53205_crashReparentNested()
1582{
1583 // This test was largely inspired by the test case submitted for the bug
1584 QMainWindow mainWindow;
1585 mainWindow.resize(w: 300, h: 200);
1586 centerOnScreen(w: &mainWindow);
1587 const TestMenu testMenus = initWindowWithComplexMenuBar(w&: mainWindow);
1588 QApplication::setActiveWindow(&mainWindow);
1589
1590 // they can't be windows
1591 QWidget hiddenParent(&mainWindow, {});
1592 //this one is going to be moved around
1593 QWidget movingParent(&hiddenParent, {});
1594
1595 //set up the container widget
1596 QWidget containerWidget(&movingParent, {});
1597
1598 //set the new parent, a window
1599 QScopedPointer<QWidget> windowedParent;
1600 windowedParent.reset(other: new QWidget(nullptr, Qt::WindowFlags()));
1601 windowedParent->setGeometry(ax: 400, ay: 10, aw: 300, ah: 300);
1602
1603 windowedParent->show();
1604 QVERIFY(QTest::qWaitForWindowExposed(windowedParent.data()));
1605
1606 //set the "container", can't be a window
1607 QWidget containedWidget(&containerWidget, {});
1608
1609 taskQTBUG53205MenuBar = new QMenuBar(&containedWidget);
1610
1611 connect(sender: testMenus.actions[0], signal: &QAction::triggered, receiver: this, slot: &tst_QMenuBar::slotForTaskQTBUG53205);
1612 //now, move things around
1613 //from : QMainWindow<-hiddenParent<-movingParent<-containerWidget<-containedWidget<-menuBar
1614 //to windowedParent<-movingParent<-containerWidget<-containedWidget<-menuBar
1615 movingParent.setParent(parent: windowedParent.data(), f: {});
1616 // this resets the parenting and the menu bar's window
1617 taskQTBUG53205MenuBar->setParent(nullptr);
1618 taskQTBUG53205MenuBar->setParent(&containedWidget);
1619 //from windowedParent<-movingParent<-containerWidget<-containedWidget<-menuBar
1620 //to : QMainWindow<-hiddenParent<-movingParent<-containerWidget<-containedWidget<-menuBar
1621 movingParent.setParent(parent: &hiddenParent, f: {});
1622 windowedParent.reset(); //make the old window invalid
1623 // trigger the aciton, reset the menu bar's window, this used to crash here.
1624 testMenus.actions[0]->trigger();
1625}
1626
1627void tst_QMenuBar::QTBUG_65488_hiddenActionTriggered()
1628{
1629 QMainWindow win;
1630 win.menuBar()->setNativeMenuBar(false);
1631 QAction *act1 = win.menuBar()->addAction(text: "A very long named action that make menuBar item wide enough");
1632 QSignalSpy spy(win.menuBar(), &QMenuBar::triggered);
1633
1634 QRect actRect = win.menuBar()->actionGeometry(act1);
1635 // resize to action's size to make Action1 hidden
1636 win.resize(w: actRect.width() - 10, h: win.size().height());
1637 win.show();
1638 QApplication::setActiveWindow(&win);
1639 QVERIFY(QTest::qWaitForWindowExposed(&win));
1640 // click center of the blank area on the menubar where Action1 resided
1641 QTest::mouseClick(window: win.windowHandle(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: win.menuBar()->geometry().center());
1642 QCoreApplication::sendPostedEvents(); // make sure all queued events also dispatched
1643 QCOMPARE(spy.count(), 0);
1644}
1645
1646// QTBUG-56526
1647void tst_QMenuBar::platformMenu()
1648{
1649 QMenuBar menuBar;
1650 QPlatformMenuBar *platformMenuBar = menuBar.platformMenuBar();
1651 if (!platformMenuBar)
1652 QSKIP("No platform menubar implementation available on this platform.");
1653
1654 // QMenu must not create a platform menu instance at creation time, because
1655 // on Unity the type of the platform menu instance must be different (QGtk3Menu
1656 // vs. QDbusPlatformMenu) depending on whether the menu is in the global menubar
1657 // or a standalone context menu.
1658 QMenu *menu = new QMenu(&menuBar);
1659 QVERIFY(!menu->platformMenu());
1660
1661 menuBar.addMenu(menu);
1662 QVERIFY(menu->platformMenu());
1663}
1664
1665class TestObject : public QObject
1666{
1667 Q_OBJECT
1668public:
1669 bool flag = false;
1670 void setFlag()
1671 {
1672 flag = true;
1673 }
1674};
1675
1676void tst_QMenuBar::addActionQt5connect()
1677{
1678 bool flag = false;
1679 auto functor = [&flag](){ flag = true; };
1680
1681 TestObject obj;
1682
1683 QMenuBar menuBar;
1684
1685 auto action1 = menuBar.addAction(QStringLiteral("1"), object: &obj, slot: &TestObject::setFlag);
1686 auto action2 = menuBar.addAction(QStringLiteral("2"), slot: functor);
1687
1688 action1->activate(event: QAction::Trigger);
1689 action2->activate(event: QAction::Trigger);
1690
1691 QVERIFY(obj.flag);
1692 QVERIFY(flag);
1693
1694 flag = false;
1695
1696 auto action3 = menuBar.addAction(QStringLiteral("3"), object: this, slot: functor);
1697 action3->activate(event: QAction::Trigger);
1698 QVERIFY(flag);
1699}
1700
1701void tst_QMenuBar::QTBUG_25669_menubarActionDoubleTriggered()
1702{
1703 QMainWindow win;
1704 win.menuBar()->setNativeMenuBar(false);
1705 QAction *act1 = win.menuBar()->addAction(text: "Action1");
1706 QAction *act2 = win.menuBar()->addAction(text: "Action2");
1707 QSignalSpy spy(win.menuBar(), &QMenuBar::triggered);
1708
1709 win.show();
1710 QApplication::setActiveWindow(&win);
1711 QVERIFY(QTest::qWaitForWindowExposed(&win));
1712
1713 QPoint posAct1 = menuBarActionWindowPos(mb: win.menuBar(), a: act1);
1714 QPoint posAct2 = menuBarActionWindowPos(mb: win.menuBar(), a: act2);
1715
1716 QTest::mouseClick(window: win.windowHandle(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: posAct1);
1717 QTRY_COMPARE(spy.count(), 1);
1718
1719 QTest::mouseClick(window: win.windowHandle(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: posAct2);
1720 QTRY_COMPARE(spy.count(), 2);
1721
1722 QTest::mouseClick(window: win.windowHandle(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: posAct2);
1723 QTRY_COMPARE(spy.count(), 3);
1724}
1725
1726void tst_QMenuBar::slotForTaskQTBUG53205()
1727{
1728 QWidget *parent = taskQTBUG53205MenuBar->parentWidget();
1729 taskQTBUG53205MenuBar->setParent(nullptr);
1730 taskQTBUG53205MenuBar->setParent(parent);
1731}
1732
1733// Qt/Mac does not use the native popups/menubar
1734#if !defined(Q_OS_DARWIN)
1735void tst_QMenuBar::taskQTBUG46812_doNotLeaveMenubarHighlighted()
1736{
1737 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1738 QSKIP("Wayland: This fails. Figure out why.");
1739
1740 QMainWindow mainWindow;
1741 QWidget *centralWidget = new QWidget;
1742 centralWidget->setFocusPolicy(Qt::StrongFocus);
1743 mainWindow.setCentralWidget(centralWidget);
1744 initWindowWithSimpleMenuBar(w&: mainWindow);
1745
1746 mainWindow.show();
1747 QApplication::setActiveWindow(&mainWindow);
1748 QVERIFY(QTest::qWaitForWindowActive(&mainWindow));
1749
1750 QVERIFY(!mainWindow.menuBar()->hasFocus());
1751 QCOMPARE(m_simpleActivatedCount, 0);
1752
1753 QTest::keyPress(widget: &mainWindow, key: Qt::Key_Alt, modifier: Qt::AltModifier);
1754 QVERIFY(!mainWindow.menuBar()->hasFocus());
1755 QCOMPARE(m_simpleActivatedCount, 0);
1756
1757 QTest::keyPress(widget: &mainWindow, key: Qt::Key_Z, modifier: Qt::AltModifier);
1758 QVERIFY(!mainWindow.menuBar()->hasFocus());
1759 QCOMPARE(m_simpleActivatedCount, 2); // the action AND the menu will activate
1760
1761 QTest::keyRelease(widget: &mainWindow, key: Qt::Key_Alt, modifier: Qt::NoModifier);
1762 QVERIFY(!mainWindow.menuBar()->hasFocus());
1763 QCOMPARE(m_simpleActivatedCount, 2);
1764
1765 QTest::keyRelease(widget: &mainWindow, key: Qt::Key_Z, modifier: Qt::NoModifier);
1766 QVERIFY(!mainWindow.menuBar()->hasFocus());
1767 QCOMPARE(m_simpleActivatedCount, 2);
1768}
1769#endif
1770
1771#ifdef Q_OS_MACOS
1772extern bool tst_qmenubar_taskQTBUG56275(QMenuBar *);
1773
1774void tst_QMenuBar::taskQTBUG56275_reinsertMenuInParentlessQMenuBar()
1775{
1776 QMenuBar menubar;
1777
1778 QMenu *menu = new QMenu("menu", &menubar);
1779 QMenu* submenu = menu->addMenu("submenu");
1780 submenu->addAction("action1");
1781 submenu->addAction("action2");
1782 menu->addAction("action3");
1783 menubar.addMenu(menu);
1784
1785 QTest::qWait(100);
1786 menubar.clear();
1787 menubar.addMenu(menu);
1788 QTest::qWait(100);
1789
1790 QVERIFY(tst_qmenubar_taskQTBUG56275(&menubar));
1791}
1792
1793void tst_QMenuBar::QTBUG_57404_existingMenuItemException()
1794{
1795 QMainWindow mw1;
1796 QMainWindow mw2;
1797 mw1.show();
1798 mw2.show();
1799
1800 QMenuBar *mb = new QMenuBar(&mw1);
1801 mw1.setMenuBar(mb);
1802 mb->show();
1803 QMenu *editMenu = new QMenu(QLatin1String("Edit"), &mw1);
1804 mb->addMenu(editMenu);
1805 QAction *copyAction = editMenu->addAction("&Copy");
1806 copyAction->setShortcut(QKeySequence("Ctrl+C"));
1807 copyAction->setMenuRole(QAction::NoRole);
1808
1809 QVERIFY(QTest::qWaitForWindowExposed(&mw2));
1810 QTest::qWait(100);
1811 mw2.close();
1812 mw1.activateWindow();
1813 QTest::qWait(100);
1814 // No crash, all fine. Ideally, there should be only one warning.
1815}
1816#endif // Q_OS_MACOS
1817
1818void tst_QMenuBar::taskQTBUG55966_subMenuRemoved()
1819{
1820 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
1821 QSKIP("Wayland: This fails. Figure out why.");
1822
1823 QMainWindow window;
1824 QMenuBar *menubar = window.menuBar();
1825 QMenu *parentMenu = menubar->addMenu(title: "Parent menu");
1826
1827 QAction *action = parentMenu->addAction(text: "Action in parent menu");
1828 QMenu *subMenu = new QMenu("Submenu");
1829 action->setMenu(subMenu);
1830 delete subMenu;
1831
1832 window.show();
1833 QApplication::setActiveWindow(&window);
1834 QVERIFY(QTest::qWaitForWindowActive(&window));
1835 QTest::qWait(ms: 500);
1836}
1837
1838void tst_QMenuBar::QTBUG_58344_invalidIcon()
1839{
1840 QMenuBar menuBar;
1841 QMenu menu("menu");
1842 menu.addAction(icon: QIcon("crash.png"), text: "crash");
1843 menuBar.addMenu(menu: &menu);
1844 // No crash, all fine.
1845}
1846
1847QTEST_MAIN(tst_QMenuBar)
1848#include "tst_qmenubar.moc"
1849

source code of qtbase/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp