/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $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 sent. Let's 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() { QSKIP("Modal dialog causes later tests to fail, see QTBUG-58474"); // 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(); QCOMPARE(ushort(kControlUnicode), QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText).at(0).unicode()); QCOMPARE(ushort(kCommandUnicode), QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText).at(0).unicode()); 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 flag at one // point, fix the test. QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta); QWidget w; w.show(); QCOMPARE(ushort(kCommandUnicode), QKeySequence(Qt::Key_Meta).toString(QKeySequence::NativeText).at(0).unicode()); QCOMPARE(ushort(kControlUnicode), QKeySequence(Qt::Key_Control).toString(QKeySequence::NativeText).at(0).unicode()); 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"