File indexing completed on 2024-04-28 05:45:10

0001 /*
0002  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
0003  *
0004  * Based on the Itemviews NG project from Trolltech Labs
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "kitemlistwidget.h"
0010 
0011 #include "kitemlistview.h"
0012 #include "private/kitemlistselectiontoggle.h"
0013 
0014 #include <KConfigGroup>
0015 #include <KSharedConfig>
0016 
0017 #include <QApplication>
0018 #include <QPainter>
0019 #include <QPropertyAnimation>
0020 #include <QStyleOption>
0021 
0022 KItemListWidgetInformant::KItemListWidgetInformant()
0023 {
0024 }
0025 
0026 KItemListWidgetInformant::~KItemListWidgetInformant()
0027 {
0028 }
0029 
0030 KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsItem *parent)
0031     : QGraphicsWidget(parent)
0032     , m_informant(informant)
0033     , m_index(-1)
0034     , m_selected(false)
0035     , m_current(false)
0036     , m_hovered(false)
0037     , m_expansionAreaHovered(false)
0038     , m_alternateBackground(false)
0039     , m_enabledSelectionToggle(false)
0040     , m_data()
0041     , m_visibleRoles()
0042     , m_columnWidths()
0043     , m_sidePadding(0)
0044     , m_styleOption()
0045     , m_siblingsInfo()
0046     , m_hoverOpacity(0)
0047     , m_hoverCache(nullptr)
0048     , m_hoverSequenceIndex(0)
0049     , m_selectionToggle(nullptr)
0050     , m_editedRole()
0051     , m_iconSize(-1)
0052 {
0053     connect(&m_hoverSequenceTimer, &QTimer::timeout, this, &KItemListWidget::slotHoverSequenceTimerTimeout);
0054 }
0055 
0056 KItemListWidget::~KItemListWidget()
0057 {
0058     clearHoverCache();
0059 }
0060 
0061 void KItemListWidget::setIndex(int index)
0062 {
0063     if (m_index != index) {
0064         delete m_selectionToggle;
0065         m_selectionToggle = nullptr;
0066 
0067         m_hoverOpacity = 0;
0068 
0069         clearHoverCache();
0070 
0071         m_index = index;
0072     }
0073 }
0074 
0075 int KItemListWidget::index() const
0076 {
0077     return m_index;
0078 }
0079 
0080 void KItemListWidget::setData(const QHash<QByteArray, QVariant> &data, const QSet<QByteArray> &roles)
0081 {
0082     clearHoverCache();
0083     if (roles.isEmpty()) {
0084         m_data = data;
0085         dataChanged(m_data);
0086     } else {
0087         for (const QByteArray &role : roles) {
0088             m_data[role] = data[role];
0089         }
0090         dataChanged(m_data, roles);
0091     }
0092     update();
0093 }
0094 
0095 QHash<QByteArray, QVariant> KItemListWidget::data() const
0096 {
0097     return m_data;
0098 }
0099 
0100 void KItemListWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
0101 {
0102     Q_UNUSED(option)
0103 
0104     if (m_alternateBackground) {
0105         QColor backgroundColor = m_styleOption.palette.color(QPalette::AlternateBase);
0106         if (!widget->hasFocus()) {
0107             QColor baseColor = m_styleOption.palette.color(QPalette::Base);
0108             if (baseColor.lightnessF() > 0.5) {
0109                 // theme seems light
0110                 backgroundColor = backgroundColor.lighter(101);
0111             } else {
0112                 // theme seems dark
0113                 backgroundColor = backgroundColor.darker(101);
0114             }
0115         }
0116 
0117         const QRectF backgroundRect(0, 0, size().width(), size().height());
0118         painter->fillRect(backgroundRect, backgroundColor);
0119     }
0120 
0121     if (m_selected && m_editedRole.isEmpty()) {
0122         const QStyle::State activeState(isActiveWindow() && widget->hasFocus() ? QStyle::State_Active : 0);
0123         drawItemStyleOption(painter, widget, activeState | QStyle::State_Enabled | QStyle::State_Selected | QStyle::State_Item);
0124     }
0125 
0126     if (m_current && m_editedRole.isEmpty()) {
0127         QStyleOptionFocusRect focusRectOption;
0128         initStyleOption(&focusRectOption);
0129         focusRectOption.rect = textFocusRect().toRect();
0130         focusRectOption.state = QStyle::State_Enabled | QStyle::State_Item | QStyle::State_KeyboardFocusChange;
0131         if (m_selected && widget->hasFocus()) {
0132             focusRectOption.state |= QStyle::State_Selected;
0133         }
0134 
0135         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusRectOption, painter, widget);
0136     }
0137 
0138     if (m_hoverOpacity > 0.0) {
0139         if (!m_hoverCache) {
0140             // Initialize the m_hoverCache pixmap to improve the drawing performance
0141             // when fading the hover background
0142             m_hoverCache = new QPixmap(size().toSize());
0143             m_hoverCache->fill(Qt::transparent);
0144 
0145             QPainter pixmapPainter(m_hoverCache);
0146             const QStyle::State activeState(isActiveWindow() && widget->hasFocus() ? QStyle::State_Active | QStyle::State_Enabled : 0);
0147             drawItemStyleOption(&pixmapPainter, widget, activeState | QStyle::State_MouseOver | QStyle::State_Item);
0148         }
0149 
0150         const qreal opacity = painter->opacity();
0151         painter->setOpacity(m_hoverOpacity * opacity);
0152         painter->drawPixmap(0, 0, *m_hoverCache);
0153         painter->setOpacity(opacity);
0154     }
0155 }
0156 
0157 void KItemListWidget::setVisibleRoles(const QList<QByteArray> &roles)
0158 {
0159     const QList<QByteArray> previousRoles = m_visibleRoles;
0160     m_visibleRoles = roles;
0161 
0162     visibleRolesChanged(roles, previousRoles);
0163     update();
0164 }
0165 
0166 QList<QByteArray> KItemListWidget::visibleRoles() const
0167 {
0168     return m_visibleRoles;
0169 }
0170 
0171 void KItemListWidget::setColumnWidth(const QByteArray &role, qreal width)
0172 {
0173     const qreal previousWidth = m_columnWidths.value(role);
0174     if (previousWidth != width) {
0175         m_columnWidths.insert(role, width);
0176         columnWidthChanged(role, width, previousWidth);
0177         update();
0178     }
0179 }
0180 
0181 qreal KItemListWidget::columnWidth(const QByteArray &role) const
0182 {
0183     return m_columnWidths.value(role);
0184 }
0185 
0186 qreal KItemListWidget::sidePadding() const
0187 {
0188     return m_sidePadding;
0189 }
0190 
0191 void KItemListWidget::setSidePadding(qreal width)
0192 {
0193     if (m_sidePadding != width) {
0194         m_sidePadding = width;
0195         sidePaddingChanged(width);
0196         update();
0197     }
0198 }
0199 
0200 void KItemListWidget::setStyleOption(const KItemListStyleOption &option)
0201 {
0202     if (m_styleOption == option) {
0203         return;
0204     }
0205 
0206     const KItemListStyleOption previous = m_styleOption;
0207     clearHoverCache();
0208     m_styleOption = option;
0209     styleOptionChanged(option, previous);
0210     update();
0211 }
0212 
0213 const KItemListStyleOption &KItemListWidget::styleOption() const
0214 {
0215     return m_styleOption;
0216 }
0217 
0218 void KItemListWidget::setSelected(bool selected)
0219 {
0220     if (m_selected != selected) {
0221         m_selected = selected;
0222         if (m_selectionToggle) {
0223             m_selectionToggle->setChecked(selected);
0224         }
0225         selectedChanged(selected);
0226         update();
0227     }
0228 }
0229 
0230 bool KItemListWidget::isSelected() const
0231 {
0232     return m_selected;
0233 }
0234 
0235 void KItemListWidget::setCurrent(bool current)
0236 {
0237     if (m_current != current) {
0238         m_current = current;
0239         currentChanged(current);
0240         update();
0241     }
0242 }
0243 
0244 bool KItemListWidget::isCurrent() const
0245 {
0246     return m_current;
0247 }
0248 
0249 void KItemListWidget::setHovered(bool hovered)
0250 {
0251     if (hovered == m_hovered) {
0252         return;
0253     }
0254 
0255     m_hovered = hovered;
0256 
0257     m_hoverSequenceIndex = 0;
0258 
0259     if (hovered) {
0260         setHoverOpacity(1.0);
0261 
0262         if (m_enabledSelectionToggle && !(QApplication::mouseButtons() & Qt::LeftButton)) {
0263             initializeSelectionToggle();
0264         }
0265 
0266         hoverSequenceStarted();
0267 
0268         const KConfigGroup globalConfig(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings"));
0269         const int interval = globalConfig.readEntry("HoverSequenceInterval", 700);
0270 
0271         m_hoverSequenceTimer.start(interval);
0272     } else {
0273         setHoverOpacity(0.0);
0274 
0275         if (m_selectionToggle) {
0276             m_selectionToggle->deleteLater();
0277             m_selectionToggle = nullptr;
0278         }
0279 
0280         hoverSequenceEnded();
0281         m_hoverSequenceTimer.stop();
0282     }
0283 
0284     hoveredChanged(hovered);
0285     update();
0286 }
0287 
0288 bool KItemListWidget::isHovered() const
0289 {
0290     return m_hovered;
0291 }
0292 
0293 void KItemListWidget::setExpansionAreaHovered(bool hovered)
0294 {
0295     if (hovered == m_expansionAreaHovered) {
0296         return;
0297     }
0298     m_expansionAreaHovered = hovered;
0299     update();
0300 }
0301 
0302 bool KItemListWidget::expansionAreaHovered() const
0303 {
0304     return m_expansionAreaHovered;
0305 }
0306 
0307 void KItemListWidget::setHoverPosition(const QPointF &pos)
0308 {
0309     if (m_selectionToggle) {
0310         m_selectionToggle->setHovered(selectionToggleRect().contains(pos));
0311     }
0312 }
0313 
0314 void KItemListWidget::setAlternateBackground(bool enable)
0315 {
0316     if (m_alternateBackground != enable) {
0317         m_alternateBackground = enable;
0318         alternateBackgroundChanged(enable);
0319         update();
0320     }
0321 }
0322 
0323 bool KItemListWidget::alternateBackground() const
0324 {
0325     return m_alternateBackground;
0326 }
0327 
0328 void KItemListWidget::setEnabledSelectionToggle(bool enable)
0329 {
0330     if (m_enabledSelectionToggle != enable) {
0331         m_enabledSelectionToggle = enable;
0332 
0333         // We want the change to take effect immediately.
0334         if (m_enabledSelectionToggle) {
0335             if (m_hovered) {
0336                 initializeSelectionToggle();
0337             }
0338         } else if (m_selectionToggle) {
0339             m_selectionToggle->deleteLater();
0340             m_selectionToggle = nullptr;
0341         }
0342 
0343         update();
0344     }
0345 }
0346 
0347 bool KItemListWidget::enabledSelectionToggle() const
0348 {
0349     return m_enabledSelectionToggle;
0350 }
0351 
0352 void KItemListWidget::setSiblingsInformation(const QBitArray &siblings)
0353 {
0354     const QBitArray previous = m_siblingsInfo;
0355     m_siblingsInfo = siblings;
0356     siblingsInformationChanged(m_siblingsInfo, previous);
0357     update();
0358 }
0359 
0360 QBitArray KItemListWidget::siblingsInformation() const
0361 {
0362     return m_siblingsInfo;
0363 }
0364 
0365 void KItemListWidget::setEditedRole(const QByteArray &role)
0366 {
0367     if (m_editedRole != role) {
0368         const QByteArray previous = m_editedRole;
0369         m_editedRole = role;
0370         editedRoleChanged(role, previous);
0371     }
0372 }
0373 
0374 QByteArray KItemListWidget::editedRole() const
0375 {
0376     return m_editedRole;
0377 }
0378 
0379 void KItemListWidget::setIconSize(int iconSize)
0380 {
0381     if (m_iconSize != iconSize) {
0382         const int previousIconSize = m_iconSize;
0383         m_iconSize = iconSize;
0384         iconSizeChanged(iconSize, previousIconSize);
0385     }
0386 }
0387 
0388 int KItemListWidget::iconSize() const
0389 {
0390     return m_iconSize;
0391 }
0392 
0393 bool KItemListWidget::contains(const QPointF &point) const
0394 {
0395     if (!QGraphicsWidget::contains(point)) {
0396         return false;
0397     }
0398 
0399     return iconRect().contains(point) || textRect().contains(point) || expansionToggleRect().contains(point) || selectionToggleRect().contains(point);
0400 }
0401 
0402 QRectF KItemListWidget::textFocusRect() const
0403 {
0404     return textRect();
0405 }
0406 
0407 QRectF KItemListWidget::selectionToggleRect() const
0408 {
0409     return QRectF();
0410 }
0411 
0412 QRectF KItemListWidget::expansionToggleRect() const
0413 {
0414     return QRectF();
0415 }
0416 
0417 QPixmap KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem *option, QWidget *widget)
0418 {
0419     QPixmap pixmap(size().toSize() * widget->devicePixelRatio());
0420     pixmap.setDevicePixelRatio(widget->devicePixelRatio());
0421     pixmap.fill(Qt::transparent);
0422 
0423     QPainter painter(&pixmap);
0424 
0425     const bool oldAlternateBackground = m_alternateBackground;
0426     const bool wasSelected = m_selected;
0427     const bool wasHovered = m_hovered;
0428 
0429     setAlternateBackground(false);
0430     setHovered(false);
0431 
0432     paint(&painter, option, widget);
0433 
0434     setAlternateBackground(oldAlternateBackground);
0435     setSelected(wasSelected);
0436     setHovered(wasHovered);
0437 
0438     return pixmap;
0439 }
0440 
0441 void KItemListWidget::dataChanged(const QHash<QByteArray, QVariant> &current, const QSet<QByteArray> &roles)
0442 {
0443     Q_UNUSED(current)
0444     Q_UNUSED(roles)
0445 }
0446 
0447 void KItemListWidget::visibleRolesChanged(const QList<QByteArray> &current, const QList<QByteArray> &previous)
0448 {
0449     Q_UNUSED(current)
0450     Q_UNUSED(previous)
0451 }
0452 
0453 void KItemListWidget::columnWidthChanged(const QByteArray &role, qreal current, qreal previous)
0454 {
0455     Q_UNUSED(role)
0456     Q_UNUSED(current)
0457     Q_UNUSED(previous)
0458 }
0459 
0460 void KItemListWidget::sidePaddingChanged(qreal width)
0461 {
0462     Q_UNUSED(width)
0463 }
0464 
0465 void KItemListWidget::styleOptionChanged(const KItemListStyleOption &current, const KItemListStyleOption &previous)
0466 {
0467     Q_UNUSED(previous)
0468 
0469     // set the initial value of m_iconSize if not set
0470     if (m_iconSize == -1) {
0471         m_iconSize = current.iconSize;
0472     }
0473 }
0474 
0475 void KItemListWidget::currentChanged(bool current)
0476 {
0477     Q_UNUSED(current)
0478 }
0479 
0480 void KItemListWidget::selectedChanged(bool selected)
0481 {
0482     Q_UNUSED(selected)
0483 }
0484 
0485 void KItemListWidget::hoveredChanged(bool hovered)
0486 {
0487     Q_UNUSED(hovered)
0488 }
0489 
0490 void KItemListWidget::alternateBackgroundChanged(bool enabled)
0491 {
0492     Q_UNUSED(enabled)
0493 }
0494 
0495 void KItemListWidget::siblingsInformationChanged(const QBitArray &current, const QBitArray &previous)
0496 {
0497     Q_UNUSED(current)
0498     Q_UNUSED(previous)
0499 }
0500 
0501 void KItemListWidget::editedRoleChanged(const QByteArray &current, const QByteArray &previous)
0502 {
0503     Q_UNUSED(current)
0504     Q_UNUSED(previous)
0505 }
0506 
0507 void KItemListWidget::iconSizeChanged(int current, int previous)
0508 {
0509     Q_UNUSED(current)
0510     Q_UNUSED(previous)
0511 }
0512 
0513 void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent *event)
0514 {
0515     QGraphicsWidget::resizeEvent(event);
0516     clearHoverCache();
0517 
0518     if (m_selectionToggle) {
0519         const QRectF &toggleRect = selectionToggleRect();
0520         m_selectionToggle->setPos(toggleRect.topLeft());
0521         m_selectionToggle->resize(toggleRect.size());
0522     }
0523 }
0524 
0525 void KItemListWidget::hoverSequenceStarted()
0526 {
0527 }
0528 
0529 void KItemListWidget::hoverSequenceIndexChanged(int sequenceIndex)
0530 {
0531     Q_UNUSED(sequenceIndex);
0532 }
0533 
0534 void KItemListWidget::hoverSequenceEnded()
0535 {
0536 }
0537 
0538 qreal KItemListWidget::hoverOpacity() const
0539 {
0540     return m_hoverOpacity;
0541 }
0542 
0543 int KItemListWidget::hoverSequenceIndex() const
0544 {
0545     return m_hoverSequenceIndex;
0546 }
0547 
0548 void KItemListWidget::slotHoverSequenceTimerTimeout()
0549 {
0550     m_hoverSequenceIndex++;
0551     hoverSequenceIndexChanged(m_hoverSequenceIndex);
0552 }
0553 
0554 void KItemListWidget::initializeSelectionToggle()
0555 {
0556     Q_ASSERT(m_enabledSelectionToggle);
0557 
0558     if (!m_selectionToggle) {
0559         m_selectionToggle = new KItemListSelectionToggle(this);
0560     }
0561 
0562     const QRectF toggleRect = selectionToggleRect();
0563     m_selectionToggle->setPos(toggleRect.topLeft());
0564     m_selectionToggle->resize(toggleRect.size());
0565 
0566     m_selectionToggle->setChecked(isSelected());
0567 }
0568 
0569 void KItemListWidget::setHoverOpacity(qreal opacity)
0570 {
0571     m_hoverOpacity = opacity;
0572     if (m_selectionToggle) {
0573         m_selectionToggle->setOpacity(opacity);
0574     }
0575 
0576     if (m_hoverOpacity <= 0.0) {
0577         delete m_hoverCache;
0578         m_hoverCache = nullptr;
0579     }
0580 
0581     update();
0582 }
0583 
0584 void KItemListWidget::clearHoverCache()
0585 {
0586     delete m_hoverCache;
0587     m_hoverCache = nullptr;
0588 }
0589 
0590 void KItemListWidget::drawItemStyleOption(QPainter *painter, QWidget *widget, QStyle::State styleState)
0591 {
0592     QStyleOptionViewItem viewItemOption;
0593     initStyleOption(&viewItemOption);
0594     viewItemOption.state = styleState;
0595     viewItemOption.viewItemPosition = QStyleOptionViewItem::OnlyOne;
0596     viewItemOption.showDecorationSelected = true;
0597     viewItemOption.rect = selectionRect().toRect();
0598     style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, painter, widget);
0599 }
0600 
0601 #include "moc_kitemlistwidget.cpp"