Added QWindow::isActive() and focus in / out events.
authorSamuel Rødal <samuel.rodal@nokia.com>
Thu, 29 Sep 2011 16:02:54 +0000 (18:02 +0200)
committerQt by Nokia <qt-info@nokia.com>
Wed, 5 Oct 2011 10:49:29 +0000 (12:49 +0200)
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 <qt_sanity_bot@ovi.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
src/gui/kernel/qguiapplication.cpp
src/gui/kernel/qguiapplication.h
src/gui/kernel/qguiapplication_p.h
src/gui/kernel/qwindow.cpp
src/gui/kernel/qwindow.h
src/widgets/kernel/qapplication_qpa.cpp
src/widgets/kernel/qwidgetwindow_qpa.cpp
tests/auto/qfiledialog2/tst_qfiledialog2.cpp
tests/auto/qwindow/tst_qwindow.cpp

index bfa60fa..986fc5c 100644 (file)
@@ -114,7 +114,7 @@ QClipboard *QGuiApplicationPrivate::qt_clipboard = 0;
 QList<QScreen *> 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);
 }
index 7b85902..80c71df 100644 (file)
@@ -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<QScreen *> screens();
 
index 7ee95b7..ccaf129 100644 (file)
@@ -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<QCursor> cursor_list;
index 489a749..b1c26a3 100644 (file)
@@ -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<QKeyEvent *>(event));
         break;
 
+    case QEvent::FocusIn:
+        focusInEvent(static_cast<QFocusEvent *>(event));
+        break;
+
+    case QEvent::FocusOut:
+        focusOutEvent(static_cast<QFocusEvent *>(event));
+        break;
+
 #ifndef QT_NO_WHEELEVENT
     case QEvent::Wheel:
         wheelEvent(static_cast<QWheelEvent*>(event));
@@ -710,6 +764,14 @@ void QWindow::keyReleaseEvent(QKeyEvent *)
 {
 }
 
+void QWindow::focusInEvent(QFocusEvent *)
+{
+}
+
+void QWindow::focusOutEvent(QFocusEvent *)
+{
+}
+
 void QWindow::inputMethodEvent(QInputMethodEvent *)
 {
 }
index fefece1..499582e 100644 (file)
@@ -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 *);
index 29ac94b..8732a19 100644 (file)
@@ -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);
index ee5cd74..2265fb5 100644 (file)
@@ -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<QKeyEvent *>(event));
index 282e953..0edc028 100644 (file)
@@ -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
 }
 
index 3eedd7c..478bcbe 100644 (file)
@@ -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<QEvent::Type, int> 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 <tst_qwindow.moc>
 QTEST_MAIN(tst_QWindow);