X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/b039392f344f4d8b0a540c542f83e418349a7d4e..8f7c3619ec734bbf31ae81315a6548db9cbd19aa:/src/kitemviews/kstandarditemlistwidget.cpp diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 3a76f14a2..4b9f33b6f 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -42,6 +42,7 @@ #include #include #include +#include // #define KSTANDARDITEMLISTWIDGET_DEBUG @@ -56,13 +57,12 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant() QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemListView* view) const { - const QHash values = view->model()->data(index); const KItemListStyleOption& option = view->styleOption(); const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); switch (static_cast(view)->itemLayout()) { case KStandardItemListWidget::IconsLayout: { - const QString text = KStringHandler::preProcessWrap(values["text"].toString()); + const QString text = KStringHandler::preProcessWrap(itemText(index, view)); const qreal itemWidth = view->itemSize().width(); const qreal maxWidth = itemWidth - 2 * option.padding; @@ -99,6 +99,7 @@ QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemList // to show all roles without horizontal clipping. qreal maximumRequiredWidth = 0.0; + const QHash values = view->model()->data(index); foreach (const QByteArray& role, view->visibleRoles()) { const QString text = roleText(role, values); const qreal requiredWidth = option.fontMetrics.width(text); @@ -158,6 +159,11 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra return width; } +QString KStandardItemListWidgetInformant::itemText(int index, const KItemListView* view) const +{ + return view->model()->data(index).value("text").toString(); +} + QString KStandardItemListWidgetInformant::roleText(const QByteArray& role, const QHash& values) const { @@ -193,7 +199,8 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* infor m_additionalInfoTextColor(), m_overlay(), m_rating(), - m_roleEditor(0) + m_roleEditor(0), + m_oldRoleEditor(0) { } @@ -202,7 +209,13 @@ KStandardItemListWidget::~KStandardItemListWidget() qDeleteAll(m_textInfo); m_textInfo.clear(); - delete m_roleEditor; + if (m_roleEditor) { + m_roleEditor->deleteLater(); + } + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } } void KStandardItemListWidget::setLayout(Layout layout) @@ -246,23 +259,63 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic const KItemListStyleOption& itemListStyleOption = styleOption(); if (isHovered()) { - // Blend the unhovered and hovered pixmap if the hovering - // animation is ongoing if (hoverOpacity() < 1.0) { - drawPixmap(painter, m_pixmap); - } + /* + * Linear interpolation between m_pixmap and m_hoverPixmap. + * + * Note that this cannot be achieved by painting m_hoverPixmap over + * m_pixmap, even if the opacities are adjusted. For details see + * https://git.reviewboard.kde.org/r/109614/ + */ + // Paint pixmap1 so that pixmap1 = m_pixmap * (1.0 - hoverOpacity()) + QPixmap pixmap1(m_pixmap.size()); + pixmap1.fill(Qt::transparent); + { + QPainter p(&pixmap1); + p.setOpacity(1.0 - hoverOpacity()); + p.drawPixmap(0, 0, m_pixmap); + } + + // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity() + QPixmap pixmap2(pixmap1.size()); + pixmap2.fill(Qt::transparent); + { + QPainter p(&pixmap2); + p.setOpacity(hoverOpacity()); + p.drawPixmap(0, 0, m_hoverPixmap); + } - const qreal opacity = painter->opacity(); - painter->setOpacity(hoverOpacity() * opacity); - drawPixmap(painter, m_hoverPixmap); - painter->setOpacity(opacity); + // Paint pixmap2 on pixmap1 using CompositionMode_Plus + // Now pixmap1 = pixmap2 + m_pixmap * (1.0 - hoverOpacity()) + // = m_hoverPixmap * hoverOpacity() + m_pixmap * (1.0 - hoverOpacity()) + { + QPainter p(&pixmap1); + p.setCompositionMode(QPainter::CompositionMode_Plus); + p.drawPixmap(0, 0, pixmap2); + } + + // Finally paint pixmap1 on the widget + drawPixmap(painter, pixmap1); + } else { + drawPixmap(painter, m_hoverPixmap); + } } else { drawPixmap(painter, m_pixmap); } painter->setFont(m_customizedFont); - painter->setPen(m_isHidden ? m_additionalInfoTextColor : textColor()); + painter->setPen(textColor()); const TextInfo* textInfo = m_textInfo.value("text"); + + if (!textInfo) { + // It seems that we can end up here even if m_textInfo does not contain + // the key "text", see bug 306167. According to triggerCacheRefreshing(), + // this can only happen if the index is negative. This can happen when + // the item is about to be removed, see KItemListView::slotItemsRemoved(). + // TODO: try to reproduce the crash and find a better fix. + return; + } + painter->drawStaticText(textInfo->pos, textInfo->staticText); bool clipAdditionalInfoBounds = false; @@ -305,6 +358,9 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic painter->setPen(Qt::green); painter->drawRect(m_iconRect); + painter->setPen(Qt::blue); + painter->drawRect(m_textRect); + painter->setPen(Qt::red); painter->drawText(QPointF(0, m_customizedFontMetrics.height()), QString::number(index())); painter->drawRect(rect()); @@ -480,8 +536,12 @@ void KStandardItemListWidget::setTextColor(const QColor& color) QColor KStandardItemListWidget::textColor() const { - if (m_customTextColor.isValid() && !isSelected()) { - return m_customTextColor; + if (!isSelected()) { + if (m_isHidden) { + return m_additionalInfoTextColor; + } else if (m_customTextColor.isValid()) { + return m_customTextColor; + } } const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; @@ -590,11 +650,21 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const { Q_UNUSED(previous); - QGraphicsView* parent = scene()->views()[0]; - if (current.isEmpty() || !parent || current != "text") { + QGraphicsView* parent = scene()->views()[0]; + if (current.isEmpty() || !parent || current != "text") { if (m_roleEditor) { emit roleEditingCanceled(index(), current, data().value(current)); - m_roleEditor->deleteLater(); + + disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } + m_oldRoleEditor = m_roleEditor; + m_roleEditor->hide(); m_roleEditor = 0; } return; @@ -605,7 +675,6 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const const TextInfo* textInfo = m_textInfo.value("text"); m_roleEditor = new KItemListRoleEditor(parent); - m_roleEditor->setIndex(index()); m_roleEditor->setRole(current); m_roleEditor->setFont(styleOption().font); @@ -624,10 +693,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const m_roleEditor->setTextCursor(cursor); } - connect(m_roleEditor, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); - connect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + connect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + connect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); // Adjust the geometry of the editor QRectF rect = roleEditingRect(current); @@ -688,21 +757,19 @@ void KStandardItemListWidget::slotCutItemsChanged() } } -void KStandardItemListWidget::slotRoleEditingCanceled(int index, - const QByteArray& role, +void KStandardItemListWidget::slotRoleEditingCanceled(const QByteArray& role, const QVariant& value) { closeRoleEditor(); - emit roleEditingCanceled(index, role, value); + emit roleEditingCanceled(index(), role, value); setEditedRole(QByteArray()); } -void KStandardItemListWidget::slotRoleEditingFinished(int index, - const QByteArray& role, +void KStandardItemListWidget::slotRoleEditingFinished(const QByteArray& role, const QVariant& value) { closeRoleEditor(); - emit roleEditingFinished(index, role, value); + emit roleEditingFinished(index(), role, value); setEditedRole(QByteArray()); } @@ -733,7 +800,6 @@ void KStandardItemListWidget::updateExpansionArea() { if (m_supportsItemExpanding) { const QHash values = data(); - Q_ASSERT(values.contains("expandedParentsCount")); const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); if (expandedParentsCount >= 0) { const qreal widgetHeight = size().height(); @@ -781,29 +847,14 @@ void KStandardItemListWidget::updatePixmapCache() // use a generic icon as fallback iconName = QLatin1String("unknown"); } - m_pixmap = pixmapForIcon(iconName, maxIconHeight); + const QStringList overlays = values["iconOverlays"].toStringList(); + m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight); } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) { // A custom pixmap has been applied. Assure that the pixmap // is scaled to the maximum available size. KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight)); } - const QStringList overlays = values["iconOverlays"].toStringList(); - - // Strangely KFileItem::overlays() returns empty string-values, so - // we need to check first whether an overlay must be drawn at all. - // It is more efficient to do it here, as KIconLoader::drawOverlays() - // assumes that an overlay will be drawn and has some additional - // setup time. - foreach (const QString& overlay, overlays) { - if (!overlay.isEmpty()) { - // There is at least one overlay, draw all overlays above m_pixmap - // and cancel the check - KIconLoader::global()->drawOverlays(overlays, m_pixmap, KIconLoader::Desktop); - break; - } - } - if (m_isCut) { KIconEffect* effect = KIconLoader::global()->iconEffect(); m_pixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); @@ -816,7 +867,7 @@ void KStandardItemListWidget::updatePixmapCache() if (isSelected()) { const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); QImage image = m_pixmap.toImage(); - KIconEffect::colorize(image, color, 1.0f); + KIconEffect::colorize(image, color, 0.8f); m_pixmap = QPixmap::fromImage(image); } } @@ -984,12 +1035,15 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() const int textLength = line.textStart() + line.textLength(); if (textLength < nameText.length()) { // Elide the last line of the text - QString lastTextLine = nameText.mid(line.textStart(), line.textLength()); + QString lastTextLine = nameText.mid(line.textStart()); lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, Qt::ElideRight, - line.naturalTextWidth() - 1); + maxWidth); const QString elidedText = nameText.left(line.textStart()) + lastTextLine; nameTextInfo->staticText.setText(elidedText); + + const qreal lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width(); + nameWidth = qMax(nameWidth, lastLineWidth); } break; } @@ -1092,7 +1146,7 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() y += lineSpacing; } - m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight); + m_textRect = QRectF(x - 2 * option.padding, 0, maximumRequiredTextWidth + 3 * option.padding, widgetHeight); } void KStandardItemListWidget::updateDetailsLayoutTextCache() @@ -1150,8 +1204,8 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() const qreal textWidth = option.extendedSelectionRegion ? size().width() - textInfo->pos.x() : requiredWidth + 2 * option.padding; - m_textRect = QRectF(textInfo->pos.x() - option.padding, 0, - textWidth, size().height()); + m_textRect = QRectF(textInfo->pos.x() - 2 * option.padding, 0, + textWidth + option.padding, size().height()); // The column after the name should always be aligned on the same x-position independent // from the expansion-level shown in the name column @@ -1207,6 +1261,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter) QRect siblingRect(x, 0, siblingSize, siblingSize); QStyleOption option; + option.palette.setColor(QPalette::Text, option.palette.color(normalTextColorRole())); bool isItemSibling = true; const QBitArray siblings = siblingsInformation(); @@ -1248,41 +1303,72 @@ QRectF KStandardItemListWidget::roleEditingRect(const QByteArray& role) const void KStandardItemListWidget::closeRoleEditor() { + disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)), + this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant))); + disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)), + this, SLOT(slotRoleEditingFinished(QByteArray,QVariant))); + if (m_roleEditor->hasFocus()) { // If the editing was not ended by a FocusOut event, we have // to transfer the keyboard focus back to the KItemListContainer. scene()->views()[0]->parentWidget()->setFocus(); } - m_roleEditor->deleteLater(); + + if (m_oldRoleEditor) { + m_oldRoleEditor->deleteLater(); + } + m_oldRoleEditor = m_roleEditor; + m_roleEditor->hide(); m_roleEditor = 0; } -QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, int size) +QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size) { - const KIcon icon(name); - - int requestedSize; - if (size <= KIconLoader::SizeSmall) { - requestedSize = KIconLoader::SizeSmall; - } else if (size <= KIconLoader::SizeSmallMedium) { - requestedSize = KIconLoader::SizeSmallMedium; - } else if (size <= KIconLoader::SizeMedium) { - requestedSize = KIconLoader::SizeMedium; - } else if (size <= KIconLoader::SizeLarge) { - requestedSize = KIconLoader::SizeLarge; - } else if (size <= KIconLoader::SizeHuge) { - requestedSize = KIconLoader::SizeHuge; - } else if (size <= KIconLoader::SizeEnormous) { - requestedSize = KIconLoader::SizeEnormous; - } else if (size <= KIconLoader::SizeEnormous * 2) { - requestedSize = KIconLoader::SizeEnormous * 2; - } else { - requestedSize = size; - } + const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(":") % ":" % QString::number(size); + QPixmap pixmap; + + if (!QPixmapCache::find(key, pixmap)) { + const KIcon icon(name); + + int requestedSize; + if (size <= KIconLoader::SizeSmall) { + requestedSize = KIconLoader::SizeSmall; + } else if (size <= KIconLoader::SizeSmallMedium) { + requestedSize = KIconLoader::SizeSmallMedium; + } else if (size <= KIconLoader::SizeMedium) { + requestedSize = KIconLoader::SizeMedium; + } else if (size <= KIconLoader::SizeLarge) { + requestedSize = KIconLoader::SizeLarge; + } else if (size <= KIconLoader::SizeHuge) { + requestedSize = KIconLoader::SizeHuge; + } else if (size <= KIconLoader::SizeEnormous) { + requestedSize = KIconLoader::SizeEnormous; + } else if (size <= KIconLoader::SizeEnormous * 2) { + requestedSize = KIconLoader::SizeEnormous * 2; + } else { + requestedSize = size; + } + + pixmap = icon.pixmap(requestedSize, requestedSize); + if (requestedSize != size) { + KPixmapModifier::scale(pixmap, QSize(size, size)); + } + + // Strangely KFileItem::overlays() returns empty string-values, so + // we need to check first whether an overlay must be drawn at all. + // It is more efficient to do it here, as KIconLoader::drawOverlays() + // assumes that an overlay will be drawn and has some additional + // setup time. + foreach (const QString& overlay, overlays) { + if (!overlay.isEmpty()) { + // There is at least one overlay, draw all overlays above m_pixmap + // and cancel the check + KIconLoader::global()->drawOverlays(overlays, pixmap, KIconLoader::Desktop); + break; + } + } - QPixmap pixmap = icon.pixmap(requestedSize, requestedSize); - if (requestedSize != size) { - KPixmapModifier::scale(pixmap, QSize(size, size)); + QPixmapCache::insert(key, pixmap); } return pixmap;