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 <qt_sanity_bot@ovi.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
bb10
Samuel Rødal 2011-09-29 18:02:54 +02:00 committed by Qt by Nokia
parent 253497b744
commit 11070a090a
9 changed files with 197 additions and 43 deletions

View 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);
}

View 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();

View 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;

View 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 *)
{
}

View 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 *);

View 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);

View 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));

View 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
}

View 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);