QWindow already has windowModality() and setWindowModality() as part of
its API from commit
516f4e283ba4626d7239630397ef867ab0366071. Platform
plugins can use this already to setup modality hints on windows that
they create, but it's not enough to implement modality fully.
QGuiApplication gets a modalWindow() static method, which is similar to
QApplication::activeModalWidget() in that it returns the last modal
window to be shown.
The modal window "stack" moves from QApplicationPrivate to
QGuiApplicationPrivate. The enterModal*() and leaveModal*() functions in
QApplicationPrivate are removed and replaced by
QGuiApplicationPrivate::showModalWindow() and hideModalWindow(), which
are called by QWindow::setVisible() just before calling
QPlatformWindow::setVisible().
The virtual QGuiApplicationPrivate::isWindowBlocked() will tell us if a
window is blocked by a modal window (and tell which modal window for any
interested callers). The default implementation works on the QWindow
level. QApplicationPrivate reimplements isWindowBlocked() and adds popup
and WA_GroupLeader support.
QGuiApplication uses the state set from isWindowBlocked() to block
user-input events: mouse press, mouse move, mouse release, wheel, key
presses, key releases, enter/leave events, close events, and touch
begin, update, and end events.
Note also that the modality helper functions in QtWidgets and
QApplicationPrivate are left in place and working as they always have.
The behavior of QWidget in the presence of modal windows/dialogs should
not change.
Change-Id: I2c89e6026d40160387787a6e009ae1fdc12dfd69
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
static unsigned applicationResourceFlags = 0;
QString *QGuiApplicationPrivate::platform_name = 0;
-bool QGuiApplicationPrivate::app_do_modal = false;
QPalette *QGuiApplicationPrivate::app_pal = 0; // default application palette
}
/*!
+ Returns the most recently shown modal window. If no modal windows are
+ visible, this function returns zero.
+
+ A modal window is a window which has its
+ \l{QWindow::windowModality}{windowModality} property set to Qt::WindowModal
+ or Qt::ApplicationModal. A modal window must be closed before the user can
+ continue with other parts of the program.
+
+ Modal window are organized in a stack. This function returns the modal
+ window at the top of the stack.
+
+ \sa Qt::WindowModality, QWindow::setWindowModality()
+*/
+QWindow *QGuiApplication::modalWindow()
+{
+ if (QGuiApplicationPrivate::self->modalWindowList.isEmpty())
+ return 0;
+ return QGuiApplicationPrivate::self->modalWindowList.first();
+}
+
+void QGuiApplicationPrivate::showModalWindow(QWindow *window)
+{
+ self->modalWindowList.prepend(window);
+
+ QEvent e(QEvent::WindowBlocked);
+ QWindowList windows = QGuiApplication::topLevelWindows();
+ for (int i = 0; i < windows.count(); ++i) {
+ QWindow *window = windows.at(i);
+ if (!window->d_func()->blockedByModalWindow && window->windowType() != Qt::Tool && self->isWindowBlocked(window)) {
+ window->d_func()->blockedByModalWindow = true;
+ QGuiApplication::sendEvent(window, &e);
+ }
+ }
+}
+
+void QGuiApplicationPrivate::hideModalWindow(QWindow *window)
+{
+ self->modalWindowList.removeAll(window);
+
+ QEvent e(QEvent::WindowUnblocked);
+ QWindowList windows = QGuiApplication::topLevelWindows();
+ for (int i = 0; i < windows.count(); ++i) {
+ QWindow *window = windows.at(i);
+ if (window->d_func()->blockedByModalWindow && window->windowType() != Qt::Tool && !self->isWindowBlocked(window)) {
+ window->d_func()->blockedByModalWindow = false;
+ QGuiApplication::sendEvent(window, &e);
+ }
+ }
+}
+
+/*
+ Returns true if \a window is blocked by a modal window. If \a
+ blockingWindow is non-zero, *blockingWindow will be set to the blocking
+ window (or to zero if \a window is not blocked).
+*/
+bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blockingWindow) const
+{
+ QWindow *unused = 0;
+ if (!blockingWindow)
+ blockingWindow = &unused;
+
+ if (modalWindowList.isEmpty()) {
+ *blockingWindow = 0;
+ return false;
+ }
+
+ for (int i = 0; i < modalWindowList.count(); ++i) {
+ QWindow *modalWindow = modalWindowList.at(i);
+
+ {
+ // check if the modal window is our window or a (transient) parent of our window
+ QWindow *w = window;
+ while (w) {
+ if (w == modalWindow) {
+ *blockingWindow = 0;
+ return false;
+ }
+ QWindow *p = w->parent();
+ if (!p)
+ p = w->transientParent();
+ w = p;
+ }
+ }
+
+ Qt::WindowModality windowModality = modalWindow->windowModality();
+ switch (windowModality) {
+ case Qt::ApplicationModal:
+ {
+ if (modalWindow != window) {
+ *blockingWindow = modalWindow;
+ return true;
+ }
+ break;
+ }
+ case Qt::WindowModal:
+ {
+ QWindow *w = window;
+ do {
+ QWindow *m = modalWindow;
+ do {
+ if (m == w) {
+ *blockingWindow = m;
+ return true;
+ }
+ QWindow *p = m->parent();
+ if (!p)
+ p = m->transientParent();
+ m = p;
+ } while (m);
+ QWindow *p = w->parent();
+ if (!p)
+ p = w->transientParent();
+ w = p;
+ } while (w);
+ break;
+ }
+ default:
+ Q_ASSERT_X(false, "QGuiApplication", "internal error, a modal widget cannot be modeless");
+ break;
+ }
+ }
+ *blockingWindow = 0;
+ return false;
+}
+
+/*!
Returns the QWindow that receives events tied to focus,
such as key events.
*/
}
if (window) {
+ if (window->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow mouse events through
+ return;
+ }
+
QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers);
ev.setTimestamp(e->timestamp);
#ifndef QT_NO_CURSOR
QWindow *window = e->window.data();
if (window) {
+ if (window->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow wheel events through
+ return;
+ }
+
QWheelEvent ev(e->localPos, e->globalPos, e->pixelDelta, e->angleDelta, e->qt4Delta, e->qt4Orientation, buttons, e->modifiers);
ev.setTimestamp(e->timestamp);
QGuiApplication::sendSpontaneousEvent(window, &ev);
window = QGuiApplication::activeWindow();
if (!window)
return;
+ if (window->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow key events through
+ return;
+ }
QKeyEventEx ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount,
e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers);
{
if (!e->enter)
return;
+ if (e->enter.data()->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow enter events through
+ return;
+ }
QEvent event(QEvent::Enter);
QCoreApplication::sendSpontaneousEvent(e->enter.data(), &event);
{
if (!e->leave)
return;
+ if (e->leave.data()->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow leave events through
+ return;
+ }
QEvent event(QEvent::Leave);
QCoreApplication::sendSpontaneousEvent(e->leave.data(), &event);
{
if (e->window.isNull())
return;
+ if (e->window.data()->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow close events through
+ return;
+ }
QCloseEvent event;
QGuiApplication::sendSpontaneousEvent(e->window.data(), &event);
break;
}
+ if (w->d_func()->blockedByModalWindow) {
+ // a modal window is blocking this window, don't allow touch events through
+ continue;
+ }
+
QTouchEvent touchEvent(eventType,
e->device,
e->modifiers,
static QString platformName();
+ static QWindow *modalWindow();
+
#ifdef QT_DEPRECATED
static QT_DEPRECATED QWindow *activeWindow() { return focusWindow(); }
#endif
static QGuiApplicationPrivate *instance() { return self; }
static QString *platform_name;
- static bool app_do_modal;
+
+ QWindowList modalWindowList;
+ static void showModalWindow(QWindow *window);
+ static void hideModalWindow(QWindow *window);
+ virtual bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = 0) const;
static Qt::MouseButtons buttons;
static ulong mousePressTime;
QGuiApplication::sendEvent(this, &showEvent);
}
+ if (isModal()) {
+ if (visible)
+ QGuiApplicationPrivate::showModalWindow(this);
+ else
+ QGuiApplicationPrivate::hideModalWindow(this);
+ }
+
d->platformWindow->setVisible(visible);
if (!visible) {
, windowOrientation(Qt::PrimaryOrientation)
, maximumSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)
, modality(Qt::NonModal)
+ , blockedByModalWindow(false)
, transientParent(0)
, screen(0)
{
QSize sizeIncrement;
Qt::WindowModality modality;
+ bool blockedByModalWindow;
+
QPointer<QWindow> transientParent;
QScreen *screen;
};
QWidgetList *QApplicationPrivate::popupWidgets = 0; // has keyboard input focus
QDesktopWidget *qt_desktopWidget = 0; // root window widgets
-QWidgetList * qt_modal_stack = 0; // stack of modal widgets
-bool app_do_modal = false;
/*!
\internal
QWidget *QApplication::activeModalWidget()
{
- return qt_modal_stack && !qt_modal_stack->isEmpty() ? qt_modal_stack->first() : 0;
+ QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(modalWindow());
+ return widgetWindow ? widgetWindow->widget() : 0;
}
/*!
bool QApplicationPrivate::isBlockedByModal(QWidget *widget)
{
widget = widget->window();
- if (!modalState())
+ return self->isWindowBlocked(widget->windowHandle());
+}
+
+bool QApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blockingWindow) const
+{
+ QWindow *unused = 0;
+ if (!blockingWindow)
+ blockingWindow = &unused;
+
+ if (modalWindowList.isEmpty()) {
+ *blockingWindow = 0;
return false;
- if (QApplication::activePopupWidget() == widget)
+ }
+ QWidget *popupWidget = QApplication::activePopupWidget();
+ QWindow *popupWindow = popupWidget ? popupWidget->windowHandle() : 0;
+ if (popupWindow == window) {
+ *blockingWindow = 0;
return false;
+ }
- for (int i = 0; i < qt_modal_stack->size(); ++i) {
- QWidget *modalWidget = qt_modal_stack->at(i);
+ for (int i = 0; i < modalWindowList.count(); ++i) {
+ QWindow *modalWindow = modalWindowList.at(i);
{
- // check if the active modal widget is our widget or a parent of our widget
- QWidget *w = widget;
+ // check if the modal window is our window or a (transient) parent of our window
+ QWindow *w = window;
while (w) {
- if (w == modalWidget)
+ if (w == modalWindow) {
+ *blockingWindow = 0;
return false;
- w = w->parentWidget();
+ }
+ QWindow *p = w->parent();
+ if (!p)
+ p = w->transientParent();
+ w = p;
}
}
- Qt::WindowModality windowModality = modalWidget->windowModality();
+ Qt::WindowModality windowModality = modalWindow->windowModality();
+ QWidgetWindow *modalWidgetWindow = qobject_cast<QWidgetWindow *>(modalWindow);
if (windowModality == Qt::NonModal) {
// determine the modality type if it hasn't been set on the
- // modalWidget, this normally happens when waiting for a
- // native dialog. use WindowModal if we are the child of a
- // group leader; otherwise use ApplicationModal.
- QWidget *m = modalWidget;
+ // modalWindow's widget, this normally happens when waiting for a
+ // native dialog. use WindowModal if we are the child of a group
+ // leader; otherwise use ApplicationModal.
+ QWidget *m = modalWidgetWindow ? modalWidgetWindow->widget() : 0;
while (m && !m->testAttribute(Qt::WA_GroupLeader)) {
m = m->parentWidget();
if (m)
switch (windowModality) {
case Qt::ApplicationModal:
- {
- QWidget *groupLeaderForWidget = widget;
- while (groupLeaderForWidget && !groupLeaderForWidget->testAttribute(Qt::WA_GroupLeader))
- groupLeaderForWidget = groupLeaderForWidget->parentWidget();
-
- if (groupLeaderForWidget) {
- // if \a widget has WA_GroupLeader, it can only be blocked by ApplicationModal children
- QWidget *m = modalWidget;
- while (m && m != groupLeaderForWidget && !m->testAttribute(Qt::WA_GroupLeader))
- m = m->parentWidget();
- if (m == groupLeaderForWidget)
- return true;
- } else if (modalWidget != widget) {
+ {
+ QWidgetWindow *widgetWindow = qobject_cast<QWidgetWindow *>(window);
+ QWidget *groupLeaderForWidget = widgetWindow ? widgetWindow->widget() : 0;
+ while (groupLeaderForWidget && !groupLeaderForWidget->testAttribute(Qt::WA_GroupLeader))
+ groupLeaderForWidget = groupLeaderForWidget->parentWidget();
+
+ if (groupLeaderForWidget) {
+ // if \a widget has WA_GroupLeader, it can only be blocked by ApplicationModal children
+ QWidget *m = modalWidgetWindow ? modalWidgetWindow->widget() : 0;
+ while (m && m != groupLeaderForWidget && !m->testAttribute(Qt::WA_GroupLeader))
+ m = m->parentWidget();
+ if (m == groupLeaderForWidget) {
+ *blockingWindow = m->windowHandle();
return true;
}
- break;
+ } else if (modalWindow != window) {
+ *blockingWindow = modalWindow;
+ return true;
}
+ break;
+ }
case Qt::WindowModal:
- {
- QWidget *w = widget;
+ {
+ QWindow *w = window;
+ do {
+ QWindow *m = modalWindow;
do {
- QWidget *m = modalWidget;
- do {
- if (m == w)
- return true;
- m = m->parentWidget();
- if (m)
- m = m->window();
- } while (m);
- w = w->parentWidget();
- if (w)
- w = w->window();
- } while (w);
- break;
- }
+ if (m == w) {
+ *blockingWindow = m;
+ return true;
+ }
+ QWindow *p = m->parent();
+ if (!p)
+ p = m->transientParent();
+ m = p;
+ } while (m);
+ QWindow *p = w->parent();
+ if (!p)
+ p = w->transientParent();
+ w = p;
+ } while (w);
+ break;
+ }
default:
- Q_ASSERT_X(false, "QApplication", "internal error, a modal widget cannot be modeless");
+ Q_ASSERT_X(false, "QApplication", "internal error, a modal window cannot be modeless");
break;
}
}
+ *blockingWindow = 0;
return false;
}
/*!\internal
- */
-void QApplicationPrivate::enterModal(QWidget *widget)
-{
- QSet<QWidget*> blocked;
- QList<QWidget*> windows = QApplication::topLevelWidgets();
- for (int i = 0; i < windows.count(); ++i) {
- QWidget *window = windows.at(i);
- if (window->windowType() != Qt::Tool && isBlockedByModal(window))
- blocked.insert(window);
- }
-
- enterModal_sys(widget);
-
- windows = QApplication::topLevelWidgets();
- QEvent e(QEvent::WindowBlocked);
- for (int i = 0; i < windows.count(); ++i) {
- QWidget *window = windows.at(i);
- if (!blocked.contains(window) && window->windowType() != Qt::Tool && isBlockedByModal(window))
- QApplication::sendEvent(window, &e);
- }
-}
-
-/*!\internal
- */
-void QApplicationPrivate::leaveModal(QWidget *widget)
-{
- QSet<QWidget*> blocked;
- QList<QWidget*> windows = QApplication::topLevelWidgets();
- for (int i = 0; i < windows.count(); ++i) {
- QWidget *window = windows.at(i);
- if (window->windowType() != Qt::Tool && isBlockedByModal(window))
- blocked.insert(window);
- }
-
- leaveModal_sys(widget);
-
- windows = QApplication::topLevelWidgets();
- QEvent e(QEvent::WindowUnblocked);
- for (int i = 0; i < windows.count(); ++i) {
- QWidget *window = windows.at(i);
- if(blocked.contains(window) && window->windowType() != Qt::Tool && !isBlockedByModal(window))
- QApplication::sendEvent(window, &e);
- }
-}
-
-
-
-/*!\internal
Called from qapplication_\e{platform}.cpp, returns true
if the widget should accept the event.
static void dispatchEnterLeave(QWidget *enter, QWidget *leave);
//modality
- static void enterModal(QWidget*);
- static void leaveModal(QWidget*);
- static void enterModal_sys(QWidget*);
- static void leaveModal_sys(QWidget*);
+ Q_DECL_OVERRIDE bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = 0) const;
static bool isBlockedByModal(QWidget *widget);
static bool modalState();
static bool tryModalHelper(QWidget *widget, QWidget **rettop = 0);
static bool widgetCount; // Coupled with -widgetcount switch
static bool load_testability; // Coupled with -testability switch
-#ifdef Q_WS_MAC
- static bool native_modal_dialog_active;
-#endif
-
static void setSystemPalette(const QPalette &pal);
static void setPalette_helper(const QPalette &palette, const char* className, bool clearWidgetPaletteHash);
static void initializeWidgetPaletteHash();
static QString appName;
static QString appFont;
static bool popupGrabOk;
-extern bool app_do_modal;
-extern QWidgetList *qt_modal_stack;
extern QWidget *qt_button_down;
extern QWidget *qt_popup_down;
extern bool qt_replay_popup_mouse_event;
return !block_event;
}
-void QApplicationPrivate::enterModal_sys(QWidget *widget)
-{
- if (!qt_modal_stack)
- qt_modal_stack = new QWidgetList;
- qt_modal_stack->insert(0, widget);
- app_do_modal = true;
-}
-
-void QApplicationPrivate::leaveModal_sys(QWidget *widget)
-{
- if (qt_modal_stack && qt_modal_stack->removeAll(widget)) {
- if (qt_modal_stack->isEmpty()) {
- delete qt_modal_stack;
- qt_modal_stack = 0;
- }
- }
- app_do_modal = qt_modal_stack != 0;
-}
-
bool QApplicationPrivate::modalState()
{
- return app_do_modal;
+ return !self->modalWindowList.isEmpty();
}
QWidget *qt_tlw_for_window(QWindow *wnd)
QShowEvent showEvent;
QApplication::sendEvent(q, &showEvent);
- if (!isEmbedded && q->isModal() && q->isWindow())
- // QApplicationPrivate::enterModal *before* show, otherwise the initial
- // stacking might be wrong
- QApplicationPrivate::enterModal(q);
-
-
show_sys();
if (!isEmbedded && q->windowType() == Qt::Popup)
if (!isEmbedded && (q->windowType() == Qt::Popup))
qApp->d_func()->closePopup(q);
- // Move test modal here. Otherwise, a modal dialog could get
- // destroyed and we lose all access to its parent because we haven't
- // left modality. (Eg. modal Progress Dialog)
- if (!isEmbedded && q->isModal() && q->isWindow())
- QApplicationPrivate::leaveModal(q);
-
#if defined(Q_WS_WIN)
if (q->isWindow() && !(q->windowType() == Qt::Popup) && q->parentWidget()
&& !q->parentWidget()->isHidden() && q->isActiveWindow())
break;
case Qt::WA_ShowModal:
if (!on) {
- if (isVisible())
- QApplicationPrivate::leaveModal(this);
- // reset modality type to Modeless when clearing WA_ShowModal
+ // reset modality type to NonModal when clearing WA_ShowModal
data->window_modality = Qt::NonModal;
} else if (data->window_modality == Qt::NonModal) {
// determine the modality type if it hasn't been set prior
data->window_modality = (w && w->testAttribute(Qt::WA_GroupLeader))
? Qt::WindowModal
: Qt::ApplicationModal;
- // Some window managers does not allow us to enter modal after the
- // window is showing. Therefore, to be consistent, we cannot call
- // QApplicationPrivate::enterModal(this) here. The window must be
- // hidden before changing modality.
+ // Some window managers do not allow us to enter modality after the
+ // window is visible.The window must be hidden before changing the
+ // windowModality property and then reshown.
}
if (testAttribute(Qt::WA_WState_Created)) {
// don't call setModal_sys() before create_sys()