/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** 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 #include #include #include #include #include "qnativeevents.h" #include "nativeeventlist.h" #include "expectedeventlist.h" #include QT_USE_NAMESPACE class tst_MacNativeEvents : public QObject { Q_OBJECT private slots: void testMouseMoveLocation(); void testPushButtonPressRelease(); void testMouseLeftDoubleClick(); void stressTestMouseLeftDoubleClick(); void testMouseDragInside(); void testMouseDragOutside(); void testMouseDragToNonClientArea(); void testDragWindow(); void testMouseEnter(); void testChildDialogInFrontOfModalParent(); // void testChildWindowInFrontOfParentWindow(); // void testChildToolWindowInFrontOfChildNormalWindow(); void testChildWindowInFrontOfStaysOnTopParentWindow(); void testKeyPressOnToplevel(); void testModifierShift(); void testModifierAlt(); void testModifierCtrl(); void testModifierCtrlWithDontSwapCtrlAndMeta(); }; void tst_MacNativeEvents::testMouseMoveLocation() { QWidget w; w.setMouseTracking(true); w.show(); QPoint p = w.geometry().center(); NativeEventList native; native.append(new QNativeMouseMoveEvent(p, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p), p, Qt::NoButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testPushButtonPressRelease() { // Check that a native mouse press and release generates the // same qevents on a pushbutton: QPushButton w("click me"); w.show(); QPoint p = w.geometry().center(); NativeEventList native; native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testMouseLeftDoubleClick() { // Check that a native double click makes // the test widget receive a press-release-click-release: QWidget w; w.show(); QPoint p = w.geometry().center(); NativeEventList native; native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 2, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonDblClick, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::stressTestMouseLeftDoubleClick() { // Check that multiple, fast, double clicks makes // the test widget receive correct click events QWidget w; w.show(); QPoint p = w.geometry().center(); NativeEventList native; ExpectedEventList expected(&w); for (int i=0; i<10; ++i){ native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 2, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p, Qt::LeftButton, 0, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonDblClick, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p), p, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); } native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testMouseDragInside() { // Check that a mouse drag inside a widget // will cause press-move-release events to be delivered QWidget w; w.show(); QPoint p1 = w.geometry().center(); QPoint p2 = p1 - QPoint(10, 0); QPoint p3 = p1 - QPoint(20, 0); QPoint p4 = p1 - QPoint(30, 0); NativeEventList native; native.append(new QNativeMouseButtonEvent(p1, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(p2, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(p3, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(p4, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(p1), p1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p2), p2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(p3), p3, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(p4), p4, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testMouseDragOutside() { // Check that if we drag the mouse from inside the // widget, and release it outside, we still get mouse move // and release events when the mouse is outside the widget. QWidget w; w.show(); QPoint inside1 = w.geometry().center(); QPoint inside2 = inside1 - QPoint(10, 0); QPoint outside1 = w.geometry().topLeft() - QPoint(50, 0); QPoint outside2 = outside1 - QPoint(10, 0); NativeEventList native; native.append(new QNativeMouseButtonEvent(inside1, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(inside2, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(outside1, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(outside2, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(inside1), inside1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside2), inside2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(outside1), outside1, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(outside2), outside2, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testMouseDragToNonClientArea() { // Check that if we drag the mouse from inside the // widget, and release it on the title bar, we still get mouse move // and release events when the mouse is on the title bar QWidget w; w.show(); QPoint inside1 = w.geometry().center(); QPoint inside2 = inside1 - QPoint(10, 0); QPoint titlebar1 = w.geometry().topLeft() - QPoint(-100, 10); QPoint titlebar2 = titlebar1 - QPoint(10, 0); NativeEventList native; native.append(new QNativeMouseButtonEvent(inside1, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(inside2, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(titlebar1, Qt::LeftButton, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(titlebar2, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::MouseButtonPress, w.mapFromGlobal(inside1), inside1, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside2), inside2, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(titlebar1), titlebar1, Qt::NoButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::MouseButtonRelease, w.mapFromGlobal(titlebar2), titlebar2, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testDragWindow() { // Check that if we drag the mouse from inside the // widgets title bar, we get a move event on the window QWidget w; w.show(); QPoint titlebar = w.geometry().topLeft() - QPoint(-100, 10); QPoint moveTo = titlebar + QPoint(100, 0); NativeEventList native; native.append(new QNativeMouseButtonEvent(titlebar, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseDragEvent(moveTo, Qt::LeftButton, Qt::NoModifier)); native.append(500, new QNativeMouseButtonEvent(moveTo, Qt::LeftButton, 0, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QMouseEvent(QEvent::NonClientAreaMouseButtonPress, w.mapFromGlobal(titlebar), titlebar, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier)); expected.append(new QMouseEvent(QEvent::NonClientAreaMouseButtonRelease, w.mapFromGlobal(titlebar), moveTo, Qt::LeftButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testMouseEnter() { // When a mouse enters a widget, both a mouse enter events and a // mouse move event should be sendt. Lets test this: QWidget w; w.setMouseTracking(true); w.show(); QPoint outside = w.geometry().topLeft() - QPoint(50, 0); QPoint inside = w.geometry().center(); NativeEventList native; native.append(new QNativeMouseMoveEvent(outside, Qt::NoModifier)); native.append(new QNativeMouseMoveEvent(inside, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QEvent(QEvent::Enter)); expected.append(new QMouseEvent(QEvent::MouseMove, w.mapFromGlobal(inside), inside, Qt::NoButton, Qt::NoButton, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testChildDialogInFrontOfModalParent() { // Test that a child dialog of a modal parent dialog is // in front of the parent, and active: QDialog parent; parent.setWindowModality(Qt::ApplicationModal); QDialog child(&parent); QPushButton button("close", &child); connect(&button, SIGNAL(clicked()), &child, SLOT(close())); parent.show(); child.show(); QPoint inside = button.mapToGlobal(button.geometry().center()); // Post a click on the button to close the child dialog: NativeEventList native; native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 0, Qt::NoModifier)); native.play(); QTest::qWait(100); QVERIFY(!child.isVisible()); } #if 0 // This test is disabled as of Qt-4.7.4 because we cannot do it // unless we use the Cocoa sub window API. But using that opens up // a world of side effects that we cannot live with. So we rather // not support child-on-top-of-parent instead. void tst_MacNativeEvents::testChildWindowInFrontOfParentWindow() { // Test that a child window always stacks in front of its parent window. // Do this by first click on the parent, then on the child window button. QWidget parent; QPushButton child("a button", &parent); child.setWindowFlags(Qt::Window); connect(&child, SIGNAL(clicked()), &child, SLOT(close())); parent.show(); child.show(); QPoint parent_p = parent.geometry().bottomLeft() + QPoint(20, -20); QPoint child_p = child.geometry().center(); NativeEventList native; native.append(new QNativeMouseButtonEvent(parent_p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(parent_p, Qt::LeftButton, 0, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(child_p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(child_p, Qt::LeftButton, 0, Qt::NoModifier)); native.play(); QTest::qWait(100); QVERIFY(!child.isVisible()); } #endif /* This test can be enabled once setStackingOrder has been fixed in qwidget_mac.mm void tst_MacNativeEvents::testChildToolWindowInFrontOfChildNormalWindow() { // Test that a child tool window always stacks in front of normal sibling windows. // Do this by first click on the sibling, then on the tool window button. QWidget parent; QWidget normalChild(&parent, Qt::Window); QPushButton toolChild("a button", &parent); toolChild.setWindowFlags(Qt::Tool); connect(&toolChild, SIGNAL(clicked()), &toolChild, SLOT(close())); parent.show(); normalChild.show(); toolChild.show(); QPoint normalChild_p = normalChild.geometry().bottomLeft() + QPoint(20, -20); QPoint toolChild_p = toolChild.geometry().center(); NativeEventList native; native.append(new QNativeMouseButtonEvent(normalChild_p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(normalChild_p, Qt::LeftButton, 0, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(toolChild_p, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(toolChild_p, Qt::LeftButton, 0, Qt::NoModifier)); native.play(); QTest::qWait(100); QVERIFY(!toolChild.isVisible()); } */ void tst_MacNativeEvents::testChildWindowInFrontOfStaysOnTopParentWindow() { // Test that a child window stacks on top of a stays-on-top parent. QWidget parent(0, Qt::WindowStaysOnTopHint); QPushButton button("close", &parent); button.setWindowFlags(Qt::Window); connect(&button, SIGNAL(clicked()), &button, SLOT(close())); parent.show(); button.show(); QPoint inside = button.geometry().center(); // Post a click on the button to close the child dialog: NativeEventList native; native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 1, Qt::NoModifier)); native.append(new QNativeMouseButtonEvent(inside, Qt::LeftButton, 0, Qt::NoModifier)); native.play(); QTest::qWait(100); QVERIFY(!button.isVisible()); } void tst_MacNativeEvents::testKeyPressOnToplevel() { // Check that we receive keyevents for // toplevel widgets. For leagacy reasons, and according to Qt on // other platforms (carbon port + linux), we should get these events // even when the focus policy is set to Qt::NoFocus when there is no // other focus widget on screen: QWidget w; w.show(); NativeEventList native; native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::NoModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testModifierShift() { QWidget w; w.show(); NativeEventList native; native.append(new QNativeModifierEvent(Qt::ShiftModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ShiftModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ShiftModifier)); native.append(new QNativeModifierEvent(Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Shift, Qt::NoModifier)); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::ShiftModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ShiftModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Shift, Qt::ShiftModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testModifierAlt() { QWidget w; w.show(); NativeEventList native; native.append(new QNativeModifierEvent(Qt::AltModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::AltModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::AltModifier)); native.append(new QNativeModifierEvent(Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Alt, Qt::NoModifier)); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::AltModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::AltModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Alt, Qt::AltModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testModifierCtrl() { // On Mac, we switch the Command and Control modifier by default, so that Command // means Meta, and Control means Command. Lets check that this works: QWidget w; w.show(); QVERIFY(kControlUnicode == QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText)[0]); QVERIFY(kCommandUnicode == QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText)[0]); NativeEventList native; native.append(new QNativeModifierEvent(Qt::ControlModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ControlModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ControlModifier)); native.append(new QNativeModifierEvent(Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Meta, Qt::NoModifier)); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::MetaModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::MetaModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Meta, Qt::MetaModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); } void tst_MacNativeEvents::testModifierCtrlWithDontSwapCtrlAndMeta() { // On Mac, we switch the Command and Control modifier by default, so that Command // means Meta, and Control means Command. Lets check that the flag to swith off // this behaviour works. While working on this test I realized that we actually // don't (and never have) respected this flag for raw key events. Only for // menus, through QKeySequence. I don't want to change this behaviour now, at // least not until someone complains. So I choose to let the test just stop // any unintended regressions instead. If we decide to resepect the the flag at one // point, fix the test. QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta); QWidget w; w.show(); QVERIFY(kCommandUnicode == QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText)[0]); QVERIFY(kControlUnicode == QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText)[0]); NativeEventList native; native.append(new QNativeModifierEvent(Qt::ControlModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, true, Qt::ControlModifier)); native.append(new QNativeKeyEvent(QNativeKeyEvent::Key_A, false, Qt::ControlModifier)); native.append(new QNativeModifierEvent(Qt::NoModifier)); ExpectedEventList expected(&w); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_Meta, Qt::NoModifier)); expected.append(new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::ControlModifier)); expected.append(new QKeyEvent(QEvent::KeyRelease, Qt::Key_Meta, Qt::ControlModifier)); native.play(); QVERIFY2(expected.waitForAllEvents(), "the test did not receive all expected events!"); QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta, false); } QTEST_MAIN(tst_MacNativeEvents) #include "tst_macnativeevents.moc"