• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

libplasma

delegate.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2007 Robert Knight <robertknight@gmail.com>
00003     Copyright 2007 Kevin Ottens <ervin@kde.org>
00004     Copyright 2008 Marco Martin <notmart@gmail.com>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 // Own
00023 #include "delegate.h"
00024 
00025 #include <cmath>
00026 #include <math.h>
00027 
00028 // Qt
00029 #include <QApplication>
00030 #include <QFontMetrics>
00031 #include <QIcon>
00032 #include <QModelIndex>
00033 #include <QPainter>
00034 #include <QStyleOptionViewItem>
00035 
00036 // KDE
00037 #include <KColorUtils>
00038 #include <KDebug>
00039 #include <KGlobal>
00040 #include <KGlobalSettings>
00041 #include <KColorScheme>
00042 
00043 // plasma
00044 #include <plasma/paintutils.h>
00045 
00046 namespace Plasma
00047 {
00048 
00049 class DelegatePrivate
00050 {
00051     public:
00052         DelegatePrivate() { }
00053 
00054         ~DelegatePrivate() { }
00055 
00056         QFont fontForSubTitle(const QFont& titleFont) const;
00057         QRect titleRect(const QStyleOptionViewItem& option, const QModelIndex& index) const;
00058         QRect subTitleRect(const QStyleOptionViewItem& option, const QModelIndex& index) const;
00059 
00060         QMap<int, int> roles;
00061 
00062         static const int ICON_TEXT_MARGIN = 10;
00063         static const int TEXT_RIGHT_MARGIN = 5;
00064         static const int ACTION_ICON_SIZE = 22;
00065 
00066         static const int ITEM_LEFT_MARGIN = 5;
00067         static const int ITEM_RIGHT_MARGIN = 5;
00068         static const int ITEM_TOP_MARGIN = 5;
00069         static const int ITEM_BOTTOM_MARGIN = 5;
00070 };
00071 
00072 
00073 QFont DelegatePrivate::fontForSubTitle(const QFont& titleFont) const
00074 {
00075     QFont subTitleFont = titleFont;
00076     subTitleFont.setPointSize(qMax(subTitleFont.pointSize() - 2,
00077                               KGlobalSettings::smallestReadableFont().pointSize()));
00078     return subTitleFont;
00079 }
00080 
00081 QRect DelegatePrivate::titleRect(const QStyleOptionViewItem& option, const QModelIndex& index) const
00082 {
00083     QFont font(option.font);
00084     font.setBold(true);
00085     QFontMetrics fm(font);
00086 
00087     Qt::Alignment textAlignment = option.decorationAlignment & Qt::AlignRight ? Qt::AlignRight : Qt::AlignLeft;
00088 
00089     QRect emptyRect;
00090     if (option.direction == Qt::LeftToRight) {
00091         emptyRect = option.rect.adjusted(option.decorationSize.width()+ICON_TEXT_MARGIN+ITEM_LEFT_MARGIN, ITEM_TOP_MARGIN, -ITEM_RIGHT_MARGIN, -ITEM_BOTTOM_MARGIN);
00092     } else {
00093         emptyRect = option.rect.adjusted(ITEM_LEFT_MARGIN, ITEM_TOP_MARGIN, -ITEM_RIGHT_MARGIN-option.decorationSize.width()-ICON_TEXT_MARGIN, -ITEM_BOTTOM_MARGIN);
00094     }
00095 
00096     if (emptyRect.width() < 0) {
00097         emptyRect.setWidth(0);
00098         return emptyRect;
00099     }
00100 
00101     QRect textRect = QStyle::alignedRect(option.direction,
00102                                          textAlignment,
00103                                          fm.boundingRect(index.data(Qt::DisplayRole).toString()).size(),
00104                                          emptyRect);
00105 
00106     textRect.setWidth(textRect.width() + TEXT_RIGHT_MARGIN);
00107     textRect.setHeight(emptyRect.height()/2);
00108     return textRect;
00109 }
00110 
00111 QRect DelegatePrivate::subTitleRect(const QStyleOptionViewItem& option, const QModelIndex& index) const
00112 {
00113     QString subTitle = index.data(roles[Delegate::SubTitleRole]).toString();
00114 
00115     QFontMetrics fm(fontForSubTitle(option.font));
00116 
00117     QRect textRect = titleRect(option, index);
00118     int right = textRect.right();
00119 
00120     //if title=subtitle subtitle won't be displayed
00121     if (subTitle != index.data(Qt::DisplayRole).toString()) {
00122         textRect.setWidth(fm.width("  " + subTitle) + TEXT_RIGHT_MARGIN);
00123     } else {
00124         textRect.setWidth(0);
00125     }
00126     textRect.translate(0, textRect.height());
00127 
00128     if (option.direction == Qt::RightToLeft) {
00129         textRect.moveRight(right);
00130     }
00131 
00132     return textRect;
00133 }
00134 
00135 Delegate::Delegate(QObject *parent)
00136     : QAbstractItemDelegate(parent),
00137       d(new DelegatePrivate)
00138 {
00139 }
00140 
00141 Delegate::~Delegate()
00142 {
00143     delete d;
00144 }
00145 
00146 void Delegate::setRoleMapping(SpecificRoles role, int actual)
00147 {
00148     d->roles[role] = actual;
00149 }
00150 
00151 int Delegate::roleMapping(SpecificRoles role) const
00152 {
00153     return d->roles[role];
00154 }
00155 
00156 QRect Delegate::rectAfterTitle(const QStyleOptionViewItem& option, const QModelIndex& index) const
00157 {
00158     QRect textRect = d->titleRect(option, index);
00159 
00160     QRect emptyRect(0, textRect.top(), option.rect.width() - textRect.width() - DelegatePrivate::ITEM_LEFT_MARGIN - DelegatePrivate::ITEM_RIGHT_MARGIN - option.decorationSize.width() - DelegatePrivate::ICON_TEXT_MARGIN, textRect.height());
00161 
00162     if (option.direction == Qt::LeftToRight) {
00163         emptyRect.moveLeft(textRect.right());
00164     } else {
00165         emptyRect.moveRight(textRect.left());
00166     }
00167 
00168     if (emptyRect.width() < 0) {
00169         emptyRect.setWidth(0);
00170     }
00171 
00172     return emptyRect;
00173 }
00174 
00175 QRect Delegate::rectAfterSubTitle(const QStyleOptionViewItem& option, const QModelIndex& index) const
00176 {
00177     QRect textRect = d->subTitleRect(option, index);
00178 
00179     QRect emptyRect(0, textRect.top(), option.rect.width() - textRect.width() - DelegatePrivate::ITEM_LEFT_MARGIN - DelegatePrivate::ITEM_RIGHT_MARGIN - option.decorationSize.width() - DelegatePrivate::ICON_TEXT_MARGIN, textRect.height());
00180 
00181     if (option.direction == Qt::LeftToRight) {
00182         emptyRect.moveLeft(textRect.right());
00183     } else {
00184         emptyRect.moveRight(textRect.left());
00185     }
00186 
00187     if (emptyRect.width() < 0) {
00188         emptyRect.setWidth(0);
00189     }
00190 
00191     return emptyRect;
00192 }
00193 
00194 QRect Delegate::emptyRect(const QStyleOptionViewItem& option, const QModelIndex& index) const
00195 {
00196     QRect afterTitleRect = rectAfterTitle(option, index);
00197     QRect afterSubTitleRect = rectAfterSubTitle(option, index);
00198 
00199     afterTitleRect.setHeight(afterTitleRect.height()*2);
00200     afterSubTitleRect.setTop(afterTitleRect.top());
00201 
00202     return afterTitleRect.intersected(afterSubTitleRect);
00203 }
00204 
00205 
00206 void Delegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
00207 {
00208     const bool hover = option.state & (QStyle::State_MouseOver | QStyle::State_Selected);
00209 
00210     QRect contentRect = option.rect;
00211     contentRect.setBottom(contentRect.bottom() - 1);
00212 
00213     QRect decorationRect = QStyle::alignedRect(option.direction,
00214                                                option.decorationPosition == QStyleOptionViewItem::Left ?
00215                                                                         Qt::AlignLeft : Qt::AlignRight,
00216                                                option.decorationSize,
00217                                                contentRect.adjusted(DelegatePrivate::ITEM_LEFT_MARGIN, DelegatePrivate::ITEM_TOP_MARGIN,-DelegatePrivate::ITEM_RIGHT_MARGIN,-DelegatePrivate::ITEM_BOTTOM_MARGIN));
00218     decorationRect.moveTop(contentRect.top() + qMax(0, (contentRect.height() - decorationRect.height())) / 2);
00219 
00220     QString titleText = index.data(Qt::DisplayRole).value<QString>();
00221     QString subTitleText = index.data(d->roles[SubTitleRole]).value<QString>();
00222 
00223     QRect titleRect = d->titleRect(option, index);
00224     QRect subTitleRect = d->subTitleRect(option, index);
00225 
00226 
00227     bool uniqueTitle = !index.data(d->roles[SubTitleMandatoryRole]).value<bool>();// true;
00228     if (uniqueTitle) {
00229         QModelIndex sib = index.sibling(index.row() + 1, index.column());
00230         if (sib.isValid()) {
00231             uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
00232         }
00233 
00234         if (uniqueTitle) {
00235             sib = index.sibling(index.row() + -1, index.column());
00236             if (sib.isValid()) {
00237                 uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
00238             }
00239         }
00240     }
00241 
00242     if (subTitleText == titleText) {
00243         subTitleText = QString();
00244     }
00245 
00246     QFont subTitleFont = d->fontForSubTitle(option.font);
00247 
00248     QFont titleFont(option.font);
00249 
00250     if (hover) {
00251         painter->save();
00252         painter->setRenderHint(QPainter::Antialiasing);
00253 
00254         const int column = index.column();
00255         const int columns = index.model()->columnCount();
00256         const int roundedRadius = 5;
00257 
00258         // use a slightly translucent version of the palette's highlight color
00259         // for the background
00260         QColor backgroundColor = option.palette.color(QPalette::Highlight);
00261         backgroundColor.setAlphaF(0.2);
00262 
00263         QColor backgroundColor2 = option.palette.color(QPalette::Highlight);
00264         backgroundColor.setAlphaF(0.5);
00265 
00266         QRect highlightRect = option.rect.adjusted(2, 2, -2, -2);
00267 
00268         QPen outlinePen(backgroundColor, 2);
00269 
00270         if (column == 0) {
00271             //clip right (or left for rtl languages) to make the connection with the next column
00272             if (columns > 1) {
00273                 if (option.direction == Qt::LeftToRight) {
00274                     painter->setClipRect(option.rect);
00275                     highlightRect.adjust(0, 0, roundedRadius, 0);
00276                 } else {
00277                     painter->setClipRect(option.rect);
00278                     highlightRect.adjust(-roundedRadius, 0, 0, 0);
00279                 }
00280             }
00281 
00282             QLinearGradient gradient(highlightRect.topLeft(), highlightRect.topRight());
00283 
00284             //reverse the gradient
00285             if (option.direction == Qt::RightToLeft) {
00286                 gradient.setStart(highlightRect.topRight());
00287                 gradient.setFinalStop(highlightRect.topLeft());
00288             }
00289 
00290             gradient.setColorAt(0, backgroundColor);
00291 
00292             gradient.setColorAt(((qreal)titleRect.width()/3.0) / (qreal)highlightRect.width(), backgroundColor2);
00293 
00294             gradient.setColorAt(0.7, backgroundColor);
00295 
00296             outlinePen.setBrush(gradient);
00297 
00298         //last column, clip left (right for rtl)
00299         } else if (column == columns-1) {
00300             if (option.direction == Qt::LeftToRight) {
00301                 painter->setClipRect(option.rect);
00302                 highlightRect.adjust(-roundedRadius, 0, 0, 0);
00303             } else {
00304                 painter->setClipRect(option.rect);
00305                 highlightRect.adjust(0, 0, +roundedRadius, 0);
00306             }
00307 
00308         //column < columns-1; clip both ways
00309         } else {
00310             painter->setClipRect(option.rect);
00311             highlightRect.adjust(-roundedRadius, 0, +roundedRadius, 0);
00312         }
00313 
00314         painter->setPen(outlinePen);
00315         painter->drawPath(PaintUtils::roundedRectangle(highlightRect, roundedRadius));
00316 
00317         painter->restore();
00318     }
00319 
00320     // draw icon
00321     QIcon decorationIcon = index.data(Qt::DecorationRole).value<QIcon>();
00322 
00323     if (index.data(d->roles[ColumnTypeRole]).toInt() == SecondaryActionColumn) {
00324         if (hover) {
00325             // Only draw on hover
00326             const int delta = floor((qreal)(option.decorationSize.width() - DelegatePrivate::ACTION_ICON_SIZE)/2.0);
00327             decorationRect.adjust(delta, delta-1, -delta-1, -delta);
00328             decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
00329         }
00330     } else {
00331         // as default always draw as main column
00332         decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
00333     }
00334 
00335     painter->save();
00336 
00337     // draw title
00338     painter->setFont(titleFont);
00339     painter->drawText(titleRect, Qt::AlignLeft|Qt::AlignVCenter, titleText);
00340 
00341     if (hover || !uniqueTitle) {
00342         // draw sub-title, BUT only if:
00343         //   * it isn't a unique title, this allows two items to have the same title and be
00344         //          disambiguated by their subtitle
00345         //   * we are directed by the model that this item should never be treated as unique
00346         //          e.g. the documents list in the recently used tab
00347         //   * the mouse is hovered over the item, causing the additional information to be
00348         //          displayed
00349         //
00350         // the rational for this is that subtitle text should in most cases not be
00351         // required to understand the item itself and that showing all the subtexts in a
00352         // listing makes the information density very high, impacting both the speed at
00353         // which one can scan the list visually and the aesthetic qualities of the listing.
00354         painter->setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 1));
00355         painter->setFont(subTitleFont);
00356         painter->drawText(subTitleRect, Qt::AlignLeft|Qt::AlignVCenter, "  " + subTitleText);
00357     }
00358 
00359     painter->restore();
00360 
00361 }
00362 
00363 QSize Delegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
00364 {
00365     Q_UNUSED(index)
00366     QSize size = option.rect.size();
00367 
00368     QFontMetrics metrics(option.font);
00369 
00370     QFontMetrics subMetrics(d->fontForSubTitle(option.font));
00371     size.setHeight(qMax(option.decorationSize.height(), qMax(size.height(), metrics.height() + subMetrics.ascent()) + 3) + 4);
00372 //    kDebug() << "size hint is" << size << (metrics.height() + subMetrics.ascent());
00373 
00374     size*=1.1;
00375 
00376     return size;
00377 }
00378 
00379 }
00380 
00381 

libplasma

Skip menu "libplasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal