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