QPA: Add interface for setting the application state explicitly

The motivation for this patch is twofold:

1: we need a way (for iOS/Android) to tell the current window to remove
focus from the focus object when the user hides the input panel.
Otherwise, if the focus object is e.g a line edit, the cursor will
continue to blink inside it, which is wrong. As it stands, telling
the active window to deactivate
(by calling QWindowSystemInterface::handleWindowActivated(0)), will cause
the whole application to deactivate if no windows are active, which
is not what we want.

2: Qt currently understands just two application states, Activated and
Deactivated. On mobile platforms we can have other states
as well, like "suspended" on iOS. So controlling the application
state should not depend on window activation, but instead be controlled
through a separate API by the platform plugin.

This patch will add the following function:
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState newState)
that lets us control the application state from the plugin. This also
means that we factor out application state handling from window
activation, which also gives us a way to remove focus from a window while
keeping the application active.

To not break existing desktop platforms that relies on application
activation being tied to window activation, we need to make this API
opt-in by using a platform integration capability hint. This is not optimal, but
found necessary after investigating several other solutions.

Which states (other that active/inactive) it makes sense
to add to Qt::ApplicationState will be a topic for later patches.

Change-Id: Ic6fdd3b66867abb67da43eba04ec86f06d82ff94
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
Richard Moe Gustavsen 2013-01-28 15:16:02 +01:00 committed by The Qt Project
parent 03b2512598
commit 22077e1609
9 changed files with 81 additions and 4 deletions

View File

@ -325,6 +325,13 @@ public:
Q_DECLARE_FLAGS(WindowStates, WindowState)
enum ApplicationState {
ApplicationInactive = 0x00000000,
ApplicationActive = 0x00000001
};
Q_DECLARE_FLAGS(ApplicationStates, ApplicationState)
enum ScreenOrientation {
PrimaryOrientation = 0x00000000,
PortraitOrientation = 0x00000001,

View File

@ -1782,6 +1782,21 @@
*/
/*!
\enum Qt::ApplicationState
\keyword application state
This enum type is used to specify the current state of the application.
The states are
\value ApplicationInactive The application is running in the background.
\value ApplicationActive The application is running in the foreground.
\since 5.1
*/
/*!
\enum Qt::ScreenOrientation

View File

@ -111,6 +111,8 @@ bool QGuiApplicationPrivate::tabletState = false;
QWindow *QGuiApplicationPrivate::tabletPressTarget = 0;
QWindow *QGuiApplicationPrivate::currentMouseWindow = 0;
Qt::ApplicationState QGuiApplicationPrivate::applicationState = Qt::ApplicationInactive;
QPlatformIntegration *QGuiApplicationPrivate::platform_integration = 0;
QPlatformTheme *QGuiApplicationPrivate::platform_theme = 0;
@ -1274,6 +1276,9 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
case QWindowSystemInterfacePrivate::WindowStateChanged:
QGuiApplicationPrivate::processWindowStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::WindowStateChangedEvent *>(e));
break;
case QWindowSystemInterfacePrivate::ApplicationStateChanged:
QGuiApplicationPrivate::processApplicationStateChangedEvent(static_cast<QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *>(e));
break;
case QWindowSystemInterfacePrivate::Close:
QGuiApplicationPrivate::processCloseEvent(
static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e));
@ -1567,7 +1572,7 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate
QCoreApplication::sendSpontaneousEvent(previous, &focusOut);
QObject::disconnect(previous, SIGNAL(focusObjectChanged(QObject*)),
qApp, SLOT(_q_updateFocusObject(QObject*)));
} else {
} else if (!platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState)) {
QEvent appActivate(QEvent::ApplicationActivate);
qApp->sendSpontaneousEvent(qApp, &appActivate);
}
@ -1577,7 +1582,7 @@ void QGuiApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate
QCoreApplication::sendSpontaneousEvent(QGuiApplicationPrivate::focus_window, &focusIn);
QObject::connect(QGuiApplicationPrivate::focus_window, SIGNAL(focusObjectChanged(QObject*)),
qApp, SLOT(_q_updateFocusObject(QObject*)));
} else {
} else if (!platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState)) {
QEvent appActivate(QEvent::ApplicationDeactivate);
qApp->sendSpontaneousEvent(qApp, &appActivate);
}
@ -1601,6 +1606,24 @@ void QGuiApplicationPrivate::processWindowStateChangedEvent(QWindowSystemInterfa
}
}
void QGuiApplicationPrivate::processApplicationStateChangedEvent(QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *e)
{
if (e->newState == applicationState)
return;
applicationState = e->newState;
switch (e->newState) {
case Qt::ApplicationActive: {
QEvent appActivate(QEvent::ApplicationActivate);
qApp->sendSpontaneousEvent(qApp, &appActivate);
break; }
case Qt::ApplicationInactive: {
QEvent appDeactivate(QEvent::ApplicationDeactivate);
qApp->sendSpontaneousEvent(qApp, &appDeactivate);
break; }
}
}
void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::ThemeChangeEvent *tce)
{
if (self)

View File

@ -127,6 +127,8 @@ public:
static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e);
static void processWindowStateChangedEvent(QWindowSystemInterfacePrivate::WindowStateChangedEvent *e);
static void processApplicationStateChangedEvent(QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *e);
static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e);
static void updateFilteredScreenOrientation(QScreen *screen);
@ -199,6 +201,7 @@ public:
static bool tabletState;
static QWindow *tabletPressTarget;
static QWindow *currentMouseWindow;
static Qt::ApplicationState applicationState;
#ifndef QT_NO_CLIPBOARD
static QClipboard *qt_clipboard;

View File

@ -199,6 +199,13 @@ QPlatformServices *QPlatformIntegration::services() const
\value MultipleWindows The platform supports multiple QWindows, i.e. does some kind
of compositing either client or server side. Some platforms might only support a
single fullscreen window.
\value ApplicationState The platform handles the application state explicitly.
This means that QEvent::ApplicationActivate and QEvent::ApplicationDeativate
will not be posted automatically. Instead, the platform must handle application
state explicitly by using QWindowSystemInterface::handleApplicationStateChanged().
If not set, application state will follow window activation, which is the normal
behavior for desktop platforms.
*/

View File

@ -88,7 +88,8 @@ public:
SharedGraphicsCache,
BufferQueueingOpenGL,
WindowMasks,
MultipleWindows
MultipleWindows,
ApplicationState
};
virtual ~QPlatformIntegration() { }

View File

@ -46,6 +46,7 @@
#include "private/qtouchdevice_p.h"
#include <QAbstractEventDispatcher>
#include <qpa/qplatformdrag.h>
#include <qpa/qplatformintegration.h>
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@ -122,6 +123,14 @@ void QWindowSystemInterface::handleWindowStateChanged(QWindow *tlw, Qt::WindowSt
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
void QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState newState)
{
Q_ASSERT(QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState));
QWindowSystemInterfacePrivate::ApplicationStateChangedEvent *e =
new QWindowSystemInterfacePrivate::ApplicationStateChangedEvent(newState);
QWindowSystemInterfacePrivate::handleWindowSystemEvent(e);
}
void QWindowSystemInterface::handleGeometryChange(QWindow *tlw, const QRect &newRect)
{
QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(tlw,newRect);

View File

@ -138,6 +138,8 @@ public:
static void handleWindowActivated(QWindow *w);
static void handleWindowStateChanged(QWindow *w, Qt::WindowState newState);
static void handleApplicationStateChanged(Qt::ApplicationState newState);
static void handleExposeEvent(QWindow *tlw, const QRegion &region);
#ifndef QT_NO_DRAGANDDROP

View File

@ -88,7 +88,8 @@ public:
TabletEnterProximity = UserInputEvent | 0x15,
TabletLeaveProximity = UserInputEvent | 0x16,
PlatformPanel = UserInputEvent | 0x17,
ContextMenu = UserInputEvent | 0x18
ContextMenu = UserInputEvent | 0x18,
ApplicationStateChanged = 0x19
};
class WindowSystemEvent {
@ -152,6 +153,15 @@ public:
Qt::WindowState newState;
};
class ApplicationStateChangedEvent : public WindowSystemEvent {
public:
ApplicationStateChangedEvent(Qt::ApplicationState newState)
: WindowSystemEvent(ApplicationStateChanged), newState(newState)
{ }
Qt::ApplicationState newState;
};
class UserEvent : public WindowSystemEvent {
public:
UserEvent(QWindow * w, ulong time, EventType t)