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