-/***************************************************************************
- * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the *
- * Free Software Foundation, Inc., *
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
#include "kstandarditemlistwidget.h"
#include <KRatingPainter>
#include <KStringHandler>
+#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsSceneResizeEvent>
#include <QGraphicsView>
{
}
-void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<std::pair<qreal, bool>>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
{
switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
case KStandardItemListView::IconsLayout:
// 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);
+ width += fontMetrics.horizontalAdvance(text);
if (role == "text") {
if (view->supportsItemExpanding()) {
bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const
{
- Q_UNUSED(index);
- Q_UNUSED(view);
+ Q_UNUSED(index)
+ Q_UNUSED(view)
return false;
}
return baseFont;
}
-void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<std::pair<qreal, bool>>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
{
const KItemListStyleOption& option = view->styleOption();
const QFont& normalFont = option.font;
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
for (int index = 0; index < logicalHeightHints.count(); ++index) {
- if (logicalHeightHints.at(index) > 0.0) {
+ if (logicalHeightHints.at(index).first > 0.0) {
continue;
}
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.beginLayout();
QTextLine line;
int lineCount = 0;
+ bool isElided = false;
while ((line = layout.createLine()).isValid()) {
line.setLineWidth(maxWidth);
line.naturalTextWidth();
++lineCount;
if (lineCount == option.maxTextLines) {
+ isElided = layout.createLine().isValid();
break;
}
}
// Add one line for each additional information
textHeight += additionalRolesSpacing;
- logicalHeightHints[index] = textHeight + spacingAndIconHeight;
+ logicalHeightHints[index].first = textHeight + spacingAndIconHeight;
+ logicalHeightHints[index].second = isElided;
}
logicalWidthHint = itemWidth;
}
-void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<std::pair<qreal, bool>>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
{
const KItemListStyleOption& option = view->styleOption();
const QFontMetrics& normalFontMetrics = option.fontMetrics;
const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
for (int index = 0; index < logicalHeightHints.count(); ++index) {
- if (logicalHeightHints.at(index) > 0.0) {
+ if (logicalHeightHints.at(index).first > 0.0) {
continue;
}
qreal maximumRequiredWidth = 0.0;
if (showOnlyTextRole) {
- maximumRequiredWidth = fontMetrics.width(itemText(index, view));
+ maximumRequiredWidth = fontMetrics.horizontalAdvance(itemText(index, view));
} else {
const QHash<QByteArray, QVariant>& values = view->model()->data(index);
- foreach (const QByteArray& role, visibleRoles) {
+ for (const QByteArray& role : visibleRoles) {
const QString& text = roleText(role, values);
- const qreal requiredWidth = fontMetrics.width(text);
+ const qreal requiredWidth = fontMetrics.horizontalAdvance(text);
maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
}
}
width = maxWidth;
}
- logicalHeightHints[index] = width;
+ logicalHeightHints[index].first = width;
}
logicalWidthHint = height;
}
-void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector<std::pair<qreal, bool>>& 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);
+ logicalHeightHints.fill(std::make_pair(height, false));
logicalWidthHint = -1.0;
}
KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
KItemListWidget(informant, parent),
+ m_textInfo(),
m_isCut(false),
m_isHidden(false),
m_customizedFont(),
m_pixmapPos(),
m_pixmap(),
m_scaledPixmapSize(),
+ m_columnWidthSum(),
m_iconRect(),
m_hoverPixmap(),
- m_textInfo(),
m_textRect(),
m_sortedVisibleRoles(),
m_expansionArea(),
return m_layout;
}
+void KStandardItemListWidget::setHighlightEntireRow(bool highlightEntireRow) {
+ if (m_highlightEntireRow != highlightEntireRow) {
+ m_highlightEntireRow = highlightEntireRow;
+ m_dirtyLayout = true;
+ update();
+ }
+}
+
+bool KStandardItemListWidget::highlightEntireRow() const {
+ return m_highlightEntireRow;
+}
+
void KStandardItemListWidget::setSupportsItemExpanding(bool supportsItemExpanding)
{
if (m_supportsItemExpanding != supportsItemExpanding) {
}
const KItemListStyleOption& itemListStyleOption = styleOption();
- if (isHovered()) {
+ if (isHovered() && !m_pixmap.isNull()) {
if (hoverOpacity() < 1.0) {
/*
* Linear interpolation between m_pixmap and m_hoverPixmap.
} else {
drawPixmap(painter, m_hoverPixmap);
}
- } else {
+ } else if (!m_pixmap.isNull()) {
drawPixmap(painter, m_pixmap);
}
QPointF pos = ratingTextInfo->pos;
const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment();
if (align & Qt::AlignHCenter) {
- pos.rx() += (size().width() - m_rating.width()) / 2 - 2;
+ pos.rx() += (size().width() - m_rating.width() / m_rating.devicePixelRatioF()) / 2 - 2;
}
painter->drawPixmap(pos, m_rating);
}
const KItemListStyleOption& option = styleOption();
if (option.extendedSelectionRegion) {
const QString text = textInfo->staticText.text();
- rect.setWidth(m_customizedFontMetrics.width(text) + 2 * option.padding);
+ rect.setWidth(m_customizedFontMetrics.horizontalAdvance(text) + 2 * option.padding);
}
return rect;
case DetailsLayout: {
const int padding = styleOption().padding;
QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding);
- return adjustedIconRect | m_textRect;
+ QRectF result = adjustedIconRect | m_textRect;
+ if (m_highlightEntireRow) {
+ result.setRight(m_columnWidthSum + leadingPadding());
+ }
+ return result;
}
default:
{
const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
- const int iconHeight = styleOption().iconSize;
-
+ const int widgetIconSize = iconSize();
int toggleSize = KIconLoader::SizeSmall;
- if (iconHeight >= KIconLoader::SizeEnormous) {
+ if (widgetIconSize >= KIconLoader::SizeEnormous) {
toggleSize = KIconLoader::SizeMedium;
- } else if (iconHeight >= KIconLoader::SizeLarge) {
+ } else if (widgetIconSize >= KIconLoader::SizeLarge) {
toggleSize = KIconLoader::SizeSmallMedium;
}
m_dirtyContent = true;
}
+void KStandardItemListWidget::invalidateIconCache()
+{
+ m_dirtyContent = true;
+ m_dirtyContentRoles.insert("iconPixmap");
+ m_dirtyContentRoles.insert("iconOverlays");
+}
+
void KStandardItemListWidget::refreshCache()
{
}
bool KStandardItemListWidget::isRoleRightAligned(const QByteArray& role) const
{
- Q_UNUSED(role);
+ Q_UNUSED(role)
return false;
}
void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
const QSet<QByteArray>& roles)
{
- Q_UNUSED(current);
+ Q_UNUSED(current)
m_dirtyContent = true;
QSet<QByteArray> dirtyRoles;
if (roles.isEmpty()) {
- dirtyRoles = visibleRoles().toSet();
+ const auto visibleRoles = this->visibleRoles();
+ dirtyRoles = QSet<QByteArray>(visibleRoles.constBegin(), visibleRoles.constEnd());
} else {
dirtyRoles = roles;
}
void KStandardItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
const QList<QByteArray>& previous)
{
- Q_UNUSED(previous);
+ Q_UNUSED(previous)
m_sortedVisibleRoles = current;
m_dirtyLayout = true;
}
qreal current,
qreal previous)
{
- Q_UNUSED(role);
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ Q_UNUSED(role)
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ m_dirtyLayout = true;
+}
+
+void KStandardItemListWidget::leadingPaddingChanged(qreal padding) {
+ Q_UNUSED(padding)
m_dirtyLayout = true;
}
void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
const KItemListStyleOption& previous)
{
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ KItemListWidget::styleOptionChanged(current, previous);
+
updateAdditionalInfoTextColor();
m_dirtyLayout = true;
}
void KStandardItemListWidget::hoveredChanged(bool hovered)
{
- Q_UNUSED(hovered);
+ Q_UNUSED(hovered)
m_dirtyLayout = true;
}
void KStandardItemListWidget::selectedChanged(bool selected)
{
- Q_UNUSED(selected);
+ Q_UNUSED(selected)
updateAdditionalInfoTextColor();
m_dirtyContent = true;
}
void KStandardItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous)
{
- Q_UNUSED(current);
- Q_UNUSED(previous);
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
m_dirtyLayout = true;
}
void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous)
{
- Q_UNUSED(previous);
+ Q_UNUSED(previous)
QGraphicsView* parent = scene()->views()[0];
if (current.isEmpty() || !parent || current != "text") {
if (m_roleEditor) {
- emit roleEditingCanceled(index(), current, data().value(current));
+ Q_EMIT roleEditingCanceled(index(), current, data().value(current));
disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
this, &KStandardItemListWidget::slotRoleEditingCanceled);
m_roleEditor = new KItemListRoleEditor(parent);
m_roleEditor->setRole(current);
+ m_roleEditor->setAllowUpDownKeyChainEdit(m_layout != IconsLayout);
m_roleEditor->setFont(styleOption().font);
const QString text = data().value(current).toString();
m_roleEditor->setFocus();
}
+void KStandardItemListWidget::iconSizeChanged(int current, int previous)
+{
+ KItemListWidget::iconSizeChanged(current, previous);
+
+ invalidateIconCache();
+ triggerCacheRefreshing();
+ update();
+}
+
void KStandardItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
if (m_roleEditor) {
const QVariant& value)
{
closeRoleEditor();
- emit roleEditingCanceled(index(), role, value);
+ Q_EMIT roleEditingCanceled(index(), role, value);
setEditedRole(QByteArray());
}
const QVariant& value)
{
closeRoleEditor();
- emit roleEditingFinished(index(), role, value);
+ Q_EMIT roleEditingFinished(index(), role, value);
setEditedRole(QByteArray());
}
m_isHidden = isHidden();
m_customizedFont = customizedFont(styleOption().font);
m_customizedFontMetrics = QFontMetrics(m_customizedFont);
+ m_columnWidthSum = std::accumulate(m_sortedVisibleRoles.begin(), m_sortedVisibleRoles.end(),
+ qreal(), [this](qreal sum, const auto &role){ return sum + columnWidth(role); });
updateExpansionArea();
updateTextsCache();
updatePixmapCache();
+ clearHoverCache();
m_dirtyLayout = false;
m_dirtyContent = false;
const QHash<QByteArray, QVariant> values = data();
const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
if (expandedParentsCount >= 0) {
- const KItemListStyleOption& option = styleOption();
+ const int widgetIconSize = iconSize();
const qreal widgetHeight = size().height();
- const qreal inc = (widgetHeight - option.iconSize) / 2;
- const qreal x = expandedParentsCount * widgetHeight + inc;
+ const qreal inc = (widgetHeight - iconSize()) / 2;
+ const qreal x =
+ layoutDirection() == Qt::LeftToRight
+ ? expandedParentsCount * widgetHeight + inc
+ : size().width() - iconSize() - (expandedParentsCount * widgetHeight + inc);
const qreal y = inc;
- m_expansionArea = QRectF(x, y, option.iconSize, option.iconSize);
+ const qreal xPadding = m_highlightEntireRow ? leadingPadding() : 0;
+ m_expansionArea = QRectF(xPadding + x, y, widgetIconSize, widgetIconSize);
return;
}
}
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 int widgetIconSize = iconSize();
+ const int maxIconWidth = iconOnTop ? widgetSize.width() - 2 * padding : widgetIconSize;
+ const int maxIconHeight = widgetIconSize;
const QHash<QByteArray, QVariant> values = data();
}
if (updatePixmap) {
- m_pixmap = values["iconPixmap"].value<QPixmap>();
+ m_pixmap = QPixmap();
+
+ int sequenceIndex = hoverSequenceIndex();
+
+ if (values.contains("hoverSequencePixmaps")) {
+ // Use one of the hover sequence pixmaps instead of the default
+ // icon pixmap.
+
+ const QVector<QPixmap> pixmaps = values["hoverSequencePixmaps"].value<QVector<QPixmap>>();
+
+ if (values.contains("hoverSequenceWraparoundPoint")) {
+ const float wap = values["hoverSequenceWraparoundPoint"].toFloat();
+ if (wap >= 1.0f) {
+ sequenceIndex %= static_cast<int>(wap);
+ }
+ }
+
+ const int loadedIndex = qMax(qMin(sequenceIndex, pixmaps.size()-1), 0);
+
+ if (loadedIndex != 0) {
+ m_pixmap = pixmaps[loadedIndex];
+ }
+ }
+
+ if (m_pixmap.isNull()) {
+ m_pixmap = values["iconPixmap"].value<QPixmap>();
+ }
+
if (m_pixmap.isNull()) {
// Use the icon that fits to the MIME-type
QString iconName = values["iconName"].toString();
KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * qApp->devicePixelRatio());
}
+ if (m_pixmap.isNull()) {
+ m_hoverPixmap = QPixmap();
+ return;
+ }
+
if (m_isCut) {
KIconEffect* effect = KIconLoader::global()->iconEffect();
m_pixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::DisabledState);
if (m_layout == IconsLayout && isSelected()) {
const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
QImage image = m_pixmap.toImage();
+ if (image.isNull()) {
+ m_hoverPixmap = QPixmap();
+ return;
+ }
KIconEffect::colorize(image, color, 0.8f);
m_pixmap = QPixmap::fromImage(image);
}
} else {
// Center horizontally and vertically within the icon-area
const TextInfo* textInfo = m_textInfo.value("text");
- m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding
- - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0);
+ const auto width = (scaledIconSize + m_scaledPixmapSize.width()) / 2.0;
+ const auto iPadding = 2.0 * padding;
+ const auto x = textInfo->pos.x();
+
+ const QHash<QByteArray, QVariant> values = data();
+ const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
+ const int expansionOffset =
+ (m_layout == DetailsLayout) ?
+ size().height() + size().height() * expandedParentsCount :
+ 0;
+
+ m_pixmapPos.setX(layoutDirection() == Qt::LeftToRight
+ ? x - iPadding - width
+ : size().width() - iPadding - width - expansionOffset);
// 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
}
}
+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);
+}
+
void KStandardItemListWidget::updateIconsLayoutTextCache()
{
// +------+
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
qreal lastLineWidth;
do {
QString lastTextLine = nameText.mid(line.textStart());
- lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
- Qt::ElideMiddle,
- elidingWidth);
+ lastTextLine = elideRightKeepExtension(lastTextLine, elidingWidth);
const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
nameTextInfo->staticText.setText(elidedText);
- lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
+ lastLineWidth = m_customizedFontMetrics.horizontalAdvance(lastTextLine);
// We do the text eliding in a loop with decreasing width (1 px / iteration)
// to avoid problems related to different width calculation code paths
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 -
- additionalRolesCount * lineSpacing -
- padding);
+ nameTextInfo->pos = QPointF(padding, iconSize() + 2 * padding);
m_textRect = QRectF(padding + (maxWidth - nameWidth) / 2,
nameTextInfo->pos.y(),
nameWidth,
// Calculate the position for each additional information
qreal y = nameTextInfo->pos.y() + nameHeight;
- foreach (const QByteArray& role, m_sortedVisibleRoles) {
+ for (const QByteArray& role : qAsConst(m_sortedVisibleRoles)) {
if (role == "text") {
continue;
}
textLine.setLineWidth(maxWidth);
requiredWidth = textLine.naturalTextWidth();
if (requiredWidth > maxWidth) {
- const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
+ const QString elidedText = elideRightKeepExtension(text, maxWidth);
textInfo->staticText.setText(elidedText);
- requiredWidth = m_customizedFontMetrics.width(elidedText);
+ requiredWidth = m_customizedFontMetrics.horizontalAdvance(elidedText);
} else if (role == "rating") {
// Use the width of the rating pixmap, because the rating text is empty.
- requiredWidth = m_rating.width();
+ requiredWidth = m_rating.width() / m_rating.devicePixelRatioF();
}
}
layout.endLayout();
textInfo->staticText.setTextWidth(maxWidth);
const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, lineSpacing);
- m_textRect |= textRect;
+
+ // Ignore empty roles. Avoids a text rect taller than the area that actually contains text.
+ if (!textRect.isEmpty()) {
+ m_textRect |= textRect;
+ }
y += lineSpacing;
}
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;
+ const qreal x = option.padding * 3 + iconSize();
qreal y = qRound((widgetHeight - textLinesHeight) / 2);
const qreal maxWidth = size().width() - x - option.padding;
- foreach (const QByteArray& role, m_sortedVisibleRoles) {
+ for (const QByteArray& role : qAsConst(m_sortedVisibleRoles)) {
const QString text = roleText(role, values);
TextInfo* textInfo = m_textInfo.value(role);
textInfo->staticText.setText(text);
- qreal requiredWidth = m_customizedFontMetrics.width(text);
+ qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
if (requiredWidth > maxWidth) {
requiredWidth = maxWidth;
- const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
+ const QString elidedText = elideRightKeepExtension(text, maxWidth);
textInfo->staticText.setText(elidedText);
}
- textInfo->pos = QPointF(x, y);
+ if (layoutDirection() == Qt::LeftToRight) {
+ textInfo->pos = QPointF(x, y);
+ } else {
+ textInfo->pos = QPointF(x - size().height(), y);
+ }
textInfo->staticText.setTextWidth(maxWidth);
maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth);
y += lineSpacing;
}
- m_textRect = QRectF(x - 2 * option.padding, 0, maximumRequiredTextWidth + 3 * option.padding, widgetHeight);
+ if (layoutDirection() == Qt::LeftToRight) {
+ m_textRect = QRectF(x - option.padding, 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight);
+ } else {
+ m_textRect = QRectF(x - option.padding - size().height(), 0, maximumRequiredTextWidth + 2 * option.padding, widgetHeight);
+ }
}
void KStandardItemListWidget::updateDetailsLayoutTextCache()
const QHash<QByteArray, QVariant> values = data();
const qreal widgetHeight = size().height();
- const int scaledIconSize = widgetHeight - 2 * option.padding;
const int fontHeight = m_customizedFontMetrics.height();
const qreal columnWidthInc = columnPadding(option);
- qreal firstColumnInc = scaledIconSize;
+
+ qreal firstColumnOffset = iconSize();
if (m_supportsItemExpanding) {
- firstColumnInc += (m_expansionArea.left() + m_expansionArea.right() + widgetHeight) / 2;
+ firstColumnOffset += (m_expansionArea.width() + widgetHeight) / 2;
} else {
- firstColumnInc += option.padding;
+ firstColumnOffset += option.padding + leadingPadding();
}
- qreal x = firstColumnInc;
+ qreal x = firstColumnOffset;
const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2);
- foreach (const QByteArray& role, m_sortedVisibleRoles) {
+ for (const QByteArray& role : qAsConst(m_sortedVisibleRoles)) {
QString text = roleText(role, values);
// Elide the text in case it does not fit into the available column-width
- qreal requiredWidth = m_customizedFontMetrics.width(text);
+ qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
const qreal roleWidth = columnWidth(role);
qreal availableTextWidth = roleWidth - columnWidthInc;
+ const QHash<QByteArray, QVariant> values = data();
+ const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
+ const int expansionOffset = size().height() * expandedParentsCount;
+
const bool isTextRole = (role == "text");
if (isTextRole) {
- availableTextWidth -= firstColumnInc;
+ availableTextWidth -= firstColumnOffset - leadingPadding();
}
if (requiredWidth > availableTextWidth) {
- text = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, availableTextWidth);
- requiredWidth = m_customizedFontMetrics.width(text);
+ text = elideRightKeepExtension(text, 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(x - (layoutDirection() == Qt::LeftToRight ? 0 : firstColumnOffset), y);
+ if (layoutDirection() == Qt::LeftToRight) {
+ textInfo->pos.rx() += columnWidthInc/2 + expansionOffset;
+ } else {
+ textInfo->pos.rx() -= expansionOffset;
+ if (textInfo->pos.x() < iconSize()) {
+ textInfo->pos.rx() = iconSize();
+ }
+ }
x += roleWidth;
if (isTextRole) {
const qreal textWidth = option.extendedSelectionRegion
? size().width() - textInfo->pos.x()
: requiredWidth + 2 * option.padding;
- m_textRect = QRectF(textInfo->pos.x() - 2 * option.padding, 0,
- textWidth + option.padding, size().height());
+ m_textRect = QRectF(textInfo->pos.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
- x -= firstColumnInc;
+ x -= firstColumnOffset - leadingPadding();
} else if (isRoleRightAligned(role)) {
textInfo->pos.rx() += roleWidth - requiredWidth - columnWidthInc;
}
QColor c1;
if (m_customTextColor.isValid()) {
c1 = m_customTextColor;
- } else if (isSelected() && m_layout != DetailsLayout) {
- c1 = styleOption().palette.highlightedText().color();
+ } else if (isSelected()) {
+ // The detail text colour needs to match the main text (HighlightedText) for the same level
+ // of readability. We short circuit early here to avoid interpolating with another colour.
+ m_additionalInfoTextColor = styleOption().palette.color(QPalette::HighlightedText);
+ return;
} else {
c1 = styleOption().palette.text().color();
}
void KStandardItemListWidget::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);
+ const int x = (m_expansionArea.width() - siblingSize) / 2;
+
+ const QHash<QByteArray, QVariant> values = data();
+ const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
+ const int expansionOffset = siblingSize * expandedParentsCount;
+
+ QRect siblingRect(
+ layoutDirection() == Qt::LeftToRight
+ ? x + expansionOffset
+ : size().width() - x - siblingSize - expansionOffset, 0, siblingSize, siblingSize);
- QStyleOption option;
- option.palette.setColor(QPalette::Text, option.palette.color(normalTextColorRole()));
bool isItemSibling = true;
const QBitArray siblings = siblingsInformation();
+ QStyleOption option;
+ option.direction = layoutDirection();
+ const auto normalColor = option.palette.color(normalTextColorRole());
+ const auto highlightColor = option.palette.color(expansionAreaHovered() ? QPalette::Highlight : normalTextColorRole());
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) {
if (data().value("isExpanded").toBool()) {
option.state |= QStyle::State_Open;
}
+ option.palette.setColor(QPalette::Text, highlightColor);
isItemSibling = false;
+ } else {
+ option.palette.setColor(QPalette::Text, normalColor);
}
style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter);
- siblingRect.translate(-siblingRect.width(), 0);
+ if (layoutDirection() == Qt::LeftToRight) {
+ siblingRect.translate(-siblingRect.width(), 0);
+ } else {
+ siblingRect.translate(siblingRect.width(), 0);
+ }
}
}
{
static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown"));
- int requestedSize = size;
- if (size <= KIconLoader::SizeSmall) {
- requestedSize = KIconLoader::SizeSmall;
- } else if (size <= KIconLoader::SizeSmallMedium) {
- requestedSize = KIconLoader::SizeSmallMedium;
- } else if (size <= KIconLoader::SizeMedium) {
- requestedSize = KIconLoader::SizeMedium;
- } else if (size <= KIconLoader::SizeLarge) {
- requestedSize = KIconLoader::SizeLarge;
- } else if (size <= KIconLoader::SizeHuge) {
- requestedSize = KIconLoader::SizeHuge;
- } else if (size <= KIconLoader::SizeEnormous) {
- requestedSize = KIconLoader::SizeEnormous;
- } else if (size <= KIconLoader::SizeEnormous * 2) {
- requestedSize = KIconLoader::SizeEnormous * 2;
- }
size *= qApp->devicePixelRatio();
- requestedSize *= qApp->devicePixelRatio();
- const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QStringLiteral(":")) % ":" % QString::number(size) % ":" % QString::number(mode);
+ const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QLatin1Char(':')) % ":" % QString::number(size) % ":" % QString::number(mode);
QPixmap pixmap;
- if (!QPixmapCache::find(key, pixmap)) {
- const QIcon icon = QIcon::fromTheme(name, fallbackIcon);
+ if (!QPixmapCache::find(key, &pixmap)) {
+ QIcon icon = QIcon::fromTheme(name);
+ if (icon.isNull()) {
+ icon = QIcon(name);
+ }
+ if (icon.isNull()
+ || icon.pixmap(size / qApp->devicePixelRatio(), size / qApp->devicePixelRatio(), mode).isNull()) {
+ icon = fallbackIcon;
+ }
- pixmap = icon.pixmap(requestedSize / qApp->devicePixelRatio(), requestedSize / qApp->devicePixelRatio(), mode);
- if (requestedSize != size) {
+ pixmap = icon.pixmap(size / qApp->devicePixelRatio(), size / qApp->devicePixelRatio(), mode);
+ if (pixmap.width() != size || pixmap.height() != size) {
KPixmapModifier::scale(pixmap, QSize(size, size));
}
// 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) {
+ for (const QString& overlay : overlays) {
if (!overlay.isEmpty()) {
int state = KIconLoader::DefaultState;