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