| 1 | /* |
| 2 | This file is part of the KDE libraries |
| 3 | SPDX-FileCopyrightText: 2006, 2007 Andreas Hartmetz <ahartmetz@gmail.com> |
| 4 | SPDX-FileCopyrightText: 2008 Urs Wolfer <uwolfer@kde.org> |
| 5 | |
| 6 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #ifndef KEXTENDABLEITEMDELEGATE_H |
| 10 | #define KEXTENDABLEITEMDELEGATE_H |
| 11 | |
| 12 | #include <QStyledItemDelegate> |
| 13 | #include <memory> |
| 14 | |
| 15 | #include <kitemviews_export.h> |
| 16 | |
| 17 | class QAbstractItemView; |
| 18 | |
| 19 | /*! |
| 20 | * \class KExtendableItemDelegate |
| 21 | * \inmodule KItemViews |
| 22 | * |
| 23 | * \brief This delegate makes it possible to display an arbitrary QWidget ("extender") that spans all columns below a line of items. |
| 24 | * |
| 25 | * The extender will logically belong to a column in the row above it. |
| 26 | * |
| 27 | * It is your responsibility to devise a way to trigger extension and contraction of items, by calling |
| 28 | * extendItem() and contractItem(). You can e.g. reimplement itemActivated() and similar functions. |
| 29 | * |
| 30 | * \warning extendItem() reparents the provided widget \a extender to the |
| 31 | * viewport of the itemview it belongs to. The \a extender is destroyed when |
| 32 | * you call contractItem() for the associated index. If you fail to do that |
| 33 | * and the associated item gets deleted you're in trouble. It remains as a |
| 34 | * visible artefact in your treeview. Additionally when closing your |
| 35 | * application you get an assertion failure from KExtendableItemDelegate. Make |
| 36 | * sure that you always call contractItem for indices before you delete them. |
| 37 | * |
| 38 | * \since 4.1 |
| 39 | */ |
| 40 | class KITEMVIEWS_EXPORT KExtendableItemDelegate : public QStyledItemDelegate |
| 41 | { |
| 42 | Q_OBJECT |
| 43 | |
| 44 | public: |
| 45 | /*! |
| 46 | * \value ShowExtensionIndicatorRole |
| 47 | */ |
| 48 | enum auxDataRoles { |
| 49 | ShowExtensionIndicatorRole = Qt::UserRole + 200, |
| 50 | }; |
| 51 | |
| 52 | /*! |
| 53 | * Create a new KExtendableItemDelegate that belongs to \a parent. In contrast to generic |
| 54 | * QAbstractItemDelegates, an instance of this class can only ever be the delegate for one |
| 55 | * instance of af QAbstractItemView subclass. |
| 56 | */ |
| 57 | KExtendableItemDelegate(QAbstractItemView *parent); |
| 58 | ~KExtendableItemDelegate() override; |
| 59 | |
| 60 | QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; |
| 61 | |
| 62 | void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; |
| 63 | |
| 64 | /*! |
| 65 | * Insert the \a extender for item at \a index into the view. |
| 66 | * If you need a parent for the extender at construction time, use the itemview's viewport(). |
| 67 | * The delegate takes ownership of the extender; the extender will also be reparented and |
| 68 | * resized to the viewport. |
| 69 | */ |
| 70 | void extendItem(QWidget *extender, const QModelIndex &index); |
| 71 | |
| 72 | /*! |
| 73 | * Remove the extender of item at \a index from the view. The extender widget |
| 74 | * will be deleted. |
| 75 | */ |
| 76 | void contractItem(const QModelIndex &index); |
| 77 | |
| 78 | /*! |
| 79 | * Close all extenders and delete all extender widgets. |
| 80 | */ |
| 81 | void contractAll(); |
| 82 | |
| 83 | /*! |
| 84 | * Return whether there is an extender that belongs to \a index. |
| 85 | */ |
| 86 | bool isExtended(const QModelIndex &index) const; |
| 87 | |
| 88 | /*! |
| 89 | * Reimplement this function to adjust the internal geometry of the extender. |
| 90 | * The external geometry of the extender will be set by the delegate. |
| 91 | */ |
| 92 | virtual void updateExtenderGeometry(QWidget *extender, const QStyleOptionViewItem &option, const QModelIndex &index) const; |
| 93 | |
| 94 | Q_SIGNALS: |
| 95 | /*! |
| 96 | * This signal indicates that the item at \a index was extended with \a extender. |
| 97 | */ |
| 98 | void extenderCreated(QWidget *extender, const QModelIndex &index); |
| 99 | |
| 100 | /*! |
| 101 | * This signal indicates that the \a extender belonging to \a index has emitted the destroyed() signal. |
| 102 | */ |
| 103 | void extenderDestroyed(QWidget *extender, const QModelIndex &index); |
| 104 | |
| 105 | protected: |
| 106 | /*! |
| 107 | * Reimplement this function to fine-tune the position of the extender. \a option.rect will be a rectangle |
| 108 | * that is as wide as the viewport and as high as the usual item height plus the extender size hint's height. |
| 109 | * Its upper left corner will be at the upper left corner of the usual item. |
| 110 | * You can place the returned rectangle of this function anywhere inside that area. |
| 111 | */ |
| 112 | QRect extenderRect(QWidget *extender, const QStyleOptionViewItem &option, const QModelIndex &index) const; |
| 113 | |
| 114 | /*! |
| 115 | * The pixmap that is displayed to extend an item. \a pixmap must have the same size as the pixmap in setContractPixmap. |
| 116 | */ |
| 117 | void setExtendPixmap(const QPixmap &pixmap); |
| 118 | |
| 119 | /*! |
| 120 | * The pixmap that is displayed to contract an item. \a pixmap must have the same size as the pixmap in setExtendPixmap. |
| 121 | */ |
| 122 | void setContractPixmap(const QPixmap &pixmap); |
| 123 | |
| 124 | /*! |
| 125 | * Return the pixmap that is displayed to extend an item. |
| 126 | */ |
| 127 | QPixmap extendPixmap(); |
| 128 | |
| 129 | /*! |
| 130 | * Return the pixmap that is displayed to contract an item. |
| 131 | */ |
| 132 | QPixmap contractPixmap(); |
| 133 | |
| 134 | private: |
| 135 | friend class KExtendableItemDelegatePrivate; |
| 136 | std::unique_ptr<class KExtendableItemDelegatePrivate> const d; |
| 137 | |
| 138 | Q_PRIVATE_SLOT(d, void _k_extenderDestructionHandler(QObject *destroyed)) |
| 139 | Q_PRIVATE_SLOT(d, void _k_verticalScroll()) |
| 140 | }; |
| 141 | #endif // KEXTENDABLEITEMDELEGATE_H |
| 142 | |