From da5033d8b48b24580e3b6faf7b4e2a8fe2cec254 Mon Sep 17 00:00:00 2001 From: Andreas Aardal Hanssen Date: Wed, 26 Aug 2009 11:02:33 +0200 Subject: Introduce QGraphicsItem::ItemIsPanel, light-weight window. ItemIsPanel allows items that act like windows. They can be activated and deactivated just like windows and focus is handled just like with windows. The main difference is that panels are more light-weight. There's less built-in functionality (e.g., clicking a panel doesn't automatically activate nor raise it). This patch also introduces QGraphicsItem::panel(), QGraphicsItem::isPanel(), and QGraphicsItem::isActive(), as well as QGraphicsScene::activePanel(), QGraphicsScene::setActivePanel(). and QGraphicsScene::isActive(). Regular windows (QGraphicsWidgets with Qt::Window set) are also panels, with added functionality. The ItemIsPanel flag is set automatically for windows. Reviewed-by: brad --- src/gui/graphicsview/qgraphicsitem.cpp | 118 +++++++++--- src/gui/graphicsview/qgraphicsitem.h | 6 +- src/gui/graphicsview/qgraphicsitem_p.h | 3 +- src/gui/graphicsview/qgraphicsscene.cpp | 246 ++++++++++++++++--------- src/gui/graphicsview/qgraphicsscene.h | 3 + src/gui/graphicsview/qgraphicsscene_p.h | 3 +- src/gui/graphicsview/qgraphicswidget.cpp | 19 +- tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp | 184 +++++++++++++++++- tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 24 +-- 9 files changed, 467 insertions(+), 139 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 620f6f4..8860677 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -335,6 +335,13 @@ \value ItemAutoDetectsFocusProxy The item will assign any child that gains input focus as its focus proxy. See also focusProxy(). This flag was introduced in Qt 4.6. + + \value ItemIsPanel. The item is a panel. A panel provides activation and + contained focus handling. Only one panel can be active at a time (see + QGraphicsItem::isActive()). When no panel is active, QGraphicsScene + activates all non-panel items. Window items (i.e., + QGraphicsItem::isWindow() returns true) are panels. This flag was + introduced in Qt 4.6. */ /*! @@ -1363,14 +1370,29 @@ QGraphicsWidget *QGraphicsItem::topLevelWidget() const */ QGraphicsWidget *QGraphicsItem::window() const { - if (isWidget() && static_cast(this)->isWindow()) - return static_cast(const_cast(this)); - if (QGraphicsWidget *parent = parentWidget()) - return parent->window(); + QGraphicsItem *p = panel(); + if (p && p->isWindow()) + return static_cast(p); return 0; } /*! + \since 4.6 + + Returns the item's panel, or 0 if this item does not have a panel. If the + item is a panel, it will return itself. Otherwise it will return the + closest ancestor that is a panel. + + \sa isPanel(), ItemIsPanel +*/ +QGraphicsItem *QGraphicsItem::panel() const +{ + if (d_ptr->flags & ItemIsPanel) + return const_cast(this); + return d_ptr->parent ? d_ptr->parent->panel() : 0; +} + +/*! \since 4.6 Return the graphics item cast to a QGraphicsObject, if the class is actually a @@ -1456,6 +1478,17 @@ bool QGraphicsItem::isWindow() const } /*! + \since 4.6 + Returns true if the item is a panel; otherwise returns false. + + \sa QGraphicsItem::panel(), ItemIsPanel +*/ +bool QGraphicsItem::isPanel() const +{ + return d_ptr->flags & ItemIsPanel; +} + +/*! Returns this item's flags. The flags describe what configurable features of the item are enabled and not. For example, if the flags include ItemIsFocusable, the item can accept input focus. @@ -1518,6 +1551,9 @@ static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::Graphics */ void QGraphicsItem::setFlags(GraphicsItemFlags flags) { + if (isWindow()) + flags |= ItemIsPanel; + // Notify change and check for adjustment. if (quint32(d_ptr->flags) == quint32(flags)) return; @@ -1872,16 +1908,16 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo q->ungrabKeyboard(); } if (q_ptr->hasFocus() && scene) { - // Hiding the closest non-window ancestor of the focus item + // Hiding the closest non-panel ancestor of the focus item QGraphicsItem *focusItem = scene->focusItem(); bool clear = true; - if (isWidget && !focusItem->isWindow()) { + if (isWidget && !focusItem->isPanel()) { do { if (focusItem == q_ptr) { clear = !static_cast(q_ptr)->focusNextPrevChild(true); break; } - } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow()); + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel()); } if (clear) q_ptr->clearFocus(); @@ -2003,17 +2039,17 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo if (scene && scene->mouseGrabberItem() == q_ptr) q_ptr->ungrabMouse(); if (q_ptr->hasFocus()) { - // Disabling the closest non-window ancestor of the focus item + // Disabling the closest non-panel ancestor of the focus item // causes focus to pop to the next item, otherwise it's cleared. QGraphicsItem *focusItem = scene->focusItem(); bool clear = true; - if (isWidget && !focusItem->isWindow() && q_ptr->isAncestorOf(focusItem)) { + if (isWidget && !focusItem->isPanel() && q_ptr->isAncestorOf(focusItem)) { do { if (focusItem == q_ptr) { clear = !static_cast(q_ptr)->focusNextPrevChild(true); break; } - } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow()); + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isPanel()); } if (clear) q_ptr->clearFocus(); @@ -2610,12 +2646,33 @@ void QGraphicsItem::setHandlesChildEvents(bool enabled) d_ptr->handlesChildEvents = enabled; d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); } +/*! + \since 4.6 + Returns true if this item is active; otherwise returns false. + + An item can only be active if the scene is active. An item is active + if it is, or is a descendent of, an active panel. Items in non-active + panels are not active. + + Items that are not part of a panel follow scene activation when the + scene has no active panel. + + Only active items can gain input focus. + + \sa QGraphicsScene::isActive(), QGraphicsScene::activePanel(), panel(), isPanel() +*/ +bool QGraphicsItem::isActive() const +{ + if (!d_ptr->scene || !d_ptr->scene->isActive()) + return false; + return panel() == d_ptr->scene->activePanel(); +} /*! - Returns true if this item or its \l{focusProxy()}{focus proxy} has keyboard - input focus; otherwise, returns false. + Returns true if this item is active, and it or its \l{focusProxy()}{focus + proxy} has keyboard input focus; otherwise, returns false. - \sa focusItem(), setFocus(), QGraphicsScene::setFocusItem() + \sa focusItem(), setFocus(), QGraphicsScene::setFocusItem(), isActive() */ bool QGraphicsItem::hasFocus() const { @@ -2632,9 +2689,10 @@ bool QGraphicsItem::hasFocus() const Only enabled items that set the ItemIsFocusable flag can accept keyboard focus. - If this item is not visible, or not associated with a scene, it will not - gain immediate input focus. However, it will be registered as the preferred - focus item for its subtree of items, should it later become visible. + If this item is not visible, not active, or not associated with a scene, + it will not gain immediate input focus. However, it will be registered as + the preferred focus item for its subtree of items, should it later become + visible. As a result of calling this function, this item will receive a \l{focusInEvent()}{focus in event} with \a focusReason. If another item @@ -2663,8 +2721,8 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) // Update the scene's focus item. if (d_ptr->scene) { - QGraphicsWidget *w = window(); - if (!w || w->isActiveWindow()) { + QGraphicsItem *p = panel(); + if (!p || p->isActive()) { // Visible items immediately gain focus from scene. d_ptr->scene->d_func()->setFocusItemHelper(f, focusReason); } @@ -2674,8 +2732,8 @@ void QGraphicsItem::setFocus(Qt::FocusReason focusReason) /*! Takes keyboard input focus from the item. - If it has focus, a \l{focusOutEvent()}{focus out event} is sent to this item - to tell it that it is about to lose the focus. + If it has focus, a \l{focusOutEvent()}{focus out event} is sent to this + item to tell it that it is about to lose the focus. Only items that set the ItemIsFocusable flag, or widgets that set an appropriate focus policy, can accept keyboard focus. @@ -4770,7 +4828,7 @@ void QGraphicsItemPrivate::setSubFocus() bool hidden = !visible; do { parent->d_func()->subFocusItem = item; - } while (!parent->isWindow() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible)); + } while (!parent->isPanel() && (parent = parent->d_ptr->parent) && (!hidden || !parent->d_func()->visible)); } /*! @@ -4784,7 +4842,7 @@ void QGraphicsItemPrivate::clearSubFocus() if (parent->d_ptr->subFocusItem != q_ptr) break; parent->d_ptr->subFocusItem = 0; - } while (!parent->isWindow() && (parent = parent->d_ptr->parent)); + } while (!parent->isPanel() && (parent = parent->d_ptr->parent)); } /*! @@ -6035,6 +6093,17 @@ bool QGraphicsItem::sceneEvent(QEvent *event) case QEvent::InputMethod: inputMethodEvent(static_cast(event)); break; + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + // Propagate panel activation. + if (d_ptr->scene) { + for (int i = 0; i < d_ptr->children.size(); ++i) { + QGraphicsItem *child = d_ptr->children.at(i); + if (!(child->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents)) + d_ptr->scene->sendEvent(child, event); + } + } + break; default: return false; } @@ -10440,6 +10509,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemAutoDetectsFocusProxy: str = "ItemAutoDetectsFocusProxy"; break; + case QGraphicsItem::ItemIsPanel: + str = "ItemIsPanel"; + break; } debug << str; return debug; @@ -10449,7 +10521,7 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags) { debug << '('; bool f = false; - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < 16; ++i) { if (flags & (1 << i)) { if (f) debug << '|'; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index 7af7c2f..b23941d 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -104,7 +104,8 @@ public: ItemSendsGeometryChanges = 0x800, ItemAcceptsInputMethod = 0x1000, ItemAutoDetectsFocusProxy = 0x2000, - ItemNegativeZStacksBehindParent = 0x4000 + ItemNegativeZStacksBehindParent = 0x4000, + ItemIsPanel = 0x8000 // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -161,11 +162,13 @@ public: QGraphicsWidget *parentWidget() const; QGraphicsWidget *topLevelWidget() const; QGraphicsWidget *window() const; + QGraphicsItem *panel() const; void setParentItem(QGraphicsItem *parent); QList children() const; // ### obsolete QList childItems() const; bool isWidget() const; bool isWindow() const; + bool isPanel() const; QGraphicsObject *toGraphicsObject(); const QGraphicsObject *toGraphicsObject() const; @@ -231,6 +234,7 @@ public: bool handlesChildEvents() const; void setHandlesChildEvents(bool enabled); + bool isActive() const; bool hasFocus() const; void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason); void clearFocus(); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 11f6f53..1a085dd 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -463,7 +463,7 @@ public: // New 32 bits quint32 fullUpdatePending : 1; - quint32 flags : 15; + quint32 flags : 16; quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; @@ -479,7 +479,6 @@ public: quint32 notifyBoundingRectChanged : 1; quint32 notifyInvalidated : 1; quint32 mouseSetsFocus : 1; - quint32 unused : 1; // feel free to use // Optional stacking order int globalStackingOrder; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a819822..7dbc996 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -288,7 +288,8 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() focusItem(0), lastFocusItem(0), tabFocusFirst(0), - activeWindow(0), + activePanel(0), + lastActivePanel(0), activationRefCount(0), lastMouseGrabberItem(0), lastMouseGrabberItemHasImplicitMouseGrab(false), @@ -516,10 +517,12 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) focusItem = 0; if (item == lastFocusItem) lastFocusItem = 0; - if (item == activeWindow) { + if (item == activePanel) { // ### deactivate... - activeWindow = 0; + activePanel = 0; } + if (item == lastActivePanel) + lastActivePanel = 0; // Disable selectionChanged() for individual items ++selectionChanging; @@ -1076,8 +1079,9 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } // Update window activation. - QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0)); - if (newActiveWindow != activeWindow) + QGraphicsItem *topItem = cachedItemsUnderMouse.value(0); + QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0; + if (newActiveWindow != q->activeWindow()) q->setActiveWindow(newActiveWindow); // Set focus on the topmost enabled item that can take focus. @@ -1112,7 +1116,7 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // check if the item we are sending to are disabled (before we send the event) bool disabled = !item->isEnabled(); - bool isWindow = item->isWindow(); + bool isPanel = item->isPanel(); if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem && lastMouseGrabberItem) { // If this item is different from the item that received the last @@ -1153,8 +1157,8 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); - // Don't propagate through windows. - if (isWindow) + // Don't propagate through panels. + if (isPanel) break; } @@ -1176,18 +1180,6 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou } } -QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) const -{ - if (!item) - return 0; - do { - if (item->isWidget()) - return static_cast(item)->window(); - item = item->parentItem(); - } while (item); - return 0; -} - /*! \internal @@ -2234,6 +2226,9 @@ void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group) moved to this scene), QGraphicsScene will send an addition notification as the item is removed from its previous scene. + If the item is a panel, the scene is active, and there is no active panel + in the scene, then the item will be activated. + \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(), addRect(), addText(), addWidget() */ @@ -2356,9 +2351,9 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Deliver post-change notification item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant); - // Auto-activate the first inactive window if the scene is active. - if (d->activationRefCount > 0 && !d->activeWindow && item->isWindow()) - setActiveWindow(static_cast(item)); + // Auto-activate the first inactive panel if the scene is active. + if (isActive() && !d->activePanel && item->isPanel()) + setActivePanel(item); // Ensure that newly added items that have subfocus set, gain // focus automatically if there isn't a focus item already. @@ -3130,40 +3125,43 @@ bool QGraphicsScene::event(QEvent *event) case QEvent::InputMethod: inputMethodEvent(static_cast(event)); break; - case QEvent::WindowActivate: { + case QEvent::WindowActivate: if (!d->activationRefCount++) { - // Notify all non-window widgets. - foreach (QGraphicsItem *item, items()) { - if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { - QEvent event(QEvent::WindowActivate); - QApplication::sendEvent(static_cast(item), &event); + if (d->lastActivePanel) { + // Activate the last panel. + setActivePanel(d->lastActivePanel); + } else if (d->tabFocusFirst && d->tabFocusFirst->isPanel()) { + // Activate the panel of the first item in the tab focus + // chain. + setActivePanel(d->tabFocusFirst); + } else { + // Activate all toplevel items. + QEvent event(QEvent::WindowActivate); + foreach (QGraphicsItem *item, items()) { + if (item->isVisible() && !item->isPanel() && !item->parentItem()) + sendEvent(item, &event); } } - - // Restore window activation. - QGraphicsItem *nextFocusItem = d->focusItem ? d->focusItem : d->lastFocusItem; - if (nextFocusItem && nextFocusItem->window()) - setActiveWindow(static_cast(nextFocusItem)); - else if (d->tabFocusFirst && d->tabFocusFirst->isWindow()) - setActiveWindow(d->tabFocusFirst); } break; - } - case QEvent::WindowDeactivate: { + case QEvent::WindowDeactivate: if (!--d->activationRefCount) { - // Remove window activation. - setActiveWindow(0); - - // Notify all non-window widgets. - foreach (QGraphicsItem *item, items()) { - if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { - QEvent event(QEvent::WindowDeactivate); - QApplication::sendEvent(static_cast(item), &event); + if (d->activePanel) { + // Deactivate the active panel (but keep it so we can + // reactivate it later). + QGraphicsItem *lastActivePanel = d->activePanel; + setActivePanel(0); + d->lastActivePanel = lastActivePanel; + } else { + // Activate all toplevel items. + QEvent event(QEvent::WindowDeactivate); + foreach (QGraphicsItem *item, items()) { + if (item->isVisible() && !item->isPanel() && !item->parentItem()) + sendEvent(item, &event); } } } break; - } case QEvent::ApplicationFontChange: { // Resolve the existing scene font. d->resolveFont(); @@ -3526,8 +3524,8 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0; while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem)) commonAncestorItem = commonAncestorItem->parentItem(); - if (commonAncestorItem && commonAncestorItem->window() != item->window()) { - // The common ancestor isn't in the same window as the two hovered + if (commonAncestorItem && commonAncestorItem->panel() != item->panel()) { + // The common ancestor isn't in the same panel as the two hovered // items. commonAncestorItem = 0; } @@ -3548,8 +3546,8 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv QGraphicsItem *parent = item; while (parent && parent != commonAncestorItem) { parents.prepend(parent); - if (parent->isWindow()) { - // Stop at the window - we don't deliver beyond this point. + if (parent->isPanel()) { + // Stop at the panel - we don't deliver beyond this point. break; } parent = parent->parentItem(); @@ -3626,7 +3624,7 @@ void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) // is filtered out, stop propagating it. if (!d->sendEvent(p, keyEvent)) break; - } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); } else { keyEvent->ignore(); } @@ -3656,7 +3654,7 @@ void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) // is filtered out, stop propagating it. if (!d->sendEvent(p, keyEvent)) break; - } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem())); } else { keyEvent->ignore(); } @@ -3818,9 +3816,9 @@ void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(), wheelEvent->widget())); wheelEvent->accept(); - bool isWindow = item->isWindow(); + bool isPanel = item->isPanel(); d->sendEvent(item, wheelEvent); - if (isWindow || wheelEvent->isAccepted()) + if (isPanel || wheelEvent->isAccepted()) break; } } @@ -4879,7 +4877,7 @@ bool QGraphicsScene::focusNextPrevChild(bool next) if (widget->flags() & QGraphicsItem::ItemIsFocusable && widget->isEnabled() && widget->isVisibleTo(0) && (widget->focusPolicy() & Qt::TabFocus) - && (!item || !item->isWindow() || item->isAncestorOf(widget)) + && (!item || !item->isPanel() || item->isAncestorOf(widget)) ) { setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason); return true; @@ -5066,84 +5064,150 @@ void QGraphicsScene::setPalette(const QPalette &palette) } /*! - \since 4.4 + \since 4.6 - Returns the current active window, or 0 if there is no window is currently - active. + Returns true if the scene is active (e.g., it's viewed by + at least one QGraphicsView that is active); otherwise returns false. - \sa QGraphicsScene::setActiveWindow() + \sa QGraphicsItem::isActive(), QWidget::isActiveWindow() */ -QGraphicsWidget *QGraphicsScene::activeWindow() const +bool QGraphicsScene::isActive() const { Q_D(const QGraphicsScene); - return d->activeWindow; + return d->activationRefCount > 0; } /*! - \since 4.4 - Activates \a widget, which must be a widget in this scene. You can also - pass 0 for \a widget, in which case QGraphicsScene will deactivate any - currently active window. + \since 4.6 + Returns the current active panel, or 0 if no panel is currently active. - \sa activeWindow(), QGraphicsWidget::isActiveWindow() + \sa QGraphicsScene::setActivePanel() */ -void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) +QGraphicsItem *QGraphicsScene::activePanel() const +{ + Q_D(const QGraphicsScene); + return d->activePanel; +} + +/*! + \since 4.6 + Activates \a item, which must be an item in this scene. You + can also pass 0 for \a item, in which case QGraphicsScene will + deactivate any currently active panel. + + \sa activePanel(), isActive(), QGraphicsItem::isActive() +*/ +void QGraphicsScene::setActivePanel(QGraphicsItem *item) { Q_D(QGraphicsScene); - if (widget && widget->scene() != this) { - qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene", - widget); + if (item && item->scene() != this) { + qWarning("QGraphicsScene::setActivePanel: item %p must be part of this scene", + item); return; } - // Activate the widget's window. - QGraphicsWidget *window = widget ? widget->window() : 0; - if (window == d->activeWindow) + // Find the item's panel. + QGraphicsItem *panel = item ? item->panel() : 0; + d->lastActivePanel = panel ? d->activePanel : 0; + if (panel == d->activePanel) return; - // Deactivate the last active window. - if (d->activeWindow) { - if (QGraphicsWidget *fw = d->activeWindow->focusWidget()) { + // Deactivate the last active panel. + if (d->activePanel) { + if (QGraphicsItem *fi = d->activePanel->focusItem()) { // Remove focus from the current focus item. - if (fw == focusItem()) + if (fi == focusItem()) setFocusItem(0, Qt::ActiveWindowFocusReason); } QEvent event(QEvent::WindowDeactivate); - QApplication::sendEvent(d->activeWindow, &event); + sendEvent(d->activePanel, &event); + } else if (panel) { + // Deactivate the scene if changing activation to a panel. + QEvent event(QEvent::WindowDeactivate); + foreach (QGraphicsItem *item, items()) { + if (item->isVisible() && !item->isPanel() && !item->parentItem()) + sendEvent(item, &event); + } } // Update activate state. - d->activeWindow = window; + d->activePanel = panel; QEvent event(QEvent::ActivationChange); QApplication::sendEvent(this, &event); // Activate - if (window) { + if (panel) { QEvent event(QEvent::WindowActivate); - QApplication::sendEvent(window, &event); + sendEvent(panel, &event); + // Set focus on the panel's focus item. + if (QGraphicsItem *focusItem = panel->focusItem()) + focusItem->setFocus(Qt::ActiveWindowFocusReason); + } else if (isActive()) { + // Activate the scene + QEvent event(QEvent::WindowActivate); + foreach (QGraphicsItem *item, items()) { + if (item->isVisible() && !item->isPanel() && !item->parentItem()) + sendEvent(item, &event); + } + } +} + +/*! + \since 4.4 + + Returns the current active window, or 0 if there is no window is currently + active. + + \sa QGraphicsScene::setActiveWindow() +*/ +QGraphicsWidget *QGraphicsScene::activeWindow() const +{ + Q_D(const QGraphicsScene); + if (d->activePanel && d->activePanel->isWindow()) + return static_cast(d->activePanel); + return 0; +} + +/*! + \since 4.4 + Activates \a widget, which must be a widget in this scene. You can also + pass 0 for \a widget, in which case QGraphicsScene will deactivate any + currently active window. + + \sa activeWindow(), QGraphicsWidget::isActiveWindow() +*/ +void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) +{ + if (widget && widget->scene() != this) { + qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene", + widget); + return; + } + + // Activate the widget's panel (all windows are panels). + QGraphicsItem *panel = widget ? widget->panel() : 0; + setActivePanel(panel); + + // Raise + if (panel) { QList siblingWindows; - QGraphicsItem *parent = window->parentItem(); + QGraphicsItem *parent = panel->parentItem(); // Raise ### inefficient for toplevels foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) { - if (sibling != window && sibling->isWidget() - && static_cast(sibling)->isWindow()) { + if (sibling != panel && sibling->isWindow()) siblingWindows << sibling; - } } // Find the highest z value. - qreal z = window->zValue(); + qreal z = panel->zValue(); for (int i = 0; i < siblingWindows.size(); ++i) z = qMax(z, siblingWindows.at(i)->zValue()); // This will probably never overflow. const qreal litt = qreal(0.001); - window->setZValue(z + litt); - - if (QGraphicsWidget *focusChild = window->focusWidget()) - focusChild->setFocus(Qt::ActiveWindowFocusReason); + panel->setZValue(z + litt); } } diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 26cb1c2..9478879 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -243,6 +243,9 @@ public: QPalette palette() const; void setPalette(const QPalette &palette); + bool isActive() const; + QGraphicsItem *activePanel() const; + void setActivePanel(QGraphicsItem *item); QGraphicsWidget *activeWindow() const; void setActiveWindow(QGraphicsWidget *widget); diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 4b8791e..e19e60e 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -129,7 +129,8 @@ public: QGraphicsItem *focusItem; QGraphicsItem *lastFocusItem; QGraphicsWidget *tabFocusFirst; - QGraphicsWidget *activeWindow; + QGraphicsItem *activePanel; + QGraphicsItem *lastActivePanel; int activationRefCount; void setFocusItemHelper(QGraphicsItem *item, Qt::FocusReason focusReason); diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 7f9fe65..40addf6 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -1285,10 +1285,6 @@ bool QGraphicsWidget::event(QEvent *event) case QEvent::WindowActivate: case QEvent::WindowDeactivate: update(); - foreach (QGraphicsItem *child, childItems()) { - if (child->isWidget()) - QApplication::sendEvent(static_cast(child), event); - } break; // Taken from QWidget::event case QEvent::ActivationChange: @@ -1598,6 +1594,8 @@ void QGraphicsWidget::ungrabKeyboardEvent(QEvent *event) /*! Returns the widgets window type. + + \sa windowFlags(), isWindow(), isPanel() */ Qt::WindowType QGraphicsWidget::windowType() const { @@ -1613,6 +1611,13 @@ Qt::WindowType QGraphicsWidget::windowType() const is platform-dependent. By default, this property contains no window flags. + + Windows are panels. If you set the Qt::Window flag, the ItemIsPanel flag + will be set automatically. If you clear the Qt::Window flag, the + ItemIsPanel flag is also cleared. Note that the ItemIsPanel flag can be + set independently of Qt::Window. + + \sa isWindow(), isPanel() */ Qt::WindowFlags QGraphicsWidget::windowFlags() const { @@ -1631,6 +1636,8 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) if (!d->setWindowFrameMargins) unsetWindowFrameMargins(); + setFlag(ItemIsPanel, d->windowFlags & Qt::Window); + bool isPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup; if (d->scene && isVisible() && wasPopup != isPopup) { // Popup state changed; update implicit mouse grab. @@ -1654,7 +1661,7 @@ void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) The active window is the window that either contains a child widget that currently has input focus, or that itself has input focus. - \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow() + \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow(), isActive() */ bool QGraphicsWidget::isActiveWindow() const { @@ -1662,7 +1669,7 @@ bool QGraphicsWidget::isActiveWindow() const if (!d->scene) return false; const QGraphicsWidget *w = window(); - return (!w && d->scene->d_func()->activationRefCount) || (w && d->scene->activeWindow() == w); + return (!w && d->scene->isActive()) || (w && d->scene->activeWindow() == w); } /*! diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index e9154d4..ba27c85 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -287,6 +287,8 @@ private slots: void focusProxyDeletion(); void negativeZStacksBehindParent(); void setGraphicsEffect(); + void panel(); + void addPanelToActiveScene(); // task specific tests below me void task141694_textItemEnsureVisible(); @@ -2922,7 +2924,7 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() int npaints = tester->repaints; qApp->processEvents(); qApp->processEvents(); - QCOMPARE(tester->events.size(), 2); // enter + move + QCOMPARE(tester->events.size(), 3); // activate + enter + move QCOMPARE(tester->repaints, npaints + 1); QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); @@ -2936,7 +2938,7 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() qApp->processEvents(); qApp->processEvents(); - QCOMPARE(tester->events.size(), 3); + QCOMPARE(tester->events.size(), 4); QCOMPARE(tester->repaints, npaints + 1); QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverMove); @@ -2950,7 +2952,7 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() qApp->processEvents(); qApp->processEvents(); - QCOMPARE(tester->events.size(), 4); + QCOMPARE(tester->events.size(), 5); QCOMPARE(tester->repaints, npaints + 2); QCOMPARE(tester->events.last(), QEvent::GraphicsSceneHoverLeave); } @@ -7764,5 +7766,181 @@ void tst_QGraphicsItem::setGraphicsEffect() delete anotherItem; } +void tst_QGraphicsItem::panel() +{ + QGraphicsScene scene; + + QGraphicsRectItem *panel1 = new QGraphicsRectItem; + QGraphicsRectItem *panel2 = new QGraphicsRectItem; + QGraphicsRectItem *panel3 = new QGraphicsRectItem; + QGraphicsRectItem *panel4 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel1 = new QGraphicsRectItem; + QGraphicsRectItem *notPanel2 = new QGraphicsRectItem; + panel1->setFlag(QGraphicsItem::ItemIsPanel); + panel2->setFlag(QGraphicsItem::ItemIsPanel); + panel3->setFlag(QGraphicsItem::ItemIsPanel); + panel4->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(panel1); + scene.addItem(panel2); + scene.addItem(panel3); + scene.addItem(panel4); + scene.addItem(notPanel1); + scene.addItem(notPanel2); + + EventSpy spy_activate_panel1(&scene, panel1, QEvent::WindowActivate); + EventSpy spy_deactivate_panel1(&scene, panel1, QEvent::WindowDeactivate); + EventSpy spy_activate_panel2(&scene, panel2, QEvent::WindowActivate); + EventSpy spy_deactivate_panel2(&scene, panel2, QEvent::WindowDeactivate); + EventSpy spy_activate_panel3(&scene, panel3, QEvent::WindowActivate); + EventSpy spy_deactivate_panel3(&scene, panel3, QEvent::WindowDeactivate); + EventSpy spy_activate_panel4(&scene, panel4, QEvent::WindowActivate); + EventSpy spy_deactivate_panel4(&scene, panel4, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel1(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel1(&scene, notPanel1, QEvent::WindowDeactivate); + EventSpy spy_activate_notPanel2(&scene, notPanel1, QEvent::WindowActivate); + EventSpy spy_deactivate_notPanel2(&scene, notPanel1, QEvent::WindowDeactivate); + + QCOMPARE(spy_activate_panel1.count(), 0); + QCOMPARE(spy_deactivate_panel1.count(), 0); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_deactivate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_deactivate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + QCOMPARE(spy_deactivate_panel4.count(), 0); + QCOMPARE(spy_activate_notPanel1.count(), 0); + QCOMPARE(spy_deactivate_notPanel1.count(), 0); + QCOMPARE(spy_activate_notPanel2.count(), 0); + QCOMPARE(spy_deactivate_notPanel2.count(), 0); + + QVERIFY(!scene.activePanel()); + QVERIFY(!scene.isActive()); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + + // No previous activation, so the scene is active. + QVERIFY(scene.isActive()); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 1); + QCOMPARE(spy_activate_notPanel2.count(), 1); + + // Switch to panel1. + scene.setActivePanel(panel1); + QVERIFY(panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 1); + QCOMPARE(spy_deactivate_notPanel2.count(), 1); + QCOMPARE(spy_activate_panel1.count(), 1); + QCOMPARE(spy_activate_panel2.count(), 0); + QCOMPARE(spy_activate_panel3.count(), 0); + QCOMPARE(spy_activate_panel4.count(), 0); + + // Switch back to scene. + scene.setActivePanel(0); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 2); + QCOMPARE(spy_activate_notPanel2.count(), 2); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(!notPanel1->isActive()); + QVERIFY(!notPanel2->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 2); + QCOMPARE(spy_deactivate_notPanel2.count(), 2); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!scene.activePanel()); + QVERIFY(!panel1->isActive()); + QVERIFY(!panel2->isActive()); + QVERIFY(!panel3->isActive()); + QVERIFY(!panel4->isActive()); + QVERIFY(notPanel1->isActive()); + QVERIFY(notPanel2->isActive()); + QCOMPARE(spy_activate_notPanel1.count(), 3); + QCOMPARE(spy_activate_notPanel2.count(), 3); + + // Switch to panel1 + scene.setActivePanel(panel1); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_deactivate_notPanel1.count(), 3); + QCOMPARE(spy_deactivate_notPanel2.count(), 3); + QCOMPARE(spy_activate_panel1.count(), 2); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QCOMPARE(spy_deactivate_panel1.count(), 2); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(panel1->isActive()); + QCOMPARE(spy_activate_panel1.count(), 3); + + // Deactivate the scene + QApplication::sendEvent(&scene, &deactivate); + QVERIFY(!panel1->isActive()); + QVERIFY(!scene.activePanel()); + scene.setActivePanel(0); + + // Reactivate the scene + QApplication::sendEvent(&scene, &activate); + QVERIFY(!panel1->isActive()); +} + +void tst_QGraphicsItem::addPanelToActiveScene() +{ + QGraphicsScene scene; + QVERIFY(!scene.isActive()); + + QGraphicsRectItem *rect = new QGraphicsRectItem; + scene.addItem(rect); + QVERIFY(!rect->isActive()); + scene.removeItem(rect); + + QEvent activate(QEvent::WindowActivate); + QEvent deactivate(QEvent::WindowDeactivate); + + QApplication::sendEvent(&scene, &activate); + QVERIFY(scene.isActive()); + scene.addItem(rect); + QVERIFY(rect->isActive()); + scene.removeItem(rect); + + rect->setFlag(QGraphicsItem::ItemIsPanel); + scene.addItem(rect); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); + + QGraphicsRectItem *rect2 = new QGraphicsRectItem; + scene.addItem(rect2); + QVERIFY(rect->isActive()); + QCOMPARE(scene.activePanel(), (QGraphicsItem *)rect); +} + QTEST_MAIN(tst_QGraphicsItem) #include "tst_qgraphicsitem.moc" diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index 505f9a4..524b756 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -376,7 +376,7 @@ void tst_QGraphicsView::interactive() view.show(); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(item->events.size(), 0); + QCOMPARE(item->events.size(), 1); // activate QPoint itemPoint = view.mapFromScene(item->scenePos()); @@ -384,16 +384,16 @@ void tst_QGraphicsView::interactive() for (int i = 0; i < 100; ++i) { sendMousePress(view.viewport(), itemPoint); - QCOMPARE(item->events.size(), i * 5 + 2); + QCOMPARE(item->events.size(), i * 5 + 3); QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GrabMouse); QCOMPARE(item->events.at(item->events.size() - 1), QEvent::GraphicsSceneMousePress); sendMouseRelease(view.viewport(), itemPoint); - QCOMPARE(item->events.size(), i * 5 + 4); + QCOMPARE(item->events.size(), i * 5 + 5); QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GraphicsSceneMouseRelease); QCOMPARE(item->events.at(item->events.size() - 1), QEvent::UngrabMouse); QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, itemPoint, view.mapToGlobal(itemPoint)); QApplication::sendEvent(view.viewport(), &contextEvent); - QCOMPARE(item->events.size(), i * 5 + 5); + QCOMPARE(item->events.size(), i * 5 + 6); QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); } @@ -401,14 +401,14 @@ void tst_QGraphicsView::interactive() for (int i = 0; i < 100; ++i) { sendMousePress(view.viewport(), itemPoint); - QCOMPARE(item->events.size(), 500); + QCOMPARE(item->events.size(), 501); QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); sendMouseRelease(view.viewport(), itemPoint); - QCOMPARE(item->events.size(), 500); + QCOMPARE(item->events.size(), 501); QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, itemPoint, view.mapToGlobal(itemPoint)); QApplication::sendEvent(view.viewport(), &contextEvent); - QCOMPARE(item->events.size(), 500); + QCOMPARE(item->events.size(), 501); QCOMPARE(item->events.last(), QEvent::GraphicsSceneContextMenu); } } @@ -1872,32 +1872,32 @@ void tst_QGraphicsView::sendEvent() item->setFocus(); QCOMPARE(scene.focusItem(), (QGraphicsItem *)item); - QCOMPARE(item->events.size(), 1); + QCOMPARE(item->events.size(), 2); QCOMPARE(item->events.last(), QEvent::FocusIn); QPoint itemPoint = view.mapFromScene(item->scenePos()); sendMousePress(view.viewport(), itemPoint); - QCOMPARE(item->events.size(), 3); + QCOMPARE(item->events.size(), 4); QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GrabMouse); QCOMPARE(item->events.at(item->events.size() - 1), QEvent::GraphicsSceneMousePress); QMouseEvent mouseMoveEvent(QEvent::MouseMove, itemPoint, view.viewport()->mapToGlobal(itemPoint), Qt::LeftButton, Qt::LeftButton, 0); QApplication::sendEvent(view.viewport(), &mouseMoveEvent); - QCOMPARE(item->events.size(), 4); + QCOMPARE(item->events.size(), 5); QCOMPARE(item->events.last(), QEvent::GraphicsSceneMouseMove); QMouseEvent mouseReleaseEvent(QEvent::MouseButtonRelease, itemPoint, view.viewport()->mapToGlobal(itemPoint), Qt::LeftButton, 0, 0); QApplication::sendEvent(view.viewport(), &mouseReleaseEvent); - QCOMPARE(item->events.size(), 6); + QCOMPARE(item->events.size(), 7); QCOMPARE(item->events.at(item->events.size() - 2), QEvent::GraphicsSceneMouseRelease); QCOMPARE(item->events.at(item->events.size() - 1), QEvent::UngrabMouse); QKeyEvent keyPress(QEvent::KeyPress, Qt::Key_Space, 0); QApplication::sendEvent(view.viewport(), &keyPress); - QCOMPARE(item->events.size(), 8); + QCOMPARE(item->events.size(), 9); QCOMPARE(item->events.at(item->events.size() - 2), QEvent::ShortcutOverride); QCOMPARE(item->events.last(), QEvent::KeyPress); } -- cgit v0.12