]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Make group-headers less ugly
authorPeter Penz <peter.penz19@gmail.com>
Sun, 23 Oct 2011 22:22:03 +0000 (00:22 +0200)
committerPeter Penz <peter.penz19@gmail.com>
Sun, 23 Oct 2011 22:22:27 +0000 (00:22 +0200)
src/kitemviews/kfileitemlistgroupheader.cpp
src/kitemviews/kfileitemlistgroupheader.h
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kitemlistgroupheader.cpp
src/kitemviews/kitemlistgroupheader.h
src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistviewlayouter.cpp

index 969bd925b170b827ff9426c4fd949424b6cf3234..06d410f28566827bcc3f68a0cfe927401261cb9b 100644 (file)
 #include <QPainter>
 
 KFileItemListGroupHeader::KFileItemListGroupHeader(QGraphicsWidget* parent) :
-    KItemListGroupHeader(parent)
+    KItemListGroupHeader(parent),
+    m_font(),
+    m_text()
 {
+    m_text.setTextFormat(Qt::PlainText);
+    m_text.setPerformanceHint(QStaticText::AggressiveCaching);
 }
 
 KFileItemListGroupHeader::~KFileItemListGroupHeader()
@@ -36,8 +40,35 @@ KFileItemListGroupHeader::~KFileItemListGroupHeader()
 void KFileItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
 {
     KItemListGroupHeader::paint(painter, option, widget);
-    // TODO: Use dataChanged() hook to prepare a cached property
-    painter->drawText(QRectF(0, 0, size().width(), size().height()), data().toString());
+
+    painter->setPen(styleOption().palette.text().color());
+    painter->setFont(m_font);
+    const int margin = styleOption().margin;
+    painter->drawStaticText(margin * 2, margin, m_text);
+}
+
+void KFileItemListGroupHeader::dataChanged(const QVariant& current, const QVariant& previous)
+{
+    Q_UNUSED(current);
+    Q_UNUSED(previous);
+    updateText();
+}
+
+void KFileItemListGroupHeader::resizeEvent(QGraphicsSceneResizeEvent* event)
+{
+    QGraphicsWidget::resizeEvent(event);
+    updateText();
+}
+
+void KFileItemListGroupHeader::updateText()
+{
+    const qreal width = size().width() - 4 * styleOption().margin;
+    m_font = font();
+    m_font.setBold(true);
+
+    QFontMetricsF fontMetrics(m_font);
+    const QString text = fontMetrics.elidedText(data().toString(), Qt::ElideRight, width);
+    m_text.setText(text);
 }
 
 #include "kfileitemlistgroupheader.moc"
index 3c23f13d4f06eaae87888b12705f849e1cc32c3c..52d9a64f19c336d6fa4a3ab1fb4c91c32a3971d1 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <kitemviews/kitemlistgroupheader.h>
 
+#include <QStaticText>
+
 class LIBDOLPHINPRIVATE_EXPORT KFileItemListGroupHeader : public KItemListGroupHeader
 {
     Q_OBJECT
@@ -32,8 +34,18 @@ public:
     KFileItemListGroupHeader(QGraphicsWidget* parent = 0);
     virtual ~KFileItemListGroupHeader();
 
-    /** @reimp */
     virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
+
+protected:
+    virtual void dataChanged(const QVariant& current, const QVariant& previous);
+    virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
+
+private:
+    void updateText();
+
+private:
+    QFont m_font;
+    QStaticText m_text;
 };
 #endif
 
index da2126aa36bdd8584f1ed275f4f2cb24e698504e..5ccca5a94b56a6ecef22f4240c7ac49ca48b15aa 100644 (file)
@@ -1168,6 +1168,13 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
     QChar firstChar;
     bool isLetter = false;
     for (int i = 0; i <= maxIndex; ++i) {
+        if (m_requestRole[ExpansionLevelRole] && m_data.at(i).value("expansionLevel").toInt() > 0) {
+            // KItemListView would be capable to show sub-groups in groups but
+            // in typical usecases this results in visual clutter, hence we
+            // just ignore sub-groups.
+            continue;
+        }
+
         const QString name = m_data.at(i).value("name").toString();
 
         // Use the first character of the name as group indication
index 5aac02c829d8c39673d755cde35691f0713d008c..7413a7d2586ca45f5a8e7406d3174d81452e498b 100644 (file)
 
 #include "kitemlistview.h"
 
+#include <QGraphicsSceneResizeEvent>
 #include <QPainter>
-
+#include <QStyleOptionGraphicsItem>
 #include <KDebug>
 
 KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) :
     QGraphicsWidget(parent, 0),
+    m_dirtyCache(true),
     m_role(),
-    m_data()
+    m_data(),
+    m_styleOption(),
+    m_scrollOrientation(Qt::Vertical),
+    m_leftBorderCache(0),
+    m_rightBorderCache(0),
+    m_outlineColor()
 {
 }
 
 KItemListGroupHeader::~KItemListGroupHeader()
 {
+    delete m_leftBorderCache;
+    delete m_rightBorderCache;
 }
 
 void KItemListGroupHeader::setRole(const QByteArray& role)
@@ -69,22 +78,51 @@ QVariant KItemListGroupHeader::data() const
     return m_data;
 }
 
-QSizeF KItemListGroupHeader::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
+void KItemListGroupHeader::setStyleOption(const KItemListStyleOption& option)
+{
+    const KItemListStyleOption previous = m_styleOption;
+    m_styleOption = option;
+    m_dirtyCache = true;
+    styleOptionChanged(option, previous);
+}
+
+const KItemListStyleOption& KItemListGroupHeader::styleOption() const
+{
+    return m_styleOption;
+}
+
+void KItemListGroupHeader::setScrollOrientation(Qt::Orientation orientation)
+{
+    if (m_scrollOrientation != orientation) {
+        const Qt::Orientation previous = m_scrollOrientation;
+        m_scrollOrientation = orientation;
+        m_dirtyCache = true;
+        scrollOrientationChanged(orientation, previous);
+    }
+}
+
+Qt::Orientation KItemListGroupHeader::scrollOrientation() const
 {
-    Q_UNUSED(which);
-    Q_UNUSED(constraint);
-    return QSizeF();
+    return m_scrollOrientation;
 }
 
 void KItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
 {
+    if (m_dirtyCache) {
+        updateCache();
+    }
+
     Q_UNUSED(option);
     Q_UNUSED(widget);
-    painter->setPen(Qt::darkGreen);
-    painter->setBrush(QColor(0, 255, 0, 50));
-    painter->drawRect(rect());
 
-    //painter->drawText(rect(), QString::number(m_index));
+    const int leftBorderX = m_leftBorderCache->width() + 1;
+    const int rightBorderX = size().width() - m_rightBorderCache->width() - 2;
+
+    painter->setPen(m_outlineColor);
+    painter->drawLine(leftBorderX, 1, rightBorderX, 1);
+
+    painter->drawPixmap(1, 1, *m_leftBorderCache);
+    painter->drawPixmap(rightBorderX, 1, *m_rightBorderCache);
 }
 
 void KItemListGroupHeader::roleChanged(const QByteArray& current, const QByteArray& previous)
@@ -99,4 +137,106 @@ void KItemListGroupHeader::dataChanged(const QVariant& current, const QVariant&
     Q_UNUSED(previous);
 }
 
+void KItemListGroupHeader::styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous)
+{
+    Q_UNUSED(current);
+    Q_UNUSED(previous);
+}
+
+void KItemListGroupHeader::scrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous)
+{
+    Q_UNUSED(current);
+    Q_UNUSED(previous);
+}
+
+void KItemListGroupHeader::resizeEvent(QGraphicsSceneResizeEvent* event)
+{
+    QGraphicsWidget::resizeEvent(event);
+    if (event->oldSize().height() != event->newSize().height()) {
+        m_dirtyCache = true;
+    }
+}
+
+void KItemListGroupHeader::updateCache()
+{
+    Q_ASSERT(m_dirtyCache);
+
+    delete m_leftBorderCache;
+    delete m_rightBorderCache;
+
+    const int length = size().height() - 1;
+    m_leftBorderCache = new QPixmap(length, length);
+    m_leftBorderCache->fill(Qt::transparent);
+
+    m_rightBorderCache = new QPixmap(length, length);
+    m_rightBorderCache->fill(Qt::transparent);
+
+    // Calculate the outline color. No alphablending is used for
+    // performance reasons.
+    const QColor c1 = m_styleOption.palette.text().color();
+    const QColor c2 = m_styleOption.palette.background().color();
+    const int p1 = 35;
+    const int p2 = 100 - p1;
+    m_outlineColor = QColor((c1.red()   * p1 + c2.red()   * p2) / 100,
+                            (c1.green() * p1 + c2.green() * p2) / 100,
+                            (c1.blue()  * p1 + c2.blue()  * p2) / 100);
+
+    // The drawing code is based on the code of DolphinCategoryDrawer from Dolphin 1.7
+    // Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
+    {
+        // Cache the left border as pixmap
+        QPainter painter(m_leftBorderCache);
+        painter.setPen(m_outlineColor);
+
+        // 1. Draw top horizontal line
+        painter.drawLine(3, 0, length, 0);
+
+        // 2. Draw vertical line with gradient
+        const QPoint start(0, 3);
+        QLinearGradient gradient(start, QPoint(0, length));
+        gradient.setColorAt(0, m_outlineColor);
+        gradient.setColorAt(1, Qt::transparent);
+        painter.fillRect(QRect(start, QSize(1, length - start.y())), gradient);
+
+        // 3. Draw arc
+        painter.setRenderHint(QPainter::Antialiasing);
+        QRectF arc(QPointF(0, 0), QSizeF(4, 4));
+        arc.translate(0.5, 0.5);
+        painter.drawArc(arc, 1440, 1440);
+    }
+
+    {
+        // Cache the right border as pixmap
+        QPainter painter(m_rightBorderCache);
+        painter.setPen(m_outlineColor);
+
+        const int right = length - 1;
+        if (m_scrollOrientation == Qt::Vertical) {
+            // 1. Draw top horizontal line
+            painter.drawLine(0, 0, length - 3, 0);
+
+            // 2. Draw vertical line with gradient
+            const QPoint start(right, 3);
+            QLinearGradient gradient(start, QPoint(right, length));
+            gradient.setColorAt(0, m_outlineColor);
+            gradient.setColorAt(1, Qt::transparent);
+            painter.fillRect(QRect(start, QSize(1, length - start.y())), gradient);
+
+            // 3. Draw arc
+            painter.setRenderHint(QPainter::Antialiasing);
+            QRectF arc(QPointF(length - 5, 0), QSizeF(4, 4));
+            arc.translate(0.5, 0.5);
+            painter.drawArc(arc, 0, 1440);
+        } else {
+            // Draw a horizontal gradiented line
+            QLinearGradient gradient(QPoint(0, 0), QPoint(length, 0));
+            gradient.setColorAt(0, m_outlineColor);
+            gradient.setColorAt(1, Qt::transparent);
+            painter.fillRect(QRect(QPoint(0, 0), QSize(length, 1)), gradient);
+        }
+    }
+
+    m_dirtyCache = false;
+}
+
 #include "kitemlistgroupheader.moc"
index 081607eefe61a5001aaa68ad7ef239d884e2df56..20a58cc5b573f16693378a2998c85e8cf4e9b854 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <libdolphin_export.h>
 
+#include <kitemviews/kitemliststyleoption.h>
+
 #include <QByteArray>
 #include <QGraphicsWidget>
 #include <QVariant>
@@ -42,17 +44,60 @@ public:
     void setData(const QVariant& data);
     QVariant data() const;
 
-    virtual QSizeF sizeHint(Qt::SizeHint which = Qt::PreferredSize, const QSizeF& constraint = QSizeF()) const;
+    void setStyleOption(const KItemListStyleOption& option);
+    const KItemListStyleOption& styleOption() const;
+
+    /**
+     * Sets the scroll orientation that is used by the KItemListView.
+     * This allows the group header to use a modified look dependent
+     * on the orientation.
+     */
+    void setScrollOrientation(Qt::Orientation orientation);
+    Qt::Orientation scrollOrientation() const;
+
     virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
 
 protected:
+    /**
+     * Is called after the role has been changed and allows the derived class
+     * to react on this change.
+     */
     virtual void roleChanged(const QByteArray& current, const QByteArray& previous);
+
+    /**
+     * Is called after the role has been changed and allows the derived class
+     * to react on this change.
+     */
     virtual void dataChanged(const QVariant& current, const QVariant& previous);
 
+    /**
+     * Is called after the style option has been changed and allows the derived class
+     * to react on this change.
+     */
+    virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
+
+    /**
+     * Is called after the scroll orientation has been changed and allows the derived class
+     * to react on this change.
+     */
+    virtual void scrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
+
+    /** @reimp */
+    virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
+
+private:
+    void updateCache();
+
 private:
+    bool m_dirtyCache;
     QByteArray m_role;
     QVariant m_data;
+    KItemListStyleOption m_styleOption;
+    Qt::Orientation m_scrollOrientation;
 
+    QPixmap* m_leftBorderCache;
+    QPixmap* m_rightBorderCache;
+    QColor m_outlineColor;
 };
 #endif
 
index 05a2d30c10e7a2903d97b2aa0c6a026fdb6bbafd..2c88160fe3719b2a838ce8681441134d60b5d204 100644 (file)
@@ -1478,6 +1478,8 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget)
 
     header->setData(groups.at(mid).second);
     header->setRole(model()->sortRole());
+    header->setStyleOption(m_styleOption);
+    header->setScrollOrientation(scrollOrientation());
 
     header->show();
 }
index 6bd9a6e27a1686331ee945d6b884c30d3bf1d13e..9b807aaf5fc561fe826148531fb90bc562e92eb0 100644 (file)
@@ -355,6 +355,25 @@ void KItemListViewLayouter::doLayout()
                     m_itemRects.append(bounds);
                 }
 
+                if (grouped && horizontalScrolling) {
+                    // When grouping is enabled in the horizontal mode, the header alignment
+                    // looks like this:
+                    //   Header-1 Header-2 Header-3
+                    //   Item 1   Item 4   Item 7
+                    //   Item 2   Item 5   Item 8
+                    //   Item 3   Item 6   Item 9
+                    // In this case 'requiredItemHeight' represents the column-width. We don't
+                    // check the content of the header in the layouter to determine the required
+                    // width, hence assure that at least a minimal width of 15 characters is given
+                    // (in average a character requires the halve width of the font height).
+                    //
+                    // TODO: Let the group headers provide a minimum width and respect this width here
+                    const qreal minimumGroupHeaderWidth = m_groupHeaderHeight * 15 / 2;
+                    if (requiredItemHeight < minimumGroupHeaderWidth) {
+                        requiredItemHeight = minimumGroupHeaderWidth;
+                    }
+                }
+
                 maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
                 x += m_columnWidth;
                 ++index;