2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
4 * Based on the Itemviews NG project from Trolltech Labs
6 * SPDX-License-Identifier: GPL-2.0-or-later
9 #include "kitemlistwidget.h"
11 #include "kitemlistview.h"
12 #include "private/kitemlistselectiontoggle.h"
14 #include <KConfigGroup>
15 #include <KSharedConfig>
17 #include <QApplication>
19 #include <QPropertyAnimation>
20 #include <QStyleOption>
22 KItemListWidgetInformant::KItemListWidgetInformant()
26 KItemListWidgetInformant::~KItemListWidgetInformant()
30 KItemListWidget::KItemListWidget(KItemListWidgetInformant
*informant
, QGraphicsItem
*parent
)
31 : QGraphicsWidget(parent
)
32 , m_informant(informant
)
37 , m_expansionAreaHovered(false)
38 , m_alternateBackground(false)
39 , m_enabledSelectionToggle(false)
40 , m_clickHighlighted(false)
49 , m_hoverCache(nullptr)
50 , m_hoverSequenceIndex(0)
51 , m_selectionToggle(nullptr)
55 connect(&m_hoverSequenceTimer
, &QTimer::timeout
, this, &KItemListWidget::slotHoverSequenceTimerTimeout
);
58 KItemListWidget::~KItemListWidget()
63 void KItemListWidget::setIndex(int index
)
65 if (m_index
!= index
) {
66 delete m_selectionToggle
;
67 m_selectionToggle
= nullptr;
77 int KItemListWidget::index() const
82 void KItemListWidget::setData(const QHash
<QByteArray
, QVariant
> &data
, const QSet
<QByteArray
> &roles
)
85 if (roles
.isEmpty()) {
89 for (const QByteArray
&role
: roles
) {
90 m_data
[role
] = data
[role
];
92 dataChanged(m_data
, roles
);
97 QHash
<QByteArray
, QVariant
> KItemListWidget::data() const
102 QVariant
KItemListWidget::value(const QByteArray
&key
) const
104 return m_data
.value(key
);
107 void KItemListWidget::paint(QPainter
*painter
, const QStyleOptionGraphicsItem
*option
, QWidget
*widget
)
111 if (m_alternateBackground
) {
112 QColor backgroundColor
= m_styleOption
.palette
.color(QPalette::AlternateBase
);
113 if (!widget
->hasFocus()) {
114 QColor baseColor
= m_styleOption
.palette
.color(QPalette::Base
);
115 if (baseColor
.lightnessF() > 0.5) {
117 backgroundColor
= backgroundColor
.lighter(101);
120 backgroundColor
= backgroundColor
.darker(101);
124 const QRectF
backgroundRect(0, 0, size().width(), size().height());
125 painter
->fillRect(backgroundRect
, backgroundColor
);
128 if ((m_selected
|| m_current
) && m_editedRole
.isEmpty()) {
129 const QStyle::State
activeState(isActiveWindow() && widget
->hasFocus() ? QStyle::State_Active
: 0);
130 drawItemStyleOption(painter
, widget
, activeState
| QStyle::State_Enabled
| QStyle::State_Selected
| QStyle::State_Item
);
133 if (m_hoverOpacity
> 0.0) {
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
);
140 QPainter
pixmapPainter(m_hoverCache
);
141 const QStyle::State
activeState(isActiveWindow() && widget
->hasFocus() ? QStyle::State_Active
| QStyle::State_Enabled
: 0);
142 drawItemStyleOption(&pixmapPainter
, widget
, activeState
| QStyle::State_MouseOver
| QStyle::State_Item
);
145 const qreal opacity
= painter
->opacity();
146 painter
->setOpacity(m_hoverOpacity
* opacity
);
147 painter
->drawPixmap(0, 0, *m_hoverCache
);
148 painter
->setOpacity(opacity
);
152 void KItemListWidget::setVisibleRoles(const QList
<QByteArray
> &roles
)
154 const QList
<QByteArray
> previousRoles
= m_visibleRoles
;
155 m_visibleRoles
= roles
;
157 visibleRolesChanged(roles
, previousRoles
);
161 QList
<QByteArray
> KItemListWidget::visibleRoles() const
163 return m_visibleRoles
;
166 void KItemListWidget::setColumnWidth(const QByteArray
&role
, qreal width
)
168 const qreal previousWidth
= m_columnWidths
.value(role
);
169 if (previousWidth
!= width
) {
170 m_columnWidths
.insert(role
, width
);
171 columnWidthChanged(role
, width
, previousWidth
);
176 qreal
KItemListWidget::columnWidth(const QByteArray
&role
) const
178 return m_columnWidths
.value(role
);
181 void KItemListWidget::setSidePadding(qreal leftPaddingWidth
, qreal rightPaddingWidth
)
183 bool changed
= false;
184 if (m_leftPadding
!= leftPaddingWidth
) {
185 m_leftPadding
= leftPaddingWidth
;
189 if (m_rightPadding
!= rightPaddingWidth
) {
190 m_rightPadding
= rightPaddingWidth
;
198 sidePaddingChanged(leftPaddingWidth
, rightPaddingWidth
);
202 qreal
KItemListWidget::leftPadding() const
204 return m_leftPadding
;
207 qreal
KItemListWidget::rightPadding() const
209 return m_rightPadding
;
212 void KItemListWidget::setStyleOption(const KItemListStyleOption
&option
)
214 if (m_styleOption
== option
) {
218 const KItemListStyleOption previous
= m_styleOption
;
220 m_styleOption
= option
;
221 styleOptionChanged(option
, previous
);
225 const KItemListStyleOption
&KItemListWidget::styleOption() const
227 return m_styleOption
;
230 void KItemListWidget::setSelected(bool selected
)
232 if (m_selected
!= selected
) {
233 m_selected
= selected
;
234 if (m_selectionToggle
) {
235 m_selectionToggle
->setChecked(selected
);
237 selectedChanged(selected
);
242 bool KItemListWidget::isSelected() const
247 void KItemListWidget::setCurrent(bool current
)
249 if (m_current
!= current
) {
251 currentChanged(current
);
256 bool KItemListWidget::isCurrent() const
261 void KItemListWidget::setHovered(bool hovered
)
263 if (hovered
== m_hovered
) {
269 m_hoverSequenceIndex
= 0;
272 setHoverOpacity(1.0);
274 if (m_enabledSelectionToggle
&& !(QApplication::mouseButtons() & Qt::LeftButton
)) {
275 initializeSelectionToggle();
278 hoverSequenceStarted();
280 const KConfigGroup
globalConfig(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings"));
281 const int interval
= globalConfig
.readEntry("HoverSequenceInterval", 700);
283 m_hoverSequenceTimer
.start(interval
);
285 setHoverOpacity(0.0);
287 if (m_selectionToggle
) {
288 m_selectionToggle
->deleteLater();
289 m_selectionToggle
= nullptr;
292 hoverSequenceEnded();
293 m_hoverSequenceTimer
.stop();
296 hoveredChanged(hovered
);
300 bool KItemListWidget::isHovered() const
305 void KItemListWidget::setExpansionAreaHovered(bool hovered
)
307 if (hovered
== m_expansionAreaHovered
) {
310 m_expansionAreaHovered
= hovered
;
314 bool KItemListWidget::expansionAreaHovered() const
316 return m_expansionAreaHovered
;
319 void KItemListWidget::setHoverPosition(const QPointF
&pos
)
321 if (m_selectionToggle
) {
322 m_selectionToggle
->setHovered(selectionToggleRect().contains(pos
));
326 void KItemListWidget::setAlternateBackground(bool enable
)
328 if (m_alternateBackground
!= enable
) {
329 m_alternateBackground
= enable
;
330 alternateBackgroundChanged(enable
);
335 bool KItemListWidget::alternateBackground() const
337 return m_alternateBackground
;
340 void KItemListWidget::setEnabledSelectionToggle(bool enable
)
342 if (m_enabledSelectionToggle
!= enable
) {
343 m_enabledSelectionToggle
= enable
;
345 // We want the change to take effect immediately.
346 if (m_enabledSelectionToggle
) {
348 initializeSelectionToggle();
350 } else if (m_selectionToggle
) {
351 m_selectionToggle
->deleteLater();
352 m_selectionToggle
= nullptr;
359 bool KItemListWidget::enabledSelectionToggle() const
361 return m_enabledSelectionToggle
;
364 void KItemListWidget::setSiblingsInformation(const QBitArray
&siblings
)
366 const QBitArray previous
= m_siblingsInfo
;
367 m_siblingsInfo
= siblings
;
368 siblingsInformationChanged(m_siblingsInfo
, previous
);
372 QBitArray
KItemListWidget::siblingsInformation() const
374 return m_siblingsInfo
;
377 void KItemListWidget::setEditedRole(const QByteArray
&role
)
379 if (m_editedRole
!= role
) {
380 const QByteArray previous
= m_editedRole
;
382 editedRoleChanged(role
, previous
);
386 QByteArray
KItemListWidget::editedRole() const
391 void KItemListWidget::setIconSize(int iconSize
)
393 if (m_iconSize
!= iconSize
) {
394 const int previousIconSize
= m_iconSize
;
395 m_iconSize
= iconSize
;
396 iconSizeChanged(iconSize
, previousIconSize
);
400 int KItemListWidget::iconSize() const
405 bool KItemListWidget::contains(const QPointF
&point
) const
407 if (!QGraphicsWidget::contains(point
)) {
411 return selectionRectFull().contains(point
) || expansionToggleRect().contains(point
);
414 QRectF
KItemListWidget::textFocusRect() const
419 QRectF
KItemListWidget::selectionToggleRect() const
424 QRectF
KItemListWidget::expansionToggleRect() const
429 QPixmap
KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem
*option
, QWidget
*widget
)
431 QPixmap
pixmap(size().toSize() * widget
->devicePixelRatio());
432 pixmap
.setDevicePixelRatio(widget
->devicePixelRatio());
433 pixmap
.fill(Qt::transparent
);
435 QPainter
painter(&pixmap
);
437 const bool oldAlternateBackground
= m_alternateBackground
;
438 const bool wasSelected
= m_selected
;
439 const bool wasHovered
= m_hovered
;
441 setAlternateBackground(false);
444 paint(&painter
, option
, widget
);
446 setAlternateBackground(oldAlternateBackground
);
447 setSelected(wasSelected
);
448 setHovered(wasHovered
);
453 void KItemListWidget::startActivateSoonAnimation(int timeUntilActivation
)
455 Q_UNUSED(timeUntilActivation
)
458 void KItemListWidget::dataChanged(const QHash
<QByteArray
, QVariant
> ¤t
, const QSet
<QByteArray
> &roles
)
464 void KItemListWidget::visibleRolesChanged(const QList
<QByteArray
> ¤t
, const QList
<QByteArray
> &previous
)
470 void KItemListWidget::columnWidthChanged(const QByteArray
&role
, qreal current
, qreal previous
)
477 void KItemListWidget::sidePaddingChanged(qreal leftPaddingWidth
, qreal rightPaddingWidth
)
479 Q_UNUSED(leftPaddingWidth
)
480 Q_UNUSED(rightPaddingWidth
)
483 void KItemListWidget::styleOptionChanged(const KItemListStyleOption
¤t
, const KItemListStyleOption
&previous
)
487 // set the initial value of m_iconSize if not set
488 if (m_iconSize
== -1) {
489 m_iconSize
= current
.iconSize
;
493 void KItemListWidget::currentChanged(bool current
)
498 void KItemListWidget::selectedChanged(bool selected
)
503 void KItemListWidget::hoveredChanged(bool hovered
)
508 void KItemListWidget::alternateBackgroundChanged(bool enabled
)
513 void KItemListWidget::siblingsInformationChanged(const QBitArray
¤t
, const QBitArray
&previous
)
519 void KItemListWidget::editedRoleChanged(const QByteArray
¤t
, const QByteArray
&previous
)
525 void KItemListWidget::iconSizeChanged(int current
, int previous
)
531 void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent
*event
)
533 QGraphicsWidget::resizeEvent(event
);
536 if (m_selectionToggle
) {
537 const QRectF
&toggleRect
= selectionToggleRect();
538 m_selectionToggle
->setPos(toggleRect
.topLeft());
539 m_selectionToggle
->resize(toggleRect
.size());
543 void KItemListWidget::hoverSequenceStarted()
547 void KItemListWidget::hoverSequenceIndexChanged(int sequenceIndex
)
549 Q_UNUSED(sequenceIndex
);
552 void KItemListWidget::hoverSequenceEnded()
556 qreal
KItemListWidget::hoverOpacity() const
558 return m_hoverOpacity
;
561 int KItemListWidget::hoverSequenceIndex() const
563 return m_hoverSequenceIndex
;
566 void KItemListWidget::slotHoverSequenceTimerTimeout()
568 m_hoverSequenceIndex
++;
569 hoverSequenceIndexChanged(m_hoverSequenceIndex
);
572 void KItemListWidget::initializeSelectionToggle()
574 Q_ASSERT(m_enabledSelectionToggle
);
576 if (!m_selectionToggle
) {
577 m_selectionToggle
= new KItemListSelectionToggle(this);
580 const QRectF toggleRect
= selectionToggleRect();
581 m_selectionToggle
->setPos(toggleRect
.topLeft());
582 m_selectionToggle
->resize(toggleRect
.size());
584 m_selectionToggle
->setChecked(isSelected());
587 void KItemListWidget::setHoverOpacity(qreal opacity
)
589 m_hoverOpacity
= opacity
;
590 if (m_selectionToggle
) {
591 m_selectionToggle
->setOpacity(opacity
);
594 if (m_hoverOpacity
<= 0.0) {
596 m_hoverCache
= nullptr;
602 void KItemListWidget::clearHoverCache()
605 m_hoverCache
= nullptr;
608 bool KItemListWidget::isPressed() const
610 return m_clickHighlighted
;
613 void KItemListWidget::setPressed(bool enabled
)
615 if (m_clickHighlighted
!= enabled
) {
616 m_clickHighlighted
= enabled
;
622 void KItemListWidget::drawItemStyleOption(QPainter
*painter
, QWidget
*widget
, QStyle::State styleState
)
624 QStyleOptionViewItem viewItemOption
;
625 constexpr int roundness
= 5; // From Breeze style.
626 constexpr qreal penWidth
= 1.5;
627 initStyleOption(&viewItemOption
);
628 viewItemOption
.state
= styleState
;
629 viewItemOption
.viewItemPosition
= QStyleOptionViewItem::OnlyOne
;
630 viewItemOption
.showDecorationSelected
= true;
631 viewItemOption
.rect
= selectionRectFull().toRect();
633 path
.addRoundedRect(selectionRectFull().adjusted(penWidth
, penWidth
, -penWidth
, -penWidth
), roundness
, roundness
);
634 QColor backgroundColor
{widget
->palette().color(QPalette::Accent
)};
635 painter
->setRenderHint(QPainter::Antialiasing
);
636 bool current
= m_current
&& styleState
& QStyle::State_Active
;
638 // Background item, alpha values are from
639 // https://invent.kde.org/plasma/libplasma/-/blob/master/src/desktoptheme/breeze/widgets/viewitem.svg
640 backgroundColor
.setAlphaF(0.0);
642 if (m_clickHighlighted
) {
643 backgroundColor
.setAlphaF(1.0);
645 if (m_selected
&& m_hovered
) {
646 backgroundColor
.setAlphaF(0.40);
647 } else if (m_selected
) {
648 backgroundColor
.setAlphaF(0.32);
649 } else if (m_hovered
) {
650 backgroundColor
= widget
->palette().color(QPalette::Text
);
651 backgroundColor
.setAlphaF(0.06);
655 painter
->fillPath(path
, backgroundColor
);
659 QColor focusColor
{widget
->palette().color(QPalette::Accent
)};
660 focusColor
= m_styleOption
.palette
.color(QPalette::Base
).lightnessF() > 0.5 ? focusColor
.darker(110) : focusColor
.lighter(110);
661 focusColor
.setAlphaF(m_selected
|| m_hovered
? 0.8 : 0.6);
662 // Set the pen color lighter or darker depending on background color
663 QPen pen
{focusColor
, penWidth
};
664 pen
.setCosmetic(true);
665 painter
->setPen(pen
);
666 painter
->drawPath(path
);
670 #include "moc_kitemlistwidget.cpp"