]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistview.h
Provide scrollbar for large items
[dolphin.git] / src / kitemviews / kitemlistview.h
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * Based on the Itemviews NG project from Trolltech Labs: *
5 * http://qt.gitorious.org/qt-labs/itemviews-ng *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
21 ***************************************************************************/
22
23 #ifndef KITEMLISTVIEW_H
24 #define KITEMLISTVIEW_H
25
26 #include <libdolphin_export.h>
27
28 #include <kitemviews/kitemliststyleoption.h>
29 #include <kitemviews/kitemlistviewanimation_p.h>
30 #include <kitemviews/kitemlistwidget.h>
31 #include <kitemviews/kitemmodelbase.h>
32 #include <QGraphicsWidget>
33 #include <QSet>
34
35 class KItemListController;
36 class KItemListGroupHeader;
37 class KItemListGroupHeaderCreatorBase;
38 class KItemListHeader;
39 class KItemListSizeHintResolver;
40 class KItemListRubberBand;
41 class KItemListViewAnimation;
42 class KItemListViewLayouter;
43 class KItemListWidget;
44 class KItemListWidgetCreatorBase;
45 class KItemListViewCreatorBase;
46 class QTimer;
47
48 /**
49 * @brief Represents the view of an item-list.
50 *
51 * The view is responsible for showing the items of the model within
52 * a GraphicsItem. Each visible item is represented by a KItemListWidget.
53 *
54 * The created view must be applied to the KItemListController with
55 * KItemListController::setView(). For showing a custom model it is not
56 * mandatory to derive from KItemListView, all that is necessary is
57 * to set a widget-creator that is capable to create KItemListWidgets
58 * showing the model items. A widget-creator can be set with
59 * KItemListView::setWidgetCreator().
60 *
61 * @see KItemListWidget
62 * @see KItemModelBase
63 */
64 class LIBDOLPHINPRIVATE_EXPORT KItemListView : public QGraphicsWidget
65 {
66 Q_OBJECT
67
68 Q_PROPERTY(qreal scrollOffset READ scrollOffset WRITE setScrollOffset)
69
70 public:
71 KItemListView(QGraphicsWidget* parent = 0);
72 virtual ~KItemListView();
73
74 void setScrollOrientation(Qt::Orientation orientation);
75 Qt::Orientation scrollOrientation() const;
76
77 void setItemSize(const QSizeF& size);
78 QSizeF itemSize() const;
79
80 // TODO: add note that offset is not checked against maximumOffset, only against 0.
81 void setScrollOffset(qreal offset);
82 qreal scrollOffset() const;
83
84 qreal maximumScrollOffset() const;
85
86 void setItemOffset(qreal scrollOffset);
87 qreal itemOffset() const;
88
89 qreal maximumItemOffset() const;
90
91 void setVisibleRoles(const QList<QByteArray>& roles);
92 QList<QByteArray> visibleRoles() const;
93
94 /**
95 * If set to true an automatic scrolling is done as soon as the
96 * mouse is moved near the borders of the view. Per default
97 * the automatic scrolling is turned off.
98 */
99 void setAutoScroll(bool enabled);
100 bool autoScroll() const;
101
102 /**
103 * Turns on the header if \p show is true. Per default the
104 * header is not shown.
105 */
106 void setHeaderShown(bool show);
107 bool isHeaderShown() const;
108
109 /**
110 * @return Controller of the item-list. The controller gets
111 * initialized by KItemListController::setView() and will
112 * result in calling KItemListController::onControllerChanged().
113 */
114 KItemListController* controller() const;
115
116 /**
117 * @return Model of the item-list. The model gets
118 * initialized by KItemListController::setView() and will
119 * result in calling KItemListController::onModelChanged().
120 */
121 KItemModelBase* model() const;
122
123 /**
124 * Sets the creator that creates a widget showing the
125 * content of one model-item. Usually it is sufficient
126 * to implement a custom widget X derived from KItemListWidget and
127 * set the creator by:
128 * <code>
129 * itemListView->setWidgetCreator(new KItemListWidgetCreator<X>());
130 * </code>
131 * Note that the ownership of the widget creator is not transferred to
132 * the item-list view: One instance of a widget creator might get shared
133 * by several item-list view instances.
134 **/
135 void setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator);
136 KItemListWidgetCreatorBase* widgetCreator() const;
137
138 void setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator);
139 KItemListGroupHeaderCreatorBase* groupHeaderCreator() const;
140
141 void setStyleOption(const KItemListStyleOption& option);
142 const KItemListStyleOption& styleOption() const;
143
144 /** @reimp */
145 virtual void setGeometry(const QRectF& rect);
146
147 int itemAt(const QPointF& pos) const;
148 bool isAboveSelectionToggle(int index, const QPointF& pos) const;
149 bool isAboveExpansionToggle(int index, const QPointF& pos) const;
150
151 int firstVisibleIndex() const;
152 int lastVisibleIndex() const;
153
154 /**
155 * @return Required size for the item with the index \p index.
156 * Per default KItemListView::itemSize() is returned.
157 * When reimplementing this method it is recommended to
158 * also reimplement KItemListView::itemSizeHintUpdateRequired().
159 */
160 virtual QSizeF itemSizeHint(int index) const;
161
162 /**
163 * @return The size of each visible role in case if KItemListView::itemSize()
164 * is empty. This allows to have dynamic but equal role sizes between
165 * all items. Per default an empty hash is returned.
166 */
167 virtual QHash<QByteArray, QSizeF> visibleRolesSizes() const;
168
169 /**
170 * @return The bounding rectangle of the item relative to the top/left of
171 * the currently visible area (see KItemListView::offset()).
172 */
173 QRectF itemBoundingRect(int index) const;
174
175 /**
176 * @return The number of items that can be shown in parallel for one offset.
177 * Assuming the scrolldirection is vertical then a value of 4 means
178 * that 4 columns for items are available. Assuming the scrolldirection
179 * is horizontal then a value of 4 means that 4 lines for items are
180 * available.
181 */
182 int itemsPerOffset() const;
183
184 void beginTransaction();
185 void endTransaction();
186 bool isTransactionActive() const;
187
188 /**
189 * @return Pixmap that is used for a drag operation based on the
190 * items given by \a indexes. The default implementation returns
191 * a null-pixmap.
192 */
193 virtual QPixmap createDragPixmap(const QSet<int>& indexes) const;
194
195 /**
196 * @reimp
197 */
198 virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
199
200 signals:
201 void scrollOffsetChanged(qreal current, qreal previous);
202 void maximumScrollOffsetChanged(qreal current, qreal previous);
203 void itemOffsetChanged(qreal current, qreal previous);
204 void maximumItemOffsetChanged(qreal current, qreal previous);
205 void scrollTo(qreal newOffset);
206
207 protected:
208 virtual void initializeItemListWidget(KItemListWidget* item);
209
210 /**
211 * @return True if at least one of the changed roles \p changedRoles might result
212 * in the need to update the item-size hint (see KItemListView::itemSizeHint()).
213 * Per default true is returned which means on each role-change of existing items
214 * the item-size hints are recalculated. For performance reasons it is recommended
215 * to return false in case if a role-change will not result in a changed
216 * item-size hint.
217 */
218 virtual bool itemSizeHintUpdateRequired(const QSet<QByteArray>& changedRoles) const;
219
220 virtual void onControllerChanged(KItemListController* current, KItemListController* previous);
221 virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous);
222
223 virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
224 virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous);
225 virtual void onScrollOffsetChanged(qreal current, qreal previous);
226 virtual void onVisibleRolesChanged(const QList<QByteArray>& current, const QList<QByteArray>& previous);
227 virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
228
229 virtual void onTransactionBegin();
230 virtual void onTransactionEnd();
231
232 virtual bool event(QEvent* event);
233 virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
234 virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
235 virtual void dragEnterEvent(QGraphicsSceneDragDropEvent* event);
236 virtual void dragMoveEvent(QGraphicsSceneDragDropEvent* event);
237 virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent* event);
238 virtual void dropEvent(QGraphicsSceneDragDropEvent* event);
239
240 QList<KItemListWidget*> visibleItemListWidgets() const;
241
242 /** @reimp */
243 virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
244
245 protected slots:
246 virtual void slotItemsInserted(const KItemRangeList& itemRanges);
247 virtual void slotItemsRemoved(const KItemRangeList& itemRanges);
248 virtual void slotItemsChanged(const KItemRangeList& itemRanges,
249 const QSet<QByteArray>& roles);
250
251 private slots:
252 void slotCurrentChanged(int current, int previous);
253 void slotSelectionChanged(const QSet<int>& current, const QSet<int>& previous);
254 void slotAnimationFinished(QGraphicsWidget* widget,
255 KItemListViewAnimation::AnimationType type);
256 void slotLayoutTimerFinished();
257
258 void slotRubberBandPosChanged();
259 void slotRubberBandActivationChanged(bool active);
260
261 /**
262 * Is invoked if the visible role-width of one role in the header has
263 * been changed by the user. It is remembered that the user has modified
264 * the role-width, so that it won't be changed anymore automatically to
265 * calculate an optimized width.
266 */
267 void slotVisibleRoleWidthChanged(const QByteArray& role,
268 qreal currentWidth,
269 qreal previousWidth);
270
271 /**
272 * Triggers the autoscrolling if autoScroll() is enabled by checking the
273 * current mouse position. If the mouse position is within the autoscroll
274 * margins a timer will be started that periodically triggers the autoscrolling.
275 */
276 void triggerAutoScrolling();
277
278 private:
279 enum LayoutAnimationHint
280 {
281 NoAnimation,
282 Animation
283 };
284
285 enum SizeType
286 {
287 LayouterSize,
288 ItemSize
289 };
290
291 void setController(KItemListController* controller);
292 void setModel(KItemModelBase* model);
293
294 KItemListRubberBand* rubberBand() const;
295
296 void updateLayout();
297 void doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
298 void doGroupHeadersLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
299 void emitOffsetChanges();
300
301 KItemListWidget* createWidget(int index);
302 void recycleWidget(KItemListWidget* widget);
303 void setWidgetIndex(KItemListWidget* widget, int index);
304
305 /**
306 * Helper method for setGeometry() and setItemSize(): Calling both methods might result
307 * in a changed number of visible items. To assure that currently invisible items can
308 * get animated from the old position to the new position prepareLayoutForIncreasedItemCount()
309 * takes care to create all item widgets that are visible with the old or the new size.
310 * @param size Size of the layouter or the item dependent on \p sizeType.
311 * @param sizeType LayouterSize: KItemListLayouter::setSize() is used.
312 * ItemSize: KItemListLayouter::setItemSize() is used.
313 */
314 void prepareLayoutForIncreasedItemCount(const QSizeF& size, SizeType sizeType);
315
316 /**
317 * Helper method for prepareLayoutForIncreasedItemCount().
318 */
319 void setLayouterSize(const QSizeF& size, SizeType sizeType);
320
321 /**
322 * Helper method for createWidget() and setWidgetIndex() to update the properties
323 * of the itemlist widget.
324 */
325 void updateWidgetProperties(KItemListWidget* widget, int index);
326
327 /**
328 * Updates the width of the KItemListHeader corresponding to the required width of
329 * the roles.
330 */
331 void updateHeaderWidth();
332
333 /**
334 * @return The widths of each visible role that is shown in the KItemListHeader.
335 */
336 QHash<QByteArray, qreal> headerRolesWidths() const;
337
338 /**
339 * Updates m_visibleRoleSizes by calling KItemListView::visibleRoleSizes()
340 * if the m_itemRect is empty and no custom header-widths are used
341 * (see m_useHeaderWidths).
342 */
343 void updateVisibleRoleSizes();
344
345 /**
346 * Helper function for triggerAutoScrolling().
347 * @param pos Logical position of the mouse relative to the range.
348 * @param range Range of the visible area.
349 * @param oldInc Previous increment. Is used to assure that the increment
350 * increases only gradually.
351 * @return Scroll increment that should be added to the offset().
352 * As soon as \a pos is inside the autoscroll-margin a
353 * value != 0 will be returned.
354 */
355 static int calculateAutoScrollingIncrement(int pos, int range, int oldInc);
356
357 private:
358 bool m_grouped;
359 int m_activeTransactions; // Counter for beginTransaction()/endTransaction()
360
361 QSizeF m_itemSize;
362 KItemListController* m_controller;
363 KItemModelBase* m_model;
364 QList<QByteArray> m_visibleRoles;
365 QHash<QByteArray, QSizeF> m_visibleRolesSizes;
366 KItemListWidgetCreatorBase* m_widgetCreator;
367 KItemListGroupHeaderCreatorBase* m_groupHeaderCreator;
368 KItemListStyleOption m_styleOption;
369
370 QHash<int, KItemListWidget*> m_visibleItems;
371 QHash<KItemListWidget*, KItemListGroupHeader*> m_visibleGroups;
372
373 int m_scrollBarExtent;
374 KItemListSizeHintResolver* m_sizeHintResolver;
375 KItemListViewLayouter* m_layouter;
376 KItemListViewAnimation* m_animation;
377
378 QTimer* m_layoutTimer; // Triggers an asynchronous doLayout() call.
379 qreal m_oldScrollOffset;
380 qreal m_oldMaximumScrollOffset;
381 qreal m_oldItemOffset;
382 qreal m_oldMaximumItemOffset;
383
384 bool m_skipAutoScrollForRubberBand;
385 KItemListRubberBand* m_rubberBand;
386
387 QPointF m_mousePos;
388 int m_autoScrollIncrement;
389 QTimer* m_autoScrollTimer;
390
391 KItemListHeader* m_header;
392 bool m_useHeaderWidths;
393
394 friend class KItemListController;
395 };
396
397 /**
398 * Allows to do a fast logical creation and deletion of QGraphicsWidgets
399 * by recycling existing QGraphicsWidgets instances. Is used by
400 * KItemListWidgetCreatorBase and KItemListGroupHeaderCreatorBase.
401 * @internal
402 */
403 class LIBDOLPHINPRIVATE_EXPORT KItemListCreatorBase
404 {
405 public:
406 virtual ~KItemListCreatorBase();
407
408 protected:
409 void addCreatedWidget(QGraphicsWidget* widget);
410 void pushRecycleableWidget(QGraphicsWidget* widget);
411 QGraphicsWidget* popRecycleableWidget();
412
413 private:
414 QSet<QGraphicsWidget*> m_createdWidgets;
415 QList<QGraphicsWidget*> m_recycleableWidgets;
416 };
417
418 /**
419 * @brief Base class for creating KItemListWidgets.
420 *
421 * It is recommended that applications simply use the KItemListWidgetCreator-template class.
422 * For a custom implementation the methods create() and recyle() must be reimplemented.
423 * The intention of the widget creator is to prevent repetitive and expensive instantiations and
424 * deletions of KItemListWidgets by recycling existing widget instances.
425 */
426 class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreatorBase : public KItemListCreatorBase
427 {
428 public:
429 virtual ~KItemListWidgetCreatorBase();
430 virtual KItemListWidget* create(KItemListView* view) = 0;
431 virtual void recycle(KItemListWidget* widget);
432 };
433
434 template <class T>
435 class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreator : public KItemListWidgetCreatorBase
436 {
437 public:
438 virtual ~KItemListWidgetCreator();
439 virtual KItemListWidget* create(KItemListView* view);
440 };
441
442 template <class T>
443 KItemListWidgetCreator<T>::~KItemListWidgetCreator()
444 {
445 }
446
447 template <class T>
448 KItemListWidget* KItemListWidgetCreator<T>::create(KItemListView* view)
449 {
450 KItemListWidget* widget = static_cast<KItemListWidget*>(popRecycleableWidget());
451 if (!widget) {
452 widget = new T(view);
453 addCreatedWidget(widget);
454 }
455 return widget;
456 }
457
458 /**
459 * @brief Base class for creating KItemListGroupHeaders.
460 *
461 * It is recommended that applications simply use the KItemListGroupHeaderCreator-template class.
462 * For a custom implementation the methods create() and recyle() must be reimplemented.
463 * The intention of the group-header creator is to prevent repetitive and expensive instantiations and
464 * deletions of KItemListGroupHeaders by recycling existing header instances.
465 */
466 class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreatorBase : public KItemListCreatorBase
467 {
468 public:
469 virtual ~KItemListGroupHeaderCreatorBase();
470 virtual KItemListGroupHeader* create(QGraphicsWidget* parent) = 0;
471 virtual void recycle(KItemListGroupHeader* header);
472 };
473
474 template <class T>
475 class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreator : public KItemListGroupHeaderCreatorBase
476 {
477 public:
478 virtual ~KItemListGroupHeaderCreator();
479 virtual KItemListGroupHeader* create(QGraphicsWidget* parent);
480 };
481
482 template <class T>
483 KItemListGroupHeaderCreator<T>::~KItemListGroupHeaderCreator()
484 {
485 }
486
487 template <class T>
488 KItemListGroupHeader* KItemListGroupHeaderCreator<T>::create(QGraphicsWidget* parent)
489 {
490 KItemListGroupHeader* widget = static_cast<KItemListGroupHeader*>(popRecycleableWidget());
491 if (!widget) {
492 widget = new T(parent);
493 addCreatedWidget(widget);
494 }
495 return widget;
496 }
497
498 #endif