1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2009-04-29
 * Description : Qt item view for images - delegate additions
 *
 * SPDX-FileCopyrightText: 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * ============================================================ */

#pragma once

// Qt includes

#include <QAbstractItemView>

// Local includes

#include "digikam_export.h"

namespace Digikam
{

class ItemViewHoverButton;

class DIGIKAM_EXPORT ItemDelegateOverlay : public QObject
{
    Q_OBJECT

public:

    explicit ItemDelegateOverlay(QObject* const parent = nullptr);
    ~ItemDelegateOverlay() override = default;

    /**
     * @brief Called when the overlay was installed and shall begin working,
     * and before it is removed and shall stop.
     * Setup your connections to view and delegate here.
     * You will be disconnected automatically on removal.
     */
    virtual void setActive(bool active);

    /**
     * @brief Only these two methods are implemented as virtual methods.
     * For all other events, connect to the view's signals.
     * There are a few signals specifically for overlays and all
     * QAbstractItemView standard signals.
     */
    virtual void mouseMoved(QMouseEvent* e, const QRect& visualRect, const QModelIndex& index);
    virtual void paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index);

    void setView(QAbstractItemView* view);
    QAbstractItemView* view()                            const;

    void setDelegate(QAbstractItemDelegate* delegate);
    QAbstractItemDelegate* delegate()                    const;

    virtual bool acceptsDelegate(QAbstractItemDelegate*) const { return true; }

Q_SIGNALS:

    void update(const QModelIndex& index);

    void requestNotification(const QModelIndex& index, const QString& message);
    void hideNotification();

protected Q_SLOTS:

    /**
     * @brief Called when any change from the delegate occurs - when the overlay is installed,
     * when size hints, styles or fonts change
     */
    virtual void visualChange();

protected:

    /**
     * @brief For the context that an overlay can affect multiple items:
     * Assuming the currently overlayed index is given.
     * Will an operation affect only the single item, or multiple?
     * If multiple, retrieve the affected selection.
     */
    bool               affectsMultiple(const QModelIndex& index)         const;
    QList<QModelIndex> affectedIndexes(const QModelIndex& index)         const;
    int                numberOfAffectedIndexes(const QModelIndex& index) const;

    /**
     * @brief Utility method
     */
    bool viewHasMultiSelection()                                         const;

protected:

    QAbstractItemView*     m_view       = nullptr;
    QAbstractItemDelegate* m_delegate   = nullptr;
};

#define REQUIRE_DELEGATE(Delegate)                                                                                                                  \
                                                                                                                                                    \
public:                                                                                                                                             \
                                                                                                                                                    \
    void setItemDelegate(Delegate* const delegate)                              { ItemDelegateOverlay::setDelegate(delegate);                     } \
    Delegate* itemDelegate() const                                              { return static_cast<Delegate*>(ItemDelegateOverlay::delegate()); } \
    virtual bool acceptsDelegate(QAbstractItemDelegate* const d) const override { return dynamic_cast<Delegate*>(d);                              } \
                                                                                                                                                    \
private:

// -------------------------------------------------------------------------------------------

class DIGIKAM_EXPORT AbstractWidgetDelegateOverlay : public ItemDelegateOverlay
{
    Q_OBJECT

public:

    /**
     * @brief This class provides functionality for using a widget in an overlay.
     * You must reimplement at least createWidget to return your widget.
     * Per default it will be shown when the cursor enters an index and hidden when left.
     * Reimplement slotEntered() and mouseMove() for more fine grained control.
     */
    explicit AbstractWidgetDelegateOverlay(QObject* const parent);

    /**
     * @brief If active is true, this will call createWidget(), initialize the widget for use,
     * and setup connections for the virtual slots.
     * If active is false, this will delete the widget and
     * disconnect all signal from model and view to this object (!)
     */
    void setActive(bool active)                     override;

protected:

    /**
     * @brief Create your widget here. When creating the object, pass parentWidget() as parent widget.
     * Ownership of the object is passed. It will be deleted in setActive(false).
     */
    virtual QWidget* createWidget() = 0;

    /**
     * @brief Called when the widget shall be hidden (mouse cursor left index, viewport, uninstalled etc.).
     * Default implementation hide()s m_widget.
     */
    virtual void hide();

    /**
     * @return the widget to be used as parent for your widget created in createWidget()
     */
    QWidget* parentWidget()                           const;

    /**
     * @return true here if you want to show the overlay for the given index.
     * The default implementation returns true.
     */
    virtual bool checkIndex(const QModelIndex& index) const;

    /**
     * @brief Called when a QEvent::Leave of the viewport is received.
     * The default implementation hide()s.
     */
    virtual void viewportLeaveEvent(QObject* obj, QEvent* event);

    /**
     * @brief Called when a QEvent::Enter resp. QEvent::Leave event for the widget is received.
     * The default implementation does nothing.
     */
    virtual void widgetEnterEvent();
    virtual void widgetLeaveEvent();

    /**
     * @brief A sample implementation for above methods
     */
    void widgetEnterNotifyMultiple(const QModelIndex& index);
    void widgetLeaveNotifyMultiple();
    virtual QString notifyMultipleMessage(const QModelIndex&, int number);

    /**
     * @brief Utility method called from slotEntered
     */
    bool checkIndexOnEnter(const QModelIndex& index)  const;

protected Q_SLOTS:

    /**
     * @brief Default implementation shows the widget iff the index is valid and checkIndex returns true.
     */
    virtual void slotEntered(const QModelIndex& index);

    /**
     * @brief Default implementations of these three slots call hide()
     */
    virtual void slotReset();
    virtual void slotViewportEntered();
    virtual void slotRowsRemoved(const QModelIndex& parent, int start, int end);
    virtual void slotLayoutChanged();

protected:

    bool eventFilter(QObject* obj, QEvent* event)           override;

protected:

    QWidget* m_widget                       = nullptr;

    bool     m_mouseButtonPressedOnWidget   = false;
};

// -------------------------------------------------------------------------------------------

class DIGIKAM_EXPORT HoverButtonDelegateOverlay : public AbstractWidgetDelegateOverlay
{
    Q_OBJECT

public:

    explicit HoverButtonDelegateOverlay(QObject* const parent);

    /**
     * @brief Will call createButton().
     */
    void setActive(bool active)                 override;

    ItemViewHoverButton* button()         const;

protected:

    /**
     * @brief Create your widget here. Pass view() as parent.
     */
    virtual ItemViewHoverButton* createButton() = 0;

    /**
     * @brief Called when a new index is entered. Reposition your button here,
     * adjust and store state.
     */
    virtual void updateButton(const QModelIndex& index) = 0;

    QWidget* createWidget()                     override;
    void visualChange()                         override;

protected Q_SLOTS:

    void slotEntered(const QModelIndex& index)  override;
    void slotReset()                            override;
};

// -------------------------------------------------------------------------------------------

class DIGIKAM_EXPORT PersistentWidgetDelegateOverlay : public AbstractWidgetDelegateOverlay
{
    Q_OBJECT

    /**
     * @brief This class offers additional / modified behavior:
     * When a "persistent" mode is entered, it will not move
     * by mouse hover, but stay and only move on mouse click.
     * If the overlay widget had focus, it will be restored on show.
     */

public:

    explicit PersistentWidgetDelegateOverlay(QObject* const parent);
    ~PersistentWidgetDelegateOverlay()                                  override;

    void setActive(bool active)                                         override;

    bool isPersistent() const;

public Q_SLOTS:

    /**
     * @brief Enters persistent mode.
     * The overlay is moved because of mouse hover.
     */
    void setPersistent(bool persistent);
    void enterPersistentMode();
    void leavePersistentMode();

    void storeFocus();

protected:

    QModelIndex index() const;

    /**
     * @brief Most overlays reimplement this slot to get the starting point
     * for repositioning a widget etc.
     * This class instead provides showOnIndex() which you shall
     * use for this purpose.
     */
    void slotEntered(const QModelIndex& index)                          override;
    void slotReset()                                                    override;
    void slotViewportEntered()                                          override;
    void slotRowsRemoved(const QModelIndex& parent, int start, int end) override;
    void slotLayoutChanged()                                            override;
    void viewportLeaveEvent(QObject* obj, QEvent* event)                override;
    void hide()                                                         override;

    /**
     * @brief Reimplement to set the focus on the correct subwidget.
     * Default implementation sets focus on widget()
     */
    virtual void setFocusOnWidget();

    /**
     * @see slotEntered()
     */
    virtual void showOnIndex(const QModelIndex& index);

    void restoreFocus();

private:

    class Private;
    Private* const d = nullptr;
};

// -------------------------------------------------------------------------------------------

class DIGIKAM_EXPORT ItemDelegateOverlayContainer
{
public:

    /**
     * @brief This is a sample implementation for
     * delegate management methods, to be inherited by a delegate.
     * Does not inherit QObject, the delegate already does.
     */

    ItemDelegateOverlayContainer()          = default;
    virtual ~ItemDelegateOverlayContainer() = default;

    QList<ItemDelegateOverlay*> overlays() const;<--- Function 'overlays()' should return member 'm_overlays' by const reference.

    void installOverlay(ItemDelegateOverlay* overlay);
    void removeOverlay(ItemDelegateOverlay* overlay);
    void setAllOverlaysActive(bool active);
    void setViewOnAllOverlays(QAbstractItemView* view);
    void removeAllOverlays();
    void mouseMoved(QMouseEvent* e, const QRect& visualRect, const QModelIndex& index);

/*
    /// Provide as signal in the delegate:
    void visualChange();
    void requestNotification(const QModelIndex& index, const QString& message);
    void hideNotification();
*/

protected:

    virtual void drawOverlays(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const;

    /// @brief Declare as slot in the derived class calling this method
    virtual void overlayDestroyed(QObject* o);

    /// @return the delegate, typically, the derived class
    virtual QAbstractItemDelegate* asDelegate() = 0;

protected:

    QList<ItemDelegateOverlay*> m_overlays;

private:

    Q_DISABLE_COPY(ItemDelegateOverlayContainer)
};

} // namespace Digikam