X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/4aedb0767490989ea00251a39b8d76f1b51a4303..bf1a8f989e75a50c9a5c839e69573a87ab9ad934:/src/kitemviews/kfileitemlistwidget.cpp diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 4fc3307ef..897600e60 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -53,10 +53,12 @@ KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : m_hoverPixmap(), m_textPos(), m_text(), - m_textBoundingRect(), + m_textRect(), m_sortedVisibleRoles(), m_expansionArea(), - m_additionalInfoTextColor() + m_customTextColor(), + m_additionalInfoTextColor(), + m_overlay() { for (int i = 0; i < TextIdCount; ++i) { m_text[i].setTextFormat(Qt::PlainText); @@ -86,7 +88,7 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte { KItemListWidget::paint(painter, option, widget); - const_cast(this)->updateCache(); + const_cast(this)->triggerCacheRefreshing(); // Draw expansion toggle '>' or 'V' if (m_isDir && !m_expansionArea.isEmpty()) { @@ -114,15 +116,32 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte } painter->setFont(itemListStyleOption.font); - painter->setPen(itemListStyleOption.palette.text().color()); + painter->setPen(textColor()); painter->drawStaticText(m_textPos[Name], m_text[Name]); + bool clipAdditionalInfoBounds = false; + if (m_layout == DetailsLayout) { + // 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; + if (m_textPos[Name + 1].x() < minX) { + clipAdditionalInfoBounds = true; + painter->save(); + painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); + } + } + painter->setPen(m_additionalInfoTextColor); painter->setFont(itemListStyleOption.font); for (int i = Name + 1; i < TextIdCount; ++i) { painter->drawStaticText(m_textPos[i], m_text[i]); } + if (clipAdditionalInfoBounds) { + painter->restore(); + } + #ifdef KFILEITEMLISTWIDGET_DEBUG painter->setPen(Qt::red); painter->setBrush(Qt::NoBrush); @@ -131,9 +150,9 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte #endif } -QRectF KFileItemListWidget::iconBoundingRect() const +QRectF KFileItemListWidget::iconRect() const { - const_cast(this)->updateCache(); + const_cast(this)->triggerCacheRefreshing(); QRectF bounds = m_hoverPixmapRect; const qreal margin = styleOption().margin; @@ -141,18 +160,145 @@ QRectF KFileItemListWidget::iconBoundingRect() const return bounds; } -QRectF KFileItemListWidget::textBoundingRect() const +QRectF KFileItemListWidget::textRect() const { - const_cast(this)->updateCache(); - return m_textBoundingRect; + const_cast(this)->triggerCacheRefreshing(); + return m_textRect; } QRectF KFileItemListWidget::expansionToggleRect() const { - const_cast(this)->updateCache(); + const_cast(this)->triggerCacheRefreshing(); return m_isDir ? m_expansionArea : QRectF(); } +QRectF KFileItemListWidget::selectionToggleRect() const +{ + const_cast(this)->triggerCacheRefreshing(); + + const int iconHeight = m_pixmap.height(); + + int toggleSize = KIconLoader::SizeSmall; + if (iconHeight >= KIconLoader::SizeEnormous) { + toggleSize = KIconLoader::SizeMedium; + } else if (iconHeight >= KIconLoader::SizeLarge) { + toggleSize = KIconLoader::SizeSmallMedium; + } + + QPointF pos = iconRect().topLeft(); + + // If the selection toggle has a very small distance to the + // widget borders, the size of the selection toggle will get + // increased to prevent an accidental clicking of the item + // when trying to hit the toggle. + const int widgetHeight = size().height(); + const int widgetWidth = size().width(); + const int minMargin = 2; + + if (toggleSize + minMargin * 2 >= widgetHeight) { + toggleSize = widgetHeight; + pos.setY(0); + } + if (toggleSize + minMargin * 2 >= widgetWidth) { + toggleSize = widgetWidth; + pos.setX(0); + } + + return QRectF(pos, QSizeF(toggleSize, toggleSize)); +} + +QString KFileItemListWidget::roleText(const QByteArray& role, const QHash& values) +{ + QString text; + const QVariant roleValue = values.value(role); + + switch (roleTextId(role)) { + case Name: + case Permissions: + case Owner: + case Group: + case Type: + case Destination: + case Path: + text = roleValue.toString(); + break; + + case Size: { + if (values.value("isDir").toBool()) { + // The item represents a directory. Show the number of sub directories + // instead of the file size of the directory. + if (roleValue.isNull()) { + text = i18nc("@item:intable", "Unknown"); + } else { + const KIO::filesize_t size = roleValue.value(); + text = i18ncp("@item:intable", "%1 item", "%1 items", size); + } + } else { + // Show the size in kilobytes (always round up) + const KLocale* locale = KGlobal::locale(); + const int roundInc = (locale->binaryUnitDialect() == KLocale::MetricBinaryDialect) ? 499 : 511; + const KIO::filesize_t size = roleValue.value() + roundInc; + text = locale->formatByteSize(size, 0, KLocale::DefaultBinaryDialect, KLocale::UnitKiloByte); + } + break; + } + + case Date: { + const QDateTime dateTime = roleValue.toDateTime(); + text = KGlobal::locale()->formatDateTime(dateTime); + break; + } + + default: + Q_ASSERT(false); + break; + } + + return text; +} + +void KFileItemListWidget::invalidateCache() +{ + m_dirtyLayout = true; + m_dirtyContent = true; +} + +void KFileItemListWidget::refreshCache() +{ +} + +void KFileItemListWidget::setTextColor(const QColor& color) +{ + if (color != m_customTextColor) { + m_customTextColor = color; + updateAdditionalInfoTextColor(); + update(); + } +} + +QColor KFileItemListWidget::textColor() const +{ + if (m_customTextColor.isValid()) { + return m_customTextColor; + } + + const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive; + const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Text; + return styleOption().palette.brush(group, role).color(); +} + +void KFileItemListWidget::setOverlay(const QPixmap& overlay) +{ + m_overlay = overlay; + m_dirtyContent = true; + update(); +} + +QPixmap KFileItemListWidget::overlay() const +{ + return m_overlay; +} + void KFileItemListWidget::dataChanged(const QHash& current, const QSet& roles) { @@ -161,7 +307,7 @@ void KFileItemListWidget::dataChanged(const QHash& current QSet dirtyRoles; if (roles.isEmpty()) { - dirtyRoles = visibleRoles().keys().toSet(); + dirtyRoles = visibleRoles().toSet(); dirtyRoles.insert("iconPixmap"); dirtyRoles.insert("iconName"); } else { @@ -175,33 +321,12 @@ void KFileItemListWidget::dataChanged(const QHash& current } } -void KFileItemListWidget::visibleRolesChanged(const QHash& current, - const QHash& previous) +void KFileItemListWidget::visibleRolesChanged(const QList& current, + const QList& previous) { KItemListWidget::visibleRolesChanged(current, previous); + m_sortedVisibleRoles = current; m_dirtyLayout = true; - - // Cache the roles sorted into m_sortedVisibleRoles: - const int visibleRolesCount = current.count(); - m_sortedVisibleRoles.clear(); - m_sortedVisibleRoles.reserve(visibleRolesCount); - for (int i = 0; i < visibleRolesCount; ++i) { - m_sortedVisibleRoles.append(QByteArray()); - } - - QHashIterator it(current); - while (it.hasNext()) { - it.next(); - - const int index = it.value(); - if (index < 0 || index >= visibleRolesCount || !m_sortedVisibleRoles.at(index).isEmpty()) { - kWarning() << "The visible roles have an invalid sort order."; - break; - } - - const QByteArray& role = it.key(); - m_sortedVisibleRoles[index] = role; - } } void KFileItemListWidget::visibleRolesSizesChanged(const QHash& current, @@ -215,18 +340,7 @@ void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current const KItemListStyleOption& previous) { KItemListWidget::styleOptionChanged(current, previous); - - // For the color of the additional info the inactive text color - // is not used as this might lead to unreadable text for some color schemes. Instead - // the text color is slightly mixed with the background color. - const QColor c1 = current.palette.text().color(); - const QColor c2 = current.palette.background().color(); - const int p1 = 70; - const int p2 = 100 - p1; - m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100, - (c1.green() * p1 + c2.green() * p2) / 100, - (c1.blue() * p1 + c2.blue() * p2) / 100); - + updateAdditionalInfoTextColor(); m_dirtyLayout = true; } @@ -236,18 +350,26 @@ void KFileItemListWidget::hoveredChanged(bool hovered) m_dirtyLayout = true; } +void KFileItemListWidget::selectedChanged(bool selected) +{ + Q_UNUSED(selected); + updateAdditionalInfoTextColor(); +} + void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event) { KItemListWidget::resizeEvent(event); m_dirtyLayout = true; } -void KFileItemListWidget::updateCache() +void KFileItemListWidget::triggerCacheRefreshing() { if ((!m_dirtyContent && !m_dirtyLayout) || index() < 0) { return; } + refreshCache(); + m_isDir = data()["isDir"].toBool(); updateExpansionArea(); @@ -333,13 +455,14 @@ void KFileItemListWidget::updatePixmapCache() squarePixmap.fill(Qt::transparent); QPainter painter(&squarePixmap); + int x, y; if (iconOnTop) { - const int x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally - const int y = iconHeight - m_pixmap.height(); // Align on bottom + x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally + y = iconHeight - m_pixmap.height(); // Align on bottom painter.drawPixmap(x, y, m_pixmap); } else { - const int x = iconHeight - m_pixmap.width(); // Align right - const int y = (iconHeight - m_pixmap.height()) / 2; // Center vertically + x = iconHeight - m_pixmap.width(); // Align right + y = (iconHeight - m_pixmap.height()) / 2; // Center vertically painter.drawPixmap(x, y, m_pixmap); } @@ -350,6 +473,10 @@ void KFileItemListWidget::updatePixmapCache() 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); @@ -457,7 +584,7 @@ void KFileItemListWidget::updateIconsLayoutTextCache() m_text[Name].setTextWidth(maxWidth); m_textPos[Name] = QPointF(option.margin, widgetHeight - textLinesCount * fontHeight - option.margin); - m_textBoundingRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2, + m_textRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2, m_textPos[Name].y(), requiredWidthForName, m_text[Name].size().height()); @@ -470,7 +597,7 @@ void KFileItemListWidget::updateIconsLayoutTextCache() continue; } - const QString text = roleText(textId, values[role]); + const QString text = roleText(role, values); m_text[textId].setText(text); qreal requiredWidth = 0; @@ -496,15 +623,15 @@ void KFileItemListWidget::updateIconsLayoutTextCache() m_textPos[textId] = QPointF(option.margin, y); m_text[textId].setTextWidth(maxWidth); - const QRectF textBoundingRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight); - m_textBoundingRect |= textBoundingRect; + const QRectF textRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight); + m_textRect |= textRect; y += fontHeight; } - // Add a margin to the text bounding rectangle + // Add a margin to the text rectangle const qreal margin = option.margin; - m_textBoundingRect.adjust(-margin, -margin, margin, margin); + m_textRect.adjust(-margin, -margin, margin, margin); } void KFileItemListWidget::updateCompactLayoutTextCache() @@ -528,7 +655,7 @@ void KFileItemListWidget::updateCompactLayoutTextCache() foreach (const QByteArray& role, m_sortedVisibleRoles) { const TextId textId = roleTextId(role); - const QString text = roleText(textId, values[role]); + const QString text = roleText(role, values); m_text[textId].setText(text); qreal requiredWidth = option.fontMetrics.width(text); @@ -546,7 +673,7 @@ void KFileItemListWidget::updateCompactLayoutTextCache() y += fontHeight; } - m_textBoundingRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight); + m_textRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight); } void KFileItemListWidget::updateDetailsLayoutTextCache() @@ -557,7 +684,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() // +------+ // | Icon | Name role Additional role 1 Additional role 2 // +------+ - m_textBoundingRect = QRectF(); + m_textRect = QRectF(); const KItemListStyleOption& option = styleOption(); const QHash values = data(); @@ -566,34 +693,46 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() const int scaledIconSize = widgetHeight - 2 * option.margin; const int fontHeight = option.fontMetrics.height(); - qreal x = m_expansionArea.right() + option.margin * 3 + scaledIconSize; + const qreal columnMargin = option.margin * 3; + const qreal firstColumnInc = m_expansionArea.right() + option.margin * 2 + scaledIconSize; + qreal x = firstColumnInc; const qreal y = qMax(qreal(option.margin), (widgetHeight - fontHeight) / 2); foreach (const QByteArray& role, m_sortedVisibleRoles) { const TextId textId = roleTextId(role); - const QString text = roleText(textId, values[role]); - m_text[textId].setText(text); - - const qreal requiredWidth = option.fontMetrics.width(text); - m_textPos[textId] = QPointF(x, y); + QString text = roleText(role, values); + // 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; + if (textId == Name) { + availableTextWidth -= firstColumnInc; + } + + if (requiredWidth > availableTextWidth) { + text = option.fontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth); + requiredWidth = option.fontMetrics.width(text); + } + + m_text[textId].setText(text); + m_textPos[textId] = QPointF(x + columnMargin, y); x += columnWidth; switch (textId) { case Name: { - m_textBoundingRect = QRectF(m_textPos[textId].x() - option.margin, 0, - requiredWidth + 2 * option.margin, size().height()); + m_textRect = QRectF(m_textPos[textId].x() - option.margin, 0, + requiredWidth + 2 * option.margin, 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 - x -= m_expansionArea.right(); + x -= firstColumnInc; break; } case Size: // The values for the size should be right aligned - m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * option.margin; + m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * columnMargin; break; default: @@ -602,48 +741,26 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() } } -QString KFileItemListWidget::roleText(TextId textId, const QVariant& roleValue) const +void KFileItemListWidget::updateAdditionalInfoTextColor() { - QString text; - - switch (textId) { - case Name: - case Permissions: - case Owner: - case Group: - case Type: - case Destination: - case Path: - text = roleValue.toString(); - break; - - case Size: { - if (data().value("isDir").toBool()) { - // The item represents a directory. Show the number of sub directories - // instead of the file size of the directory. - if (!roleValue.isNull()) { - const KIO::filesize_t size = roleValue.value(); - text = i18ncp("@item:intable", "%1 item", "%1 items", size); - } - } else { - const KIO::filesize_t size = roleValue.value(); - text = KIO::convertSize(size); - } - break; - } - - case Date: { - const QDateTime dateTime = roleValue.toDateTime(); - text = KGlobal::locale()->formatDateTime(dateTime); - break; - } - - default: - Q_ASSERT(false); - break; + QColor c1; + if (m_customTextColor.isValid()) { + c1 = m_customTextColor; + } else if (isSelected() && m_layout != DetailsLayout) { + c1 = styleOption().palette.highlightedText().color(); + } else { + c1 = styleOption().palette.text().color(); } - return text; + // For the color of the additional info the inactive text color + // is not used as this might lead to unreadable text for some color schemes. Instead + // the text color c1 is slightly mixed with the background color. + const QColor c2 = styleOption().palette.base().color(); + const int p1 = 70; + const int p2 = 100 - p1; + m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100, + (c1.green() * p1 + c2.green() * p2) / 100, + (c1.blue() * p1 + c2.blue() * p2) / 100); } void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)