]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kstandarditemlistwidget.cpp
Fix alignment of icons in Places panel and Compact view mode
[dolphin.git] / src / kitemviews / kstandarditemlistwidget.cpp
index 54546b440716c811fa23f976988553ffa8f10df8..bc3bb29de94daf12b115fd18b1579d69ccaa60f9 100644 (file)
 
 #include "kfileitemlistview.h"
 #include "kfileitemmodel.h"
+#include "private/kfileitemclipboard.h"
+#include "private/kitemlistroleeditor.h"
+#include "private/kpixmapmodifier.h"
 
-#include <KIcon>
 #include <KIconEffect>
 #include <KIconLoader>
-#include <KLocale>
-#include <kratingpainter.h>
+#include <KRatingPainter>
 #include <KStringHandler>
-#include <KDebug>
 
-#include "private/kfileitemclipboard.h"
-#include "private/kitemlistroleeditor.h"
-#include "private/kpixmapmodifier.h"
-
-#include <QFontMetricsF>
 #include <QGraphicsScene>
 #include <QGraphicsSceneResizeEvent>
 #include <QGraphicsView>
-#include <QPainter>
-#include <QStyleOption>
-#include <QTextLayout>
-#include <QTextLine>
+#include <QGuiApplication>
 #include <QPixmapCache>
+#include <QStyleOption>
 
 // #define KSTANDARDITEMLISTWIDGET_DEBUG
 
@@ -55,84 +48,25 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant()
 {
 }
 
-QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
 {
-    const KItemListStyleOption& option = view->styleOption();
-    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
-
     switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
-    case KStandardItemListWidget::IconsLayout: {
-        const QString text = KStringHandler::preProcessWrap(itemText(index, view));
-
-        const qreal itemWidth = view->itemSize().width();
-        const qreal maxWidth = itemWidth - 2 * option.padding;
-        QTextLine line;
-
-        // Calculate the number of lines required for wrapping the name
-        QTextOption textOption(Qt::AlignHCenter);
-        textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
-
-        qreal textHeight = 0;
-        QTextLayout layout(text, option.font);
-        layout.setTextOption(textOption);
-        layout.beginLayout();
-        while ((line = layout.createLine()).isValid()) {
-            line.setLineWidth(maxWidth);
-            line.naturalTextWidth();
-            textHeight += line.height();
-        }
-        layout.endLayout();
-
-        // Add one line for each additional information
-        textHeight += additionalRolesCount * option.fontMetrics.lineSpacing();
-
-        const qreal maxTextHeight = option.maxTextSize.height();
-        if (maxTextHeight > 0 && textHeight > maxTextHeight) {
-            textHeight = maxTextHeight;
-        }
-
-        return QSizeF(itemWidth, textHeight + option.iconSize + option.padding * 3);
-    }
-
-    case KStandardItemListWidget::CompactLayout: {
-        // For each row exactly one role is shown. Calculate the maximum required width that is necessary
-        // to show all roles without horizontal clipping.
-        qreal maximumRequiredWidth = 0.0;
-
-        const QList<QByteArray>& visibleRoles = view->visibleRoles();
-        const bool showOnlyTextRole = (visibleRoles.count() == 1) && (visibleRoles.first() == "text");
-
-        if (showOnlyTextRole) {
-            maximumRequiredWidth = option.fontMetrics.width(itemText(index, view));
-        } else {
-            const QHash<QByteArray, QVariant> values = view->model()->data(index);
-            foreach (const QByteArray& role, view->visibleRoles()) {
-                const QString text = roleText(role, values);
-                const qreal requiredWidth = option.fontMetrics.width(text);
-                maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
-            }
-        }
+    case KStandardItemListView::IconsLayout:
+        calculateIconsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
-        qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth;
-        const qreal maxWidth = option.maxTextSize.width();
-        if (maxWidth > 0 && width > maxWidth) {
-            width = maxWidth;
-        }
-        const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.lineSpacing());
-        return QSizeF(width, height);
-    }
+    case KStandardItemListView::CompactLayout:
+        calculateCompactLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
-    case KStandardItemListWidget::DetailsLayout: {
-        const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
-        return QSizeF(-1, height);
-    }
+    case KStandardItemListView::DetailsLayout:
+        calculateDetailsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
     default:
         Q_ASSERT(false);
         break;
     }
-
-    return QSize();
 }
 
 qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArray& role,
@@ -145,16 +79,22 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra
     const QString text = roleText(role, values);
     qreal width = KStandardItemListWidget::columnPadding(option);
 
+    const QFontMetrics& normalFontMetrics = option.fontMetrics;
+    const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
+
     if (role == "rating") {
         width += KStandardItemListWidget::preferredRatingSize(option).width();
     } else {
-        width += option.fontMetrics.width(text);
+        // If current item is a link, we use the customized link font metrics instead of the normal font metrics.
+        const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics;
+
+        width += fontMetrics.width(text);
 
         if (role == "text") {
             if (view->supportsItemExpanding()) {
                 // Increase the width by the expansion-toggle and the current expansion level
                 const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
-                const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
+                const qreal height = option.padding * 2 + qMax(option.iconSize, fontMetrics.height());
                 width += (expandedParentsCount + 1) * height;
             }
 
@@ -171,6 +111,13 @@ QString KStandardItemListWidgetInformant::itemText(int index, const KItemListVie
     return view->model()->data(index).value("text").toString();
 }
 
+bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const
+{
+    Q_UNUSED(index);
+    Q_UNUSED(view);
+    return false;
+}
+
 QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
                                                    const QHash<QByteArray, QVariant>& values) const
 {
@@ -181,6 +128,121 @@ QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
     return values.value(role).toString();
 }
 
+QFont KStandardItemListWidgetInformant::customizedFontForLinks(const QFont& baseFont) const
+{
+    return baseFont;
+}
+
+void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const QFont& normalFont = option.font;
+    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
+
+    const qreal itemWidth = view->itemSize().width();
+    const qreal maxWidth = itemWidth - 2 * option.padding;
+    const qreal additionalRolesSpacing = additionalRolesCount * option.fontMetrics.lineSpacing();
+    const qreal spacingAndIconHeight = option.iconSize + option.padding * 3;
+
+    const QFont linkFont = customizedFontForLinks(normalFont);
+
+    QTextOption textOption(Qt::AlignHCenter);
+    textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
+            continue;
+        }
+
+        // If the current item is a link, we use the customized link font instead of the normal font.
+        const QFont& font = itemIsLink(index, view) ? linkFont : normalFont;
+
+        const QString& text = KStringHandler::preProcessWrap(itemText(index, view));
+
+        // Calculate the number of lines required for wrapping the name
+        qreal textHeight = 0;
+        QTextLayout layout(text, font);
+        layout.setTextOption(textOption);
+        layout.beginLayout();
+        QTextLine line;
+        int lineCount = 0;
+        while ((line = layout.createLine()).isValid()) {
+            line.setLineWidth(maxWidth);
+            line.naturalTextWidth();
+            textHeight += line.height();
+
+            ++lineCount;
+            if (lineCount == option.maxTextLines) {
+                break;
+            }
+        }
+        layout.endLayout();
+
+        // Add one line for each additional information
+        textHeight += additionalRolesSpacing;
+
+        logicalHeightHints[index] = textHeight + spacingAndIconHeight;
+    }
+
+    logicalWidthHint = itemWidth;
+}
+
+void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const QFontMetrics& normalFontMetrics = option.fontMetrics;
+    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
+
+    const QList<QByteArray>& visibleRoles = view->visibleRoles();
+    const bool showOnlyTextRole = (visibleRoles.count() == 1) && (visibleRoles.first() == "text");
+    const qreal maxWidth = option.maxTextWidth;
+    const qreal paddingAndIconWidth = option.padding * 4 + option.iconSize;
+    const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * normalFontMetrics.lineSpacing());
+
+    const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
+
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
+            continue;
+        }
+
+        // If the current item is a link, we use the customized link font metrics instead of the normal font metrics.
+        const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics;
+
+        // For each row exactly one role is shown. Calculate the maximum required width that is necessary
+        // to show all roles without horizontal clipping.
+        qreal maximumRequiredWidth = 0.0;
+
+        if (showOnlyTextRole) {
+            maximumRequiredWidth = fontMetrics.width(itemText(index, view));
+        } else {
+            const QHash<QByteArray, QVariant>& values = view->model()->data(index);
+            foreach (const QByteArray& role, visibleRoles) {
+                const QString& text = roleText(role, values);
+                const qreal requiredWidth = fontMetrics.width(text);
+                maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
+            }
+        }
+
+        qreal width = paddingAndIconWidth + maximumRequiredWidth;
+        if (maxWidth > 0 && width > maxWidth) {
+            width = maxWidth;
+        }
+
+        logicalHeightHints[index] = width;
+    }
+
+    logicalWidthHint = height;
+}
+
+void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
+    logicalHeightHints.fill(height);
+    logicalWidthHint = -1.0;
+}
+
 KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
     KItemListWidget(informant, parent),
     m_isCut(false),
@@ -206,8 +268,8 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* infor
     m_additionalInfoTextColor(),
     m_overlay(),
     m_rating(),
-    m_roleEditor(0),
-    m_oldRoleEditor(0)
+    m_roleEditor(nullptr),
+    m_oldRoleEditor(nullptr)
 {
 }
 
@@ -276,6 +338,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
              */
             // Paint pixmap1 so that pixmap1 = m_pixmap * (1.0 - hoverOpacity())
             QPixmap pixmap1(m_pixmap.size());
+            pixmap1.setDevicePixelRatio(m_pixmap.devicePixelRatio());
             pixmap1.fill(Qt::transparent);
             {
                 QPainter p(&pixmap1);
@@ -285,6 +348,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
 
             // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity()
             QPixmap pixmap2(pixmap1.size());
+            pixmap2.setDevicePixelRatio(pixmap1.devicePixelRatio());
             pixmap2.fill(Qt::transparent);
             {
                 QPainter p(&pixmap2);
@@ -427,6 +491,29 @@ QRectF KStandardItemListWidget::textFocusRect() const
     return m_textRect;
 }
 
+QRectF KStandardItemListWidget::selectionRect() const
+{
+    const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
+
+    switch (m_layout) {
+    case IconsLayout:
+        return m_textRect;
+
+    case CompactLayout:
+    case DetailsLayout: {
+        const int padding = styleOption().padding;
+        QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding);
+        return adjustedIconRect | m_textRect;
+    }
+
+    default:
+        Q_ASSERT(false);
+        break;
+    }
+
+    return m_textRect;
+}
+
 QRectF KStandardItemListWidget::expansionToggleRect() const
 {
     const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
@@ -592,7 +679,7 @@ void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& cur
     // The URL might have changed (i.e., if the sort order of the items has
     // been changed). Therefore, the "is cut" state must be updated.
     KFileItemClipboard* clipboard = KFileItemClipboard::instance();
-    const KUrl itemUrl = data().value("url").value<KUrl>();
+    const QUrl itemUrl = data().value("url").toUrl();
     m_isCut = clipboard->isCut(itemUrl);
 
     // The icon-state might depend from other roles and hence is
@@ -668,17 +755,17 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
         if (m_roleEditor) {
             emit roleEditingCanceled(index(), current, data().value(current));
 
-            disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)),
-                       this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant)));
-            disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)),
-                       this, SLOT(slotRoleEditingFinished(QByteArray,QVariant)));
+            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 = 0;
+            m_roleEditor = nullptr;
         }
         return;
     }
@@ -706,10 +793,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
         m_roleEditor->setTextCursor(cursor);
     }
 
-    connect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant)));
-    connect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingFinished(QByteArray,QVariant)));
+    connect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+            this, &KStandardItemListWidget::slotRoleEditingCanceled);
+    connect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+            this, &KStandardItemListWidget::slotRoleEditingFinished);
 
     // Adjust the geometry of the editor
     QRectF rect = roleEditingRect(current);
@@ -743,24 +830,41 @@ void KStandardItemListWidget::showEvent(QShowEvent* event)
     // Listen to changes of the clipboard to mark the item as cut/uncut
     KFileItemClipboard* clipboard = KFileItemClipboard::instance();
 
-    const KUrl itemUrl = data().value("url").value<KUrl>();
+    const QUrl itemUrl = data().value("url").toUrl();
     m_isCut = clipboard->isCut(itemUrl);
 
-    connect(clipboard, SIGNAL(cutItemsChanged()),
-            this, SLOT(slotCutItemsChanged()));
+    connect(clipboard, &KFileItemClipboard::cutItemsChanged,
+            this, &KStandardItemListWidget::slotCutItemsChanged);
 }
 
 void KStandardItemListWidget::hideEvent(QHideEvent* event)
 {
-    disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()),
-               this, SLOT(slotCutItemsChanged()));
+    disconnect(KFileItemClipboard::instance(), &KFileItemClipboard::cutItemsChanged,
+               this, &KStandardItemListWidget::slotCutItemsChanged);
 
     KItemListWidget::hideEvent(event);
 }
 
+bool KStandardItemListWidget::event(QEvent *event)
+{
+    if (event->type() == QEvent::WindowDeactivate || event->type() == QEvent::WindowActivate
+            || event->type() == QEvent::PaletteChange) {
+        m_dirtyContent = true;
+    }
+
+    return KItemListWidget::event(event);
+}
+
+void KStandardItemListWidget::finishRoleEditing()
+{
+    if (!editedRole().isEmpty() && m_roleEditor) {
+        slotRoleEditingFinished(editedRole(), KIO::encodeFileName(m_roleEditor->toPlainText()));
+    }
+}
+
 void KStandardItemListWidget::slotCutItemsChanged()
 {
-    const KUrl itemUrl = data().value("url").value<KUrl>();
+    const QUrl itemUrl = data().value("url").toUrl();
     const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl);
     if (m_isCut != isCut) {
         m_isCut = isCut;
@@ -859,14 +963,15 @@ void KStandardItemListWidget::updatePixmapCache()
             if (iconName.isEmpty()) {
                 // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
                 // use a generic icon as fallback
-                iconName = QLatin1String("unknown");
+                iconName = QStringLiteral("unknown");
             }
             const QStringList overlays = values["iconOverlays"].toStringList();
-            m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight);
-        } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) {
+            m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight, isSelected() && isActiveWindow() ? QIcon::Selected : QIcon::Normal);
+
+        } else if (m_pixmap.width() / m_pixmap.devicePixelRatio() != maxIconWidth || m_pixmap.height() / m_pixmap.devicePixelRatio() != 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));
+            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * qApp->devicePixelRatio());
         }
 
         if (m_isCut) {
@@ -878,7 +983,7 @@ void KStandardItemListWidget::updatePixmapCache()
             KIconEffect::semiTransparent(m_pixmap);
         }
 
-        if (isSelected()) {
+        if (m_layout == IconsLayout && isSelected()) {
             const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
             QImage image = m_pixmap.toImage();
             KIconEffect::colorize(image, color, 0.8f);
@@ -888,7 +993,7 @@ void KStandardItemListWidget::updatePixmapCache()
 
     if (!m_overlay.isNull()) {
         QPainter painter(&m_pixmap);
-        painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay);
+        painter.drawPixmap(0, (m_pixmap.height() - m_overlay.height()) / m_pixmap.devicePixelRatio(), m_overlay);
     }
 
     int scaledIconSize = 0;
@@ -910,15 +1015,21 @@ void KStandardItemListWidget::updatePixmapCache()
 
     if (iconOnTop) {
         // Center horizontally and align on bottom within the icon-area
-        m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
+        m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2.0);
         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_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding
+                      - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0);
+
+        // Derive icon's vertical center from the center of the text frame, including
+        // any necessary adjustment if the font's midline is offset from the frame center
+        const qreal midlineShift = m_customizedFontMetrics.height() / 2.0
+                    - m_customizedFontMetrics.descent()
+                    - m_customizedFontMetrics.capHeight() / 2.0;
+        m_pixmapPos.setY(m_textRect.center().y() + midlineShift - m_scaledPixmapSize.height() / 2.0);
+
     }
 
     m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize));
@@ -987,11 +1098,13 @@ void KStandardItemListWidget::updateTextsCache()
         if (ratingSize.width() > availableWidth) {
             ratingSize.rwidth() = availableWidth;
         }
-        m_rating = QPixmap(ratingSize.toSize());
+        const qreal dpr = qApp->devicePixelRatio();
+        m_rating = QPixmap(ratingSize.toSize() * dpr);
+        m_rating.setDevicePixelRatio(dpr);
         m_rating.fill(Qt::transparent);
 
         QPainter painter(&m_rating);
-        const QRect rect(0, 0, m_rating.width(), m_rating.height());
+        const QRect rect(QPoint(0, 0), ratingSize.toSize());
         const int rating = data().value("rating").toInt();
         KRatingPainter::paintRating(&painter, rect, Qt::AlignJustify | Qt::AlignVCenter, rating);
     } else if (!m_rating.isNull()) {
@@ -1030,9 +1143,6 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
     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();
@@ -1043,20 +1153,30 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
         nameHeight += line.height();
 
         ++nameLineIndex;
-        if (nameLineIndex == maxNameLines) {
+        if (nameLineIndex == option.maxTextLines) {
             // 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());
-                lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
-                                                                  Qt::ElideRight,
-                                                                  maxWidth);
-                const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
-                nameTextInfo->staticText.setText(elidedText);
-
-                const qreal lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
+                qreal elidingWidth = maxWidth;
+                qreal lastLineWidth;
+                do {
+                    QString lastTextLine = nameText.mid(line.textStart());
+                    lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
+                                                                      Qt::ElideRight,
+                                                                      elidingWidth);
+                    const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
+                    nameTextInfo->staticText.setText(elidedText);
+
+                    lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
+
+                    // We do the text eliding in a loop with decreasing width (1 px / iteration)
+                    // to avoid problems related to different width calculation code paths
+                    // within Qt. (see bug 337104)
+                    elidingWidth -= 1.0;
+                } while (lastLineWidth > maxWidth);
+
                 nameWidth = qMax(nameWidth, lastLineWidth);
             }
             break;
@@ -1065,6 +1185,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
     layout.endLayout();
 
     // Use one line for each additional information
+    const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
     nameTextInfo->staticText.setTextWidth(maxWidth);
     nameTextInfo->pos = QPointF(padding, widgetHeight -
                                          nameHeight -
@@ -1254,9 +1375,10 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor()
 
 void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
 {
-    if (m_scaledPixmapSize != pixmap.size()) {
+    if (m_scaledPixmapSize != pixmap.size() / pixmap.devicePixelRatio()) {
         QPixmap scaledPixmap = pixmap;
-        KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
+        KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize * qApp->devicePixelRatio());
+        scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio());
         painter->drawPixmap(m_pixmapPos, scaledPixmap);
 
 #ifdef KSTANDARDITEMLISTWIDGET_DEBUG
@@ -1288,7 +1410,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter)
             if (m_isExpandable) {
                 option.state |= QStyle::State_Children;
             }
-            if (data()["isExpanded"].toBool()) {
+            if (data().value("isExpanded").toBool()) {
                 option.state |= QStyle::State_Open;
             }
             isItemSibling = false;
@@ -1317,10 +1439,10 @@ 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)));
+    disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+               this, &KStandardItemListWidget::slotRoleEditingCanceled);
+    disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+               this, &KStandardItemListWidget::slotRoleEditingFinished);
 
     if (m_roleEditor->hasFocus()) {
         // If the editing was not ended by a FocusOut event, we have
@@ -1333,16 +1455,18 @@ void KStandardItemListWidget::closeRoleEditor()
     }
     m_oldRoleEditor = m_roleEditor;
     m_roleEditor->hide();
-    m_roleEditor = 0;
+    m_roleEditor = nullptr;
 }
 
-QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size)
+QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size, QIcon::Mode mode)
 {
-    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(":") % ":" % QString::number(size);
+    static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown"));
+    size *= qApp->devicePixelRatio();
+    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QStringLiteral(":")) % ":" % QString::number(size) % ":" % QString::number(mode);
     QPixmap pixmap;
 
     if (!QPixmapCache::find(key, pixmap)) {
-        const KIcon icon(name);
+        const QIcon icon = QIcon::fromTheme(name, fallbackIcon);
 
         int requestedSize;
         if (size <= KIconLoader::SizeSmall) {
@@ -1363,7 +1487,7 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin
             requestedSize = size;
         }
 
-        pixmap = icon.pixmap(requestedSize, requestedSize);
+        pixmap = icon.pixmap(requestedSize / qApp->devicePixelRatio(), requestedSize / qApp->devicePixelRatio(), mode);
         if (requestedSize != size) {
             KPixmapModifier::scale(pixmap, QSize(size, size));
         }
@@ -1384,6 +1508,7 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin
 
         QPixmapCache::insert(key, pixmap);
     }
+    pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
 
     return pixmap;
 }
@@ -1399,4 +1524,3 @@ qreal KStandardItemListWidget::columnPadding(const KItemListStyleOption& option)
     return option.padding * 6;
 }
 
-#include "kstandarditemlistwidget.moc"