From 11070a090a9cc77d02315a3cb39eaf628bd9bfe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 29 Sep 2011 18:02:54 +0200 Subject: Added QWindow::isActive() and focus in / out events. Renamed QGuiApplication::activeWindow() to QGuiApplication::focusWindow(), implemented QWindow::isActive() as a style hint, and added focus in / out events. Change-Id: I71866e76c5a817def3e17bcc20a4fc32081a0e7a Reviewed-on: http://codereview.qt-project.org/5811 Reviewed-by: Qt Sanity Bot Reviewed-by: Gunnar Sletta --- src/gui/kernel/qguiapplication.cpp | 22 +++-- src/gui/kernel/qguiapplication.h | 4 +- src/gui/kernel/qguiapplication_p.h | 2 +- src/gui/kernel/qwindow.cpp | 72 ++++++++++++++-- src/gui/kernel/qwindow.h | 12 +++ src/widgets/kernel/qapplication_qpa.cpp | 2 +- src/widgets/kernel/qwidgetwindow_qpa.cpp | 6 ++ tests/auto/qfiledialog2/tst_qfiledialog2.cpp | 2 +- tests/auto/qwindow/tst_qwindow.cpp | 118 ++++++++++++++++++++------- 9 files changed, 197 insertions(+), 43 deletions(-) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index bfa60fa657..986fc5c11d 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -114,7 +114,7 @@ QClipboard *QGuiApplicationPrivate::qt_clipboard = 0; QList QGuiApplicationPrivate::screen_list; QWindowList QGuiApplicationPrivate::window_list; -QWindow *QGuiApplicationPrivate::active_window = 0; +QWindow *QGuiApplicationPrivate::focus_window = 0; Q_GLOBAL_STATIC(QMutex, applicationFontMutex) QFont *QGuiApplicationPrivate::app_font = 0; @@ -181,9 +181,9 @@ QGuiApplicationPrivate::QGuiApplicationPrivate(int &argc, char **argv, int flags self = this; } -QWindow *QGuiApplication::activeWindow() +QWindow *QGuiApplication::focusWindow() { - return QGuiApplicationPrivate::active_window; + return QGuiApplicationPrivate::focus_window; } QWindowList QGuiApplication::topLevelWindows() @@ -678,8 +678,20 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate if (!e->activated) return; - QWindow *previous = QGuiApplicationPrivate::active_window; - QGuiApplicationPrivate::active_window = e->activated.data(); + QWindow *previous = QGuiApplicationPrivate::focus_window; + QGuiApplicationPrivate::focus_window = e->activated.data(); + + if (previous == QGuiApplicationPrivate::focus_window) + return; + + if (previous) { + QFocusEvent focusOut(QEvent::FocusOut); + QCoreApplication::sendSpontaneousEvent(previous, &focusOut); + } + + QFocusEvent focusIn(QEvent::FocusIn); + QCoreApplication::sendSpontaneousEvent(QGuiApplicationPrivate::focus_window, &focusIn); + if (self) self->notifyActiveWindowChange(previous); } diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h index 7b8590233e..80c71dfa2a 100644 --- a/src/gui/kernel/qguiapplication.h +++ b/src/gui/kernel/qguiapplication.h @@ -85,7 +85,9 @@ public: static QWindowList topLevelWindows(); static QWindow *topLevelAt(const QPoint &pos); - static QWindow *activeWindow(); + static QT_DEPRECATED QWindow *activeWindow() { return focusWindow(); } + static QWindow *focusWindow(); + static QScreen *primaryScreen(); static QList screens(); diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 7ee95b777f..ccaf1292ae 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -161,7 +161,7 @@ public: static QPalette *app_pal; static QWindowList window_list; - static QWindow *active_window; + static QWindow *focus_window; #ifndef QT_NO_CURSOR QList cursor_list; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 489a7498f4..b1c26a39f1 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -95,8 +95,8 @@ QWindow::QWindow(QWindowPrivate &dd, QWindow *parent) QWindow::~QWindow() { - if (QGuiApplicationPrivate::active_window == this) - QGuiApplicationPrivate::active_window = 0; + if (QGuiApplicationPrivate::focus_window == this) + QGuiApplicationPrivate::focus_window = 0; QGuiApplicationPrivate::window_list.removeAll(this); destroy(); } @@ -320,9 +320,32 @@ void QWindow::setOpacity(qreal level) void QWindow::requestActivateWindow() { Q_D(QWindow); - QGuiApplicationPrivate::active_window = this; - if (d->platformWindow) { + if (d->platformWindow) d->platformWindow->requestActivateWindow(); +} + +/*! + Returns true if the window should appear active from a style perspective. + + This is the case for the window that has input focus as well as windows + that are in the same parent / transient parent chain as the focus window. + + To get the window that currently has focus, use QGuiApplication::focusWindow(). +*/ +bool QWindow::isActive() const +{ + Q_D(const QWindow); + if (!d->platformWindow) + return false; + + QWindow *focus = QGuiApplication::focusWindow(); + if (focus == this) + return true; + + if (!parent() && !transientParent()) { + return isAncestorOf(focus); + } else { + return (parent() && parent()->isActive()) || (transientParent() && transientParent()->isActive()); } } @@ -335,7 +358,7 @@ Qt::WindowState QWindow::windowState() const void QWindow::setWindowState(Qt::WindowState state) { if (state == Qt::WindowActive) { - requestActivateWindow(); + qWarning() << "QWindow::setWindowState does not accept Qt::WindowActive"; return; } @@ -361,6 +384,29 @@ QWindow *QWindow::transientParent() const return d->transientParent.data(); } +/*! + \enum QWindow::AncestorMode + + This enum is used to control whether or not transient parents + should be considered ancestors. + + \value ExcludeTransients Transient parents are not considered ancestors. + \value IncludeTransients Transient parents are considered ancestors. +*/ + +/*! + Returns true if the window is an ancestor of the given child. If mode is + IncludeTransients transient parents are also considered ancestors. +*/ +bool QWindow::isAncestorOf(const QWindow *child, AncestorMode mode) const +{ + if (child->parent() == this || (mode == IncludeTransients && child->transientParent() == this)) + return true; + + return (child->parent() && isAncestorOf(child->parent(), mode)) + || (mode == IncludeTransients && child->transientParent() && isAncestorOf(child->transientParent(), mode)); +} + QSize QWindow::minimumSize() const { Q_D(const QWindow); @@ -670,6 +716,14 @@ bool QWindow::event(QEvent *event) keyReleaseEvent(static_cast(event)); break; + case QEvent::FocusIn: + focusInEvent(static_cast(event)); + break; + + case QEvent::FocusOut: + focusOutEvent(static_cast(event)); + break; + #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: wheelEvent(static_cast(event)); @@ -710,6 +764,14 @@ void QWindow::keyReleaseEvent(QKeyEvent *) { } +void QWindow::focusInEvent(QFocusEvent *) +{ +} + +void QWindow::focusOutEvent(QFocusEvent *) +{ +} + void QWindow::inputMethodEvent(QInputMethodEvent *) { } diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h index fefece1e9e..499582e348 100644 --- a/src/gui/kernel/qwindow.h +++ b/src/gui/kernel/qwindow.h @@ -60,6 +60,7 @@ QT_MODULE(Gui) class QWindowPrivate; class QExposeEvent; +class QFocusEvent; class QMoveEvent; class QResizeEvent; class QShowEvent; @@ -123,12 +124,21 @@ public: void setOpacity(qreal level); void requestActivateWindow(); + bool isActive() const; + Qt::WindowState windowState() const; void setWindowState(Qt::WindowState state); void setTransientParent(QWindow *parent); QWindow *transientParent() const; + enum AncestorMode { + ExcludeTransients, + IncludeTransients + }; + + bool isAncestorOf(const QWindow *child, AncestorMode mode = IncludeTransients) const; + QSize minimumSize() const; QSize maximumSize() const; QSize baseSize() const; @@ -205,6 +215,8 @@ protected: virtual void exposeEvent(QExposeEvent *); virtual void resizeEvent(QResizeEvent *); virtual void moveEvent(QMoveEvent *); + virtual void focusInEvent(QFocusEvent *); + virtual void focusOutEvent(QFocusEvent *); virtual void showEvent(QShowEvent *); virtual void hideEvent(QHideEvent *); diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp index 29ac94bfb1..8732a194e8 100644 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ b/src/widgets/kernel/qapplication_qpa.cpp @@ -157,7 +157,7 @@ void QApplicationPrivate::notifyActiveWindowChange(QWindow *previous) { Q_UNUSED(previous); Q_Q(QApplication); - QWindow *wnd = QGuiApplicationPrivate::active_window; + QWindow *wnd = QGuiApplicationPrivate::focus_window; if (inPopupMode()) // some delayed focus event to ignore return; QWidget *tlw = qt_tlw_for_window(wnd); diff --git a/src/widgets/kernel/qwidgetwindow_qpa.cpp b/src/widgets/kernel/qwidgetwindow_qpa.cpp index ee5cd7482e..2265fb55fb 100644 --- a/src/widgets/kernel/qwidgetwindow_qpa.cpp +++ b/src/widgets/kernel/qwidgetwindow_qpa.cpp @@ -79,6 +79,12 @@ bool QWidgetWindow::event(QEvent *event) handleEnterLeaveEvent(event); return true; + // these should not be sent to QWidget, the corresponding events + // are sent by QApplicationPrivate::notifyActiveWindowChange() + case QEvent::FocusIn: + case QEvent::FocusOut: + return false; + case QEvent::KeyPress: case QEvent::KeyRelease: handleKeyEvent(static_cast(event)); diff --git a/tests/auto/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/qfiledialog2/tst_qfiledialog2.cpp index 282e95313f..0edc028ce6 100644 --- a/tests/auto/qfiledialog2/tst_qfiledialog2.cpp +++ b/tests/auto/qfiledialog2/tst_qfiledialog2.cpp @@ -589,7 +589,7 @@ void tst_QFileDialog2::completionOnLevelAfterRoot() QCOMPARE(edit->text(), QString("completionOnLevelAfterRootTest")); current.rmdir("completionOnLevelAfterRootTest"); #else - QCOMPARE(edit->text(), QString("etc")); + QTRY_COMPARE(edit->text(), QString("etc")); #endif } diff --git a/tests/auto/qwindow/tst_qwindow.cpp b/tests/auto/qwindow/tst_qwindow.cpp index 3eedd7cf6f..478bcbe78c 100644 --- a/tests/auto/qwindow/tst_qwindow.cpp +++ b/tests/auto/qwindow/tst_qwindow.cpp @@ -52,6 +52,7 @@ class tst_QWindow: public QObject private slots: void mapGlobal(); void positioning(); + void isActive(); }; @@ -78,35 +79,30 @@ class Window : public QWindow { public: Window() - : gotResizeEvent(false) - , gotMapEvent(false) - , gotMoveEvent(false) { + reset(); setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); } + void reset() + { + m_received.clear(); + } + bool event(QEvent *event) { - switch (event->type()) { - case QEvent::Map: - gotMapEvent = true; - break; - case QEvent::Resize: - gotResizeEvent = true; - break; - case QEvent::Move: - gotMoveEvent = true; - break; - default: - break; - } + m_received[event->type()]++; return QWindow::event(event); } - bool gotResizeEvent; - bool gotMapEvent; - bool gotMoveEvent; + int received(QEvent::Type type) + { + return m_received.value(type, 0); + } + +private: + QHash m_received; }; void tst_QWindow::positioning() @@ -118,7 +114,8 @@ void tst_QWindow::positioning() QCOMPARE(window.geometry(), geometry); window.show(); - QTRY_VERIFY(window.gotResizeEvent && window.gotMapEvent); + QTRY_COMPARE(window.received(QEvent::Resize), 1); + QTRY_COMPARE(window.received(QEvent::Map), 1); QMargins originalMargins = window.frameMargins(); @@ -128,14 +125,11 @@ void tst_QWindow::positioning() QPoint originalPos = window.pos(); QPoint originalFramePos = window.framePos(); - window.gotResizeEvent = false; - window.setWindowState(Qt::WindowFullScreen); - QTRY_VERIFY(window.gotResizeEvent); + QTRY_COMPARE(window.received(QEvent::Resize), 2); - window.gotResizeEvent = false; window.setWindowState(Qt::WindowNoState); - QTRY_VERIFY(window.gotResizeEvent); + QTRY_COMPARE(window.received(QEvent::Resize), 3); QTRY_COMPARE(originalPos, window.pos()); QTRY_COMPARE(originalFramePos, window.framePos()); @@ -146,22 +140,88 @@ void tst_QWindow::positioning() if (originalPos == geometry.topLeft() && (originalMargins.top() != 0 || originalMargins.left() != 0)) { QPoint framePos(40, 40); - window.gotMoveEvent = false; + window.reset(); window.setFramePos(framePos); - QTRY_VERIFY(window.gotMoveEvent); + QTRY_VERIFY(window.received(QEvent::Move)); QTRY_COMPARE(framePos, window.framePos()); QTRY_COMPARE(originalMargins, window.frameMargins()); QCOMPARE(window.pos(), window.framePos() + QPoint(originalMargins.left(), originalMargins.top())); // and back to regular positioning - window.gotMoveEvent = false; + window.reset(); window.setPos(originalPos); - QTRY_VERIFY(window.gotMoveEvent); + QTRY_VERIFY(window.received(QEvent::Move)); QTRY_COMPARE(originalPos, window.pos()); } } +void tst_QWindow::isActive() +{ + Window window; + window.setGeometry(80, 80, 40, 40); + window.show(); + + QTRY_COMPARE(window.received(QEvent::Map), 1); + QTRY_COMPARE(window.received(QEvent::Resize), 1); + QTRY_VERIFY(QGuiApplication::focusWindow() == &window); + QVERIFY(window.isActive()); + + Window child; + child.setParent(&window); + child.setGeometry(10, 10, 20, 20); + child.show(); + + QTRY_COMPARE(child.received(QEvent::Map), 1); + + child.requestActivateWindow(); + + QTRY_VERIFY(QGuiApplication::focusWindow() == &child); + QVERIFY(child.isActive()); + + // parent shouldn't receive new map or resize events from child being shown + QTRY_COMPARE(window.received(QEvent::Map), 1); + QTRY_COMPARE(window.received(QEvent::Resize), 1); + QTRY_COMPARE(window.received(QEvent::FocusIn), 1); + QTRY_COMPARE(window.received(QEvent::FocusOut), 1); + QTRY_COMPARE(child.received(QEvent::FocusIn), 1); + + // child has focus + QVERIFY(window.isActive()); + + Window dialog; + dialog.setTransientParent(&window); + dialog.setGeometry(110, 110, 30, 30); + dialog.show(); + + dialog.requestActivateWindow(); + + QTRY_COMPARE(dialog.received(QEvent::Map), 1); + QTRY_COMPARE(dialog.received(QEvent::Resize), 1); + QTRY_VERIFY(QGuiApplication::focusWindow() == &dialog); + QVERIFY(dialog.isActive()); + + // transient child has focus + QVERIFY(window.isActive()); + + // parent is active + QVERIFY(child.isActive()); + + window.requestActivateWindow(); + + QTRY_VERIFY(QGuiApplication::focusWindow() == &window); + QTRY_COMPARE(dialog.received(QEvent::FocusOut), 1); + QTRY_COMPARE(window.received(QEvent::FocusIn), 2); + + QVERIFY(window.isActive()); + + // transient parent has focus + QVERIFY(dialog.isActive()); + + // parent has focus + QVERIFY(child.isActive()); +} + #include QTEST_MAIN(tst_QWindow); -- cgit v1.2.3