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