qt5base-lts/tests/auto/qmenu/tst_qmenu.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

1031 lines
29 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qapplication.h>
#include <QPushButton>
#include <QMainWindow>
#include <QMenuBar>
#include <QToolBar>
#include <QToolButton>
#include <QStatusBar>
#include <QListWidget>
#include <QWidgetAction>
#include <QDesktopWidget>
#include <qdialog.h>
#include <qmenu.h>
#include <qstyle.h>
#include <qdebug.h>
#include "../../shared/util.h"
//TESTED_CLASS=
//TESTED_FILES=
class tst_QMenu : public QObject
{
Q_OBJECT
public:
tst_QMenu();
virtual ~tst_QMenu();
public slots:
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
private slots:
void getSetCheck();
void addActionsAndClear();
void keyboardNavigation_data();
void keyboardNavigation();
void focus();
void overrideMenuAction();
void statusTip();
void widgetActionFocus();
void mouseActivation();
void tearOff();
void layoutDirection();
#if defined(QT3_SUPPORT)
void indexBasedInsertion_data();
void indexBasedInsertion();
#endif
void task208001_stylesheet();
void activeSubMenuPosition();
void task242454_sizeHint();
void task176201_clear();
void task250673_activeMultiColumnSubMenuPosition();
void task256918_setFont();
void menuSizeHint();
void task258920_mouseBorder();
void setFixedWidth();
void deleteActionInTriggered();
void pushButtonPopulateOnAboutToShow();
void QTBUG7907_submenus_autoselect();
void QTBUG7411_submenus_activate();
void QTBUG_10735_crashWithDialog();
protected slots:
void onActivated(QAction*);
void onHighlighted(QAction*);
void onStatusMessageChanged(const QString &);
void onStatusTipTimer();
void deleteAction(QAction *a) { delete a; }
void populateMenu();
private:
void createActions();
QMenu *menus[2], *lastMenu;
enum { num_builtins = 10 };
QAction *activated, *highlighted, *builtins[num_builtins];
QString statustip;
};
// Testing get/set functions
void tst_QMenu::getSetCheck()
{
QMenu obj1;
// QAction * QMenu::defaultAction()
// void QMenu::setDefaultAction(QAction *)
QAction *var1 = new QAction(0);
obj1.setDefaultAction(var1);
QCOMPARE(var1, obj1.defaultAction());
obj1.setDefaultAction((QAction *)0);
QCOMPARE((QAction *)0, obj1.defaultAction());
delete var1;
// QAction * QMenu::activeAction()
// void QMenu::setActiveAction(QAction *)
QAction *var2 = new QAction(0);
obj1.setActiveAction(var2);
QCOMPARE(var2, obj1.activeAction());
obj1.setActiveAction((QAction *)0);
QCOMPARE((QAction *)0, obj1.activeAction());
delete var2;
}
tst_QMenu::tst_QMenu()
{
QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false);
}
tst_QMenu::~tst_QMenu()
{
}
void
tst_QMenu::initTestCase()
{
for(int i = 0; i < num_builtins; i++)
builtins[i] = 0;
for(int i = 0; i < 2; i++) {
menus[i] = new QMenu;
QObject::connect(menus[i], SIGNAL(triggered(QAction*)), this, SLOT(onActivated(QAction*)));
QObject::connect(menus[i], SIGNAL(hovered(QAction*)), this, SLOT(onHighlighted(QAction*)));
}
}
void
tst_QMenu::cleanupTestCase()
{
for(int i = 0; i < 2; i++)
menus[i]->clear();
for(int i = 0; i < num_builtins; i++) {
bool menuAction = false;
for (int j = 0; j < 2; ++j)
if (menus[j]->menuAction() == builtins[i])
menuAction = true;
if (!menuAction)
delete builtins[i];
}
delete menus[0];
delete menus[1];
}
void
tst_QMenu::init()
{
activated = highlighted = 0;
lastMenu = 0;
}
void
tst_QMenu::cleanup()
{
}
void
tst_QMenu::createActions()
{
if(!builtins[0])
builtins[0] = new QAction("New", 0);
menus[0]->addAction(builtins[0]);
if(!builtins[1]) {
builtins[1] = new QAction(0);
builtins[1]->setSeparator(true);
}
menus[0]->addAction(builtins[1]);
if(!builtins[2]) {
builtins[2] = menus[1]->menuAction();
builtins[2]->setText("&Open..");
builtins[8] = new QAction("Close", 0);
menus[1]->addAction(builtins[8]);
builtins[9] = new QAction("Quit", 0);
menus[1]->addAction(builtins[9]);
}
menus[0]->addAction(builtins[2]);
if(!builtins[3])
builtins[3] = new QAction("Open &as..", 0);
menus[0]->addAction(builtins[3]);
if(!builtins[4]) {
builtins[4] = new QAction("Save", 0);
builtins[4]->setEnabled(false);
}
menus[0]->addAction(builtins[4]);
if(!builtins[5])
builtins[5] = new QAction("Sa&ve as..", 0);
menus[0]->addAction(builtins[5]);
if(!builtins[6]) {
builtins[6] = new QAction(0);
builtins[6]->setSeparator(true);
}
menus[0]->addAction(builtins[6]);
if(!builtins[7])
builtins[7] = new QAction("Prin&t", 0);
menus[0]->addAction(builtins[7]);
}
void
tst_QMenu::onHighlighted(QAction *action)
{
highlighted = action;
lastMenu = qobject_cast<QMenu*>(sender());
}
void
tst_QMenu::onActivated(QAction *action)
{
activated = action;
lastMenu = qobject_cast<QMenu*>(sender());
}
void tst_QMenu::onStatusMessageChanged(const QString &s)
{
statustip=s;
}
void tst_QMenu::populateMenu(){
//just adds 3 dummy actions and a separator.
lastMenu->addAction("Foo");
lastMenu->addAction("Bar");
lastMenu->addAction("FooBar");
lastMenu->addSeparator();
}
//actual tests
void
tst_QMenu::addActionsAndClear()
{
#ifdef QT_SOFTKEYS_ENABLED
// Softkeys add extra "Select" and "Back" actions to menu by default.
// Two first actions will be Select and Back when softkeys are enabled
int numSoftkeyActions = 2;
#else
int numSoftkeyActions = 0;
#endif
QCOMPARE(menus[0]->actions().count(), 0 + numSoftkeyActions);
createActions();
QCOMPARE(menus[0]->actions().count(), 8 + numSoftkeyActions);
menus[0]->clear();
QCOMPARE(menus[0]->actions().count(), 0);
}
void tst_QMenu::mouseActivation()
{
#ifdef Q_OS_WINCE_WM
QSKIP("We have a separate mouseActivation test for Windows mobile.", SkipAll);
#endif
QWidget topLevel;
QMenu menu(&topLevel);
topLevel.show();
menu.addAction("Menu Action");
menu.show();
QTest::mouseClick(&menu, Qt::LeftButton, 0, menu.rect().center(), 300);
QVERIFY(!menu.isVisible());
//context menus can allways be accessed with right click except on windows
menu.show();
QTest::mouseClick(&menu, Qt::RightButton, 0, menu.rect().center(), 300);
QVERIFY(!menu.isVisible());
#ifdef Q_OS_WIN
//on windows normal mainwindow menus Can only be accessed with left mouse button
QMenuBar menubar;
QMenu submenu("Menu");
submenu.addAction("action");
QAction *action = menubar.addMenu(&submenu);
menubar.show();
QTest::mouseClick(&menubar, Qt::LeftButton, 0, menubar.actionGeometry(action).center(), 300);
QVERIFY(submenu.isVisible());
QTest::mouseClick(&submenu, Qt::LeftButton, 0, QPoint(5, 5), 300);
QVERIFY(!submenu.isVisible());
QTest::mouseClick(&menubar, Qt::LeftButton, 0, menubar.actionGeometry(action).center(), 300);
QVERIFY(submenu.isVisible());
QTest::mouseClick(&submenu, Qt::RightButton, 0, QPoint(5, 5), 300);
QVERIFY(submenu.isVisible());
#endif
}
void
tst_QMenu::keyboardNavigation_data()
{
QTest::addColumn<int>("key");
QTest::addColumn<int>("expected_action");
QTest::addColumn<int>("expected_menu");
QTest::addColumn<bool>("init");
QTest::addColumn<bool>("expected_activated");
QTest::addColumn<bool>("expected_highlighted");
//test up and down (order is important here)
QTest::newRow("data0") << int(Qt::Key_Down) << 0 << 0 << true << false << true;
QTest::newRow("data1") << int(Qt::Key_Down) << 2 << 0 << false << false << true; //skips the separator
QTest::newRow("data2") << int(Qt::Key_Down) << 3 << 0 << false << false << true;
if (QApplication::style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled))
QTest::newRow("data3_noMac") << int(Qt::Key_Down) << 4 << 0 << false << false << true;
else
QTest::newRow("data3_Mac") << int(Qt::Key_Down) << 5 << 0 << false << false << true;
QTest::newRow("data4") << int(Qt::Key_Up) << 3 << 0 << false << false << true;
QTest::newRow("data5") << int(Qt::Key_Up) << 2 << 0 << false << false << true;
QTest::newRow("data6") << int(Qt::Key_Right) << 8 << 1 << false << false << true;
QTest::newRow("data7") << int(Qt::Key_Down) << 9 << 1 << false << false << true;
QTest::newRow("data8") << int(Qt::Key_Escape) << 2 << 0 << false << false << false;
QTest::newRow("data9") << int(Qt::Key_Down) << 3 << 0 << false << false<< true;
QTest::newRow("data10") << int(Qt::Key_Return) << 3 << 0 << false << true << false;
//test shortcuts
#if 0
QTest::newRow("shortcut0") << (Qt::ALT | Qt::Key_A) << 2 << 0 << true << true << false;
#endif
}
void
tst_QMenu::keyboardNavigation()
{
DEPENDS_ON( "addActionsAndClear" ); //if add/clear fails...
QFETCH(int, key);
QFETCH(int, expected_action);
QFETCH(int, expected_menu);
QFETCH(bool, init);
QFETCH(bool, expected_activated);
QFETCH(bool, expected_highlighted);
if(init) {
lastMenu = menus[0];
lastMenu->clear();
createActions();
lastMenu->popup(QPoint(0, 0));
}
QTest::keyClick(lastMenu, (Qt::Key)key);
if(expected_activated) {
QCOMPARE(activated, builtins[expected_action]);
QCOMPARE(menus[expected_menu]->activeAction(), (QAction *)0);
} else {
QCOMPARE(menus[expected_menu]->activeAction(), builtins[expected_action]);
if(expected_highlighted)
QCOMPARE(menus[expected_menu]->activeAction(), highlighted);
}
}
#ifdef Q_WS_MAC
QT_BEGIN_NAMESPACE
extern bool qt_tab_all_widgets; // qapplication_mac.cpp
QT_END_NAMESPACE
#endif
void tst_QMenu::focus()
{
QMenu menu;
menu.addAction("One");
menu.addAction("Two");
menu.addAction("Three");
bool fullKeyboardControl = true;
#ifdef Q_WS_MAC
fullKeyboardControl = qt_tab_all_widgets;
#endif
if (!fullKeyboardControl)
QSKIP("Computer is currently set up to NOT tab to all widgets,"
" this test assumes you can tab to all widgets", SkipAll);
QWidget window;
QPushButton button("Push me", &window);
window.show();
qApp->setActiveWindow(&window);
QVERIFY(button.hasFocus());
QCOMPARE(QApplication::focusWidget(), (QWidget *)&button);
QCOMPARE(QApplication::activeWindow(), &window);
menu.show();
#if 0
QVERIFY(!button.hasFocus());
QCOMPARE(QApplication::focusWidget(), &menu);
QCOMPARE(QApplication::activeWindow(), &window);
#else
QVERIFY(button.hasFocus());
QCOMPARE(QApplication::focusWidget(), (QWidget *)&button);
QCOMPARE(QApplication::activeWindow(), &window);
#endif
menu.hide();
QVERIFY(button.hasFocus());
QCOMPARE(QApplication::focusWidget(), (QWidget *)&button);
QCOMPARE(QApplication::activeWindow(), &window);
}
void tst_QMenu::overrideMenuAction()
{
//test the override menu action by first creating an action to which we set its menu
QMainWindow w;
QAction *aFileMenu = new QAction("&File", &w);
w.menuBar()->addAction(aFileMenu);
QMenu *m = new QMenu(&w);
QAction *menuaction = m->menuAction();
connect(m, SIGNAL(triggered(QAction*)), SLOT(onActivated(QAction*)));
aFileMenu->setMenu(m); //this sets the override menu action for the QMenu
QCOMPARE(m->menuAction(), aFileMenu);
#ifdef Q_WS_MAC
QSKIP("On Mac, we need to create native key events to test menu action activation", SkipAll);
#elif defined(Q_OS_WINCE)
QSKIP("On Windows CE, we need to create native key events to test menu action activation", SkipAll);
#elif defined(Q_OS_SYMBIAN)
QSKIP("On Symbian OS, we need to create native key events to test menu action activation", SkipAll);
#endif
QAction *aQuit = new QAction("Quit", &w);
aQuit->setShortcut(QKeySequence("Ctrl+X"));
m->addAction(aQuit);
w.show();
QTest::qWaitForWindowShown(&w);
QApplication::setActiveWindow(&w);
w.setFocus();
QTRY_VERIFY(w.hasFocus());
//test of the action inside the menu
QTest::keyClick(&w, Qt::Key_X, Qt::ControlModifier);
QTRY_COMPARE(activated, aQuit);
//test if the menu still pops out
QTest::keyClick(&w, Qt::Key_F, Qt::AltModifier);
QTRY_VERIFY(m->isVisible());
delete aFileMenu;
//after the deletion of the override menu action,
//the menu should have its default menu action back
QCOMPARE(m->menuAction(), menuaction);
}
void tst_QMenu::statusTip()
{
//check that the statustip of actions inserted into the menu are displayed
QMainWindow w;
connect(w.statusBar(), SIGNAL(messageChanged(const QString &)), SLOT(onStatusMessageChanged(const QString &)));; //creates the status bar
QToolBar tb;
QAction a("main action", &tb);
a.setStatusTip("main action");
QMenu m(&tb);
QAction subact("sub action", &m);
subact.setStatusTip("sub action");
m.addAction(&subact);
a.setMenu(&m);
tb.addAction(&a);
w.addToolBar(&tb);
w.show();
QTest::qWaitForWindowShown(&w);
QRect rect1 = tb.actionGeometry(&a);
QToolButton *btn = qobject_cast<QToolButton*>(tb.childAt(rect1.center()));
QVERIFY(btn != NULL);
//because showMenu calls QMenu::exec, we need to use a singleshot
//to continue the test
QTimer::singleShot(200,this, SLOT(onStatusTipTimer()));
btn->showMenu();
QVERIFY(statustip.isEmpty());
}
//2nd part of the test
void tst_QMenu::onStatusTipTimer()
{
QMenu *menu = qobject_cast<QMenu*>(QApplication::activePopupWidget());
QVERIFY(menu != 0);
QVERIFY(menu->isVisible());
QTest::keyClick(menu, Qt::Key_Down);
//we store the statustip to press escape in any case
//otherwise, if the test fails it blocks (never gets out of QMenu::exec
const QString st=statustip;
menu->close(); //goes out of the menu
QCOMPARE(st, QString("sub action"));
QVERIFY(menu->isVisible() == false);
}
void tst_QMenu::widgetActionFocus()
{
//test if the focus is correctly handled with a QWidgetAction
QMenu m;
QListWidget *l = new QListWidget(&m);
for(int i = 1; i<3 ; i++)
l->addItem(QString("item%1").arg(i));
QWidgetAction *wa = new QWidgetAction(&m);
wa->setDefaultWidget(l);
m.addAction(wa);
m.setActiveAction(wa);
l->setFocus(); //to ensure it has primarily the focus
QAction *menuitem1=m.addAction("menuitem1");
QAction *menuitem2=m.addAction("menuitem2");
m.popup(QPoint());
QVERIFY(m.isVisible());
QVERIFY(l->hasFocus());
QVERIFY(l->currentItem());
QCOMPARE(l->currentItem()->text(), QString("item1"));
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
QVERIFY(l->currentItem());
QCOMPARE(l->currentItem()->text(), QString("item2"));
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
QVERIFY(m.hasFocus());
QCOMPARE(m.activeAction(), menuitem1);
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
QVERIFY(m.hasFocus());
QCOMPARE(m.activeAction(), menuitem2);
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Up);
QVERIFY(m.hasFocus());
QCOMPARE(m.activeAction(), menuitem1);
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Up);
QVERIFY(l->hasFocus());
QCOMPARE(m.activeAction(), (QAction *)wa);
}
void tst_QMenu::tearOff()
{
QWidget widget;
QMenu *menu = new QMenu(&widget);
QVERIFY(!menu->isTearOffEnabled()); //default value
menu->setTearOffEnabled(true);
menu->addAction("aaa");
menu->addAction("bbb");
QVERIFY(menu->isTearOffEnabled());
widget.show();
QTest::qWaitForWindowShown(&widget);
widget.activateWindow();
menu->popup(QPoint(0,0));
QTest::qWait(50);
QVERIFY(!menu->isTearOffMenuVisible());
QTest::mouseClick(menu, Qt::LeftButton, 0, QPoint(3, 3), 10);
QTest::qWait(100);
QVERIFY(menu->isTearOffMenuVisible());
QPointer<QMenu> torn = 0;
foreach (QWidget *w, QApplication::allWidgets()) {
if (w->inherits("QTornOffMenu")) {
torn = static_cast<QMenu *>(w);
break;
}
}
QVERIFY(torn);
QVERIFY(torn->isVisible());
menu->hideTearOffMenu();
QVERIFY(!menu->isTearOffMenuVisible());
QVERIFY(!torn->isVisible());
}
void tst_QMenu::layoutDirection()
{
QMainWindow win;
win.setLayoutDirection(Qt::RightToLeft);
QMenu menu(&win);
menu.show();
QTest::qWaitForWindowShown(&menu);
QCOMPARE(menu.layoutDirection(), Qt::RightToLeft);
menu.close();
menu.setParent(0);
menu.show();
QTest::qWaitForWindowShown(&menu);
QCOMPARE(menu.layoutDirection(), QApplication::layoutDirection());
menu.close();
//now the menubar
QAction *action = win.menuBar()->addMenu(&menu);
win.menuBar()->setActiveAction(action);
QTest::qWaitForWindowShown(&menu);
QCOMPARE(menu.layoutDirection(), Qt::RightToLeft);
}
#if defined(QT3_SUPPORT)
void tst_QMenu::indexBasedInsertion_data()
{
QTest::addColumn<int>("indexForInsertion");
QTest::addColumn<int>("expectedIndex");
QTest::newRow("negative-index-appends") << -1 << 1;
QTest::newRow("prepend") << 0 << 0;
QTest::newRow("append") << 1 << 1;
}
void tst_QMenu::indexBasedInsertion()
{
// test the compat'ed index based insertion
QFETCH(int, indexForInsertion);
QFETCH(int, expectedIndex);
{
QMenu menu;
menu.addAction("Regular Item");
menu.insertItem("New Item", -1 /*id*/, indexForInsertion);
QAction *act = menu.actions().value(expectedIndex);
QVERIFY(act);
QCOMPARE(act->text(), QString("New Item"));
}
{
QMenu menu;
menu.addAction("Regular Item");
menu.insertSeparator(indexForInsertion);
QAction *act = menu.actions().value(expectedIndex);
QVERIFY(act);
QVERIFY(act->isSeparator());
}
}
#endif
void tst_QMenu::task208001_stylesheet()
{
//test if it crash
QMainWindow main;
main.setStyleSheet("QMenu [title =\"File\"] { color: red;}");
main.menuBar()->addMenu("File");
}
void tst_QMenu::activeSubMenuPosition()
{
QPushButton lab("subMenuPosition test");
QMenu *sub = new QMenu("Submenu", &lab);
sub->addAction("Sub-Item1");
QAction *subAction = sub->addAction("Sub-Item2");
QMenu *main = new QMenu("Menu-Title", &lab);
(void)main->addAction("Item 1");
QAction *menuAction = main->addMenu(sub);
(void)main->addAction("Item 3");
(void)main->addAction("Item 4");
main->setActiveAction(menuAction);
sub->setActiveAction(subAction);
#ifdef Q_OS_SYMBIAN
main->popup(QPoint(50,200));
#else
main->popup(QPoint(200,200));
#endif
QVERIFY(main->isVisible());
QCOMPARE(main->activeAction(), menuAction);
QVERIFY(sub->isVisible());
QVERIFY(sub->pos() != QPoint(0,0));
// well, it's enough to check the pos is not (0,0) but it's more safe
// to check that submenu is to the right of the main menu too.
#ifdef Q_OS_WINCE_WM
QSKIP("Not true for Windows Mobile Soft Keys", SkipSingle);
#endif
#ifdef Q_OS_SYMBIAN
// On Symbian, QS60Style::pixelMetric(QStyle::PM_SubMenuOverlap) is different with other styles.
QVERIFY(sub->pos().x() < main->pos().x());
#else
QVERIFY(sub->pos().x() > main->pos().x());
#endif
QCOMPARE(sub->activeAction(), subAction);
}
void tst_QMenu::task242454_sizeHint()
{
QMenu menu;
QString s = QLatin1String("foo\nfoo\nfoo\nfoo");
menu.addAction(s);
QVERIFY(menu.sizeHint().width() > menu.fontMetrics().boundingRect(QRect(), Qt::TextSingleLine, s).width());
}
class Menu : public QMenu
{
Q_OBJECT
public slots:
void clear()
{
QMenu::clear();
}
};
void tst_QMenu::task176201_clear()
{
//this test used to crash
Menu menu;
QAction *action = menu.addAction("test");
menu.connect(action, SIGNAL(triggered()), SLOT(clear()));
menu.popup(QPoint());
QTest::mouseClick(&menu, Qt::LeftButton, 0, menu.rect().center());
}
void tst_QMenu::task250673_activeMultiColumnSubMenuPosition()
{
class MyMenu : public QMenu
{
public:
int columnCount() const { return QMenu::columnCount(); }
};
QMenu sub;
if (sub.style()->styleHint(QStyle::SH_Menu_Scrollable, 0, &sub)) {
//the style prevents the menus from getting columns
QSKIP("the style doesn't support multiple columns, it makes the menu scrollable", SkipSingle);
}
sub.addAction("Sub-Item1");
QAction *subAction = sub.addAction("Sub-Item2");
MyMenu main;
main.addAction("Item 1");
QAction *menuAction = main.addMenu(&sub);
main.popup(QPoint(200,200));
uint i = 2;
while (main.columnCount() < 2) {
main.addAction(QString("Item %1").arg(i));
++i;
Q_ASSERT(i<1000);
}
main.setActiveAction(menuAction);
sub.setActiveAction(subAction);
QVERIFY(main.isVisible());
QCOMPARE(main.activeAction(), menuAction);
QVERIFY(sub.isVisible());
QVERIFY(sub.pos().x() > main.pos().x());
const int subMenuOffset = main.style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, &main);
QVERIFY((sub.geometry().left() - subMenuOffset + 5) < main.geometry().right());
}
void tst_QMenu::task256918_setFont()
{
QMenu menu;
QAction *action = menu.addAction("foo");
QFont f;
f.setPointSize(30);
action->setFont(f);
menu.show(); //ensures that the actiongeometry are calculated
QVERIFY(menu.actionGeometry(action).height() > f.pointSize());
}
void tst_QMenu::menuSizeHint()
{
QMenu menu;
//this is a list of arbitrary strings so that we check the geometry
QStringList list = QStringList() << "trer" << "ezrfgtgvqd" << "sdgzgzerzerzer" << "eerzertz" << "er";
foreach(QString str, list)
menu.addAction(str);
int left, top, right, bottom;
menu.getContentsMargins(&left, &top, &right, &bottom);
const int panelWidth = menu.style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, &menu);
const int hmargin = menu.style()->pixelMetric(QStyle::PM_MenuHMargin, 0, &menu),
vmargin = menu.style()->pixelMetric(QStyle::PM_MenuVMargin, 0, &menu);
int maxWidth =0;
QRect result;
foreach(QAction *action, menu.actions()) {
#ifdef QT_SOFTKEYS_ENABLED
// Softkey actions are not widgets and have no geometry.
if (menu.actionGeometry(action).topLeft() == QPoint(0,0))
continue;
#endif
maxWidth = qMax(maxWidth, menu.actionGeometry(action).width());
result |= menu.actionGeometry(action);
QCOMPARE(result.x(), left + hmargin + panelWidth);
QCOMPARE(result.y(), top + vmargin + panelWidth);
}
QStyleOption opt(0);
opt.rect = menu.rect();
opt.state = QStyle::State_None;
QSize resSize = QSize(result.x(), result.y()) + result.size() + QSize(hmargin + right + panelWidth, vmargin + top + panelWidth);
resSize = menu.style()->sizeFromContents(QStyle::CT_Menu, &opt,
resSize.expandedTo(QApplication::globalStrut()), &menu);
QCOMPARE(resSize, menu.sizeHint());
}
class Menu258920 : public QMenu
{
Q_OBJECT
public slots:
void paintEvent(QPaintEvent *e)
{
QMenu::paintEvent(e);
painted = true;
}
public:
bool painted;
};
void tst_QMenu::task258920_mouseBorder()
{
#ifdef Q_OS_WINCE_WM
QSKIP("Mouse move related signals for Windows Mobile unavailable", SkipAll);
#endif
Menu258920 menu;
// On Symbian, styleHint(QStyle::SH_Menu_MouseTracking) in QS60Style is false.
// For other styles which inherit from QWindowsStyle, the value is true.
menu.setMouseTracking(true);
QAction *action = menu.addAction("test");
menu.popup(QApplication::desktop()->availableGeometry().center());
QTest::qWaitForWindowShown(&menu);
QTest::qWait(100);
QRect actionRect = menu.actionGeometry(action);
QTest::mouseMove(&menu, actionRect.center());
QTest::qWait(30);
QTest::mouseMove(&menu, actionRect.center() + QPoint(10, 0));
QTest::qWait(30);
QCOMPARE(action, menu.activeAction());
menu.painted = false;
QTest::mouseMove(&menu, QPoint(actionRect.center().x(), actionRect.bottom() + 1));
QTest::qWait(30);
QCOMPARE(static_cast<QAction*>(0), menu.activeAction());
QVERIFY(menu.painted);
}
void tst_QMenu::setFixedWidth()
{
QMenu menu;
menu.addAction("action");
menu.setFixedWidth(300);
//the sizehint should reflect the minimumwidth because the action will try to
//get as much space as possible
QCOMPARE(menu.sizeHint().width(), menu.minimumWidth());
}
void tst_QMenu::deleteActionInTriggered()
{
// should not crash
QMenu m;
QObject::connect(&m, SIGNAL(triggered(QAction*)), this, SLOT(deleteAction(QAction*)));
QWeakPointer<QAction> a = m.addAction("action");
a.data()->trigger();
QVERIFY(!a);
}
void tst_QMenu::pushButtonPopulateOnAboutToShow()
{
QPushButton b("Test PushButton");
b.setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
lastMenu = new QMenu;
b.setMenu(lastMenu);
const int scrNumber = QApplication::desktop()->screenNumber(&b);
connect(lastMenu, SIGNAL(aboutToShow()), this, SLOT(populateMenu()));
b.show();
const QRect screen = QApplication::desktop()->screenGeometry(scrNumber);
b.move(10, screen.bottom()-b.height()-5);
QTest::qWaitForWindowShown(&b);
QTimer::singleShot(300,lastMenu, SLOT(hide()));
QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center());
QVERIFY(!lastMenu->geometry().intersects(b.geometry()));
b.move(10, screen.bottom()-lastMenu->height()-5);
QTimer::singleShot(300,lastMenu, SLOT(hide()));
QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center());
QVERIFY(!lastMenu->geometry().intersects(b.geometry()));
}
void tst_QMenu::QTBUG7907_submenus_autoselect()
{
QMenu menu("Test Menu");
QMenu set1("Setting1");
QMenu set2("Setting2");
QMenu subset("Subsetting");
subset.addAction("Values");
set1.addMenu(&subset);
menu.addMenu(&set1);
menu.addMenu(&set2);
menu.show();
QTest::qWaitForWindowShown(&menu);
QTest::mouseClick(&menu, Qt::LeftButton, Qt::NoModifier, QPoint(5,5) );
QTest::qWait(500);
QVERIFY(!subset.isVisible());
}
void tst_QMenu::QTBUG7411_submenus_activate()
{
QMenu menu("Test Menu");
QAction *act = menu.addAction("foo");
QMenu sub1("&sub1");
sub1.addAction("foo");
sub1.setTitle("&sub1");
QAction *act1 = menu.addMenu(&sub1);
menu.show();
QTest::qWaitForWindowShown(&menu);
menu.setActiveAction(act);
QTest::keyPress(&menu, Qt::Key_Down);
QCOMPARE(menu.activeAction(), act1);
QVERIFY(!sub1.isVisible());
QTest::keyPress(&menu, Qt::Key_S);
QTRY_VERIFY(sub1.isVisible());
}
class MyMenu : public QMenu
{
Q_OBJECT
public:
MyMenu() : m_currentIndex(0)
{
for (int i = 0; i < 2; ++i)
dialogActions[i] = addAction( QString("dialog %1").arg(i), dialogs + i, SLOT(exec()));
}
void activateAction(int index)
{
m_currentIndex = index;
popup(QPoint());
QTest::qWaitForWindowShown(this);
setActiveAction(dialogActions[index]);
QTimer::singleShot(500, this, SLOT(checkVisibility()));
QTest::keyClick(this, Qt::Key_Enter); //activation
}
public slots:
void activateLastAction()
{
activateAction(1);
}
void checkVisibility()
{
QTRY_VERIFY(dialogs[m_currentIndex].isVisible());
if (m_currentIndex == 1) {
QApplication::closeAllWindows(); //this is the end of the test
}
}
private:
QAction *dialogActions[2];
QDialog dialogs[2];
int m_currentIndex;
};
void tst_QMenu::QTBUG_10735_crashWithDialog()
{
MyMenu menu;
QTimer::singleShot(1000, &menu, SLOT(activateLastAction()));
menu.activateAction(0);
}
QTEST_MAIN(tst_QMenu)
#include "tst_qmenu.moc"