/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2016-2019 Ivailo Monev ** ** This file is part of the QtGui module of the Katie Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QGRAPHICSITEM_P_H #define QGRAPHICSITEM_P_H // // W A R N I N G // ------------- // // This file is not part of the Katie API. It exists for the convenience // of other Qt classes. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "qgraphicsitem.h" #include "qset.h" #include "qpixmapcache.h" #include "qgraphicsview_p.h" #include "qgraphicstransform.h" #include "qgraphicstransform_p.h" #include "qgraphicseffect_p.h" #include "qgraphicseffect.h" #include #ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE class QGraphicsItemPrivate; #ifndef QDECLARATIVELISTPROPERTY #define QDECLARATIVELISTPROPERTY template class QDeclarativeListProperty { public: typedef void (*AppendFunction)(QDeclarativeListProperty *, T*); typedef int (*CountFunction)(QDeclarativeListProperty *); typedef T *(*AtFunction)(QDeclarativeListProperty *, int); typedef void (*ClearFunction)(QDeclarativeListProperty *); QDeclarativeListProperty() : object(0), data(0), append(0), count(0), at(0), clear(0) {} QDeclarativeListProperty(QObject *o, QList &list) : object(o), data(&list), append(qlist_append), count(qlist_count), at(qlist_at), clear(qlist_clear) {} QDeclarativeListProperty(QObject *o, void *d, AppendFunction a, CountFunction c = 0, AtFunction t = 0, ClearFunction r = 0) : object(o), data(d), append(a), count(c), at(t), clear(r) {} bool operator==(const QDeclarativeListProperty &o) const { return object == o.object && data == o.data && append == o.append && count == o.count && at == o.at && clear == o.clear; } QObject *object; void *data; AppendFunction append; CountFunction count; AtFunction at; ClearFunction clear; private: static void qlist_append(QDeclarativeListProperty *p, T *v) { ((QList *)p->data)->append(v); } static int qlist_count(QDeclarativeListProperty *p) { return ((QList *)p->data)->count(); } static T *qlist_at(QDeclarativeListProperty *p, int idx) { return ((QList *)p->data)->at(idx); } static void qlist_clear(QDeclarativeListProperty *p) { return ((QList *)p->data)->clear(); } }; #endif class QGraphicsItemCache { public: QGraphicsItemCache() : allExposed(false) { } // ItemCoordinateCache only QRect boundingRect; QSize fixedSize; QPixmapCache::Key key; // DeviceCoordinateCache only struct DeviceData { DeviceData() {} QTransform lastTransform; QPoint cacheIndent; QPixmapCache::Key key; }; QMap deviceData; // List of logical exposed rects QVector exposed; bool allExposed; // Empty cache void purge(); }; class Q_GUI_EXPORT QGraphicsItemPrivate { Q_DECLARE_PUBLIC(QGraphicsItem) public: enum Extra { ExtraToolTip, ExtraCursor, ExtraCacheData, ExtraMaxDeviceCoordCacheSize, ExtraBoundingRegionGranularity }; enum AncestorFlag { NoFlag = 0, AncestorHandlesChildEvents = 0x1, AncestorClipsChildren = 0x2, AncestorIgnoresTransformations = 0x4, AncestorFiltersChildEvents = 0x8 }; inline QGraphicsItemPrivate() : z(0), opacity(1.), scene(0), parent(0), transformData(0), graphicsEffect(0), index(-1), siblingIndex(-1), itemDepth(-1), focusProxy(0), subFocusItem(0), focusScopeItem(0), panelModality(QGraphicsItem::NonModal), visible(true), explicitlyHidden(false), enabled(true), explicitlyDisabled(false), selected(false), acceptsHover(false), acceptDrops(false), isMemberOfGroup(false), handlesChildEvents(false), itemDiscovered(false), hasCursor(false), hasBoundingRegionGranularity(false), isWidget(false), dirty(false), dirtyChildren(false), localCollisionHack(false), inSetPosHelper(false), needSortChildren(false), allChildrenDirty(false), fullUpdatePending(false), dirtyChildrenBoundingRect(true), paintedViewBoundingRectsNeedRepaint(false), dirtySceneTransform(true), geometryChanged(true), inDestructor(false), isObject(false), ignoreVisible(false), ignoreOpacity(false), acceptTouchEvents(false), acceptedTouchBeginEvent(false), filtersDescendantEvents(false), sceneTransformTranslateOnly(false), notifyBoundingRectChanged(false), notifyInvalidated(false), mouseSetsFocus(true), explicitActivate(false), wantsActive(false), holesInSiblingIndex(false), sequentialOrdering(true), updateDueToGraphicsEffect(false), scenePosDescendants(false), pendingPolish(false), mayHaveChildWithGraphicsEffect(false), isDeclarativeItem(false), sendParentChangeNotification(false), cacheMode(QGraphicsItem::NoCache), acceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton | Qt::XButton1 | Qt::XButton2), flags(0), ancestorFlags(0), globalStackingOrder(-1), q_ptr(0) { } inline virtual ~QGraphicsItemPrivate() { } static const QGraphicsItemPrivate *get(const QGraphicsItem *item) { return item->d_ptr.data(); } static QGraphicsItemPrivate *get(QGraphicsItem *item) { return item->d_ptr.data(); } void updateChildWithGraphicsEffectFlagRecursively(); void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag, AncestorFlag flag = NoFlag, bool enabled = false, bool root = true); void updateAncestorFlags(); void setIsMemberOfGroup(bool enabled); void remapItemPos(QEvent *event, QGraphicsItem *item); QPointF genericMapFromScene(const QPointF &pos, const QWidget *viewport) const; inline bool itemIsUntransformable() const { return (flags & QGraphicsItem::ItemIgnoresTransformations) || (ancestorFlags & AncestorIgnoresTransformations); } void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const; void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const; virtual void updateSceneTransformFromParent(); static bool movableAncestorIsSelected(const QGraphicsItem *item); virtual void setPosHelper(const QPointF &pos); void setTransformHelper(const QTransform &transform); void prependGraphicsTransform(QGraphicsTransform *t); void appendGraphicsTransform(QGraphicsTransform *t); void setVisibleHelper(bool newVisible, bool explicitly, bool update = true); void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; virtual void transformChanged() {} int depth() const; #ifndef QT_NO_GRAPHICSEFFECT enum InvalidateReason { OpacityChanged }; void invalidateParentGraphicsEffectsRecursively(); void invalidateChildGraphicsEffectsRecursively(InvalidateReason reason); #endif //QT_NO_GRAPHICSEFFECT void invalidateDepthRecursively(); void resolveDepth(); void addChild(QGraphicsItem *child); void removeChild(QGraphicsItem *child); QDeclarativeListProperty childrenList(); void setParentItemHelper(QGraphicsItem *parent, const QVariant *newParentVariant, const QVariant *thisPointerVariant); void childrenBoundingRectHelper(QTransform *x, QRectF *rect, QGraphicsItem *topMostEffectItem); void initStyleOption(QStyleOptionGraphicsItem *option, const QTransform &worldTransform, const QRegion &exposedRegion, bool allItems = false) const; QRectF effectiveBoundingRect(QGraphicsItem *topMostEffectItem = 0) const; QRectF sceneEffectiveBoundingRect() const; QRectF effectiveBoundingRect(const QRectF &rect) const; virtual void resolveFont(uint inheritedMask) { for (int i = 0; i < children.size(); ++i) children.at(i)->d_ptr->resolveFont(inheritedMask); } virtual void resolvePalette(uint inheritedMask) { for (int i = 0; i < children.size(); ++i) children.at(i)->d_ptr->resolveFont(inheritedMask); } virtual bool isProxyWidget() const; inline QVariant extra(Extra type) const { for (int i = 0; i < extras.size(); ++i) { const ExtraStruct &extra = extras.at(i); if (extra.type == type) return extra.value; } return QVariant(); } inline void setExtra(Extra type, const QVariant &value) { int index = -1; for (int i = 0; i < extras.size(); ++i) { if (extras.at(i).type == type) { index = i; break; } } if (index == -1) { extras << ExtraStruct(type, value); } else { extras[index].value = value; } } inline void unsetExtra(Extra type) { for (int i = 0; i < extras.size(); ++i) { if (extras.at(i).type == type) { extras.removeAt(i); return; } } } struct ExtraStruct { ExtraStruct(Extra type, QVariant value) : type(type), value(value) { } Extra type; QVariant value; bool operator<(Extra extra) const { return type < extra; } }; QList extras; QGraphicsItemCache *maybeExtraItemCache() const; QGraphicsItemCache *extraItemCache() const; void removeExtraItemCache(); void updatePaintedViewBoundingRects(bool updateChildren); void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem); inline void ensureSceneTransform() { QGraphicsItem *that = q_func(); ensureSceneTransformRecursive(&that); } inline bool hasTranslateOnlySceneTransform() { ensureSceneTransform(); return sceneTransformTranslateOnly; } inline void invalidateChildrenSceneTransform() { for (int i = 0; i < children.size(); ++i) children.at(i)->d_ptr->dirtySceneTransform = true; } inline qreal calcEffectiveOpacity() const { qreal o = opacity; QGraphicsItem *p = parent; int myFlags = flags; while (p) { int parentFlags = p->d_ptr->flags; // If I have a parent, and I don't ignore my parent's opacity, and my // parent propagates to me, then combine my local opacity with my parent's // effective opacity into my effective opacity. if ((myFlags & QGraphicsItem::ItemIgnoresParentOpacity) || (parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { break; } o *= p->d_ptr->opacity; p = p->d_ptr->parent; myFlags = parentFlags; } return o; } inline bool isOpacityNull() const { return (opacity < qreal(0.001)); } static inline bool isOpacityNull(qreal opacity) { return (opacity < qreal(0.001)); } inline bool isFullyTransparent() const { if (isOpacityNull()) return true; if (!parent) return false; return isOpacityNull(calcEffectiveOpacity()); } inline qreal effectiveOpacity() const { if (!parent || !opacity) return opacity; return calcEffectiveOpacity(); } inline qreal combineOpacityFromParent(qreal parentOpacity) const { if (parent && !(flags & QGraphicsItem::ItemIgnoresParentOpacity) && !(parent->d_ptr->flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { return parentOpacity * opacity; } return opacity; } inline bool childrenCombineOpacity() const { if (!children.size()) return true; if (flags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren) return false; for (int i = 0; i < children.size(); ++i) { if (children.at(i)->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity) return false; } return true; } inline bool childrenClippedToShape() const { return (flags & QGraphicsItem::ItemClipsChildrenToShape) || children.isEmpty(); } inline bool isInvisible() const { return !visible || (childrenCombineOpacity() && isFullyTransparent()); } inline void markParentDirty(bool updateBoundingRect = false); void setFocusHelper(Qt::FocusReason focusReason, bool climb, bool focusFromHide); void clearFocusHelper(bool giveFocusToParent); void setSubFocus(QGraphicsItem *rootItem = 0, QGraphicsItem *stopItem = 0); void clearSubFocus(QGraphicsItem *rootItem = 0, QGraphicsItem *stopItem = 0); void resetFocusProxy(); virtual void subFocusItemChange(); virtual void focusScopeItemChange(bool isSubFocusItem); static void children_append(QDeclarativeListProperty *list, QGraphicsObject *item); static int children_count(QDeclarativeListProperty *list); static QGraphicsObject *children_at(QDeclarativeListProperty *list, int); static void children_clear(QDeclarativeListProperty *list); inline QTransform transformToParent() const; inline void ensureSortedChildren(); static inline bool insertionOrder(QGraphicsItem *a, QGraphicsItem *b); void ensureSequentialSiblingIndex(); inline void sendScenePosChange(); virtual void siblingOrderChange(); // Private Properties virtual qreal width() const; virtual void setWidth(qreal); virtual void resetWidth(); virtual qreal height() const; virtual void setHeight(qreal); virtual void resetHeight(); QRectF childrenBoundingRect; QRectF needsRepaint; QMap paintedViewBoundingRects; QPointF pos; qreal z; qreal opacity; QGraphicsScene *scene; QGraphicsItem *parent; QList children; struct TransformData; TransformData *transformData; QGraphicsEffect *graphicsEffect; QTransform sceneTransform; int index; int siblingIndex; int itemDepth; // Lazily calculated when calling depth(). QGraphicsItem *focusProxy; QList focusProxyRefs; QGraphicsItem *subFocusItem; QGraphicsItem *focusScopeItem; QGraphicsItem::PanelModality panelModality; #ifndef QT_NO_GESTURES QMap gestureContext; #endif bool visible; bool explicitlyHidden; bool enabled; bool explicitlyDisabled; bool selected; bool acceptsHover; bool acceptDrops; bool isMemberOfGroup; bool handlesChildEvents; bool itemDiscovered; bool hasCursor; bool hasBoundingRegionGranularity; bool isWidget; bool dirty; bool dirtyChildren; bool localCollisionHack; bool inSetPosHelper; bool needSortChildren; bool allChildrenDirty; bool fullUpdatePending; bool dirtyChildrenBoundingRect; bool paintedViewBoundingRectsNeedRepaint; bool dirtySceneTransform; bool geometryChanged; bool inDestructor; bool isObject; bool ignoreVisible; bool ignoreOpacity; bool acceptTouchEvents; bool acceptedTouchBeginEvent; bool filtersDescendantEvents; bool sceneTransformTranslateOnly; bool notifyBoundingRectChanged; bool notifyInvalidated; bool mouseSetsFocus; bool explicitActivate; bool wantsActive; bool holesInSiblingIndex; bool sequentialOrdering; bool updateDueToGraphicsEffect; bool scenePosDescendants; bool pendingPolish; bool mayHaveChildWithGraphicsEffect; bool isDeclarativeItem ; bool sendParentChangeNotification; QGraphicsItem::CacheMode cacheMode; Qt::MouseButtons acceptedMouseButtons; QGraphicsItem::GraphicsItemFlags flags; uint ancestorFlags; // Optional stacking order int globalStackingOrder; QGraphicsItem *q_ptr; }; struct QGraphicsItemPrivate::TransformData { QTransform transform; qreal scale; qreal rotation; qreal xOrigin; qreal yOrigin; QList graphicsTransforms; bool onlyTransform; TransformData() : scale(1.0), rotation(0.0), xOrigin(0.0), yOrigin(0.0), onlyTransform(true) { } QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const { if (onlyTransform) { if (!postmultiplyTransform || postmultiplyTransform->isIdentity()) return transform; if (transform.isIdentity()) return *postmultiplyTransform; return transform * *postmultiplyTransform; } QTransform x(transform); if (!graphicsTransforms.isEmpty()) { QMatrix4x4 m; for (int i = 0; i < graphicsTransforms.size(); ++i) graphicsTransforms.at(i)->applyTo(&m); x *= m.toTransform(); } x.translate(xOrigin, yOrigin); x.rotate(rotation); x.scale(scale, scale); x.translate(-xOrigin, -yOrigin); if (postmultiplyTransform) x *= *postmultiplyTransform; return x; } }; struct QGraphicsItemPaintInfo { inline QGraphicsItemPaintInfo(const QTransform *const xform1, const QTransform *const xform2, const QTransform *const xform3, QRegion *r, QWidget *w, QStyleOptionGraphicsItem *opt, QPainter *p, qreal o, bool b1, bool b2) : viewTransform(xform1), transformPtr(xform2), effectTransform(xform3), exposedRegion(r), widget(w), option(opt), painter(p), opacity(o), wasDirtySceneTransform(b1), drawItem(b2) {} const QTransform *viewTransform; const QTransform *transformPtr; const QTransform *effectTransform; QRegion *exposedRegion; QWidget *widget; QStyleOptionGraphicsItem *option; QPainter *painter; qreal opacity; bool wasDirtySceneTransform; bool drawItem; }; #ifndef QT_NO_GRAPHICSEFFECT class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate { public: QGraphicsItemEffectSourcePrivate(QGraphicsItem *i) : QGraphicsEffectSourcePrivate(), item(i), info(0) {} inline void detach() { item->d_ptr->graphicsEffect = 0; item->prepareGeometryChange(); } inline const QGraphicsItem *graphicsItem() const { return item; } inline const QWidget *widget() const { return 0; } inline void update() { item->d_ptr->updateDueToGraphicsEffect = true; item->update(); item->d_ptr->updateDueToGraphicsEffect = false; } inline void effectBoundingRectChanged() { item->prepareGeometryChange(); } inline bool isPixmap() const { return item->type() == QGraphicsPixmapItem::Type && !(item->flags() & QGraphicsItem::ItemIsSelectable) && item->d_ptr->children.size() == 0; //|| (item->d_ptr->isObject && qobject_cast(q_func())); } inline const QStyleOption *styleOption() const { return info ? info->option : 0; } inline QRect deviceRect() const { if (!info || !info->widget) { qWarning("QGraphicsEffectSource::deviceRect: Not yet implemented, lacking device context"); return QRect(); } return info->widget->rect(); } QRectF boundingRect(Qt::CoordinateSystem system) const; void draw(QPainter *); QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset, QGraphicsEffect::PixmapPadMode mode) const; QRect paddedEffectRect(Qt::CoordinateSystem system, QGraphicsEffect::PixmapPadMode mode, const QRectF &sourceRect, bool *unpadded = 0) const; QGraphicsItem *item; QGraphicsItemPaintInfo *info; QTransform lastEffectTransform; }; #endif //QT_NO_GRAPHICSEFFECT /*! Returns true if \a item1 is on top of \a item2. The items don't need to be siblings. \internal */ inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2) { // Siblings? Just check their z-values. const QGraphicsItemPrivate *d1 = item1->d_ptr.data(); const QGraphicsItemPrivate *d2 = item2->d_ptr.data(); if (d1->parent == d2->parent) return qt_closestLeaf(item1, item2); // Find common ancestor, and each item's ancestor closest to the common // ancestor. int item1Depth = d1->depth(); int item2Depth = d2->depth(); const QGraphicsItem *p = item1; const QGraphicsItem *t1 = item1; while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { if (p == item2) { // item2 is one of item1's ancestors; item1 is on top return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); } t1 = p; --item1Depth; } p = item2; const QGraphicsItem *t2 = item2; while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { if (p == item1) { // item1 is one of item2's ancestors; item1 is not on top return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); } t2 = p; --item2Depth; } // item1Ancestor is now at the same level as item2Ancestor, but not the same. const QGraphicsItem *p1 = t1; const QGraphicsItem *p2 = t2; while (t1 && t1 != t2) { p1 = t1; p2 = t2; t1 = t1->d_ptr->parent; t2 = t2->d_ptr->parent; } // in case we have a common ancestor, we compare the immediate children in the ancestor's path. // otherwise we compare the respective items' topLevelItems directly. return qt_closestLeaf(p1, p2); } /*! Returns true if \a item2 is on top of \a item1. The items don't need to be siblings. \internal */ inline bool qt_closestItemLast(const QGraphicsItem *item1, const QGraphicsItem *item2) { return qt_closestItemFirst(item2, item1); } /*! \internal */ inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) { // Return true if sibling item1 is on top of item2. const QGraphicsItemPrivate *d1 = item1->d_ptr.data(); const QGraphicsItemPrivate *d2 = item2->d_ptr.data(); bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; if (f1 != f2) return f2; if (d1->z != d2->z) return d1->z > d2->z; return d1->siblingIndex > d2->siblingIndex; } /*! \internal */ inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) { return qt_closestLeaf(item2, item1); } /* return the full transform of the item to the parent. This include the position and all the transform data */ inline QTransform QGraphicsItemPrivate::transformToParent() const { QTransform matrix; combineTransformToParent(&matrix); return matrix; } /*! \internal */ inline void QGraphicsItemPrivate::ensureSortedChildren() { if (needSortChildren) { needSortChildren = false; sequentialOrdering = true; if (children.isEmpty()) return; qSort(children.begin(), children.end(), qt_notclosestLeaf); for (int i = 0; i < children.size(); ++i) { if (children.at(i)->d_ptr->siblingIndex != i) { sequentialOrdering = false; break; } } } } /*! \internal */ inline bool QGraphicsItemPrivate::insertionOrder(QGraphicsItem *a, QGraphicsItem *b) { return a->d_ptr->siblingIndex < b->d_ptr->siblingIndex; } /*! \internal */ inline void QGraphicsItemPrivate::markParentDirty(bool updateBoundingRect) { QGraphicsItemPrivate *parentp = this; #ifndef QT_NO_GRAPHICSEFFECT if (updateBoundingRect && parentp->graphicsEffect && !parentp->inSetPosHelper) { parentp->notifyInvalidated = true; static_cast(parentp->graphicsEffect->d_func() ->source->d_func())->invalidateCache(); } #endif while (parentp->parent) { parentp = parentp->parent->d_ptr.data(); parentp->dirtyChildren = 1; if (updateBoundingRect) { parentp->dirtyChildrenBoundingRect = true; // ### Only do this if the parent's effect applies to the entire subtree. parentp->notifyBoundingRectChanged = true; } #ifndef QT_NO_GRAPHICSEFFECT if (parentp->graphicsEffect) { if (updateBoundingRect) { static_cast(parentp->graphicsEffect->d_func() ->source->d_func())->invalidateCache(); parentp->notifyInvalidated = true; } if (parentp->scene && parentp->graphicsEffect->isEnabled()) { parentp->dirty = true; parentp->fullUpdatePending = true; } } #endif } } QT_END_NAMESPACE #endif // QT_NO_GRAPHICSVIEW #endif