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