X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/b5b8d48bb7848b15d1dcf99d5ac23f2d2a6d433d..3fc96ef97bbea25418bb22a18e82f6b874eedb38:/src/kitemviews/kfileitemlistwidget.cpp diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index aba5129df..fb0f4df57 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -45,6 +45,7 @@ KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : m_isCut(false), m_isHidden(false), m_isExpandable(false), + m_supportsItemExpanding(false), m_dirtyLayout(true), m_dirtyContent(true), m_dirtyContentRoles(), @@ -52,7 +53,6 @@ KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : m_pixmapPos(), m_pixmap(), m_scaledPixmapSize(), - m_originalPixmapSize(), m_iconRect(), m_hoverPixmap(), m_textPos(), @@ -79,6 +79,7 @@ void KFileItemListWidget::setLayout(Layout layout) if (m_layout != layout) { m_layout = layout; m_dirtyLayout = true; + updateAdditionalInfoTextColor(); update(); } } @@ -88,19 +89,28 @@ KFileItemListWidget::Layout KFileItemListWidget::layout() const return m_layout; } +void KFileItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding) +{ + if (m_supportsItemExpanding != supportsItemExpanding) { + m_supportsItemExpanding = supportsItemExpanding; + m_dirtyLayout = true; + update(); + } +} + +bool KFileItemListWidget::supportsItemExpanding() const +{ + return m_supportsItemExpanding; +} + void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { const_cast(this)->triggerCacheRefreshing(); KItemListWidget::paint(painter, option, widget); - // Draw expansion toggle '>' or 'V' - if (m_isExpandable && !m_expansionArea.isEmpty()) { - QStyleOption arrowOption; - arrowOption.rect = m_expansionArea.toRect(); - const QStyle::PrimitiveElement arrow = data()["isExpanded"].toBool() - ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight; - style()->drawPrimitive(arrow, &arrowOption, painter); + if (!m_expansionArea.isEmpty()) { + drawSiblingsInformation(painter); } const KItemListStyleOption& itemListStyleOption = styleOption(); @@ -124,11 +134,11 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte painter->drawStaticText(m_textPos[Name], m_text[Name]); bool clipAdditionalInfoBounds = false; - if (m_layout == DetailsLayout) { + if (m_supportsItemExpanding) { // Prevent a possible overlapping of the additional-information texts // with the icon. This can happen if the user has minimized the width // of the name-column to a very small value. - const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.margin; + const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding; if (m_textPos[Name + 1].x() < minX) { clipAdditionalInfoBounds = true; painter->save(); @@ -147,8 +157,11 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte } #ifdef KFILEITEMLISTWIDGET_DEBUG - painter->setPen(Qt::red); painter->setBrush(Qt::NoBrush); + painter->setPen(Qt::green); + painter->drawRect(m_iconRect); + + painter->setPen(Qt::red); painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index())); painter->drawRect(rect()); #endif @@ -176,7 +189,7 @@ QRectF KFileItemListWidget::selectionToggleRect() const { const_cast(this)->triggerCacheRefreshing(); - const int iconHeight = m_pixmap.height(); + const int iconHeight = styleOption().iconSize; int toggleSize = KIconLoader::SizeSmall; if (iconHeight >= KIconLoader::SizeEnormous) { @@ -196,10 +209,12 @@ QRectF KFileItemListWidget::selectionToggleRect() const const int minMargin = 2; if (toggleSize + minMargin * 2 >= widgetHeight) { + pos.rx() -= (widgetHeight - toggleSize) / 2; toggleSize = widgetHeight; pos.setY(0); } if (toggleSize + minMargin * 2 >= widgetWidth) { + pos.ry() -= (widgetWidth - toggleSize) / 2; toggleSize = widgetWidth; pos.setX(0); } @@ -227,11 +242,13 @@ QString KFileItemListWidget::roleText(const QByteArray& role, const QHash(); - text = i18ncp("@item:intable", "%1 item", "%1 items", size); + if (!roleValue.isNull()) { + const int count = roleValue.toInt(); + if (count < 0) { + text = i18nc("@item:intable", "Unknown"); + } else { + text = i18ncp("@item:intable", "%1 item", "%1 items", count); + } } } else { // Show the size in kilobytes (always round up) @@ -302,7 +319,8 @@ QPixmap KFileItemListWidget::overlay() const void KFileItemListWidget::dataChanged(const QHash& current, const QSet& roles) { - KItemListWidget::dataChanged(current, roles); + Q_UNUSED(current); + m_dirtyContent = true; QSet dirtyRoles; @@ -324,7 +342,7 @@ void KFileItemListWidget::dataChanged(const QHash& current void KFileItemListWidget::visibleRolesChanged(const QList& current, const QList& previous) { - KItemListWidget::visibleRolesChanged(current, previous); + Q_UNUSED(previous); m_sortedVisibleRoles = current; m_dirtyLayout = true; } @@ -332,14 +350,16 @@ void KFileItemListWidget::visibleRolesChanged(const QList& current, void KFileItemListWidget::visibleRolesSizesChanged(const QHash& current, const QHash& previous) { - KItemListWidget::visibleRolesSizesChanged(current, previous); + Q_UNUSED(current); + Q_UNUSED(previous); m_dirtyLayout = true; } void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) { - KItemListWidget::styleOptionChanged(current, previous); + Q_UNUSED(current); + Q_UNUSED(previous); updateAdditionalInfoTextColor(); m_dirtyLayout = true; } @@ -356,6 +376,14 @@ void KFileItemListWidget::selectedChanged(bool selected) updateAdditionalInfoTextColor(); } +void KFileItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + m_dirtyLayout = true; +} + + void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) { KItemListWidget::resizeEvent(event); @@ -405,7 +433,7 @@ void KFileItemListWidget::triggerCacheRefreshing() refreshCache(); const QHash values = data(); - m_isExpandable = values["isExpandable"].toBool(); + m_isExpandable = m_supportsItemExpanding && values["isExpandable"].toBool(); m_isHidden = values["name"].toString().startsWith(QLatin1Char('.')); updateExpansionArea(); @@ -419,17 +447,16 @@ void KFileItemListWidget::triggerCacheRefreshing() void KFileItemListWidget::updateExpansionArea() { - if (m_layout == DetailsLayout) { + if (m_supportsItemExpanding) { const QHash values = data(); - Q_ASSERT(values.contains("expansionLevel")); - const KItemListStyleOption& option = styleOption(); - const int expansionLevel = values.value("expansionLevel", 0).toInt(); - if (expansionLevel >= 0) { + Q_ASSERT(values.contains("expandedParentsCount")); + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + if (expandedParentsCount >= 0) { const qreal widgetHeight = size().height(); - const qreal expansionLevelSize = KIconLoader::SizeSmall; - const qreal x = option.margin + expansionLevel * widgetHeight; - const qreal y = (widgetHeight - expansionLevelSize) / 2; - m_expansionArea = QRectF(x, y, expansionLevelSize, expansionLevelSize); + const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2; + const qreal x = expandedParentsCount * widgetHeight + inc; + const qreal y = inc; + m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall); return; } } @@ -442,23 +469,17 @@ void KFileItemListWidget::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 int iconHeight = option.iconSize; + const qreal padding = option.padding; - const QHash values = data(); - const QSizeF widgetSize = size(); + const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : option.iconSize; + const int maxIconHeight = option.iconSize; - int scaledIconHeight = 0; - if (iconOnTop) { - scaledIconHeight = static_cast(m_textPos[Name].y() - 3 * option.margin); - } else { - const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; - const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height(); - scaledIconHeight = (requiredTextHeight < iconHeight) ? widgetSize.height() - 2 * option.margin : iconHeight; - } + const QHash values = data(); - bool updatePixmap = (iconHeight != m_pixmap.height()); + bool updatePixmap = (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight); if (!updatePixmap && m_dirtyContent) { updatePixmap = m_dirtyContentRoles.isEmpty() || m_dirtyContentRoles.contains("iconPixmap") @@ -476,38 +497,11 @@ void KFileItemListWidget::updatePixmapCache() // use a generic icon as fallback iconName = QLatin1String("unknown"); } - m_pixmap = pixmapForIcon(iconName, iconHeight); - m_originalPixmapSize = m_pixmap.size(); - } else if (m_pixmap.size() != QSize(iconHeight, iconHeight)) { + 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 available size. - const bool scale = m_pixmap.width() > iconHeight || m_pixmap.height() > iconHeight || - (m_pixmap.width() < iconHeight && m_pixmap.height() < iconHeight); - if (scale) { - KPixmapModifier::scale(m_pixmap, QSize(iconHeight, iconHeight)); - } - m_originalPixmapSize = m_pixmap.size(); - - // To simplify the handling of scaling the original pixmap - // will be embedded into a square pixmap. - QPixmap squarePixmap(iconHeight, iconHeight); - squarePixmap.fill(Qt::transparent); - - QPainter painter(&squarePixmap); - int x, y; - if (iconOnTop) { - x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally - y = iconHeight - m_pixmap.height(); // Align on bottom - painter.drawPixmap(x, y, m_pixmap); - } else { - x = iconHeight - m_pixmap.width(); // Align right - y = (iconHeight - m_pixmap.height()) / 2; // Center vertically - painter.drawPixmap(x, y, m_pixmap); - } - - m_pixmap = squarePixmap; - } else { - m_originalPixmapSize = m_pixmap.size(); + // is scaled to the maximum available size. + KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight)); } const QStringList overlays = values["iconOverlays"].toStringList(); @@ -533,37 +527,43 @@ void KFileItemListWidget::updatePixmapCache() if (m_isHidden) { applyHiddenEffect(m_pixmap); } - - Q_ASSERT(m_pixmap.height() == iconHeight); } + if (!m_overlay.isNull()) { QPainter painter(&m_pixmap); painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay); } - m_scaledPixmapSize = QSize(scaledIconHeight, scaledIconHeight); + int scaledIconSize = 0; + if (iconOnTop) { + scaledIconSize = static_cast(m_textPos[Name].y() - 2 * padding); + } else { + const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; + const qreal requiredTextHeight = textRowsCount * option.fontMetrics.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 { - m_pixmapPos.setX(m_textPos[Name].x() - 2 * option.margin - scaledIconHeight); - } - m_pixmapPos.setY(option.margin); - - // Center the hover rectangle horizontally and align it on bottom - qreal hoverWidth = m_originalPixmapSize.width(); - qreal hoverHeight = m_originalPixmapSize.height(); - if (scaledIconHeight != m_pixmap.height()) { - const qreal scaleFactor = qreal(scaledIconHeight) / qreal(m_pixmap.height()); - hoverWidth *= scaleFactor; - hoverHeight *= scaleFactor; - } - const qreal hoverX = m_pixmapPos.x() + (m_scaledPixmapSize.width() - hoverWidth) / 2.0; - const qreal hoverY = m_pixmapPos.y() + m_scaledPixmapSize.height() - hoverHeight; - m_iconRect = QRectF(hoverX, hoverY, hoverWidth, hoverHeight); - const qreal margin = option.margin; - m_iconRect.adjust(-margin, -margin, margin, margin); - + // Center horizontally and vertically within the icon-area + m_pixmapPos.setX(m_textPos[Name].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; @@ -626,7 +626,8 @@ void KFileItemListWidget::updateIconsLayoutTextCache() const QHash values = data(); const KItemListStyleOption& option = styleOption(); - const qreal maxWidth = size().width() - 2 * option.margin; + const qreal padding = option.padding; + const qreal maxWidth = size().width() - 2 * padding; const qreal widgetHeight = size().height(); const qreal fontHeight = option.fontMetrics.height(); @@ -655,8 +656,8 @@ void KFileItemListWidget::updateIconsLayoutTextCache() textLinesCount += additionalRolesCount; m_text[Name].setTextWidth(maxWidth); - m_textPos[Name] = QPointF(option.margin, widgetHeight - textLinesCount * fontHeight - option.margin); - m_textRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2, + m_textPos[Name] = QPointF(padding, widgetHeight - textLinesCount * fontHeight - padding); + m_textRect = QRectF(padding + (maxWidth - requiredWidthForName) / 2, m_textPos[Name].y(), requiredWidthForName, textLinesCountForName * fontHeight); @@ -685,25 +686,24 @@ void KFileItemListWidget::updateIconsLayoutTextCache() // TODO: QFontMetrics::elidedText() works different regarding the given width // in comparison to QTextLine::setLineWidth(). It might happen that the text does // not get elided although it does not fit into the given width. As workaround - // the margin is substracted. - const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - option.margin); + // the padding is substracted. + const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - padding); m_text[textId].setText(elidedText); } } layout.endLayout(); - m_textPos[textId] = QPointF(option.margin, y); + m_textPos[textId] = QPointF(padding, y); m_text[textId].setTextWidth(maxWidth); - const QRectF textRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight); + const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight); m_textRect |= textRect; y += fontHeight; } - // Add a margin to the text rectangle - const qreal margin = option.margin; - m_textRect.adjust(-margin, -margin, margin, margin); + // Add a padding to the text rectangle + m_textRect.adjust(-padding, -padding, padding, padding); } void KFileItemListWidget::updateCompactLayoutTextCache() @@ -718,12 +718,12 @@ void KFileItemListWidget::updateCompactLayoutTextCache() const qreal widgetHeight = size().height(); const qreal fontHeight = option.fontMetrics.height(); const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * fontHeight; - const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.margin : option.iconSize; + const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.padding : option.iconSize; qreal maximumRequiredTextWidth = 0; - const qreal x = option.margin * 3 + scaledIconSize; + const qreal x = option.padding * 3 + scaledIconSize; qreal y = (widgetHeight - textLinesHeight) / 2; - const qreal maxWidth = size().width() - x - option.margin; + const qreal maxWidth = size().width() - x - option.padding; foreach (const QByteArray& role, m_sortedVisibleRoles) { const TextId textId = roleTextId(role); @@ -745,7 +745,7 @@ void KFileItemListWidget::updateCompactLayoutTextCache() y += fontHeight; } - m_textRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight); + m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight); } void KFileItemListWidget::updateDetailsLayoutTextCache() @@ -762,13 +762,19 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() const QHash values = data(); const qreal widgetHeight = size().height(); - const int scaledIconSize = widgetHeight - 2 * option.margin; + const int scaledIconSize = widgetHeight - 2 * option.padding; const int fontHeight = option.fontMetrics.height(); - const qreal columnMargin = option.margin * 3; - const qreal firstColumnInc = m_expansionArea.right() + option.margin * 2 + scaledIconSize; + const qreal columnPadding = option.padding * 3; + qreal firstColumnInc = scaledIconSize; + if (m_supportsItemExpanding) { + firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; + } else { + firstColumnInc += option.padding; + } + qreal x = firstColumnInc; - const qreal y = qMax(qreal(option.margin), (widgetHeight - fontHeight) / 2); + const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); foreach (const QByteArray& role, m_sortedVisibleRoles) { const TextId textId = roleTextId(role); @@ -778,7 +784,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() // Elide the text in case it does not fit into the available column-width qreal requiredWidth = option.fontMetrics.width(text); const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width(); - qreal availableTextWidth = columnWidth - 2 * columnMargin; + qreal availableTextWidth = columnWidth - 2 * columnPadding; if (textId == Name) { availableTextWidth -= firstColumnInc; } @@ -789,13 +795,16 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() } m_text[textId].setText(text); - m_textPos[textId] = QPointF(x + columnMargin, y); + m_textPos[textId] = QPointF(x + columnPadding, y); x += columnWidth; switch (textId) { case Name: { - m_textRect = QRectF(m_textPos[textId].x() - option.margin, 0, - requiredWidth + 2 * option.margin, size().height()); + const qreal textWidth = option.extendedSelectionRegion + ? size().width() - m_textPos[textId].x() + : requiredWidth + 2 * option.padding; + m_textRect = QRectF(m_textPos[textId].x() - option.padding, 0, + textWidth, 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 @@ -804,7 +813,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() } case Size: // The values for the size should be right aligned - m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnMargin; + m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnPadding; break; default: @@ -843,14 +852,45 @@ void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap) painter->drawPixmap(m_pixmapPos, scaledPixmap); #ifdef KFILEITEMLISTWIDGET_DEBUG - painter->setPen(Qt::green); - painter->drawRect(QRectF(m_pixmapPos, QSizeF(scaledPixmap.size()))); + painter->setPen(Qt::blue); + painter->drawRect(QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize))); #endif } else { painter->drawPixmap(m_pixmapPos, pixmap); } } +void KFileItemListWidget::drawSiblingsInformation(QPainter* painter) +{ + const int siblingSize = size().height(); + const int x = (m_expansionArea.left() + m_expansionArea.right() - siblingSize) / 2; + QRect siblingRect(x, 0, siblingSize, siblingSize); + + QStyleOption option; + bool isItemSibling = true; + + const QBitArray siblings = siblingsInformation(); + for (int i = siblings.count() - 1; i >= 0; --i) { + option.rect = siblingRect; + option.state = siblings.at(i) ? QStyle::State_Sibling : QStyle::State_None; + + if (isItemSibling) { + option.state |= QStyle::State_Item; + if (m_isExpandable) { + option.state |= QStyle::State_Children; + } + if (data()["isExpanded"].toBool()) { + option.state |= QStyle::State_Open; + } + isItemSibling = false; + } + + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); + + siblingRect.translate(-siblingRect.width(), 0); + } +} + QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size) { const KIcon icon(name);