From 7d49812197294dc0cb33e1ad0bd2d56e3557040a Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Wed, 15 Jul 2009 13:23:34 +1000 Subject: Add experimental FocusRealm support. --- src/declarative/fx/fx.pri | 2 + src/declarative/fx/qfxitem.cpp | 35 +++++++++-- src/declarative/fx/qfxitem_p.h | 13 ++++ src/declarative/util/qfxview.cpp | 2 +- src/gui/graphicsview/qgraphicsitem.cpp | 10 +++ src/gui/graphicsview/qgraphicsitem_p.h | 18 +++++- src/gui/graphicsview/qgraphicsscene.cpp | 104 ++++++++++++++++++++++++++------ src/gui/graphicsview/qgraphicsscene_p.h | 2 + 8 files changed, 162 insertions(+), 24 deletions(-) diff --git a/src/declarative/fx/fx.pri b/src/declarative/fx/fx.pri index 71517fd..b4ba742 100644 --- a/src/declarative/fx/fx.pri +++ b/src/declarative/fx/fx.pri @@ -14,6 +14,7 @@ HEADERS += \ fx/qfximage_p.h \ fx/qfxitem.h \ fx/qfxitem_p.h \ + fx/qfxfocusrealm.h \ fx/qfxkeyactions.h \ fx/qfxkeyproxy.h \ fx/qfxlayouts.h \ @@ -49,6 +50,7 @@ SOURCES += \ fx/qfximage.cpp \ fx/qfxpainteditem.cpp \ fx/qfxitem.cpp \ + fx/qfxfocusrealm.cpp \ fx/qfxkeyactions.cpp \ fx/qfxkeyproxy.cpp \ fx/qfxlayouts.cpp \ diff --git a/src/declarative/fx/qfxitem.cpp b/src/declarative/fx/qfxitem.cpp index f343f4e..62a4d95 100644 --- a/src/declarative/fx/qfxitem.cpp +++ b/src/declarative/fx/qfxitem.cpp @@ -2074,6 +2074,9 @@ QVariant QFxItem::itemChange(GraphicsItemChange change, if (options() & QFxItem::MouseFilter) d->gvAddMouseFilter(); + + if (d->canvas && d->isFocusItemForArea) + d->canvas->setFocusItem(this); } else if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { childrenChanged(); @@ -2409,13 +2412,28 @@ QFxItem *QFxItem::mouseGrabberItem() const bool QFxItem::hasFocus() const { - return false; + Q_D(const QFxItem); + return d->isFocusItemForArea; } void QFxItem::setFocus(bool focus) { - Q_UNUSED(focus) - return; + Q_D(QFxItem); + QGraphicsScene *s = scene(); + if (s) { + if (d->hasActiveFocus) + s->setFocusItem(focus ? this : 0); + else if (focus) + s->setFocusItem(this); + else { + d->isFocusItemForArea = false; + focusChanged(false); + } + + } else { + d->isFocusItemForArea = focus; + focusChanged(focus); + } } /*! @@ -2425,7 +2443,8 @@ void QFxItem::setFocus(bool focus) bool QFxItem::hasActiveFocus() const { - return false; + Q_D(const QFxItem); + return d->hasActiveFocus; } bool QFxItem::activeFocusPanel() const @@ -2460,6 +2479,13 @@ void QFxItem::setOptions(Options options, bool set) Q_D(QFxItem); Options old = (Options)d->options; + if (options & IsFocusRealm) { + if (!set) { + qWarning("QFxItem::setOptions: Cannot unset IsFocusRealm"); + return; + } + } + if (set) d->options |= options; else @@ -2474,6 +2500,7 @@ void QFxItem::setOptions(Options options, bool set) setFiltersChildEvents(d->options & ChildMouseFilter); setFlag(QGraphicsItem::ItemAcceptsInputMethod, (d->options & AcceptsInputMethods)); setAcceptHoverEvents(d->options & HoverEvents); + d->isFocusRealm = static_cast(d->options & IsFocusRealm); if ((old & MouseFilter) != (d->options & MouseFilter)) { if (d->options & MouseFilter) diff --git a/src/declarative/fx/qfxitem_p.h b/src/declarative/fx/qfxitem_p.h index 4a0ea18..eddeb9b 100644 --- a/src/declarative/fx/qfxitem_p.h +++ b/src/declarative/fx/qfxitem_p.h @@ -95,6 +95,7 @@ public: q->setAcceptedMouseButtons(Qt::NoButton); q->setFlag(QGraphicsItem::ItemHasNoContents, true); q->setFlag(QGraphicsItem::ItemIsFocusable, true); + mouseSetsFocus = false; } QString _id; @@ -195,6 +196,18 @@ public: void gvRemoveMouseFilter(); void gvAddMouseFilter(); + + virtual void setActiveFocus(bool b) { + Q_Q(QFxItem); + QGraphicsItemPrivate::setActiveFocus(b); + q->activeFocusChanged(b); + } + + virtual void setFocusItemForArea(bool b) { + Q_Q(QFxItem); + QGraphicsItemPrivate::setFocusItemForArea(b); + q->focusChanged(b); + } }; QT_END_NAMESPACE diff --git a/src/declarative/util/qfxview.cpp b/src/declarative/util/qfxview.cpp index 05acf87..e307232 100644 --- a/src/declarative/util/qfxview.cpp +++ b/src/declarative/util/qfxview.cpp @@ -163,7 +163,7 @@ void QFxViewPrivate::init() scene.setItemIndexMethod(QGraphicsScene::NoIndex); q->viewport()->setFocusPolicy(Qt::NoFocus); - + scene.setStickyFocus(true); //### needed for correct focus handling } /*! diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 4a0311c..916724c 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1167,6 +1167,16 @@ QGraphicsItem::~QGraphicsItem() d_ptr->inDestructor = 1; d_ptr->removeExtraItemCache(); + if (d_ptr->isFocusRealm && d_ptr->scene) + d_ptr->scene->d_func()->focusItemForFocusArea.remove(this); + if (d_ptr->isFocusItemForArea && d_ptr->scene) { + QGraphicsItem *prnt = parentItem(); + while (prnt && !prnt->d_func()->isFocusRealm) + prnt = prnt->parentItem(); + if (prnt && d_ptr->scene->d_func()->focusItemForFocusArea.value(prnt) == this) + d_ptr->scene->d_func()->focusItemForFocusArea.remove(prnt); + } + clearFocus(); if (!d_ptr->children.isEmpty()) { QList oldChildren = d_ptr->children; diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 540cb44..d16a720 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -160,6 +160,10 @@ public: acceptedTouchBeginEvent(0), filtersDescendantEvents(0), sceneTransformTranslateOnly(0), + isFocusRealm(0), + isFocusItemForArea(0), + hasActiveFocus(0), + mouseSetsFocus(1), globalStackingOrder(-1), q_ptr(0) { @@ -393,6 +397,14 @@ public: inline QTransform transformToParent() const; inline void ensureSortedChildren(); + virtual void setActiveFocus(bool b) { + hasActiveFocus = b; + } + + virtual void setFocusItemForArea(bool b) { + isFocusItemForArea = b; + } + QPainterPath cachedClipPath; QRectF childrenBoundingRect; QRectF needsRepaint; @@ -451,7 +463,11 @@ public: quint32 acceptedTouchBeginEvent : 1; quint32 filtersDescendantEvents : 1; quint32 sceneTransformTranslateOnly : 1; - quint32 unused : 7; // feel free to use + quint32 isFocusRealm : 1; + quint32 isFocusItemForArea : 1; + quint32 hasActiveFocus : 1; + quint32 mouseSetsFocus : 1; + quint32 unused : 3; // feel free to use // Optional stacking order int globalStackingOrder; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 0aea678..3d77ecf 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -1012,7 +1012,7 @@ void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mou // Set focus on the topmost enabled item that can take focus. bool setFocus = false; foreach (QGraphicsItem *item, cachedItemsUnderMouse) { - if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { setFocus = true; if (item != q->focusItem()) @@ -2495,27 +2495,76 @@ void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReas item = 0; } + QGraphicsItem *oldFocus = d->focusItem; + bool isActive = true; if (item) { setFocus(focusReason); if (item == d->focusItem) return; + + //focus area handling + { + //determine the focus area the item belongs to + QGraphicsItem *scope = 0; + QGraphicsItem *citem = item; + while(citem && !scope) { + if (citem != item && citem->d_ptr->isFocusRealm) + scope = citem; + citem = citem->parentItem(); + } + + //if it has a focus area, set the item as the focusitem for that area + if (scope) { + QGraphicsItem *oldFFA = d->focusItemForFocusArea.value(scope); + if (!scope->d_ptr->hasActiveFocus && oldFFA) { //active case is handled by d->focusItem below??? + oldFFA->d_ptr->setFocusItemForArea(false); + } + if (oldFFA) + oldFocus = oldFFA; + isActive = scope->d_ptr->hasActiveFocus ? true : false; + d->focusItemForFocusArea.insert(scope, item); + } + } } - if (d->focusItem) { + if (oldFocus) { QFocusEvent event(QEvent::FocusOut, focusReason); - d->lastFocusItem = d->focusItem; - d->focusItem = 0; - d->sendEvent(d->lastFocusItem, &event); + if (isActive) { + d->lastFocusItem = oldFocus; + d->focusItem = 0; + } + + //focus area handling + if (!oldFocus->d_ptr->inDestructor) + oldFocus->d_ptr->setFocusItemForArea(false); + + if (isActive) { + //focus area handling + while(true) { + if (!oldFocus->d_ptr->inDestructor) + oldFocus->d_ptr->setActiveFocus(false); + if (oldFocus->d_ptr->isFocusRealm) { + QGraphicsItem *citem = d->focusItemForFocusArea.value(oldFocus); + if (citem) + oldFocus = citem; + else + break; + } else + break; + } - if (d->lastFocusItem - && (d->lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { - // Reset any visible preedit text - QInputMethodEvent imEvent; - d->sendEvent(d->lastFocusItem, &imEvent); + d->sendEvent(oldFocus, &event); - // Close any external input method panel - for (int i = 0; i < d->views.size(); ++i) - d->views.at(i)->inputContext()->reset(); + if (oldFocus + && (oldFocus->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + // Reset any visible preedit text + QInputMethodEvent imEvent; + d->sendEvent(oldFocus, &imEvent); + + // Close any external input method panel + for (int i = 0; i < d->views.size(); ++i) + d->views.at(i)->inputContext()->reset(); + } } } @@ -2525,9 +2574,27 @@ void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReas static_cast(item)->d_func()->setFocusWidget(); } - d->focusItem = item; - QFocusEvent event(QEvent::FocusIn, focusReason); - d->sendEvent(item, &event); + //focus area handling + item->d_ptr->setFocusItemForArea(true); + + if (isActive) { + //focus area handling + while(true) { + item->d_ptr->setActiveFocus(true); + if (item->d_ptr->isFocusRealm) { + QGraphicsItem *citem = d->focusItemForFocusArea.value(item); + if (citem) + item = citem; + else + break; + } else + break; + } + + d->focusItem = item; + QFocusEvent event(QEvent::FocusIn, focusReason); + d->sendEvent(item, &event); + } } d->updateInputMethodSensitivityInViews(); @@ -3644,7 +3711,8 @@ void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) bool hasSetFocus = false; foreach (QGraphicsItem *item, wheelCandidates) { - if (!hasSetFocus && item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!hasSetFocus && item->isEnabled() + && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { if (item->isWidget() && static_cast(item)->focusPolicy() == Qt::WheelFocus) { hasSetFocus = true; if (item != focusItem()) @@ -5140,7 +5208,7 @@ bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEve // Set focus on the topmost enabled item that can take focus. bool setFocus = false; foreach (QGraphicsItem *item, cachedItemsUnderMouse) { - if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) { if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { setFocus = true; if (item != q->focusItem()) diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 9a91acc..ff7e8c6 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -256,6 +256,8 @@ public: bool allItemsIgnoreTouchEvents; void enableTouchEventsOnViews(); + QHash focusItemForFocusArea; + void updateInputMethodSensitivityInViews(); }; -- cgit v0.12