File indexing completed on 2025-04-27 03:58:28

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-04-29
0007  * Description : Qt item view for images - delegate additions
0008  *
0009  * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #ifndef DIGIKAM_ITEM_DELEGATE_OVERLAY_H
0016 #define DIGIKAM_ITEM_DELEGATE_OVERLAY_H
0017 
0018 // Qt includes
0019 
0020 #include <QAbstractItemView>
0021 
0022 // Local includes
0023 
0024 #include "digikam_export.h"
0025 
0026 namespace Digikam
0027 {
0028 
0029 class ItemViewHoverButton;
0030 
0031 class DIGIKAM_EXPORT ItemDelegateOverlay : public QObject
0032 {
0033     Q_OBJECT
0034 
0035 public:
0036 
0037     explicit ItemDelegateOverlay(QObject* const parent = nullptr);
0038     ~ItemDelegateOverlay() override;
0039 
0040     /**
0041      * Called when the overlay was installed and shall begin working,
0042      * and before it is removed and shall stop.
0043      * Setup your connections to view and delegate here.
0044      * You will be disconnected automatically on removal.
0045      */
0046     virtual void setActive(bool active);
0047 
0048     /**
0049      * Only these two methods are implemented as virtual methods.
0050      * For all other events, connect to the view's signals.
0051      * There are a few signals specifically for overlays and all
0052      * QAbstractItemView standard signals.
0053      */
0054     virtual void mouseMoved(QMouseEvent* e, const QRect& visualRect, const QModelIndex& index);
0055     virtual void paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index);
0056 
0057     void setView(QAbstractItemView* view);
0058     QAbstractItemView* view()                            const;
0059 
0060     void setDelegate(QAbstractItemDelegate* delegate);
0061     QAbstractItemDelegate* delegate()                    const;
0062 
0063     virtual bool acceptsDelegate(QAbstractItemDelegate*) const { return true; }
0064 
0065 Q_SIGNALS:
0066 
0067     void update(const QModelIndex& index);
0068 
0069     void requestNotification(const QModelIndex& index, const QString& message);
0070     void hideNotification();
0071 
0072 protected Q_SLOTS:
0073 
0074     /**
0075      * Called when any change from the delegate occurs - when the overlay is installed,
0076      * when size hints, styles or fonts change
0077      */
0078     virtual void visualChange();
0079 
0080 protected:
0081 
0082     /**
0083      * For the context that an overlay can affect multiple items:
0084      * Assuming the currently overlayed index is given.
0085      * Will an operation affect only the single item, or multiple?
0086      * If multiple, retrieve the affected selection.
0087      */
0088     bool               affectsMultiple(const QModelIndex& index)         const;
0089     QList<QModelIndex> affectedIndexes(const QModelIndex& index)         const;
0090     int                numberOfAffectedIndexes(const QModelIndex& index) const;
0091 
0092     /**
0093      * Utility method
0094      */
0095     bool viewHasMultiSelection()                                         const;
0096 
0097 protected:
0098 
0099     QAbstractItemView*     m_view;
0100     QAbstractItemDelegate* m_delegate;
0101 };
0102 
0103 #define REQUIRE_DELEGATE(Delegate)                                                                                                           \
0104                                                                                                                                              \
0105 public:                                                                                                                                      \
0106                                                                                                                                              \
0107     void setDelegate(Delegate* delegate)                                 { ItemDelegateOverlay::setDelegate(delegate);                     } \
0108     Delegate* delegate() const                                           { return static_cast<Delegate*>(ItemDelegateOverlay::delegate()); } \
0109     virtual bool acceptsDelegate(QAbstractItemDelegate*d) const override { return dynamic_cast<Delegate*>(d);                              } \
0110                                                                                                                                              \
0111 private:
0112 
0113 // -------------------------------------------------------------------------------------------
0114 
0115 class DIGIKAM_EXPORT AbstractWidgetDelegateOverlay : public ItemDelegateOverlay
0116 {
0117     Q_OBJECT
0118 
0119 public:
0120 
0121     /**
0122      * This class provides functionality for using a widget in an overlay.
0123      * You must reimplement at least createWidget to return your widget.
0124      * Per default it will be shown when the cursor enters an index and hidden when left.
0125      * Reimplement slotEntered() and mouseMove() for more fine grained control.
0126      */
0127     explicit AbstractWidgetDelegateOverlay(QObject* const parent);
0128 
0129     /**
0130      * If active is true, this will call createWidget(), initialize the widget for use,
0131      * and setup connections for the virtual slots.
0132      * If active is false, this will delete the widget and
0133      * disconnect all signal from model and view to this object (!)
0134      */
0135     void setActive(bool active)                     override;
0136 
0137 protected:
0138 
0139     /**
0140      * Create your widget here. When creating the object, pass parentWidget() as parent widget.
0141      * Ownership of the object is passed. It will be deleted in setActive(false).
0142      */
0143     virtual QWidget* createWidget() = 0;
0144 
0145     /**
0146      * Called when the widget shall be hidden (mouse cursor left index, viewport, uninstalled etc.).
0147      * Default implementation hide()s m_widget.
0148      */
0149     virtual void hide();
0150 
0151     /**
0152      * Returns the widget to be used as parent for your widget created in createWidget()
0153      */
0154     QWidget* parentWidget()                           const;
0155 
0156     /**
0157      * Return true here if you want to show the overlay for the given index.
0158      * The default implementation returns true.
0159      */
0160     virtual bool checkIndex(const QModelIndex& index) const;
0161 
0162     /**
0163      * Called when a QEvent::Leave of the viewport is received.
0164      * The default implementation hide()s.
0165      */
0166     virtual void viewportLeaveEvent(QObject* obj, QEvent* event);
0167 
0168     /**
0169      * Called when a QEvent::Enter resp. QEvent::Leave event for the widget is received.
0170      * The default implementation does nothing.
0171      */
0172     virtual void widgetEnterEvent();
0173     virtual void widgetLeaveEvent();
0174 
0175     /**
0176      * A sample implementation for above methods
0177      */
0178     void widgetEnterNotifyMultiple(const QModelIndex& index);
0179     void widgetLeaveNotifyMultiple();
0180     virtual QString notifyMultipleMessage(const QModelIndex&, int number);
0181 
0182     /**
0183      * Utility method called from slotEntered
0184      */
0185     bool checkIndexOnEnter(const QModelIndex& index)  const;
0186 
0187 protected Q_SLOTS:
0188 
0189     /**
0190      * Default implementation shows the widget iff the index is valid and checkIndex returns true.
0191      */
0192     virtual void slotEntered(const QModelIndex& index);
0193 
0194     /**
0195      * Default implementations of these three slots call hide()
0196      */
0197     virtual void slotReset();
0198     virtual void slotViewportEntered();
0199     virtual void slotRowsRemoved(const QModelIndex& parent, int start, int end);
0200     virtual void slotLayoutChanged();
0201 
0202 protected:
0203 
0204     bool eventFilter(QObject* obj, QEvent* event)           override;
0205 
0206 protected:
0207 
0208     QWidget* m_widget;
0209 
0210     bool     m_mouseButtonPressedOnWidget;
0211 };
0212 
0213 // -------------------------------------------------------------------------------------------
0214 
0215 class DIGIKAM_EXPORT HoverButtonDelegateOverlay : public AbstractWidgetDelegateOverlay
0216 {
0217     Q_OBJECT
0218 
0219 public:
0220 
0221     explicit HoverButtonDelegateOverlay(QObject* const parent);
0222 
0223     /**
0224      * Will call createButton().
0225      */
0226     void setActive(bool active)                 override;
0227 
0228     ItemViewHoverButton* button()                 const;
0229 
0230 protected:
0231 
0232     /**
0233      * Create your widget here. Pass view() as parent.
0234      */
0235     virtual ItemViewHoverButton* createButton() = 0;
0236 
0237     /**
0238      * Called when a new index is entered. Reposition your button here,
0239      * adjust and store state.
0240      */
0241     virtual void updateButton(const QModelIndex& index) = 0;
0242 
0243     QWidget* createWidget()                     override;
0244     void visualChange()                         override;
0245 
0246 protected Q_SLOTS:
0247 
0248     void slotEntered(const QModelIndex& index)  override;
0249     void slotReset()                            override;
0250 };
0251 
0252 // -------------------------------------------------------------------------------------------
0253 
0254 class DIGIKAM_EXPORT PersistentWidgetDelegateOverlay : public AbstractWidgetDelegateOverlay
0255 {
0256     Q_OBJECT
0257 
0258     /**
0259      * This class offers additional / modified behavior:
0260      * When a "persistent" mode is entered, it will not move
0261      * by mouse hover, but stay and only move on mouse click.
0262      * If the overlay widget had focus, it will be restored on show.
0263      */
0264 
0265 public:
0266 
0267     explicit PersistentWidgetDelegateOverlay(QObject* const parent);
0268     ~PersistentWidgetDelegateOverlay() override;
0269 
0270     void setActive(bool active)                                         override;
0271 
0272     bool isPersistent() const;
0273 
0274 public Q_SLOTS:
0275 
0276     /**
0277      * Enters persistent mode.
0278      * The overlay is moved because of mouse hover.
0279      */
0280     void setPersistent(bool persistent);
0281     void enterPersistentMode();
0282     void leavePersistentMode();
0283 
0284     void storeFocus();
0285 
0286 protected:
0287 
0288     QModelIndex index() const;
0289 
0290     /**
0291      * Most overlays reimplement this slot to get the starting point
0292      * for repositioning a widget etc.
0293      * This class instead provides showOnIndex() which you shall
0294      * use for this purpose.
0295      */
0296     void slotEntered(const QModelIndex& index)                          override;
0297     void slotReset()                                                    override;
0298     void slotViewportEntered()                                          override;
0299     void slotRowsRemoved(const QModelIndex& parent, int start, int end) override;
0300     void slotLayoutChanged()                                            override;
0301     void viewportLeaveEvent(QObject* obj, QEvent* event)                override;
0302     void hide()                                                         override;
0303 
0304     /**
0305      * Reimplement to set the focus on the correct subwidget.
0306      * Default implementation sets focus on widget()
0307      */
0308     virtual void setFocusOnWidget();
0309 
0310     /// see slotEntered()
0311     virtual void showOnIndex(const QModelIndex& index);
0312 
0313     void restoreFocus();
0314 
0315 private:
0316 
0317     class Private;
0318     Private* const d;
0319 };
0320 
0321 // -------------------------------------------------------------------------------------------
0322 
0323 class DIGIKAM_EXPORT ItemDelegateOverlayContainer
0324 {
0325 public:
0326 
0327     /**
0328      * This is a sample implementation for
0329      * delegate management methods, to be inherited by a delegate.
0330      * Does not inherit QObject, the delegate already does.
0331      */
0332 
0333     ItemDelegateOverlayContainer()          = default;
0334     virtual ~ItemDelegateOverlayContainer() = default;
0335 
0336     QList<ItemDelegateOverlay*> overlays() const;
0337 
0338     void installOverlay(ItemDelegateOverlay* overlay);
0339     void removeOverlay(ItemDelegateOverlay* overlay);
0340     void setAllOverlaysActive(bool active);
0341     void setViewOnAllOverlays(QAbstractItemView* view);
0342     void removeAllOverlays();
0343     void mouseMoved(QMouseEvent* e, const QRect& visualRect, const QModelIndex& index);
0344 
0345 /*
0346     /// Provide as signal in the delegate:
0347     void visualChange();
0348     void requestNotification(const QModelIndex& index, const QString& message);
0349     void hideNotification();
0350 */
0351 
0352 protected:
0353 
0354     virtual void drawOverlays(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const;
0355 
0356     /// Declare as slot in the derived class calling this method
0357     virtual void overlayDestroyed(QObject* o);
0358 
0359     /// Returns the delegate, typically, the derived class
0360     virtual QAbstractItemDelegate* asDelegate() = 0;
0361 
0362 protected:
0363 
0364     QList<ItemDelegateOverlay*> m_overlays;
0365 
0366 private:
0367 
0368     Q_DISABLE_COPY(ItemDelegateOverlayContainer)
0369 };
0370 
0371 } // namespace Digikam
0372 
0373 #endif // DIGIKAM_ITEM_DELEGATE_OVERLAY_H