| /**************************************************************************** |
| ** |
| ** 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 <QApplication> |
| #include <QDebug> |
| #include <QMainWindow> |
| #include <QStatusBar> |
| #include <QPlainTextEdit> |
| #include <QMenuBar> |
| #include <QMenu> |
| #include <QAction> |
| #include <QGridLayout> |
| #include <QVBoxLayout> |
| #include <QMessageBox> |
| #include <QLabel> |
| #include <QPushButton> |
| #include <QCheckBox> |
| #include <QComboBox> |
| #include <QTimer> |
| #include <QLineEdit> |
| |
| // Compiles with Qt 4.8 and Qt 5. |
| |
| class MainWindow : public QMainWindow |
| { |
| Q_OBJECT |
| public: |
| MainWindow(); |
| |
| bool eventFilter(QObject *, QEvent *); |
| |
| private slots: |
| void showModalDialog(); |
| void mouseGrabToggled(bool); |
| void delayedMouseGrab(); |
| void grabMouseWindowToggled(bool); |
| void delayedMouseWindowGrab(); |
| void keyboardGrabToggled(bool); |
| void grabKeyboardWindowToggled(bool); |
| void forceNativeWidgets(); |
| |
| private: |
| void toggleMouseWidgetGrab(QWidget *w, bool on); |
| void toggleKeyboardWidgetGrab(QWidget *w, bool on); |
| |
| int m_mouseEventCount; |
| int m_enterLeaveEventCount; |
| QPlainTextEdit *m_logEdit; |
| QCheckBox *m_grabMouseCheckBox; |
| QCheckBox *m_grabMouseWindowCheckBox; |
| QCheckBox *m_grabKeyboardCheckBox; |
| QCheckBox *m_grabKeyboardWindowCheckBox; |
| QPushButton *m_forceNativeButton; |
| |
| QString m_lastMouseMoveEvent; |
| }; |
| |
| class ClickableLabel : public QLabel |
| { |
| Q_OBJECT |
| public: |
| explicit ClickableLabel(const QString &text, QWidget *parent = 0) : QLabel(text, parent) {} |
| |
| signals: |
| void pressed(); |
| |
| protected: |
| void mousePressEvent(QMouseEvent *ev) |
| { |
| emit pressed(); |
| QLabel::mousePressEvent(ev); |
| } |
| }; |
| |
| static const char testCasesC[] = |
| "- Drag a scrollbar, move mouse out of the window. The scrollbar should still react.\n\n" |
| "- Press mouse inside window, move outside while pressing the button. Mouse events" |
| " should be reported until the button is released.\n\n" |
| "- 'Show modal dialog on press' opens a modal dialog on mouse press. This should not lock up.\n\n" |
| "- Check the 'Grab Mouse' box. Only the checkbox should then receive mouse move events.\n\n" |
| "- Open popup menu. Mouse events should all go to popup menu while it is visible.\n\n" |
| "- Click delayed grab and then open popup immediately. Wait for Grab checkbox to be marked. " |
| "Click on popup menu to close it. Mouse events should be going to Grab checkbox. " |
| "Click on Grab checkbox to clear it. UI should respond normally after that. \n\n" |
| ; |
| |
| MainWindow::MainWindow() |
| : m_mouseEventCount(0) |
| , m_enterLeaveEventCount(0) |
| , m_logEdit(new QPlainTextEdit(this)) |
| , m_grabMouseCheckBox(new QCheckBox(QLatin1String("Grab Mouse"))) |
| , m_grabMouseWindowCheckBox(new QCheckBox(QLatin1String("Grab Mouse Window Ctrl+W"))) |
| , m_grabKeyboardCheckBox(new QCheckBox(QLatin1String("Grab Keyboard"))) |
| , m_grabKeyboardWindowCheckBox(new QCheckBox(QLatin1String("Grab Keyboard Window"))) |
| , m_forceNativeButton(new QPushButton(QLatin1String("Force native widgets"))) |
| { |
| setObjectName(QLatin1String("MainWindow")); |
| setMinimumWidth(800); |
| setWindowTitle(QString::fromLatin1("Manual Grab Test %1").arg(QLatin1String(QT_VERSION_STR))); |
| |
| QMenu *fileMenu = menuBar()->addMenu(QLatin1String("File")); |
| fileMenu->setObjectName("FileMenu"); |
| QAction *quit = fileMenu->addAction(QLatin1String("Quit")); |
| quit->setShortcut(QKeySequence::Quit); |
| connect(quit, SIGNAL(triggered()), this, SLOT(close())); |
| |
| QMenu *editMenu = menuBar()->addMenu(QLatin1String("Edit")); |
| editMenu->setObjectName("EditMenu"); |
| QAction *clearLog = editMenu->addAction(QLatin1String("Clear Log")); |
| connect(clearLog, SIGNAL(triggered()), m_logEdit, SLOT(clear())); |
| |
| QWidget *w = new QWidget(this); |
| w->setObjectName(QLatin1String("CentralWidget")); |
| QVBoxLayout *layout = new QVBoxLayout(w); |
| QPlainTextEdit *instructions = new QPlainTextEdit(this); |
| instructions->setObjectName(QLatin1String("InstructionsEdit")); |
| instructions->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); |
| instructions->setPlainText(QLatin1String(testCasesC)); |
| instructions->setReadOnly(true); |
| layout->addWidget(instructions); |
| |
| int row = 0; |
| QGridLayout *controlLayout = new QGridLayout; |
| layout->addLayout(controlLayout); |
| QPushButton *modalDialogButton = new QPushButton("Show modal dialog on release"); |
| modalDialogButton->setObjectName(QLatin1String("ModalDialogButton")); |
| connect(modalDialogButton, SIGNAL(clicked()), this, SLOT(showModalDialog())); |
| controlLayout->addWidget(modalDialogButton, row, 0); |
| ClickableLabel *modalDialogLabel = new ClickableLabel("Show modal dialog on press"); |
| modalDialogLabel->setObjectName(QLatin1String("ModalDialogLabel")); |
| controlLayout->addWidget(modalDialogLabel, row, 1); |
| connect(modalDialogLabel, SIGNAL(pressed()), this, SLOT(showModalDialog())); |
| |
| row++; |
| m_grabMouseCheckBox->setObjectName(QLatin1String("GrabCheckBox")); |
| connect(m_grabMouseCheckBox, SIGNAL(toggled(bool)), this, SLOT(mouseGrabToggled(bool))); |
| controlLayout->addWidget(m_grabMouseCheckBox, row, 0); |
| QPushButton *delayedGrabButton = new QPushButton("Delayed grab"); |
| delayedGrabButton->setObjectName(QLatin1String("DelayedGrabButton")); |
| connect(delayedGrabButton, SIGNAL(clicked()), this, SLOT(delayedMouseGrab())); |
| controlLayout->addWidget(delayedGrabButton, row, 1); |
| |
| row++; |
| m_grabMouseWindowCheckBox->setObjectName(QLatin1String("GrabWindowCheckBox")); |
| m_grabMouseWindowCheckBox->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_W)); |
| connect(m_grabMouseWindowCheckBox, SIGNAL(toggled(bool)), this, SLOT(grabMouseWindowToggled(bool))); |
| controlLayout->addWidget(m_grabMouseWindowCheckBox, row, 0); |
| QPushButton *delayedWindowGrabButton = new QPushButton("Delayed window grab"); |
| delayedWindowGrabButton->setObjectName(QLatin1String("DelayedWindowGrabButton")); |
| connect(delayedWindowGrabButton, SIGNAL(clicked()), this, SLOT(delayedMouseWindowGrab())); |
| controlLayout->addWidget(delayedWindowGrabButton, row, 1); |
| |
| row++; |
| m_grabKeyboardCheckBox->setObjectName(QLatin1String("GrabKeyboardBox")); |
| connect(m_grabKeyboardCheckBox, SIGNAL(toggled(bool)), this, SLOT(keyboardGrabToggled(bool))); |
| controlLayout->addWidget(m_grabKeyboardCheckBox, row, 0); |
| m_grabKeyboardWindowCheckBox->setObjectName(QLatin1String("GrabKeyboardWindowBox")); |
| connect(m_grabKeyboardWindowCheckBox, SIGNAL(toggled(bool)), this, SLOT(grabKeyboardWindowToggled(bool))); |
| controlLayout->addWidget(m_grabKeyboardWindowCheckBox, row, 1); |
| |
| row++; |
| QComboBox *combo = new QComboBox; |
| combo->addItems(QStringList() << QLatin1String("Popup test 1") << QLatin1String("Popup test 2")); |
| controlLayout->addWidget(combo, row, 0); |
| |
| QPushButton *popupMenuButton = new QPushButton("Popup menu"); |
| popupMenuButton->setObjectName(QLatin1String("PopupMenuButton")); |
| QMenu *popupMenu = new QMenu(this); |
| popupMenu->setObjectName(QLatin1String("PopupMenu")); |
| popupMenu->addAction(tr("&First Item")); |
| popupMenu->addAction(tr("&Second Item")); |
| popupMenu->addAction(tr("&Third Item")); |
| popupMenu->addAction(tr("F&ourth Item")); |
| popupMenuButton->setMenu(popupMenu); |
| controlLayout->addWidget(popupMenuButton, row, 1); |
| |
| row++; |
| m_forceNativeButton->setObjectName("ForceNativeWidgetsButton"); |
| controlLayout->addWidget(m_forceNativeButton, row, 0); |
| connect(m_forceNativeButton, SIGNAL(clicked()), this, SLOT(forceNativeWidgets())); |
| |
| row++; |
| QLineEdit *lineEdit = new QLineEdit(this); |
| lineEdit->setObjectName(QLatin1String("LineEdit")); |
| controlLayout->addWidget(lineEdit, row, 0, 1, 2); |
| |
| m_logEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); |
| m_logEdit->setObjectName(QLatin1String("LogEdit")); |
| layout->addWidget(m_logEdit); |
| setCentralWidget(w); |
| |
| qApp->installEventFilter(this); |
| } |
| |
| bool MainWindow::eventFilter(QObject *o, QEvent *e) |
| { |
| if (o->isWidgetType()) { |
| switch (e->type()) { |
| case QEvent::Enter: { |
| QString message; |
| QDebug debug(&message); |
| #if QT_VERSION >= 0x050000 |
| const QEnterEvent *ee = static_cast<QEnterEvent *>(e); |
| debug.nospace() << '#' << m_enterLeaveEventCount++ << " Enter for " << o->objectName() |
| << " at " << ee->localPos() << " global: " << ee->globalPos(); |
| #else |
| debug.nospace() << '#' << m_enterLeaveEventCount++ << " Enter for " << o->objectName(); |
| #endif |
| m_logEdit->appendPlainText(message); |
| } |
| break; |
| case QEvent::Leave: { |
| QString message; |
| QDebug debug(&message); |
| debug.nospace() << '#' << m_enterLeaveEventCount++ << " Leave for " << o->objectName(); |
| m_logEdit->appendPlainText(message); |
| } |
| break; |
| case QEvent::MouseButtonPress: |
| case QEvent::MouseButtonRelease: { |
| const QMouseEvent *me = static_cast<QMouseEvent *>(e); |
| QString message; |
| QDebug debug = QDebug(&message).nospace(); |
| debug << '#' << m_mouseEventCount++ << ' '; |
| if (e->type() == QEvent::MouseButtonPress) { |
| if (me->buttons() & Qt::LeftButton) |
| debug << "Left button press"; |
| if (me->buttons() & Qt::MiddleButton) |
| debug << "Middle button press"; |
| if (me->buttons() & Qt::RightButton) |
| debug << "Right button press"; |
| } else { |
| debug << "Button release"; |
| } |
| debug << " on " << o->objectName() << " Mousegrabber " << QWidget::mouseGrabber(); |
| m_logEdit->appendPlainText(message); |
| } |
| break; |
| case QEvent::MouseMove: { |
| const QMouseEvent *me = static_cast<const QMouseEvent *>(e); |
| const QWidget *widgetUnderMouse = QApplication::widgetAt(me->globalPos()); |
| QString message; |
| QDebug d = QDebug(&message).nospace(); |
| d << " Mouse move reported for " << o->objectName(); |
| if (widgetUnderMouse) { |
| d << " over " << widgetUnderMouse; |
| } else { |
| d << " outside "; |
| } |
| d << " mouse grabber " << QWidget::mouseGrabber(); |
| // Compress mouse move event logging. |
| if (message != m_lastMouseMoveEvent) { |
| m_lastMouseMoveEvent = message; |
| m_logEdit->appendPlainText(QString::fromLatin1("#%1 %2").arg(m_mouseEventCount++).arg(message)); |
| } |
| } |
| break; |
| case QEvent::KeyRelease: |
| case QEvent::KeyPress: { |
| const QKeyEvent *ke = static_cast<const QKeyEvent *>(e); |
| QString message; |
| QDebug d = QDebug(&message).nospace(); |
| d << (e->type() == QEvent::KeyPress ? "Key press" : "Key release") |
| << ' ' << ke->text() << " on " << o << " key grabber " << QWidget::keyboardGrabber(); |
| m_logEdit->appendPlainText(message); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| return QMainWindow::eventFilter(o ,e); |
| } |
| |
| void MainWindow::showModalDialog() |
| { |
| QMessageBox::information(this, QLatin1String("Information"), QLatin1String("Modal Dialog")); |
| } |
| |
| void MainWindow::toggleMouseWidgetGrab(QWidget *w, bool on) |
| { |
| if (on) { |
| m_logEdit->appendPlainText(w->objectName() + QLatin1String(" grabbed mouse.")); |
| w->grabMouse(); |
| } else { |
| w->releaseMouse(); |
| m_logEdit->appendPlainText(w->objectName() + QLatin1String(" released mouse.")); |
| } |
| } |
| |
| void MainWindow::toggleKeyboardWidgetGrab(QWidget *w, bool on) |
| { |
| if (on) { |
| m_logEdit->appendPlainText(w->objectName() + QLatin1String(" grabbed keyboard.")); |
| w->grabKeyboard(); |
| } else { |
| w->releaseKeyboard(); |
| m_logEdit->appendPlainText(w->objectName() + QLatin1String(" released keyboard.")); |
| } |
| } |
| |
| void MainWindow::mouseGrabToggled(bool g) |
| { |
| toggleMouseWidgetGrab(m_grabMouseCheckBox, g); |
| } |
| |
| void MainWindow::delayedMouseGrab() |
| { |
| QTimer::singleShot(2000, m_grabMouseCheckBox, SLOT(animateClick())); |
| } |
| |
| void MainWindow::grabMouseWindowToggled(bool g) |
| { |
| toggleMouseWidgetGrab(this, g); |
| } |
| |
| void MainWindow::delayedMouseWindowGrab() |
| { |
| QTimer::singleShot(2000, m_grabMouseWindowCheckBox, SLOT(animateClick())); |
| } |
| |
| void MainWindow::keyboardGrabToggled(bool g) |
| { |
| toggleKeyboardWidgetGrab(m_grabKeyboardCheckBox, g); |
| } |
| |
| void MainWindow::grabKeyboardWindowToggled(bool g) |
| { |
| toggleKeyboardWidgetGrab(this, g); |
| } |
| |
| void MainWindow::forceNativeWidgets() |
| { |
| const WId platformWid = m_forceNativeButton->winId(); |
| #if QT_VERSION < 0x050000 && defined(Q_OS_WIN) |
| const quintptr wid = quintptr(platformWid); // HWND on Qt 4.8/Windows. |
| #else |
| const WId wid = platformWid; |
| #endif |
| m_logEdit->appendPlainText(QString::fromLatin1("Created native widget %1").arg(wid)); |
| m_forceNativeButton->setEnabled(false); |
| m_forceNativeButton->setText(QLatin1String("Native widgets created")); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| QApplication a(argc, argv); |
| MainWindow w; |
| w.show(); |
| return a.exec(); |
| } |
| |
| #include "main.moc" |