X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/22d5283eb7e7858d1375eaabfca467c2b03fed47..f42e81fb5d71ffa23948c4edea9bb3f86c84e8c7:/src/kitemviews/kstandarditemlistwidget.cpp diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index d2806082a..a30a1bcd2 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include #include // #define KSTANDARDITEMLISTWIDGET_DEBUG @@ -107,11 +109,20 @@ bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView return false; } -QString KStandardItemListWidgetInformant::roleText(const QByteArray &role, const QHash &values) const +QString KStandardItemListWidgetInformant::roleText(const QByteArray &role, const QHash &values, ForUsageAs forUsageAs) const { if (role == "rating") { - // Always use an empty text, as the rating is shown by the image m_rating. - return QString(); + if (forUsageAs == ForUsageAs::DisplayedText) { + // Always use an empty text, as the rating is shown by the image m_rating. + return QString(); + } else { + const int rating{values.value(role).toInt()}; + // Check if there are half stars + if (rating % 2) { + return i18ncp("@accessible rating", "%1 and a half stars", "%1 and a half stars", rating / 2); + } + return i18ncp("@accessible rating", "%1 star", "%1 stars", rating / 2); + } } return values.value(role).toString(); } @@ -398,15 +409,26 @@ void KStandardItemListWidget::paint(QPainter *painter, const QStyleOptionGraphic painter->drawStaticText(textInfo->pos, textInfo->staticText); bool clipAdditionalInfoBounds = false; - 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.padding; - if (textInfo->pos.x() + columnWidth("text") > minX) { - clipAdditionalInfoBounds = true; - painter->save(); - painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); + if (m_supportsItemExpanding && m_sortedVisibleRoles.count() > 1) { + // Prevent a possible overlapping of the additional-information-texts with the icon. + // This will happen if the user has resized the width of the name-column to be very narrow while having folders expanded. + // We only want to draw additional info text outside the area of the icon or expansion area, so we set a clip rect that does not contain the icon area. + // This needs to work both for left-to-right as well as right-to-left layout directions. + const TextInfo *potentiallyOverlappingRoleText = m_textInfo.value(m_sortedVisibleRoles[1]); // Only the first column after the name column can overlap. + if (layoutDirection() == Qt::LeftToRight) { // In left-to-right languages the left end of text would overlap. This is mirrored for right-to-left. + const qreal minX = m_iconRect.right() + 2 * itemListStyleOption.padding; + if (potentiallyOverlappingRoleText->pos.x() < minX) { + clipAdditionalInfoBounds = true; + painter->save(); + painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); + } + } else { + const qreal maxX = m_iconRect.left() - 2 * itemListStyleOption.padding; + if (potentiallyOverlappingRoleText->pos.x() + m_customizedFontMetrics.horizontalAdvance(potentiallyOverlappingRoleText->staticText.text()) > maxX) { + clipAdditionalInfoBounds = true; + painter->save(); + painter->setClipRect(0, 0, maxX, size().height(), Qt::IntersectClip); + } } } @@ -513,7 +535,11 @@ QRectF KStandardItemListWidget::selectionRect() const QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding); QRectF result = adjustedIconRect | m_textRect; if (m_highlightEntireRow) { - result.setRight(m_columnWidthSum + sidePadding()); + if (layoutDirection() == Qt::LeftToRight) { + result.setRight(leftPadding() + m_columnWidthSum); + } else { + result.setLeft(size().width() - m_columnWidthSum - rightPadding()); + } } return result; } @@ -775,9 +801,10 @@ void KStandardItemListWidget::columnWidthChanged(const QByteArray &role, qreal c m_dirtyLayout = true; } -void KStandardItemListWidget::sidePaddingChanged(qreal padding) +void KStandardItemListWidget::sidePaddingChanged(qreal leftPaddingWidth, qreal rightPaddingWidth) { - Q_UNUSED(padding) + Q_UNUSED(leftPaddingWidth) + Q_UNUSED(rightPaddingWidth) m_dirtyLayout = true; } @@ -811,9 +838,19 @@ void KStandardItemListWidget::siblingsInformationChanged(const QBitArray ¤ m_dirtyLayout = true; } +int KStandardItemListWidget::numberOfUnicodeCharactersIn(const QString &text) +{ + int count = 0; + QTextBoundaryFinder boundaryFinder(QTextBoundaryFinder::Grapheme, text); + while (boundaryFinder.toNextBoundary() != -1) { + ++count; + } + return count; +} + int KStandardItemListWidget::selectionLength(const QString &text) const { - return text.length(); + return numberOfUnicodeCharactersIn(text); } void KStandardItemListWidget::editedRoleChanged(const QByteArray ¤t, const QByteArray &previous) @@ -824,16 +861,7 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray ¤t, const if (current.isEmpty() || !parent || current != "text") { if (m_roleEditor) { Q_EMIT roleEditingCanceled(index(), current, data().value(current)); - - disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled, this, &KStandardItemListWidget::slotRoleEditingCanceled); - disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished, this, &KStandardItemListWidget::slotRoleEditingFinished); - - if (m_oldRoleEditor) { - m_oldRoleEditor->deleteLater(); - } - m_oldRoleEditor = m_roleEditor; - m_roleEditor->hide(); - m_roleEditor = nullptr; + closeRoleEditor(); } return; } @@ -1000,8 +1028,13 @@ void KStandardItemListWidget::updateExpansionArea() const qreal inc = (widgetHeight - widgetIconSize) / 2; const qreal x = expandedParentsCount * widgetHeight + inc; const qreal y = inc; - const qreal xPadding = m_highlightEntireRow ? sidePadding() : 0; - m_expansionArea = QRectF(xPadding + x, y, widgetIconSize, widgetIconSize); + if (layoutDirection() == Qt::LeftToRight) { + const qreal leftPaddingWidth = m_highlightEntireRow ? leftPadding() : 0; + m_expansionArea = QRectF(leftPaddingWidth + x, y, widgetIconSize, widgetIconSize); + return; + } + const qreal rightPaddingWidth = m_highlightEntireRow ? rightPadding() : 0; + m_expansionArea = QRectF(size().width() - rightPaddingWidth - x - widgetIconSize, y, widgetIconSize, widgetIconSize); return; } } @@ -1088,8 +1121,14 @@ void KStandardItemListWidget::updatePixmapCache() } if (m_isCut) { - KIconEffect *effect = KIconLoader::global()->iconEffect(); - m_pixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); +#if KICONTHEMES_VERSION >= QT_VERSION_CHECK(6, 5, 0) + KIconEffect::toDisabled(m_pixmap); +#else + QImage img = m_pixmap.toImage(); + KIconEffect::toGray(img, 1); + KIconEffect::semiTransparent(img); + m_pixmap = QPixmap::fromImage(img); +#endif } if (m_isHidden) { @@ -1137,8 +1176,8 @@ void KStandardItemListWidget::updatePixmapCache() } else { // Center horizontally and vertically within the icon-area const TextInfo *textInfo = m_textInfo.value("text"); - if (QApplication::isRightToLeft() && m_layout == CompactLayout) { - m_pixmapPos.setX(size().width() - padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); + if (QApplication::isRightToLeft()) { + m_pixmapPos.setX(m_textRect.right() + 2.0 * padding); } else { m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); } @@ -1162,13 +1201,13 @@ void KStandardItemListWidget::updatePixmapCache() // 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; - } +#if KICONTHEMES_VERSION >= QT_VERSION_CHECK(6, 5, 0) + KIconEffect::toActive(m_hoverPixmap); +#else + QImage img = m_pixmap.toImage(); + KIconEffect::toGamma(img, 0.7); + m_hoverPixmap = QPixmap::fromImage(img); +#endif } else if (hoverOpacity() <= 0.0) { // No hover animation is ongoing. Clear m_hoverPixmap to save memory. m_hoverPixmap = QPixmap(); @@ -1246,27 +1285,6 @@ void KStandardItemListWidget::updateTextsCache() } } -QString KStandardItemListWidget::elideRightKeepExtension(const QString &text, int elidingWidth) const -{ - const auto extensionIndex = text.lastIndexOf('.'); - if (extensionIndex != -1) { - // has file extension - const auto extensionLength = text.length() - extensionIndex; - const auto extensionWidth = m_customizedFontMetrics.horizontalAdvance(text.right(extensionLength)); - if (elidingWidth > extensionWidth && extensionLength < 6 && (float(extensionWidth) / float(elidingWidth)) < 0.3) { - // if we have room to display the file extension and the extension is not too long - QString ret = m_customizedFontMetrics.elidedText(text.chopped(extensionLength), Qt::ElideRight, elidingWidth - extensionWidth); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - ret.append(text.rightRef(extensionLength)); -#else - ret.append(QStringView(text).right(extensionLength)); -#endif - return ret; - } - } - return m_customizedFontMetrics.elidedText(text, Qt::ElideRight, elidingWidth); -} - QString KStandardItemListWidget::escapeString(const QString &text) const { QString escaped(text); @@ -1327,7 +1345,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() qreal lastLineWidth; do { QString lastTextLine = nameText.mid(line.textStart()); - lastTextLine = elideRightKeepExtension(lastTextLine, elidingWidth); + lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine, Qt::ElideMiddle, elidingWidth); const QString elidedText = nameText.left(line.textStart()) + lastTextLine; nameTextInfo->staticText.setText(elidedText); @@ -1375,7 +1393,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() textLine.setLineWidth(maxWidth); requiredWidth = textLine.naturalTextWidth(); if (requiredWidth > maxWidth) { - const QString elidedText = elideRightKeepExtension(text, maxWidth); + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth); textInfo->staticText.setText(elidedText); requiredWidth = m_customizedFontMetrics.horizontalAdvance(elidedText); } else if (role == "rating") { @@ -1427,7 +1445,7 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text); if (requiredWidth > maxWidth) { requiredWidth = maxWidth; - const QString elidedText = elideRightKeepExtension(text, maxWidth); + const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth); textInfo->staticText.setText(elidedText); } @@ -1450,6 +1468,8 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() // +------+ // | Icon | Name role Additional role 1 Additional role 2 // +------+ + // Mirror the above for right-to-left languages. + const bool isLeftToRight = QApplication::layoutDirection() == Qt::LeftToRight; m_textRect = QRectF(); const KItemListStyleOption &option = styleOption(); @@ -1461,9 +1481,10 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() const qreal columnWidthInc = columnPadding(option); qreal firstColumnInc = iconSize(); if (m_supportsItemExpanding) { - firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2; + firstColumnInc += isLeftToRight ? (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2 + : ((size().width() - m_expansionArea.left()) + (size().width() - m_expansionArea.right()) + widgetHeight) / 2; } else { - firstColumnInc += option.padding + sidePadding(); + firstColumnInc += option.padding + (isLeftToRight ? leftPadding() : rightPadding()); } qreal x = firstColumnInc; @@ -1480,27 +1501,26 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() const bool isTextRole = (role == "text"); if (isTextRole) { text = escapeString(text); - availableTextWidth -= firstColumnInc - sidePadding(); + availableTextWidth -= firstColumnInc - (isLeftToRight ? leftPadding() : rightPadding()); } if (requiredWidth > availableTextWidth) { - text = elideRightKeepExtension(text, availableTextWidth); + text = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, availableTextWidth); requiredWidth = m_customizedFontMetrics.horizontalAdvance(text); } TextInfo *textInfo = m_textInfo.value(role); textInfo->staticText.setText(text); - textInfo->pos = QPointF(x + columnWidthInc / 2, y); + textInfo->pos = QPointF(isLeftToRight ? (x + columnWidthInc / 2) : (size().width() - (x + columnWidthInc / 2) - requiredWidth), y); x += roleWidth; if (isTextRole) { - 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() - option.padding, 0, requiredWidth + 2 * 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 - x -= firstColumnInc - sidePadding(); - } else if (isRoleRightAligned(role)) { + x -= firstColumnInc - (isLeftToRight ? leftPadding() : rightPadding()); + } else if (isRoleRightAligned(role) && isLeftToRight) { textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc; } } @@ -1580,7 +1600,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter *painter) style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); - siblingRect.translate(-siblingRect.width(), 0); + siblingRect.translate(layoutDirection() == Qt::LeftToRight ? -siblingRect.width() : siblingRect.width(), 0); } } @@ -1645,30 +1665,12 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString &name, const QStrin // 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. for (const QString &overlay : overlays) { if (!overlay.isEmpty()) { - int state = KIconLoader::DefaultState; - - switch (mode) { - case QIcon::Normal: - break; - case QIcon::Active: - state = KIconLoader::ActiveState; - break; - case QIcon::Disabled: - state = KIconLoader::DisabledState; - break; - case QIcon::Selected: - state = KIconLoader::SelectedState; - break; - } - // There is at least one overlay, draw all overlays above m_pixmap // and cancel the check - KIconLoader::global()->drawOverlays(overlays, pixmap, KIconLoader::Desktop, state); + const QSize size = pixmap.size(); + pixmap = KIconUtils::addOverlays(pixmap, overlays).pixmap(size, mode); break; } }