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
KDE 4.6 API Reference