94b6f1ad52
We are trying to test the case of having a widget right next to the edge of the screen, but the window manager is likely to forbid this geometry in many cases. When this happens, we can't continue the test. Change-Id: I6bce2263bdf444221c5303e83d70a254bbb9194c Reviewed-on: http://codereview.qt.nokia.com/2892 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Kalle Lehtonen <kalle.ju.lehtonen@nokia.com>
1002 lines
29 KiB
C++
1002 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$
|
|
** GNU Lesser General Public License Usage
|
|
** 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.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU General
|
|
** Public License version 3.0 as published by the Free Software Foundation
|
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
|
** file. Please review the following information to ensure the GNU General
|
|
** Public License version 3.0 requirements will be met:
|
|
** http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Other Usage
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $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();
|
|
|
|
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 always 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);
|
|
}
|
|
|
|
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;
|
|
QVERIFY(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
|
|
#ifdef Q_WS_QPA
|
|
QSKIP("QTBUG-20753 QCursor::setPos() / QTest::mouseMove() doesn't work on qpa", 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);
|
|
|
|
QRect desiredGeometry = b.geometry();
|
|
desiredGeometry.moveTopLeft(QPoint(10, screen.bottom()-b.height()-5));
|
|
|
|
b.setGeometry(desiredGeometry);
|
|
QTest::qWaitForWindowShown(&b);
|
|
|
|
if (b.geometry() != desiredGeometry) {
|
|
// We are trying to put the button very close to the edge of the screen,
|
|
// explicitly to test behavior when the popup menu goes off the screen.
|
|
// However a modern window manager is quite likely to reject this requested geometry
|
|
// (kwin in kde4 does, for example, since the button would probably appear behind
|
|
// or partially behind the taskbar).
|
|
// Your best bet is to run this test _without_ a WM.
|
|
QSKIP("Your window manager won't allow a window against the bottom of the screen", SkipAll);
|
|
}
|
|
|
|
QTimer::singleShot(300,lastMenu, SLOT(hide()));
|
|
QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center());
|
|
QVERIFY(!lastMenu->geometry().intersects(b.geometry()));
|
|
|
|
// note: we're assuming that, if we previously got the desired geometry, we'll get it here too
|
|
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"
|