From: Peter Penz Date: Mon, 26 Mar 2012 22:44:39 +0000 (+0200) Subject: KItemListView interface and implementation simplification X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/793311dac163592a8b63fc4859fdc054d7a22022 KItemListView interface and implementation simplification - Remove KItemListView::preferredRoleColumnWidth() and allow implementing this as part of derived classes from KItemListWidget. Those derived classes are aware about the layout and hence also can provide the preferred role width. - Make KItemListView::itemSizeHint() non-virtual and also allow implementing the size hint as part of derived classes from KItemListWidget. --- diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index ab111b912..8f7c9c899 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -46,8 +46,7 @@ KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : m_itemLayout(IconsLayout), m_modelRolesUpdater(0), m_updateVisibleIndexRangeTimer(0), - m_updateIconSizeTimer(0), - m_minimumRolesWidths() + m_updateIconSizeTimer(0) { setAcceptDrops(true); @@ -66,8 +65,6 @@ KFileItemListView::KFileItemListView(QGraphicsWidget* parent) : connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize())); setVisibleRoles(QList() << "name"); - - updateMinimumRolesWidths(); } KFileItemListView::~KFileItemListView() @@ -126,98 +123,6 @@ QStringList KFileItemListView::enabledPlugins() const return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList(); } -QSizeF KFileItemListView::itemSizeHint(int index) const -{ - const QHash values = model()->data(index); - const KItemListStyleOption& option = styleOption(); - const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); - - switch (m_itemLayout) { - case IconsLayout: { - const QString text = KStringHandler::preProcessWrap(values["name"].toString()); - - const qreal maxWidth = itemSize().width() - 2 * option.padding; - int textLinesCount = 0; - QTextLine line; - - // Calculate the number of lines required for wrapping the name - QTextOption textOption(Qt::AlignHCenter); - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - - QTextLayout layout(text, option.font); - layout.setTextOption(textOption); - layout.beginLayout(); - while ((line = layout.createLine()).isValid()) { - line.setLineWidth(maxWidth); - line.naturalTextWidth(); - ++textLinesCount; - } - layout.endLayout(); - - // Add one line for each additional information - textLinesCount += additionalRolesCount; - - const qreal height = textLinesCount * option.fontMetrics.height() + - option.iconSize + - option.padding * 3; - return QSizeF(itemSize().width(), height); - } - - case 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; - - foreach (const QByteArray& role, visibleRoles()) { - const QString text = KFileItemListWidget::roleText(role, values); - const qreal requiredWidth = option.fontMetrics.width(text); - maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); - } - - const qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth; - const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.height()); - return QSizeF(width, height); - } - - case DetailsLayout: { - // The width will be determined dynamically by KFileItemListView::visibleRoleSizes() - const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); - return QSizeF(-1, height); - } - - default: - Q_ASSERT(false); - break; - } - - return QSize(); -} - -qreal KFileItemListView::preferredRoleColumnWidth(const QByteArray& role, int index) const -{ - const KItemListStyleOption& option = styleOption(); - - qreal width = m_minimumRolesWidths.value(role, 0); - - const QHash values = model()->data(index); - const QString text = KFileItemListWidget::roleText(role, values); - if (!text.isEmpty()) { - const qreal columnPadding = option.padding * 3; - width = qMax(width, qreal(2 * columnPadding + option.fontMetrics.width(text))); - } - - if (role == "name") { - // Increase the width by the expansion-toggle and the current expansion level - const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); - width += option.padding + (expandedParentsCount + 1) * itemSize().height() + KIconLoader::SizeSmall; - - // Increase the width by the required space for the icon - width += option.padding * 2 + option.iconSize; - } - - return width; -} - QPixmap KFileItemListView::createDragPixmap(const QSet& indexes) const { if (!model()) { @@ -502,15 +407,6 @@ void KFileItemListView::updateTimersInterval() m_updateIconSizeTimer->setInterval(interval); } -void KFileItemListView::updateMinimumRolesWidths() -{ - m_minimumRolesWidths.clear(); - - const KItemListStyleOption& option = styleOption(); - const QString sizeText = QLatin1String("888888") + i18nc("@item:intable", "items"); - m_minimumRolesWidths.insert("size", option.fontMetrics.width(sizeText)); -} - void KFileItemListView::applyRolesToModel() { if (!model()) { diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h index a94e2b6f5..40808c4de 100644 --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -74,12 +74,6 @@ public: */ QStringList enabledPlugins() const; - /** @reimp */ - virtual QSizeF itemSizeHint(int index) const; - - /** @reimp */ - virtual qreal preferredRoleColumnWidth(const QByteArray& role, int index) const; - /** @reimp */ virtual QPixmap createDragPixmap(const QSet& indexes) const; @@ -111,7 +105,6 @@ private slots: private: void updateLayoutOfVisibleItems(); void updateTimersInterval(); - void updateMinimumRolesWidths(); /** * Applies the roles defined by KItemListView::visibleRoles() to the @@ -135,9 +128,6 @@ private: QTimer* m_updateVisibleIndexRangeTimer; QTimer* m_updateIconSizeTimer; - // Cache for calculating visibleRoleSizes() in a fast way - QHash m_minimumRolesWidths; - friend class KFileItemListViewTest; // For unit testing }; diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 5c865d1ca..022f3b472 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -20,8 +20,8 @@ #include "kfileitemlistwidget.h" #include "kfileitemclipboard_p.h" +#include "kfileitemlistview.h" #include "kfileitemmodel.h" -#include "kitemlistview.h" #include "kpixmapmodifier_p.h" #include @@ -222,48 +222,62 @@ QRectF KFileItemListWidget::selectionToggleRect() const return QRectF(pos, QSizeF(toggleSize, toggleSize)); } -QString KFileItemListWidget::roleText(const QByteArray& role, const QHash& values) +QSizeF KFileItemListWidget::itemSizeHint(int index, const KItemListView* view) { - QString text; - const QVariant roleValue = values.value(role); + const QHash values = view->model()->data(index); + const KItemListStyleOption& option = view->styleOption(); + const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0); - switch (roleTextId(role)) { - case Name: - case Permissions: - case Owner: - case Group: - case Type: - case Destination: - case Path: - text = roleValue.toString(); - break; + switch (static_cast(view)->itemLayout()) { + case IconsLayout: { + const QString text = KStringHandler::preProcessWrap(values["name"].toString()); - 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()) { - const int count = roleValue.toInt(); - if (count < 0) { - text = i18nc("@item:intable", "Unknown"); - } else { - text = i18ncp("@item:intable", "%1 item", "%1 items", count); - } - } - } else { - // Show the size in kilobytes (always round up) - 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); + const qreal maxWidth = view->itemSize().width() - 2 * option.padding; + int textLinesCount = 0; + QTextLine line; + + // Calculate the number of lines required for wrapping the name + QTextOption textOption(Qt::AlignHCenter); + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + QTextLayout layout(text, option.font); + layout.setTextOption(textOption); + layout.beginLayout(); + while ((line = layout.createLine()).isValid()) { + line.setLineWidth(maxWidth); + line.naturalTextWidth(); + ++textLinesCount; } - break; + layout.endLayout(); + + // Add one line for each additional information + textLinesCount += additionalRolesCount; + + const qreal height = textLinesCount * option.fontMetrics.height() + + option.iconSize + + option.padding * 3; + return QSizeF(view->itemSize().width(), height); } - case Date: { - const QDateTime dateTime = roleValue.toDateTime(); - text = KGlobal::locale()->formatDateTime(dateTime); - break; + case 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; + + foreach (const QByteArray& role, view->visibleRoles()) { + const QString text = KFileItemListWidget::roleText(role, values); + const qreal requiredWidth = option.fontMetrics.width(text); + maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); + } + + const qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth; + const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.height()); + return QSizeF(width, height); + } + + case DetailsLayout: { + const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height()); + return QSizeF(-1, height); } default: @@ -271,7 +285,34 @@ QString KFileItemListWidget::roleText(const QByteArray& role, const QHash values = view->model()->data(index); + const KItemListStyleOption& option = view->styleOption(); + + const QString text = KFileItemListWidget::roleText(role, values); + if (!text.isEmpty()) { + const qreal columnPadding = option.padding * 3; + width = qMax(width, qreal(2 * columnPadding + option.fontMetrics.width(text))); + } + + if (role == "name") { + // Increase the width by the expansion-toggle and the current expansion level + const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt(); + width += option.padding + (expandedParentsCount + 1) * view->itemSize().height() + KIconLoader::SizeSmall; + + // Increase the width by the required space for the icon + width += option.padding * 2 + option.iconSize; + } + + return width; } void KFileItemListWidget::invalidateCache() @@ -924,6 +965,17 @@ QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size) return pixmap; } +void KFileItemListWidget::applyCutEffect(QPixmap& pixmap) +{ + KIconEffect* effect = KIconLoader::global()->iconEffect(); + pixmap = effect->apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); +} + +void KFileItemListWidget::applyHiddenEffect(QPixmap& pixmap) +{ + KIconEffect::semiTransparent(pixmap); +} + KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role) { static QHash rolesHash; @@ -942,15 +994,55 @@ KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& ro return rolesHash.value(role); } -void KFileItemListWidget::applyCutEffect(QPixmap& pixmap) +QString KFileItemListWidget::roleText(const QByteArray& role, const QHash& values) { - KIconEffect* effect = KIconLoader::global()->iconEffect(); - pixmap = effect->apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState); -} + QString text; + const QVariant roleValue = values.value(role); -void KFileItemListWidget::applyHiddenEffect(QPixmap& pixmap) -{ - KIconEffect::semiTransparent(pixmap); -} + 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()) { + const int count = roleValue.toInt(); + if (count < 0) { + text = i18nc("@item:intable", "Unknown"); + } else { + text = i18ncp("@item:intable", "%1 item", "%1 items", count); + } + } + } else { + // Show the size in kilobytes (always round up) + 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; +} #include "kfileitemlistwidget.moc" diff --git a/src/kitemviews/kfileitemlistwidget.h b/src/kitemviews/kfileitemlistwidget.h index 3e6cf8d2a..76d090040 100644 --- a/src/kitemviews/kfileitemlistwidget.h +++ b/src/kitemviews/kfileitemlistwidget.h @@ -28,6 +28,9 @@ #include #include +class KItemListView; +class KItemListStyleOption; + class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidget : public KItemListWidget { Q_OBJECT @@ -57,12 +60,20 @@ public: virtual QRectF selectionToggleRect() const; /** - * @return Shown string for the role \p role of the item with the values \p values. + * Implementation of KItemListWidgetCreatorBase::itemSizeHint() when + * using the KItemListWidgetCreator-template. + * + * @see KItemListView */ - // TODO: Move this method to a helper class shared by KFileItemListWidget and - // KFileItemListView to share information that is required to calculate the size hints - // in KFileItemListView and to represent the actual data in KFileItemListWidget. - static QString roleText(const QByteArray& role, const QHash& values); + static QSizeF itemSizeHint(int index, const KItemListView* view); + + /** + * Implementation of KItemListWidgetCreatorBase::preferredRoleColumnWidth() when + * using the KItemListWidgetCreator-template. + * + * @see KItemListView + */ + static qreal preferredRoleColumnWidth(const QByteArray& role, int index, const KItemListView* view); protected: /** @@ -126,9 +137,14 @@ private: void drawSiblingsInformation(QPainter* painter); static QPixmap pixmapForIcon(const QString& name, int size); - static TextId roleTextId(const QByteArray& role); static void applyCutEffect(QPixmap& pixmap); static void applyHiddenEffect(QPixmap& pixmap); + static TextId roleTextId(const QByteArray& role); + + /** + * @return Shown string for the role \p role of the item with the values \p values. + */ + static QString roleText(const QByteArray& role, const QHash& values); private: bool m_isCut; diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 7d1cd6e71..8a18991a0 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -510,15 +510,7 @@ int KItemListView::lastVisibleIndex() const QSizeF KItemListView::itemSizeHint(int index) const { - Q_UNUSED(index); - return itemSize(); -} - -qreal KItemListView::preferredRoleColumnWidth(const QByteArray& role, int index) const -{ - Q_UNUSED(role); - Q_UNUSED(index); - return 100; + return m_widgetCreator->itemSizeHint(index, this); } void KItemListView::setSupportsItemExpanding(bool supportsExpanding) @@ -1915,7 +1907,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi for (int i = startIndex; i <= endIndex; ++i) { foreach (const QByteArray& visibleRole, visibleRoles()) { qreal maxWidth = widths.value(visibleRole, 0); - const qreal width = preferredRoleColumnWidth(visibleRole, i); + const qreal width = m_widgetCreator->preferredRoleColumnWidth(visibleRole, i, this); maxWidth = qMax(width, maxWidth); widths.insert(visibleRole, maxWidth); } diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index 9c42a6f3e..d65ece8e2 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -186,17 +186,11 @@ public: /** * @return Required size for the item with the index \p index. - * Per default KItemListView::itemSize() is returned. - * When reimplementing this method it is recommended to - * also reimplement KItemListView::itemSizeHintUpdateRequired(). + * The returned value might be larger than KItemListView::itemSize(). + * In this case the layout grid will be stretched to assure an + * unclipped item. */ - virtual QSizeF itemSizeHint(int index) const; - - /** - * @return The preferred column-width of the given \a role for the item - * with the index \a index. - */ - virtual qreal preferredRoleColumnWidth(const QByteArray& role, int index) const; + QSizeF itemSizeHint(int index) const; /** * If set to true, items having child-items can be expanded to show the child-items as @@ -714,24 +708,50 @@ private: * @brief Base class for creating KItemListWidgets. * * It is recommended that applications simply use the KItemListWidgetCreator-template class. - * For a custom implementation the methods create() and recyle() must be reimplemented. - * The intention of the widget creator is to prevent repetitive and expensive instantiations and - * deletions of KItemListWidgets by recycling existing widget instances. + * For a custom implementation the methods create(), itemSizeHint() and preferredColumnWith() + * must be reimplemented. The intention of the widget creator is to prevent repetitive and + * expensive instantiations and deletions of KItemListWidgets by recycling existing widget + * instances. */ class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreatorBase : public KItemListCreatorBase { public: virtual ~KItemListWidgetCreatorBase(); + virtual KItemListWidget* create(KItemListView* view) = 0; + virtual void recycle(KItemListWidget* widget); + + virtual QSizeF itemSizeHint(int index, const KItemListView* view) const = 0; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const = 0; }; +/** + * @brief Template class for creating KItemListWidgets. + * + * The template class must provide the following two static methods: + * - QSizeF itemSizeHint(int index, const KItemListView* view) + * - preferredRoleColumnWidth(const QByteArray& role, int index, const KItemListView* view) + * Those static methods are used as implementation for + * KItemListWidgetCreatorBase::itemSizeHint() and + * KItemListWidgetCreatorBase::preferedRoleColumnWidth(). + */ template class KItemListWidgetCreator : public KItemListWidgetCreatorBase { public: virtual ~KItemListWidgetCreator(); + virtual KItemListWidget* create(KItemListView* view); + + virtual QSizeF itemSizeHint(int index, const KItemListView* view) const; + + virtual qreal preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const; }; template @@ -750,6 +770,20 @@ KItemListWidget* KItemListWidgetCreator::create(KItemListView* view) return widget; } +template +QSizeF KItemListWidgetCreator::itemSizeHint(int index, const KItemListView* view) const +{ + return T::itemSizeHint(index, view); +} + +template +qreal KItemListWidgetCreator::preferredRoleColumnWidth(const QByteArray& role, + int index, + const KItemListView* view) const +{ + return T::preferredRoleColumnWidth(role, index, view); +} + /** * @brief Base class for creating KItemListGroupHeaders. *