diff options
author | Bradley T. Hughes <bradley.hughes@nokia.com> | 2009-10-01 07:21:02 (GMT) |
---|---|---|
committer | Bradley T. Hughes <bradley.hughes@nokia.com> | 2009-10-01 08:14:35 (GMT) |
commit | 790583dc16a89ed77954c096aae52bf9f91aec09 (patch) | |
tree | 4a410f902f134b66856ee8e803d87b4a3cde1e9e /src/gui/graphicsview/qgraphicsscene.cpp | |
parent | 91b58202c089ea8f4e150717cee8dbc40b8f903c (diff) | |
download | Qt-790583dc16a89ed77954c096aae52bf9f91aec09.zip Qt-790583dc16a89ed77954c096aae52bf9f91aec09.tar.gz Qt-790583dc16a89ed77954c096aae52bf9f91aec09.tar.bz2 |
Add support for modality to QGraphicsItem panels.
This add QGraphicsItem::setPanelModality(), panelModality(),
isBlockedByModalPanel() and the QGraphicsItem::PanelModality enum
(enumerators are either SceneModal, PanelModal, or NonModal, which
mirror Qt::ApplicationModal, Qt::WindowModal, and Qt::NonModal).
Reviewed-by: ahanssen
Squashed commit of the following:
commit a980a1b9c2972c676f3a70e8577d4eace54a25b3
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 30 14:42:01 2009 +0200
Fix tst_QGraphicsItem::modality_hover() test failures
As pointed out by Andreas, QGraphicsScenePrivate::dispatchHoverEvent()
already has all the logic needed to correctly dispatch the hover
events when changing modality. All we need to do is store the last
known mouse position, and call dispatchHoverEvent() when entering,
changing, and leaving the modal state.
commit ba41633c96ece8da3a8bbf9c7491c15b14f83f76
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 30 14:40:59 2009 +0200
Fix tst_QGraphicsItem::mixedModality() failure
When changing modality from SceneModal to PanelModal, we may need to
send WindowUnblocked events in addition to the WindowBlocked events.
commit d1076e315de10b1b2fb7617ebaee552c14e49c8b
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 30 14:33:21 2009 +0200
Update the expected events counts in tst_QGraphicsItem::modality_hover()
HoverEnter and HoverMove always come in pairs, and should do so when
entering or leaving modality as well. This means though that changing
modality can cause spurious HoverMove events (as seen in this test).
commit a29b098e4c391651ef61dd4714a66b22654e4628
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 30 14:20:25 2009 +0200
Update tst_QGraphicsItem::mixedModality() to do more detailed checking
The test looks for unwanted WindowBlocked/WindowUnblocked events now,
making it easier to spot where the failure comes from.
commit c1ae96126ed01d0e662bddf38ff161e50a804b1c
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Tue Sep 29 12:17:17 2009 +0200
Documentation for QGraphicsItem::PanelModality
Document the behavior of the QGraphicsItem::NonModal, PanelModal, and
SceneModal enumerators. Corrected a qdoc error in the
isBlockedByModalPanel() documentation
commit 02fec999e660180ff65bbbf79c8085e582879ed1
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Thu Sep 17 10:31:44 2009 +0200
Add bool QGraphicsItem::isBlockedByModalPanel()
This function can be used to figure out 1) if an item is modally
shadowed and 2) which panel is blocking the item in question.
This will make it possible to implement window-manager like behavior
of activating/highlighting/animating modal panels when the user tries
to interact with a blocked item (this will most likely need to be done
by reimplementing QGraphicsScene::event()).
commit eab9a975dcd71b68135325d479374108bd7f3b2a
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 16 14:38:55 2009 +0200
Block key events to the focus item if it is blocked by a modal panel.
We don't want the event to propagate either, just stop propagating
once we reach an item that is blocked.
commit 038b61a10bb837b353f988cb0d1665dd53656cdb
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Tue Sep 15 12:40:27 2009 +0200
Add a test for click-to-focus behavior in the presence of modality
Clicking on a widget should neither give it focus nor set it as the
sub-focus item.
commit 3bb3662556efe8d76af5a56e65b1df7a9f4b476a
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Tue Sep 8 09:08:42 2009 +0200
Newly blocked QGraphicsItem panels should lose implicit grabs when
modality is enabled.
If an item has an implicit grab when blocked by a modal panel, this grab
is lost and no mouse events are sent until the mouse is released and re-
commit 3be51be3da36e782a5a1f282c552064d5d490a71
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Thu Sep 3 13:39:14 2009 +0200
Support changing modality from PanelModal to SceneModal or vice-versa
Don't leave modality first, and then re-enter... this sends
unnecessary events.
commit bb0aea559ba01a8bbb03c0370a247ab902f561f5
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 2 16:58:23 2009 +0200
Fix hover event delivery in the presence of modal panels
Panels that are modally shadowed should not get hover events. When
entering and leaving the modal state, GraphicsSceneHoverEnter and
GraphicsSceneHoverLeave event should be send to the widgets that
become or are no longer blocked.
Auto-test included.
commit cad00b1d9da19565e2d7ea2d30d37eb45005b5ae
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 2 10:45:20 2009 +0200
Fix tst_QGraphicsItem::modality_hover() test
Don't send hover events to items that are modally shadowed.
commit ae15df331901110e19eb2037f37ff7f84cd7cd16
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Wed Sep 2 10:44:14 2009 +0200
Enable the ItemIsPanel flag in the modality_hover() test
Otherwise the modality settings are ignored.
commit 1580f43c8feabc3a2bf9c1450e1a8916e8940a4c
Author: Andreas Aardal Hanssen <andreas.aardal.hanssen@nokia.com>
Date: Fri Aug 28 15:20:37 2009 +0200
More work on QGraphicsItem::PanelModality. Added many tests.
commit ed2064ad2ec8bc06d62cf1e931b973d5d92c0563
Author: Bradley T. Hughes <bradley.hughes@nokia.com>
Date: Thu Aug 27 13:45:11 2009 +0200
Add support for modality to QGraphicsItem panels.
This add QGraphicsItem::setPanelModality() and the
QGraphicsItem::PanelModality enum (enumerators are either SceneModal,
PanelModal, or NonModal, which mirror Qt::ApplicationModal,
Qt::WindowModal, and Qt::NonModal).
Reviewed-by: ahanssen
Diffstat (limited to 'src/gui/graphicsview/qgraphicsscene.cpp')
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 164 |
1 files changed, 156 insertions, 8 deletions
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 4b74b67..0acef0b 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -565,6 +565,9 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) q->removeItem(item->d_ptr->children.at(i)); } + if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal) + leaveModal(item); + // Reset the mouse grabber and focus item data. if (mouseGrabberItems.contains(item)) ungrabMouse(item, /* item is dying */ item->d_ptr->inDestructor); @@ -1111,6 +1114,9 @@ void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent) } QGraphicsItem *item = mouseGrabberItems.last(); + if (item->isBlockedByModalPanel()) + return; + for (int i = 0x1; i <= 0x10; i <<= 1) { Qt::MouseButton button = Qt::MouseButton(i); mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget()))); @@ -1134,6 +1140,8 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // Deliver to any existing mouse grabber. if (!mouseGrabberItems.isEmpty()) { + if (mouseGrabberItems.last()->isBlockedByModalPanel()) + return; // The event is ignored by default, but we disregard the event's // accepted state after delivery; the mouse is grabbed, after all. sendMouseEvent(mouseEvent); @@ -1151,12 +1159,22 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // Update window activation. QGraphicsItem *topItem = cachedItemsUnderMouse.value(0); QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0; + if (newActiveWindow && newActiveWindow->isBlockedByModalPanel(&topItem)) { + // pass activation to the blocking modal window + newActiveWindow = topItem ? topItem->window() : 0; + } + if (newActiveWindow != q->activeWindow()) q->setActiveWindow(newActiveWindow); // Set focus on the topmost enabled item that can take focus. bool setFocus = false; foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isBlockedByModalPanel()) { + // Make sure we don't clear focus. + setFocus = true; + break; + } if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { setFocus = true; @@ -1165,12 +1183,27 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou break; } } + if (item->isPanel()) + break; + } + + // Check for scene modality. + bool sceneModality = false; + for (int i = 0; i < modalPanels.size(); ++i) { + if (modalPanels.at(i)->panelModality() == QGraphicsItem::SceneModal) { + sceneModality = true; + break; + } } // If nobody could take focus, clear it. - if (!stickyFocus && !setFocus) + if (!stickyFocus && !setFocus && !sceneModality) q->setFocusItem(0, Qt::MouseFocusReason); + // Any item will do. + if (sceneModality && cachedItemsUnderMouse.isEmpty()) + cachedItemsUnderMouse << modalPanels.first(); + // Find a mouse grabber by sending mouse press events to all mouse grabber // candidates one at a time, until the event is accepted. It's accepted by // default, so the receiver has to explicitly ignore it for it to pass @@ -1181,6 +1214,10 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou continue; } + // Check if this item is blocked by a modal panel and deliver the mouse event to the + // blocking panel instead of this item if blocked. + (void) item->isBlockedByModalPanel(&item); + grabMouse(item, /* implicit = */ true); mouseEvent->accept(); @@ -2403,6 +2440,8 @@ void QGraphicsScene::addItem(QGraphicsItem *item) d->selectedItems << item; if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup) d->addPopup(static_cast<QGraphicsWidget *>(item)); + if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal) + d->enterModal(item); // Update creation order focus chain. Make sure to leave the widget's // internal tab order intact. @@ -3205,8 +3244,12 @@ bool QGraphicsScene::event(QEvent *event) } return false; case QEvent::GraphicsSceneMouseMove: - mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + { + QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event); + d->lastSceneMousePos = mouseEvent->scenePos(); + mouseMoveEvent(mouseEvent); break; + } case QEvent::GraphicsSceneMousePress: mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); break; @@ -3228,8 +3271,12 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::GraphicsSceneHoverEnter: case QEvent::GraphicsSceneHoverLeave: case QEvent::GraphicsSceneHoverMove: - d->dispatchHoverEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + { + QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event); + d->lastSceneMousePos = hoverEvent->scenePos(); + d->dispatchHoverEvent(hoverEvent); break; + } case QEvent::Leave: d->leaveScene(); break; @@ -3599,8 +3646,10 @@ void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const { - return item->acceptHoverEvents() - || (item->isWidget() && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()); + return (!item->isBlockedByModalPanel() && + (item->acceptHoverEvents() + || (item->isWidget() + && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()))); } /*! @@ -3674,7 +3723,9 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv } // Generate a move event for the item itself - if (item && !hoverItems.isEmpty() && item == hoverItems.last()) { + if (item + && !hoverItems.isEmpty() + && item == hoverItems.last()) { sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent); return true; } @@ -3708,8 +3759,7 @@ void QGraphicsScenePrivate::leaveScene() while (!hoverItems.isEmpty()) { QGraphicsItem *lastItem = hoverItems.takeLast(); - if (lastItem->acceptHoverEvents() - || (lastItem->isWidget() && static_cast<QGraphicsWidget*>(lastItem)->d_func()->hasDecoration())) + if (itemAcceptsHoverEvents_helper(lastItem)) sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent); } } @@ -3736,6 +3786,8 @@ void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) keyEvent->accept(); // Send it; QGraphicsItem::keyPressEvent ignores it. If the event // is filtered out, stop propagating it. + if (p->isBlockedByModalPanel()) + break; if (!d->sendEvent(p, keyEvent)) break; } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); @@ -3766,6 +3818,8 @@ void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) keyEvent->accept(); // Send it; QGraphicsItem::keyPressEvent ignores it. If the event // is filtered out, stop propagating it. + if (p->isBlockedByModalPanel()) + break; if (!d->sendEvent(p, keyEvent)) break; } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); @@ -5420,6 +5474,8 @@ void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent) for (; it != end; ++it) { QGraphicsItem *item = it.key(); + (void) item->isBlockedByModalPanel(&item); + // determine event type from the state mask QEvent::Type eventType; switch (it.value().first) { @@ -5493,6 +5549,8 @@ bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEve break; } } + if (item->isPanel()) + break; } // If nobody could take focus, clear it. @@ -5518,6 +5576,8 @@ bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEve } break; } + if (item->isPanel()) + break; } touchEvent->setAccepted(eventAccepted); @@ -5536,6 +5596,94 @@ void QGraphicsScenePrivate::updateInputMethodSensitivityInViews() views.at(i)->d_func()->updateInputMethodSensitivity(); } +void QGraphicsScenePrivate::enterModal(QGraphicsItem *panel, QGraphicsItem::PanelModality previousModality) +{ + Q_Q(QGraphicsScene); + Q_ASSERT(panel && panel->isPanel()); + + QGraphicsItem::PanelModality panelModality = panel->d_ptr->panelModality; + if (previousModality != QGraphicsItem::NonModal) { + // the panel is changing from one modality type to another... temporarily set it back so + // that blockedPanels is populated correctly + panel->d_ptr->panelModality = previousModality; + } + + QSet<QGraphicsItem *> blockedPanels; + QList<QGraphicsItem *> items = q->items(); // ### store panels separately + for (int i = 0; i < items.count(); ++i) { + QGraphicsItem *item = items.at(i); + if (item->isPanel() && item->isBlockedByModalPanel()) + blockedPanels.insert(item); + } + // blockedPanels contains all currently blocked panels + + if (previousModality != QGraphicsItem::NonModal) { + // reset the modality to the proper value, since we changed it above + panel->d_ptr->panelModality = panelModality; + // remove this panel so that it will be reinserted at the front of the stack + modalPanels.removeAll(panel); + } + + modalPanels.prepend(panel); + + if (!hoverItems.isEmpty()) { + // send GraphicsSceneHoverLeave events to newly blocked hoverItems + QGraphicsSceneHoverEvent hoverEvent; + hoverEvent.setScenePos(lastSceneMousePos); + dispatchHoverEvent(&hoverEvent); + } + + if (!mouseGrabberItems.isEmpty() && lastMouseGrabberItemHasImplicitMouseGrab) { + QGraphicsItem *item = mouseGrabberItems.last(); + if (item->isBlockedByModalPanel()) + ungrabMouse(item, /*itemIsDying =*/ false); + } + + QEvent windowBlockedEvent(QEvent::WindowBlocked); + QEvent windowUnblockedEvent(QEvent::WindowUnblocked); + for (int i = 0; i < items.count(); ++i) { + QGraphicsItem *item = items.at(i); + if (item->isPanel()) { + if (!blockedPanels.contains(item) && item->isBlockedByModalPanel()) { + // send QEvent::WindowBlocked to newly blocked panels + sendEvent(item, &windowBlockedEvent); + } else if (blockedPanels.contains(item) && !item->isBlockedByModalPanel()) { + // send QEvent::WindowUnblocked to unblocked panels when downgrading + // a panel from SceneModal to PanelModal + sendEvent(item, &windowUnblockedEvent); + } + } + } +} + +void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel) +{ + Q_Q(QGraphicsScene); + Q_ASSERT(panel && panel->isPanel()); + + QSet<QGraphicsItem *> blockedPanels; + QList<QGraphicsItem *> items = q->items(); // ### same as above + for (int i = 0; i < items.count(); ++i) { + QGraphicsItem *item = items.at(i); + if (item->isPanel() && item->isBlockedByModalPanel()) + blockedPanels.insert(item); + } + + modalPanels.removeAll(panel); + + QEvent e(QEvent::WindowUnblocked); + for (int i = 0; i < items.count(); ++i) { + QGraphicsItem *item = items.at(i); + if (item->isPanel() && blockedPanels.contains(item) && !item->isBlockedByModalPanel()) + sendEvent(item, &e); + } + + // send GraphicsSceneHoverEnter events to newly unblocked items + QGraphicsSceneHoverEvent hoverEvent; + hoverEvent.setScenePos(lastSceneMousePos); + dispatchHoverEvent(&hoverEvent); +} + QT_END_NAMESPACE #include "moc_qgraphicsscene.cpp" |