]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/kitemlistwidget.cpp
GIT_SILENT Sync po/docbooks with svn
[dolphin.git] / src / kitemviews / kitemlistwidget.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 *
4 * Based on the Itemviews NG project from Trolltech Labs
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "kitemlistwidget.h"
10
11 #include "kitemlistview.h"
12 #include "private/kitemlistselectiontoggle.h"
13
14 #include <KConfigGroup>
15 #include <KSharedConfig>
16
17 #include <QApplication>
18 #include <QPainter>
19 #include <QPropertyAnimation>
20 #include <QStyleOption>
21
22 KItemListWidgetInformant::KItemListWidgetInformant()
23 {
24 }
25
26 KItemListWidgetInformant::~KItemListWidgetInformant()
27 {
28 }
29
30 KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsItem *parent)
31 : QGraphicsWidget(parent)
32 , m_informant(informant)
33 , m_index(-1)
34 , m_selected(false)
35 , m_current(false)
36 , m_hovered(false)
37 , m_expansionAreaHovered(false)
38 , m_alternateBackground(false)
39 , m_enabledSelectionToggle(false)
40 , m_data()
41 , m_visibleRoles()
42 , m_columnWidths()
43 , m_styleOption()
44 , m_siblingsInfo()
45 , m_hoverOpacity(0)
46 , m_hoverCache(nullptr)
47 , m_hoverAnimation(nullptr)
48 , m_hoverSequenceIndex(0)
49 , m_selectionToggle(nullptr)
50 , m_editedRole()
51 , m_iconSize(-1)
52 {
53 connect(&m_hoverSequenceTimer, &QTimer::timeout, this, &KItemListWidget::slotHoverSequenceTimerTimeout);
54 }
55
56 KItemListWidget::~KItemListWidget()
57 {
58 clearHoverCache();
59 }
60
61 void KItemListWidget::setIndex(int index)
62 {
63 if (m_index != index) {
64 delete m_selectionToggle;
65 m_selectionToggle = nullptr;
66
67 if (m_hoverAnimation) {
68 m_hoverAnimation->stop();
69 m_hoverOpacity = 0;
70 }
71 clearHoverCache();
72
73 m_index = index;
74 }
75 }
76
77 int KItemListWidget::index() const
78 {
79 return m_index;
80 }
81
82 void KItemListWidget::setData(const QHash<QByteArray, QVariant> &data, const QSet<QByteArray> &roles)
83 {
84 clearHoverCache();
85 if (roles.isEmpty()) {
86 m_data = data;
87 dataChanged(m_data);
88 } else {
89 for (const QByteArray &role : roles) {
90 m_data[role] = data[role];
91 }
92 dataChanged(m_data, roles);
93 }
94 update();
95 }
96
97 QHash<QByteArray, QVariant> KItemListWidget::data() const
98 {
99 return m_data;
100 }
101
102 void KItemListWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
103 {
104 Q_UNUSED(option)
105
106 if (m_alternateBackground) {
107 const QColor backgroundColor = m_styleOption.palette.color(QPalette::AlternateBase);
108 const QRectF backgroundRect(0, 0, size().width(), size().height());
109 painter->fillRect(backgroundRect, backgroundColor);
110 }
111
112 if (m_selected && m_editedRole.isEmpty()) {
113 const QStyle::State activeState(isActiveWindow() ? QStyle::State_Active : 0);
114 drawItemStyleOption(painter, widget, activeState | QStyle::State_Enabled | QStyle::State_Selected | QStyle::State_Item);
115 }
116
117 if (m_current && m_editedRole.isEmpty()) {
118 QStyleOptionFocusRect focusRectOption;
119 initStyleOption(&focusRectOption);
120 focusRectOption.rect = textFocusRect().toRect();
121 focusRectOption.state = QStyle::State_Enabled | QStyle::State_Item | QStyle::State_KeyboardFocusChange;
122 if (m_selected) {
123 focusRectOption.state |= QStyle::State_Selected;
124 }
125
126 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusRectOption, painter, widget);
127 }
128
129 if (m_hoverOpacity > 0.0) {
130 if (!m_hoverCache) {
131 // Initialize the m_hoverCache pixmap to improve the drawing performance
132 // when fading the hover background
133 m_hoverCache = new QPixmap(size().toSize());
134 m_hoverCache->fill(Qt::transparent);
135
136 QPainter pixmapPainter(m_hoverCache);
137 const QStyle::State activeState(isActiveWindow() ? QStyle::State_Active : 0);
138 drawItemStyleOption(&pixmapPainter, widget, activeState | QStyle::State_Enabled | QStyle::State_MouseOver | QStyle::State_Item);
139 }
140
141 const qreal opacity = painter->opacity();
142 painter->setOpacity(m_hoverOpacity * opacity);
143 painter->drawPixmap(0, 0, *m_hoverCache);
144 painter->setOpacity(opacity);
145 }
146 }
147
148 void KItemListWidget::setVisibleRoles(const QList<QByteArray> &roles)
149 {
150 const QList<QByteArray> previousRoles = m_visibleRoles;
151 m_visibleRoles = roles;
152
153 visibleRolesChanged(roles, previousRoles);
154 update();
155 }
156
157 QList<QByteArray> KItemListWidget::visibleRoles() const
158 {
159 return m_visibleRoles;
160 }
161
162 void KItemListWidget::setColumnWidth(const QByteArray &role, qreal width)
163 {
164 const qreal previousWidth = m_columnWidths.value(role);
165 if (previousWidth != width) {
166 m_columnWidths.insert(role, width);
167 columnWidthChanged(role, width, previousWidth);
168 update();
169 }
170 }
171
172 qreal KItemListWidget::columnWidth(const QByteArray &role) const
173 {
174 return m_columnWidths.value(role);
175 }
176
177 qreal KItemListWidget::sidePadding() const
178 {
179 return m_sidePadding;
180 }
181
182 void KItemListWidget::setSidePadding(qreal width)
183 {
184 if (m_sidePadding != width) {
185 m_sidePadding = width;
186 sidePaddingChanged(width);
187 update();
188 }
189 }
190
191 void KItemListWidget::setStyleOption(const KItemListStyleOption &option)
192 {
193 if (m_styleOption == option) {
194 return;
195 }
196
197 const KItemListStyleOption previous = m_styleOption;
198 clearHoverCache();
199 m_styleOption = option;
200 styleOptionChanged(option, previous);
201 update();
202 }
203
204 const KItemListStyleOption &KItemListWidget::styleOption() const
205 {
206 return m_styleOption;
207 }
208
209 void KItemListWidget::setSelected(bool selected)
210 {
211 if (m_selected != selected) {
212 m_selected = selected;
213 if (m_selectionToggle) {
214 m_selectionToggle->setChecked(selected);
215 }
216 selectedChanged(selected);
217 update();
218 }
219 }
220
221 bool KItemListWidget::isSelected() const
222 {
223 return m_selected;
224 }
225
226 void KItemListWidget::setCurrent(bool current)
227 {
228 if (m_current != current) {
229 m_current = current;
230 currentChanged(current);
231 update();
232 }
233 }
234
235 bool KItemListWidget::isCurrent() const
236 {
237 return m_current;
238 }
239
240 void KItemListWidget::setHovered(bool hovered)
241 {
242 if (hovered == m_hovered) {
243 return;
244 }
245
246 m_hovered = hovered;
247
248 if (!m_hoverAnimation) {
249 m_hoverAnimation = new QPropertyAnimation(this, "hoverOpacity", this);
250 const int duration = style()->styleHint(QStyle::SH_Widget_Animate) ? 200 : 1;
251 m_hoverAnimation->setDuration(duration);
252 connect(m_hoverAnimation, &QPropertyAnimation::finished, this, &KItemListWidget::slotHoverAnimationFinished);
253 }
254 m_hoverAnimation->stop();
255
256 m_hoverSequenceIndex = 0;
257
258 if (hovered) {
259 const qreal startValue = qMax(hoverOpacity(), qreal(0.1));
260 m_hoverAnimation->setStartValue(startValue);
261 m_hoverAnimation->setEndValue(1.0);
262 if (m_enabledSelectionToggle && !(QApplication::mouseButtons() & Qt::LeftButton)) {
263 initializeSelectionToggle();
264 }
265
266 hoverSequenceStarted();
267
268 const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings");
269 const int interval = globalConfig.readEntry("HoverSequenceInterval", 700);
270
271 m_hoverSequenceTimer.start(interval);
272 } else {
273 m_hoverAnimation->setStartValue(hoverOpacity());
274 m_hoverAnimation->setEndValue(0.0);
275
276 hoverSequenceEnded();
277 m_hoverSequenceTimer.stop();
278 }
279
280 m_hoverAnimation->start();
281
282 hoveredChanged(hovered);
283 update();
284 }
285
286 bool KItemListWidget::isHovered() const
287 {
288 return m_hovered;
289 }
290
291 void KItemListWidget::setExpansionAreaHovered(bool hovered)
292 {
293 if (hovered == m_expansionAreaHovered) {
294 return;
295 }
296 m_expansionAreaHovered = hovered;
297 update();
298 }
299
300 bool KItemListWidget::expansionAreaHovered() const
301 {
302 return m_expansionAreaHovered;
303 }
304
305 void KItemListWidget::setHoverPosition(const QPointF &pos)
306 {
307 if (m_selectionToggle) {
308 m_selectionToggle->setHovered(selectionToggleRect().contains(pos));
309 }
310 }
311
312 void KItemListWidget::setAlternateBackground(bool enable)
313 {
314 if (m_alternateBackground != enable) {
315 m_alternateBackground = enable;
316 alternateBackgroundChanged(enable);
317 update();
318 }
319 }
320
321 bool KItemListWidget::alternateBackground() const
322 {
323 return m_alternateBackground;
324 }
325
326 void KItemListWidget::setEnabledSelectionToggle(bool enable)
327 {
328 if (m_enabledSelectionToggle != enable) {
329 m_enabledSelectionToggle = enable;
330
331 // We want the change to take effect immediately.
332 if (m_enabledSelectionToggle) {
333 if (m_hovered) {
334 initializeSelectionToggle();
335 }
336 } else if (m_selectionToggle) {
337 m_selectionToggle->deleteLater();
338 m_selectionToggle = nullptr;
339 }
340
341 update();
342 }
343 }
344
345 bool KItemListWidget::enabledSelectionToggle() const
346 {
347 return m_enabledSelectionToggle;
348 }
349
350 void KItemListWidget::setSiblingsInformation(const QBitArray &siblings)
351 {
352 const QBitArray previous = m_siblingsInfo;
353 m_siblingsInfo = siblings;
354 siblingsInformationChanged(m_siblingsInfo, previous);
355 update();
356 }
357
358 QBitArray KItemListWidget::siblingsInformation() const
359 {
360 return m_siblingsInfo;
361 }
362
363 void KItemListWidget::setEditedRole(const QByteArray &role)
364 {
365 if (m_editedRole != role) {
366 const QByteArray previous = m_editedRole;
367 m_editedRole = role;
368 editedRoleChanged(role, previous);
369 }
370 }
371
372 QByteArray KItemListWidget::editedRole() const
373 {
374 return m_editedRole;
375 }
376
377 void KItemListWidget::setIconSize(int iconSize)
378 {
379 if (m_iconSize != iconSize) {
380 const int previousIconSize = m_iconSize;
381 m_iconSize = iconSize;
382 iconSizeChanged(iconSize, previousIconSize);
383 }
384 }
385
386 int KItemListWidget::iconSize() const
387 {
388 return m_iconSize;
389 }
390
391 bool KItemListWidget::contains(const QPointF &point) const
392 {
393 if (!QGraphicsWidget::contains(point)) {
394 return false;
395 }
396
397 return iconRect().contains(point) || textRect().contains(point) || expansionToggleRect().contains(point) || selectionToggleRect().contains(point);
398 }
399
400 QRectF KItemListWidget::textFocusRect() const
401 {
402 return textRect();
403 }
404
405 QRectF KItemListWidget::selectionToggleRect() const
406 {
407 return QRectF();
408 }
409
410 QRectF KItemListWidget::expansionToggleRect() const
411 {
412 return QRectF();
413 }
414
415 QPixmap KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem *option, QWidget *widget)
416 {
417 QPixmap pixmap(size().toSize() * widget->devicePixelRatio());
418 pixmap.setDevicePixelRatio(widget->devicePixelRatio());
419 pixmap.fill(Qt::transparent);
420
421 QPainter painter(&pixmap);
422
423 const bool oldAlternateBackground = m_alternateBackground;
424 const bool wasSelected = m_selected;
425 const bool wasHovered = m_hovered;
426
427 setAlternateBackground(false);
428 setHovered(false);
429
430 paint(&painter, option, widget);
431
432 setAlternateBackground(oldAlternateBackground);
433 setSelected(wasSelected);
434 setHovered(wasHovered);
435
436 return pixmap;
437 }
438
439 void KItemListWidget::dataChanged(const QHash<QByteArray, QVariant> &current, const QSet<QByteArray> &roles)
440 {
441 Q_UNUSED(current)
442 Q_UNUSED(roles)
443 }
444
445 void KItemListWidget::visibleRolesChanged(const QList<QByteArray> &current, const QList<QByteArray> &previous)
446 {
447 Q_UNUSED(current)
448 Q_UNUSED(previous)
449 }
450
451 void KItemListWidget::columnWidthChanged(const QByteArray &role, qreal current, qreal previous)
452 {
453 Q_UNUSED(role)
454 Q_UNUSED(current)
455 Q_UNUSED(previous)
456 }
457
458 void KItemListWidget::sidePaddingChanged(qreal width)
459 {
460 Q_UNUSED(width)
461 }
462
463 void KItemListWidget::styleOptionChanged(const KItemListStyleOption &current, const KItemListStyleOption &previous)
464 {
465 Q_UNUSED(previous)
466
467 // set the initial value of m_iconSize if not set
468 if (m_iconSize == -1) {
469 m_iconSize = current.iconSize;
470 }
471 }
472
473 void KItemListWidget::currentChanged(bool current)
474 {
475 Q_UNUSED(current)
476 }
477
478 void KItemListWidget::selectedChanged(bool selected)
479 {
480 Q_UNUSED(selected)
481 }
482
483 void KItemListWidget::hoveredChanged(bool hovered)
484 {
485 Q_UNUSED(hovered)
486 }
487
488 void KItemListWidget::alternateBackgroundChanged(bool enabled)
489 {
490 Q_UNUSED(enabled)
491 }
492
493 void KItemListWidget::siblingsInformationChanged(const QBitArray &current, const QBitArray &previous)
494 {
495 Q_UNUSED(current)
496 Q_UNUSED(previous)
497 }
498
499 void KItemListWidget::editedRoleChanged(const QByteArray &current, const QByteArray &previous)
500 {
501 Q_UNUSED(current)
502 Q_UNUSED(previous)
503 }
504
505 void KItemListWidget::iconSizeChanged(int current, int previous)
506 {
507 Q_UNUSED(current)
508 Q_UNUSED(previous)
509 }
510
511 void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
512 {
513 QGraphicsWidget::resizeEvent(event);
514 clearHoverCache();
515
516 if (m_selectionToggle) {
517 const QRectF &toggleRect = selectionToggleRect();
518 m_selectionToggle->setPos(toggleRect.topLeft());
519 m_selectionToggle->resize(toggleRect.size());
520 }
521 }
522
523 void KItemListWidget::hoverSequenceStarted()
524 {
525 }
526
527 void KItemListWidget::hoverSequenceIndexChanged(int sequenceIndex)
528 {
529 Q_UNUSED(sequenceIndex);
530 }
531
532 void KItemListWidget::hoverSequenceEnded()
533 {
534 }
535
536 qreal KItemListWidget::hoverOpacity() const
537 {
538 return m_hoverOpacity;
539 }
540
541 int KItemListWidget::hoverSequenceIndex() const
542 {
543 return m_hoverSequenceIndex;
544 }
545
546 void KItemListWidget::slotHoverAnimationFinished()
547 {
548 if (!m_hovered && m_selectionToggle) {
549 m_selectionToggle->deleteLater();
550 m_selectionToggle = nullptr;
551 }
552 }
553
554 void KItemListWidget::slotHoverSequenceTimerTimeout()
555 {
556 m_hoverSequenceIndex++;
557 hoverSequenceIndexChanged(m_hoverSequenceIndex);
558 }
559
560 void KItemListWidget::initializeSelectionToggle()
561 {
562 Q_ASSERT(m_enabledSelectionToggle);
563
564 if (!m_selectionToggle) {
565 m_selectionToggle = new KItemListSelectionToggle(this);
566 }
567
568 const QRectF toggleRect = selectionToggleRect();
569 m_selectionToggle->setPos(toggleRect.topLeft());
570 m_selectionToggle->resize(toggleRect.size());
571
572 m_selectionToggle->setChecked(isSelected());
573 }
574
575 void KItemListWidget::setHoverOpacity(qreal opacity)
576 {
577 m_hoverOpacity = opacity;
578 if (m_selectionToggle) {
579 m_selectionToggle->setOpacity(opacity);
580 }
581
582 if (m_hoverOpacity <= 0.0) {
583 delete m_hoverCache;
584 m_hoverCache = nullptr;
585 }
586
587 update();
588 }
589
590 void KItemListWidget::clearHoverCache()
591 {
592 delete m_hoverCache;
593 m_hoverCache = nullptr;
594 }
595
596 void KItemListWidget::drawItemStyleOption(QPainter *painter, QWidget *widget, QStyle::State styleState)
597 {
598 QStyleOptionViewItem viewItemOption;
599 initStyleOption(&viewItemOption);
600 viewItemOption.state = styleState;
601 viewItemOption.viewItemPosition = QStyleOptionViewItem::OnlyOne;
602 viewItemOption.showDecorationSelected = true;
603 viewItemOption.rect = selectionRect().toRect();
604 style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, painter, widget);
605 }