• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

Plasma

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.h>
00038 #include <kdebug.h>
00039 #include <kglobal.h>
00040 #include <kglobalsettings.h>
00041 #include <kcolorscheme.h>
00042 
00043 // plasma
00044 #include <plasma/paintutils.h>
00045 #include <plasma/framesvg.h>
00046 
00047 namespace Plasma
00048 {
00049 
00050 class DelegatePrivate
00051 {
00052     public:
00053         DelegatePrivate() { }
00054 
00055         ~DelegatePrivate() { }
00056 
00057         QFont fontForSubTitle(const QFont &titleFont) const;
00058         QRect titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
00059         QRect subTitleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
00060 
00061         QMap<int, int> roles;
00062 
00063         static const int ICON_TEXT_MARGIN = 10;
00064         static const int TEXT_RIGHT_MARGIN = 5;
00065         static const int ACTION_ICON_SIZE = 22;
00066 
00067         static const int ITEM_LEFT_MARGIN = 5;
00068         static const int ITEM_RIGHT_MARGIN = 5;
00069         static const int ITEM_TOP_MARGIN = 5;
00070         static const int ITEM_BOTTOM_MARGIN = 5;
00071 
00072         bool m_showToolTip;
00073         FrameSvg *svg;
00074 };
00075 
00076 QFont DelegatePrivate::fontForSubTitle(const QFont &titleFont) const
00077 {
00078     QFont subTitleFont = titleFont;
00079     subTitleFont.setPointSize(qMax(subTitleFont.pointSize() - 2,
00080                               KGlobalSettings::smallestReadableFont().pointSize()));
00081     return subTitleFont;
00082 }
00083 
00084 QRect DelegatePrivate::titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
00085 {
00086     QFont font(option.font);
00087     font.setBold(true);
00088     QFontMetrics fm(font);
00089 
00090     Qt::Alignment textAlignment =
00091         option.decorationAlignment & Qt::AlignRight ? Qt::AlignRight : Qt::AlignLeft;
00092 
00093     QRect emptyRect;
00094     if (option.direction == Qt::LeftToRight) {
00095         emptyRect = option.rect.adjusted(
00096             option.decorationSize.width() + ICON_TEXT_MARGIN + ITEM_LEFT_MARGIN,
00097             ITEM_TOP_MARGIN, -ITEM_RIGHT_MARGIN, -ITEM_BOTTOM_MARGIN);
00098     } else {
00099         emptyRect = option.rect.adjusted(
00100             ITEM_LEFT_MARGIN, ITEM_TOP_MARGIN,
00101             -ITEM_RIGHT_MARGIN - option.decorationSize.width() - ICON_TEXT_MARGIN, -ITEM_BOTTOM_MARGIN);
00102     }
00103 
00104     if (emptyRect.width() < 0) {
00105         emptyRect.setWidth(0);
00106         return emptyRect;
00107     }
00108 
00109     QRect textRect = QStyle::alignedRect(
00110         option.direction,
00111         textAlignment,
00112         fm.boundingRect(index.data(Qt::DisplayRole).toString()).size(),
00113         emptyRect);
00114 
00115     textRect.setWidth(textRect.width() + TEXT_RIGHT_MARGIN);
00116     textRect.setHeight(emptyRect.height() / 2);
00117     return textRect;
00118 }
00119 
00120 QRect DelegatePrivate::subTitleRect(const QStyleOptionViewItem &option,
00121                                     const QModelIndex &index) const
00122 {
00123     QString subTitle = index.data(roles[Delegate::SubTitleRole]).toString();
00124 
00125     QFontMetrics fm(fontForSubTitle(option.font));
00126 
00127     QRect textRect = titleRect(option, index);
00128     int right = textRect.right();
00129 
00130     //if title=subtitle subtitle won't be displayed
00131     if (subTitle != index.data(Qt::DisplayRole).toString()) {
00132         textRect.setWidth(fm.width("  " + subTitle) + TEXT_RIGHT_MARGIN);
00133     } else {
00134         textRect.setWidth(0);
00135     }
00136     textRect.translate(0, textRect.height());
00137 
00138     if (option.direction == Qt::RightToLeft) {
00139         textRect.moveRight(right);
00140     }
00141 
00142     return textRect;
00143 }
00144 
00145 Delegate::Delegate(QObject *parent)
00146     : QAbstractItemDelegate(parent),
00147       d(new DelegatePrivate)
00148 {
00149     d->svg = new FrameSvg(this);
00150     d->svg->setImagePath("widgets/viewitem");
00151     d->svg->setElementPrefix("hover");
00152 }
00153 
00154 Delegate::~Delegate()
00155 {
00156     delete d;
00157 }
00158 
00159 void Delegate::setRoleMapping(SpecificRoles role, int actual)
00160 {
00161     d->roles[role] = actual;
00162 }
00163 
00164 int Delegate::roleMapping(SpecificRoles role) const
00165 {
00166     return d->roles[role];
00167 }
00168 
00169 QRect Delegate::rectAfterTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
00170 {
00171     QRect textRect = d->titleRect(option, index);
00172 
00173     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());
00174 
00175     if (option.direction == Qt::LeftToRight) {
00176         emptyRect.moveLeft(textRect.right());
00177     } else {
00178         emptyRect.moveRight(textRect.left());
00179     }
00180 
00181     if (emptyRect.width() < 0) {
00182         emptyRect.setWidth(0);
00183     }
00184 
00185     return emptyRect;
00186 }
00187 
00188 QRect Delegate::rectAfterSubTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
00189 {
00190     QRect textRect = d->subTitleRect(option, index);
00191 
00192     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());
00193 
00194     if (option.direction == Qt::LeftToRight) {
00195         emptyRect.moveLeft(textRect.right());
00196     } else {
00197         emptyRect.moveRight(textRect.left());
00198     }
00199 
00200     if (emptyRect.width() < 0) {
00201         emptyRect.setWidth(0);
00202     }
00203 
00204     return emptyRect;
00205 }
00206 
00207 QRect Delegate::emptyRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
00208 {
00209     QRect afterTitleRect = rectAfterTitle(option, index);
00210     QRect afterSubTitleRect = rectAfterSubTitle(option, index);
00211 
00212     afterTitleRect.setHeight(afterTitleRect.height() * 2);
00213     afterSubTitleRect.setTop(afterTitleRect.top());
00214 
00215     return afterTitleRect.intersected(afterSubTitleRect);
00216 }
00217 
00218 void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
00219                      const QModelIndex &index) const
00220 {
00221     const bool hover = option.state & (QStyle::State_MouseOver | QStyle::State_Selected);
00222 
00223     QRect contentRect = option.rect;
00224     contentRect.setBottom(contentRect.bottom() - 1);
00225 
00226     QRect decorationRect =
00227         QStyle::alignedRect(option.direction,
00228                             option.decorationPosition == QStyleOptionViewItem::Left ?
00229                             Qt::AlignLeft : Qt::AlignRight,
00230                             option.decorationSize,
00231                             contentRect.adjusted(DelegatePrivate::ITEM_LEFT_MARGIN, DelegatePrivate::ITEM_TOP_MARGIN, -DelegatePrivate::ITEM_RIGHT_MARGIN, -DelegatePrivate::ITEM_BOTTOM_MARGIN));
00232     decorationRect.moveTop(contentRect.top() + qMax(0, (contentRect.height() - decorationRect.height())) / 2);
00233 
00234     QString titleText = index.data(Qt::DisplayRole).value<QString>();
00235     QString subTitleText = index.data(d->roles[SubTitleRole]).value<QString>();
00236     //kDebug() << subTitleText;
00237 
00238     QRect titleRect = d->titleRect(option, index);
00239     titleRect.moveTopLeft(titleRect.topLeft()-option.rect.topLeft());
00240     QRect subTitleRect = d->subTitleRect(option, index);
00241     subTitleRect.moveTopLeft(subTitleRect.topLeft()-option.rect.topLeft());
00242 
00243     if (subTitleText == titleText) {
00244         subTitleText.clear();
00245     }
00246 
00247     QFont titleFont(option.font);
00248 
00249     // draw icon
00250     QIcon decorationIcon = index.data(Qt::DecorationRole).value<QIcon>();
00251 
00252     if (index.data(d->roles[ColumnTypeRole]).toInt() == SecondaryActionColumn) {
00253         if (hover) {
00254             // Only draw on hover
00255             const int delta = floor((qreal)(option.decorationSize.width() - DelegatePrivate::ACTION_ICON_SIZE) / 2.0);
00256             decorationRect.adjust(delta, delta-1, -delta-1, -delta);
00257             decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
00258         }
00259     } else {
00260         // as default always draw as main column
00261         decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
00262     }
00263 
00264     QPixmap buffer(option.rect.size());
00265     buffer.fill(Qt::transparent);
00266     QPainter p(&buffer);
00267     // draw title
00268     p.setFont(titleFont);
00269     if (option.palette.color(QPalette::Base).alpha() > 0) {
00270         p.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText), 1));
00271     } else {
00272         p.setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
00273     }
00274     p.drawText(titleRect, Qt::AlignLeft|Qt::AlignVCenter, titleText);
00275 
00276     // draw sub-title, BUT only if:
00277     //   * SubTitleMandatoryRole is defined and model returns 'true'
00278     //   * SubTitleMandatoryRole is not defined and the adjasent model indexes
00279     //     have the same contents of the Qt::DisplayRole
00280     //   * when model doesn't provide a valid data for SubTitleMandatory role
00281     //     we also show title on mouse hover
00282     //
00283     // the rationale for this is that subtitle text should in most cases not be
00284     // required to understand the item itself and that showing all the subtexts in a
00285     // listing makes the information density very high, impacting both the speed at
00286     // which one can scan the list visually and the aesthetic qualities of the listing.
00287     bool drawSubTitle = !subTitleText.isEmpty();
00288 
00289     if (drawSubTitle && !hover) {
00290         // If the model wants to have exact control for subtitles showing
00291         // it is expected to return a valid data for SubTitleMandatoryRole.
00292         // If it doesn't return a valid data for this role
00293         // then by default we well be showing a subtitles for
00294         // adjasent items with the same content (see comments below too)
00295         QVariant mandatoryRoleData = index.data(d->roles[SubTitleMandatoryRole]);
00296         if (mandatoryRoleData.isValid()) {
00297             drawSubTitle = mandatoryRoleData.value<bool>();
00298         } else {
00299             bool uniqueTitle = true;
00300             QModelIndex sib = index.sibling(index.row() + 1, index.column());
00301             if (sib.isValid()) {
00302                 uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
00303             }
00304 
00305             if (uniqueTitle) {
00306                 sib = index.sibling(index.row() + -1, index.column());
00307                 if (sib.isValid()) {
00308                     uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
00309                 }
00310             }
00311 
00312             drawSubTitle = !uniqueTitle;
00313         }
00314     }
00315 
00316 
00317     if (drawSubTitle) {
00318         if (option.palette.color(QPalette::Base).alpha() > 0) {
00319             p.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 1));
00320         } else {
00321             QColor textColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
00322             textColor.setAlphaF(0.6);
00323             p.setPen(textColor);
00324         }
00325 
00326         const QFont subTitleFont = d->fontForSubTitle(option.font);
00327         p.setFont(subTitleFont);
00328         p.drawText(subTitleRect, Qt::AlignLeft|Qt::AlignVCenter, subTitleText);
00329     }
00330     p.end();
00331 
00332 
00333     d->m_showToolTip = false;
00334 
00335     const QColor gradientColor = KColorScheme(QPalette::Active).background(KColorScheme::NormalBackground).color();
00336 
00337     if (option.direction == Qt::LeftToRight) {
00338         if (((titleRect.width() + decorationRect.width() + 10) > option.rect.width() ||
00339             (subTitleRect.width() + decorationRect.width() + 15) > option.rect.width()) &&
00340         (titleRect.width() > 120 || subTitleRect.width() > 120)) {
00341             QPainter p(&buffer);
00342             p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00343             p.setPen(Qt::NoPen);
00344             QLinearGradient gr;
00345             QRect gradientRect(option.rect.width() - 60, titleRect.y(),
00346                   80, titleRect.height() + subTitleRect.height());
00347             // draw it on the right side
00348             gr.setStart(gradientRect.topLeft());
00349             gr.setFinalStop(gradientRect.topRight());
00350             gr.setColorAt(0.0, Qt::transparent);
00351             gr.setColorAt(0.7, gradientColor);
00352             p.setBrush(QBrush(gr));
00353             p.drawRect(gradientRect);
00354             d->m_showToolTip = true;
00355             p.end();
00356         }
00357 
00358     } else {
00359         if (((titleRect.width() + decorationRect.width() + 10) > option.rect.width() ||
00360             (subTitleRect.width() + decorationRect.width() + 15 )> option.rect.width()) &&
00361         (titleRect.width() > 120 || subTitleRect.width() > 120)) {
00362             buffer.fill(Qt::transparent);
00363             QPainter p(&buffer);
00364             p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00365             p.setPen(Qt::NoPen);
00366             QLinearGradient gr;
00367             QRect gradientRect(option.rect.x() - 55, titleRect.y(),
00368                                 60, titleRect.height() + subTitleRect.height());
00369             gr.setStart(gradientRect.topRight());
00370             gr.setFinalStop(gradientRect.topLeft());
00371             gr.setColorAt(0.0, Qt::transparent);
00372             gr.setColorAt(0.6, gradientColor);
00373             p.setBrush(QBrush(gr));
00374             p.drawRect(gradientRect);
00375 
00376             d->m_showToolTip = true;
00377             p.end();
00378         }
00379     }
00380 
00381     painter->drawPixmap(option.rect, buffer, buffer.rect());
00382 
00383     if (hover) {
00384         painter->save();
00385         painter->setRenderHint(QPainter::Antialiasing);
00386 
00387         const int column = index.column();
00388         const int columns = index.model()->columnCount();
00389         int roundedRadius = 5;
00390         const bool useSvg = option.palette.color(QPalette::Base).alpha() == 0;
00391 
00392         // use a slightly translucent version of the palette's highlight color
00393         // for the background
00394         QColor backgroundColor = option.palette.color(QPalette::Highlight);
00395         backgroundColor.setAlphaF(0.2);
00396 
00397         QColor backgroundColor2 = option.palette.color(QPalette::Highlight);
00398         backgroundColor2.setAlphaF(0.5);
00399 
00400         QRect highlightRect = option.rect;
00401         if (!useSvg) {
00402             highlightRect.adjust(2, 2, -2, -2);
00403         }
00404 
00405         QPen outlinePen(backgroundColor, 2);
00406 
00407         if (column == 0) {
00408             //clip right (or left for rtl languages) to make the connection with the next column
00409             if (columns > 1) {
00410                 if (useSvg) {
00411                     roundedRadius = d->svg->marginSize(Plasma::RightMargin);
00412                 }
00413                 painter->setClipRect(option.rect);
00414                 highlightRect.adjust(0, 0, roundedRadius, 0);
00415             }
00416 
00417             QLinearGradient gradient(highlightRect.topLeft(), highlightRect.topRight());
00418 
00419             //reverse the gradient
00420             if (option.direction == Qt::RightToLeft) {
00421                 gradient.setStart(highlightRect.topRight());
00422                 gradient.setFinalStop(highlightRect.topLeft());
00423             }
00424 
00425             gradient.setColorAt(0, backgroundColor);
00426             gradient.setColorAt(((qreal)titleRect.width()/3.0) / (qreal)highlightRect.width(), backgroundColor2);
00427             gradient.setColorAt(0.7, backgroundColor);
00428             outlinePen.setBrush(gradient);
00429         //last column, clip left (right for rtl)
00430         } else if (column == columns-1) {
00431             if (useSvg) {
00432                 roundedRadius = d->svg->marginSize(Plasma::LeftMargin);
00433             }
00434             painter->setClipRect(option.rect);
00435             highlightRect.adjust(-roundedRadius, 0, 0, 0);
00436 
00437         //column < columns-1; clip both ways
00438         } else {
00439             if (useSvg) {
00440                 roundedRadius = d->svg->marginSize(Plasma::LeftMargin);
00441             }
00442             painter->setClipRect(option.rect);
00443             highlightRect.adjust(-roundedRadius, 0, +roundedRadius, 0);
00444         }
00445 
00446         //if the view is transparent paint as plasma, otherwise paint with kde colors
00447         if (useSvg) {
00448             d->svg->resizeFrame(highlightRect.size());
00449             d->svg->paintFrame(painter, highlightRect.topLeft());
00450         } else {
00451             painter->setPen(outlinePen);
00452             painter->drawPath(PaintUtils::roundedRectangle(highlightRect, roundedRadius));
00453         }
00454 
00455         painter->restore();
00456     }
00457 
00458 
00459 }
00460 
00461 QSize Delegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
00462 {
00463     Q_UNUSED(index)
00464     QSize size = option.rect.size();
00465 
00466     QFontMetrics metrics(option.font);
00467 
00468     QFontMetrics subMetrics(d->fontForSubTitle(option.font));
00469     size.setHeight(qMax(option.decorationSize.height(), qMax(size.height(), metrics.height() + subMetrics.ascent()) + 3) + 4);
00470 //    kDebug() << "size hint is" << size << (metrics.height() + subMetrics.ascent());
00471 
00472     const bool useSvg = option.palette.color(QPalette::Base).alpha() == 0;
00473 
00474     if (useSvg) {
00475         qreal left, top, right, bottom;
00476         d->svg->getMargins(left, top, right, bottom);
00477         size += QSize(left+right, top+bottom);
00478     } else {
00479         size *= 1.1;
00480     }
00481 
00482     return size;
00483 }
00484 
00485 bool Delegate::showToolTip() const
00486 {
00487     return d->m_showToolTip;
00488 }
00489 
00490 }
00491 
00492 #include "delegate.moc"
00493 

Plasma

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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