void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current, const QSet<QByteArray>& roles) { Q_UNUSED(current); m_dirtyContent = true; QSet<QByteArray> dirtyRoles; if (roles.isEmpty()) { dirtyRoles = visibleRoles().toSet(); } else { dirtyRoles = roles; } // The icon-state might depend from other roles and hence is // marked as dirty whenever a role has been changed dirtyRoles.insert("iconPixmap"); dirtyRoles.insert("iconName"); QSetIterator<QByteArray> it(dirtyRoles); while (it.hasNext()) { const QByteArray& role = it.next(); m_dirtyContentRoles.insert(role); } }
void KFileItemListView::applyRolesToModel() { if (!model()) { return; } Q_ASSERT(qobject_cast<KFileItemModel*>(model())); KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model()); // KFileItemModel does not distinct between "visible" and "invisible" roles. // Add all roles that are mandatory for having a working KFileItemListView: QSet<QByteArray> roles = visibleRoles().toSet(); roles.insert("iconPixmap"); roles.insert("iconName"); roles.insert("text"); roles.insert("isDir"); roles.insert("isLink"); if (supportsItemExpanding()) { roles.insert("isExpanded"); roles.insert("isExpandable"); roles.insert("expandedParentsCount"); } // Assure that the role that is used for sorting will be determined roles.insert(fileItemModel->sortRole()); fileItemModel->setRoles(roles); m_modelRolesUpdater->setRoles(roles); }
void KFileItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) { const QByteArray sortRole = model()->sortRole(); if (!visibleRoles().contains(sortRole)) { applyRolesToModel(); } KStandardItemListView::slotSortRoleChanged(current, previous); }
void KStandardItemListWidget::updateCompactLayoutTextCache() { // +------+ Name role // | Icon | Additional role 1 // +------+ Additional role 2 const QHash<QByteArray, QVariant> values = data(); const KItemListStyleOption& option = styleOption(); const qreal widgetHeight = size().height(); const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * lineSpacing; const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; qreal maximumRequiredTextWidth = 0; const qreal x = option.padding * 3 + scaledIconSize; qreal y = qRound((widgetHeight - textLinesHeight) / 2); const qreal maxWidth = size().width() - x - option.padding; foreach (const QByteArray& role, m_sortedVisibleRoles) { const QString text = roleText(role, values); TextInfo* textInfo = m_textInfo.value(role); textInfo->staticText.setText(text); qreal requiredWidth = m_customizedFontMetrics.width(text); if (requiredWidth > maxWidth) { requiredWidth = maxWidth; const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); textInfo->staticText.setText(elidedText); } textInfo->pos = QPointF(x, y); textInfo->staticText.setTextWidth(maxWidth); maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth); y += lineSpacing; } m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight); }
void KStandardItemListWidget::updateIconsLayoutTextCache() { // +------+ // | Icon | // +------+ // // Name role that // might get wrapped above // several lines. // Additional role 1 // Additional role 2 const QHash<QByteArray, QVariant> values = data(); const KItemListStyleOption& option = styleOption(); const qreal padding = option.padding; const qreal maxWidth = size().width() - 2 * padding; const qreal widgetHeight = size().height(); const qreal lineSpacing = m_customizedFontMetrics.lineSpacing(); // Initialize properties for the "text" role. It will be used as anchor // for initializing the position of the other roles. TextInfo* nameTextInfo = m_textInfo.value("text"); const QString nameText = KStringHandler::preProcessWrap(values["text"].toString()); nameTextInfo->staticText.setText(nameText); // Calculate the number of lines required for the name and the required width qreal nameWidth = 0; qreal nameHeight = 0; QTextLine line; const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); const int maxNameLines = (option.maxTextSize.height() / int(lineSpacing)) - additionalRolesCount; QTextLayout layout(nameTextInfo->staticText.text(), m_customizedFont); layout.setTextOption(nameTextInfo->staticText.textOption()); layout.beginLayout(); int nameLineIndex = 0; while ((line = layout.createLine()).isValid()) { line.setLineWidth(maxWidth); nameWidth = qMax(nameWidth, line.naturalTextWidth()); nameHeight += line.height(); ++nameLineIndex; if (nameLineIndex == maxNameLines) { // The maximum number of textlines has been reached. If this is // the case provide an elided text if necessary. 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()); lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, Qt::ElideRight, line.naturalTextWidth() - 1); const QString elidedText = nameText.left(line.textStart()) + lastTextLine; nameTextInfo->staticText.setText(elidedText); } break; } } layout.endLayout(); // Use one line for each additional information nameTextInfo->staticText.setTextWidth(maxWidth); nameTextInfo->pos = QPointF(padding, widgetHeight - nameHeight - additionalRolesCount * lineSpacing - padding); m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2, nameTextInfo->pos.y(), nameWidth, nameHeight); // Calculate the position for each additional information qreal y = nameTextInfo->pos.y() + nameHeight; foreach (const QByteArray& role, m_sortedVisibleRoles) { if (role == "text") { continue; } const QString text = roleText(role, values); TextInfo* textInfo = m_textInfo.value(role); textInfo->staticText.setText(text); qreal requiredWidth = 0; QTextLayout layout(text, m_customizedFont); QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); layout.setTextOption(textOption); layout.beginLayout(); QTextLine textLine = layout.createLine(); if (textLine.isValid()) { textLine.setLineWidth(maxWidth); requiredWidth = textLine.naturalTextWidth(); if (requiredWidth > maxWidth) { const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth); textInfo->staticText.setText(elidedText); requiredWidth = m_customizedFontMetrics.width(elidedText); } else if (role == "rating") { // Use the width of the rating pixmap, because the rating text is empty. requiredWidth = m_rating.width(); } } layout.endLayout(); textInfo->pos = QPointF(padding, y); textInfo->staticText.setTextWidth(maxWidth); const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing); m_textRect |= textRect; y += lineSpacing; } // Add a padding to the text rectangle m_textRect.adjust(-padding, -padding, padding, padding); }
void KStandardItemListWidget::updatePixmapCache() { // Precondition: Requires already updated m_textPos values to calculate // the remaining height when the alignment is vertical. const QSizeF widgetSize = size(); const bool iconOnTop = (m_layout == IconsLayout); const KItemListStyleOption& option = styleOption(); const qreal padding = option.padding; const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; const int maxIconHeight = option.iconSize; const QHash<QByteArray, QVariant> values = data(); bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight); if (!updatePixmap && m_dirtyContent) { updatePixmap = m_dirtyContentRoles.isEmpty() || m_dirtyContentRoles.contains("iconPixmap") || m_dirtyContentRoles.contains("iconName") || m_dirtyContentRoles.contains("iconOverlays"); } if (updatePixmap) { m_pixmap = values["iconPixmap"].value<QPixmap>(); if (m_pixmap.isNull()) { // Use the icon that fits to the MIME-type QString iconName = values["iconName"].toString(); if (iconName.isEmpty()) { // The icon-name has not been not resolved by KFileItemModelRolesUpdater, // use a generic icon as fallback iconName = QLatin1String("unknown"); } m_pixmap = pixmapForIcon(iconName, 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); } if (m_isHidden) { KIconEffect::semiTransparent(m_pixmap); } if (isSelected()) { const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); QImage image = m_pixmap.toImage(); KIconEffect::colorize(image, color, 1.0f); m_pixmap = QPixmap::fromImage(image); } } if (!m_overlay.isNull()) { QPainter painter(&m_pixmap); painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay); } int scaledIconSize = 0; if (iconOnTop) { const TextInfo* textInfo = m_textInfo.value("text"); scaledIconSize = static_cast<int>(textInfo->pos.y() - 2 * padding); } else { const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; const qreal requiredTextHeight = textRowsCount * m_customizedFontMetrics.height(); scaledIconSize = (requiredTextHeight < maxIconHeight) ? widgetSize.height() - 2 * padding : maxIconHeight; } const int maxScaledIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : scaledIconSize; const int maxScaledIconHeight = scaledIconSize; m_scaledPixmapSize = m_pixmap.size(); m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio); if (iconOnTop) { // Center horizontally and align on bottom within the icon-area m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2); m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); } else { // Center horizontally and vertically within the icon-area const TextInfo* textInfo = m_textInfo.value("text"); m_pixmapPos.setX(textInfo->pos.x() - 2 * padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2); m_pixmapPos.setY(padding + (scaledIconSize - m_scaledPixmapSize.height()) / 2); } m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)); // Prepare the pixmap that is used when the item gets hovered if (isHovered()) { m_hoverPixmap = m_pixmap; KIconEffect* effect = KIconLoader::global()->iconEffect(); // In the KIconLoader terminology, active = hover. if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) { m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState); } else { m_hoverPixmap = m_pixmap; } } else if (hoverOpacity() <= 0.0) { // No hover animation is ongoing. Clear m_hoverPixmap to save memory. m_hoverPixmap = QPixmap(); } }