┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/kitemviews/kitemlistcontroller.h
blob: 48da07206df1231647b855a9aee48b7b4a23672b (plain)
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
373
374
375
376
377
378
379
380
381
382
/*
 * SPDX-FileCopyrightText: 2011 Peter Penz <[email protected]>
 *
 * Based on the Itemviews NG project from Trolltech Labs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef KITEMLISTCONTROLLER_H
#define KITEMLISTCONTROLLER_H

#include <optional>

#include "dolphin_export.h"
#include "kitemset.h"

#include <QObject>
#include <QPointF>
#include <QScroller>

class QTimer;
class KItemModelBase;
class KItemListKeyboardSearchManager;
class KItemListSelectionManager;
class KItemListView;
class KItemListWidget;
class QContextMenuEvent;
class QGestureEvent;
class QGraphicsSceneHoverEvent;
class QGraphicsSceneDragDropEvent;
class QGraphicsSceneMouseEvent;
class QGraphicsSceneResizeEvent;
class QGraphicsSceneWheelEvent;
class QInputMethodEvent;
class QKeyEvent;
class QTapGesture;
class QTransform;
class QTouchEvent;

/**
 * @brief Controls the view, model and selection of an item-list.
 *
 * For a working item-list it is mandatory to set a compatible view and model
 * with KItemListController::setView() and KItemListController::setModel().
 *
 * @see KItemListView
 * @see KItemModelBase
 * @see KItemListSelectionManager
 */
class DOLPHIN_EXPORT KItemListController : public QObject
{
    Q_OBJECT
    Q_PROPERTY(KItemModelBase *model READ model WRITE setModel NOTIFY modelChanged)
    Q_PROPERTY(KItemListView *view READ view WRITE setView NOTIFY viewChanged)

public:
    enum SelectionBehavior { NoSelection, SingleSelection, MultiSelection };
    Q_ENUM(SelectionBehavior)

    enum AutoActivationBehavior { ActivationAndExpansion, ExpansionOnly };

    enum MouseDoubleClickAction { ActivateAndExpandItem, ActivateItemOnly };

    /**
     * @param model  Model of the controller. The ownership is passed to the controller.
     * @param view   View of the controller. The ownership is passed to the controller.
     * @param parent Optional parent object.
     */
    KItemListController(KItemModelBase *model, KItemListView *view, QObject *parent = nullptr);
    ~KItemListController() override;

    void setModel(KItemModelBase *model);
    KItemModelBase *model() const;

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

    KItemListSelectionManager *selectionManager() const;

    void setSelectionBehavior(SelectionBehavior behavior);
    SelectionBehavior selectionBehavior() const;

    void setAutoActivationBehavior(AutoActivationBehavior behavior);
    AutoActivationBehavior autoActivationBehavior() const;

    void setMouseDoubleClickAction(MouseDoubleClickAction action);
    MouseDoubleClickAction mouseDoubleClickAction() const;

    int indexCloseToMousePressedPosition() const;

    void setAutoActivationEnabled(bool enabled);
    bool isAutoActivationEnabled() const;

    /**
     * If set to true, the signals itemActivated() and itemsActivated() are emitted
     * after a single-click of the left mouse button. If set to false (the default),
     * the setting from style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick) is used.
     */
    void setSingleClickActivationEnforced(bool singleClick);
    bool singleClickActivationEnforced() const;

    /**
     * Setting the selection mode to enabled will make selecting and deselecting easier by acting
     * kind of similar to when the Control Key is held down.
     */
    void setSelectionModeEnabled(bool enabled);
    bool selectionMode() const;

    /**
     * @return \c true if search as you type is active, or \c false otherwise.
     */
    bool isSearchAsYouTypeActive() const;

    bool processEvent(QEvent *event, const QTransform &transform);

Q_SIGNALS:
    /**
     * Is emitted if exactly one item has been activated by e.g. a mouse-click
     * or by pressing Return/Enter.
     */
    void itemActivated(int index);

    /**
     * Is emitted if more than one item has been activated by pressing Return/Enter
     * when having a selection.
     */
    void itemsActivated(const KItemSet &indexes);

    void itemMiddleClicked(int index);

    /**
     * Emitted if a context-menu is requested for the item with
     * the index \a index. It is assured that the index is valid.
     */
    void itemContextMenuRequested(int index, const QPointF &pos);

    /**
     * Emitted if a context-menu is requested for the KItemListView.
     */
    void viewContextMenuRequested(const QPointF &pos);

    /**
     * Emitted if a context-menu is requested for the header of the KItemListView.
     */
    void headerContextMenuRequested(const QPointF &pos);

    /**
     * Is emitted if the item with the index \p index gets hovered.
     */
    void itemHovered(int index);

    /**
     * Is emitted if the item with the index \p index gets unhovered.
     * It is assured that the signal itemHovered() for this index
     * has been emitted before.
     */
    void itemUnhovered(int index);

    /**
     * Is emitted if a mouse-button has been pressed above an item.
     * If the index is smaller than 0, the mouse-button has been pressed
     * above the viewport.
     */
    void mouseButtonPressed(int itemIndex, Qt::MouseButtons buttons);

    /**
     * Is emitted if a mouse-button has been released above an item.
     * It is assured that the signal mouseButtonPressed() has been emitted before.
     * If the index is smaller than 0, the mouse-button has been pressed
     * above the viewport.
     */
    void mouseButtonReleased(int itemIndex, Qt::MouseButtons buttons);

    void itemExpansionToggleClicked(int index);

    /**
     * Is emitted if a drop event is done above the item with the index
     * \a index. If \a index is < 0 the drop event is done above an
     * empty area of the view.
     * TODO: Introduce a new signal viewDropEvent(QGraphicsSceneDragDropEvent),
     *       which is emitted if the drop event occurs on an empty area in
     *       the view, and make sure that index is always >= 0 in itemDropEvent().
     */
    void itemDropEvent(int index, QGraphicsSceneDragDropEvent *event);

    /**
     * Is emitted if a drop event is done between the item with the index
     * \a index and the previous item.
     */
    void aboveItemDropEvent(int index, QGraphicsSceneDragDropEvent *event);

    /**
     * Is emitted if the Escape key is pressed.
     */
    void escapePressed();

    /**
     * Used to request either entering or leaving of selection mode
     * Leaving is requested by pressing Escape when no item is selected.
     *
     * Entering is requested if left click is pressed down for a long time without moving the cursor too much.
     * Moving the cursor would either trigger an item drag if the click was initiated on top of an item
     * or a selection rectangle if the click was not initiated on top of an item.
     * So long press is only emitted if there wasn't a lot of cursor movement.
     */
    void selectionModeChangeRequested(bool enabled);

    void modelChanged(KItemModelBase *current, KItemModelBase *previous);
    void viewChanged(KItemListView *current, KItemListView *previous);

    void selectedItemTextPressed(int index);

    void scrollerStop();
    void increaseZoom();
    void decreaseZoom();
    void swipeUp();

    /**
     * Emitted when the view's background is double-clicked.
     * Used to trigger an user configured action.
     */
    void doubleClickViewBackground(Qt::MouseButton button);

public Q_SLOTS:
    void slotStateChanged(QScroller::State newState);

private Q_SLOTS:
    void slotViewScrollOffsetChanged(qreal current, qreal previous);

    /**
     * Is invoked when the rubberband boundaries have been changed and will select
     * all items that are touched by the rubberband.
     */
    void slotRubberBandChanged();

    void slotChangeCurrentItem(const QString &text, bool searchFromNextItem);

    void slotAutoActivationTimeout();

private:
    /**
     * Creates a QDrag object and initiates a drag-operation.
     */
    void startDragging();

    /**
     * @return Widget that is currently in the hovered state. 0 is returned
     *         if no widget is marked as hovered.
     */
    KItemListWidget *hoveredWidget() const;

    /**
     * @return Widget that is below the position \a pos. 0 is returned
     *         if no widget is below the position.
     */
    KItemListWidget *widgetForPos(const QPointF &pos) const;

    /**
     * @return Widget that should receive a drop event if an item is dropped at \a pos. 0 is returned
     *         if no widget should receive a drop event at the position.
     *
     * While widgetForPos() returns a widget if \a pos is anywhere inside the hover highlight area of the widget,
     * widgetForDropPos() only returns a widget if \a pos is directly above the widget (widget->contains(pos) == true).
     */
    KItemListWidget *widgetForDropPos(const QPointF &pos) const;

    /**
     * Updates m_keyboardAnchorIndex and m_keyboardAnchorPos. If no anchor is
     * set, it will be adjusted to the current item. If it is set it will be
     * checked whether it is still valid, otherwise it will be reset to the
     * current item.
     */
    void updateKeyboardAnchor();

    /**
     * @return Index for the next row based on \a index.
     *         If there is no next row \a index will be returned.
     */
    int nextRowIndex(int index) const;

    /**
     * @return Index for the previous row based on  \a index.
     *         If there is no previous row \a index will be returned.
     */
    int previousRowIndex(int index) const;

    /**
     * Helper method for updateKeyboardAnchor(), previousRowIndex() and nextRowIndex().
     * @return The position of the keyboard anchor for the item with the index \a index.
     *         If a horizontal scrolling is used the y-position of the item will be returned,
     *         for the vertical scrolling the x-position will be returned.
     */
    qreal keyboardAnchorPos(int index) const;

    /**
     * Dependent on the selection-behavior the extendedSelectionRegion-property
     * of the KItemListStyleOption from the view should be adjusted: If no
     * rubberband selection is used the property should be enabled.
     */
    void updateExtendedSelectionRegion();

    bool keyPressEvent(QKeyEvent *event);
    bool inputMethodEvent(QInputMethodEvent *event);
    bool mousePressEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
    bool mouseMoveEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
    bool mouseReleaseEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
    bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, const QTransform &transform);
    bool contextMenuEvent(QContextMenuEvent *event);
    bool dragEnterEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
    bool dragLeaveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
    bool dragMoveEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
    bool dropEvent(QGraphicsSceneDragDropEvent *event, const QTransform &transform);
    bool hoverEnterEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
    bool hoverMoveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
    bool hoverLeaveEvent(QGraphicsSceneHoverEvent *event, const QTransform &transform);
    bool wheelEvent(QGraphicsSceneWheelEvent *event, const QTransform &transform);
    bool resizeEvent(QGraphicsSceneResizeEvent *event, const QTransform &transform);
    bool gestureEvent(QGestureEvent *event, const QTransform &transform);
    bool touchBeginEvent(QTouchEvent *event, const QTransform &transform);
    void tapTriggered(QTapGesture *tap, const QTransform &transform);
    void tapAndHoldTriggered(QGestureEvent *event, const QTransform &transform);
    void pinchTriggered(QGestureEvent *event, const QTransform &transform);
    void swipeTriggered(QGestureEvent *event, const QTransform &transform);
    void twoFingerTapTriggered(QGestureEvent *event, const QTransform &transform);
    bool onPress(const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons);
    bool onRelease(const QPointF &pos, const Qt::KeyboardModifiers modifiers, const Qt::MouseButtons buttons, bool touch);
    void startRubberBand();

private:
    bool m_singleClickActivationEnforced;
    bool m_selectionMode;
    bool m_selectionTogglePressed;
    bool m_clearSelectionIfItemsAreNotDragged;
    bool m_isSwipeGesture;
    bool m_dragActionOrRightClick;
    bool m_scrollerIsScrolling;
    bool m_pinchGestureInProgress;
    bool m_mousePress;
    bool m_isTouchEvent;
    SelectionBehavior m_selectionBehavior;
    AutoActivationBehavior m_autoActivationBehavior;
    MouseDoubleClickAction m_mouseDoubleClickAction;
    KItemModelBase *m_model;
    KItemListView *m_view;
    KItemListSelectionManager *m_selectionManager;
    KItemListKeyboardSearchManager *m_keyboardManager;
    std::optional<int> m_pressedIndex;
    QPointF m_pressedMouseGlobalPos;

    bool m_autoActivationEnabled = false;
    QTimer *m_autoActivationTimer;

    Qt::GestureType m_swipeGesture;
    Qt::GestureType m_twoFingerTapGesture;

    /**
     * When starting a rubberband selection during a Shift- or Control-key has been
     * pressed the current selection should never be deleted. To be able to restore
     * the current selection it is remembered in m_oldSelection before the
     * rubberband gets activated.
     */
    KItemSet m_oldSelection;

    /**
     * Assuming a view is given with a vertical scroll-orientation, grouped items and
     * a maximum of 4 columns:
     *
     *  1  2  3  4
     *  5  6  7
     *  8  9 10 11
     * 12 13 14
     *
     * If the current index is on 4 and key-down is pressed, then item 7 gets the current
     * item. Now when pressing key-down again item 11 should get the current item and not
     * item 10. This makes it necessary to keep track of the requested column to have a
     * similar behavior like in a text editor:
     */
    int m_keyboardAnchorIndex;
    qreal m_keyboardAnchorPos;
};

#endif