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

Plasma

  • sources
  • kde-4.12
  • kdelibs
  • plasma
delegate.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2007 Robert Knight <robertknight@gmail.com>
3  Copyright 2007 Kevin Ottens <ervin@kde.org>
4  Copyright 2008 Marco Martin <notmart@gmail.com>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 // Own
23 #include "delegate.h"
24 
25 #include <cmath>
26 #include <math.h>
27 
28 // Qt
29 #include <QApplication>
30 #include <QFontMetrics>
31 #include <QIcon>
32 #include <QModelIndex>
33 #include <QPainter>
34 #include <QStyleOptionViewItem>
35 
36 // KDE
37 #include <kcolorutils.h>
38 #include <kdebug.h>
39 #include <kglobal.h>
40 #include <kglobalsettings.h>
41 #include <kcolorscheme.h>
42 
43 // plasma
44 #include <plasma/paintutils.h>
45 #include <plasma/framesvg.h>
46 
47 namespace Plasma
48 {
49 
50 class DelegatePrivate
51 {
52  public:
53  DelegatePrivate() { }
54 
55  ~DelegatePrivate() { }
56 
57  QFont fontForSubTitle(const QFont &titleFont) const;
58  QRect titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
59  QRect subTitleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
60 
61  QMap<int, int> roles;
62 
63  static const int ICON_TEXT_MARGIN = 10;
64  static const int TEXT_RIGHT_MARGIN = 5;
65  static const int ACTION_ICON_SIZE = 22;
66 
67  static const int ITEM_LEFT_MARGIN = 5;
68  static const int ITEM_RIGHT_MARGIN = 5;
69  static const int ITEM_TOP_MARGIN = 5;
70  static const int ITEM_BOTTOM_MARGIN = 5;
71 
72  bool m_showToolTip;
73  FrameSvg *svg;
74 };
75 
76 QFont DelegatePrivate::fontForSubTitle(const QFont &titleFont) const
77 {
78  QFont subTitleFont = titleFont;
79  subTitleFont.setPointSize(qMax(subTitleFont.pointSize() - 2,
80  KGlobalSettings::smallestReadableFont().pointSize()));
81  return subTitleFont;
82 }
83 
84 QRect DelegatePrivate::titleRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
85 {
86  QFont font(option.font);
87  font.setBold(true);
88  QFontMetrics fm(font);
89 
90  Qt::Alignment textAlignment =
91  option.decorationAlignment & Qt::AlignRight ? Qt::AlignRight : Qt::AlignLeft;
92 
93  QRect emptyRect;
94  if (option.direction == Qt::LeftToRight) {
95  emptyRect = option.rect.adjusted(
96  option.decorationSize.width() + ICON_TEXT_MARGIN + ITEM_LEFT_MARGIN,
97  ITEM_TOP_MARGIN, -ITEM_RIGHT_MARGIN, -ITEM_BOTTOM_MARGIN);
98  } else {
99  emptyRect = option.rect.adjusted(
100  ITEM_LEFT_MARGIN, ITEM_TOP_MARGIN,
101  -ITEM_RIGHT_MARGIN - option.decorationSize.width() - ICON_TEXT_MARGIN, -ITEM_BOTTOM_MARGIN);
102  }
103 
104  if (emptyRect.width() < 0) {
105  emptyRect.setWidth(0);
106  return emptyRect;
107  }
108 
109  QRect textRect = QStyle::alignedRect(
110  option.direction,
111  textAlignment,
112  fm.boundingRect(index.data(Qt::DisplayRole).toString()).size(),
113  emptyRect);
114 
115  textRect.setWidth(textRect.width() + TEXT_RIGHT_MARGIN);
116  textRect.setHeight(emptyRect.height() / 2);
117  return textRect;
118 }
119 
120 QRect DelegatePrivate::subTitleRect(const QStyleOptionViewItem &option,
121  const QModelIndex &index) const
122 {
123  QString subTitle = index.data(roles[Delegate::SubTitleRole]).toString();
124 
125  QFontMetrics fm(fontForSubTitle(option.font));
126 
127  QRect textRect = titleRect(option, index);
128  int right = textRect.right();
129 
130  //if title=subtitle subtitle won't be displayed
131  if (subTitle != index.data(Qt::DisplayRole).toString()) {
132  textRect.setWidth(fm.width(" " + subTitle) + TEXT_RIGHT_MARGIN);
133  } else {
134  textRect.setWidth(0);
135  }
136  textRect.translate(0, textRect.height());
137 
138  if (option.direction == Qt::RightToLeft) {
139  textRect.moveRight(right);
140  }
141 
142  return textRect;
143 }
144 
145 Delegate::Delegate(QObject *parent)
146  : QAbstractItemDelegate(parent),
147  d(new DelegatePrivate)
148 {
149  d->svg = new FrameSvg(this);
150  d->svg->setImagePath("widgets/viewitem");
151  d->svg->setElementPrefix("hover");
152 }
153 
154 Delegate::~Delegate()
155 {
156  delete d;
157 }
158 
159 void Delegate::setRoleMapping(SpecificRoles role, int actual)
160 {
161  d->roles[role] = actual;
162 }
163 
164 int Delegate::roleMapping(SpecificRoles role) const
165 {
166  return d->roles[role];
167 }
168 
169 QRect Delegate::rectAfterTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
170 {
171  QRect textRect = d->titleRect(option, index);
172 
173  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());
174 
175  if (option.direction == Qt::LeftToRight) {
176  emptyRect.moveLeft(textRect.right());
177  } else {
178  emptyRect.moveRight(textRect.left());
179  }
180 
181  if (emptyRect.width() < 0) {
182  emptyRect.setWidth(0);
183  }
184 
185  return emptyRect;
186 }
187 
188 QRect Delegate::rectAfterSubTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
189 {
190  QRect textRect = d->subTitleRect(option, index);
191 
192  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());
193 
194  if (option.direction == Qt::LeftToRight) {
195  emptyRect.moveLeft(textRect.right());
196  } else {
197  emptyRect.moveRight(textRect.left());
198  }
199 
200  if (emptyRect.width() < 0) {
201  emptyRect.setWidth(0);
202  }
203 
204  return emptyRect;
205 }
206 
207 QRect Delegate::emptyRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
208 {
209  QRect afterTitleRect = rectAfterTitle(option, index);
210  QRect afterSubTitleRect = rectAfterSubTitle(option, index);
211 
212  afterTitleRect.setHeight(afterTitleRect.height() * 2);
213  afterSubTitleRect.setTop(afterTitleRect.top());
214 
215  return afterTitleRect.intersected(afterSubTitleRect);
216 }
217 
218 void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
219  const QModelIndex &index) const
220 {
221  const bool hover = option.state & (QStyle::State_MouseOver | QStyle::State_Selected);
222 
223  QRect contentRect = option.rect;
224  contentRect.setBottom(contentRect.bottom() - 1);
225 
226  QRect decorationRect =
227  QStyle::alignedRect(option.direction,
228  option.decorationPosition == QStyleOptionViewItem::Left ?
229  Qt::AlignLeft : Qt::AlignRight,
230  option.decorationSize,
231  contentRect.adjusted(DelegatePrivate::ITEM_LEFT_MARGIN, DelegatePrivate::ITEM_TOP_MARGIN, -DelegatePrivate::ITEM_RIGHT_MARGIN, -DelegatePrivate::ITEM_BOTTOM_MARGIN));
232  decorationRect.moveTop(contentRect.top() + qMax(0, (contentRect.height() - decorationRect.height())) / 2);
233 
234  QString titleText = index.data(Qt::DisplayRole).value<QString>();
235  QString subTitleText = index.data(d->roles[SubTitleRole]).value<QString>();
236  //kDebug() << subTitleText;
237 
238  QRect titleRect = d->titleRect(option, index);
239  titleRect.moveTopLeft(titleRect.topLeft()-option.rect.topLeft());
240  QRect subTitleRect = d->subTitleRect(option, index);
241  subTitleRect.moveTopLeft(subTitleRect.topLeft()-option.rect.topLeft());
242 
243  if (subTitleText == titleText) {
244  subTitleText.clear();
245  }
246 
247  QFont titleFont(option.font);
248 
249  // draw icon
250  QIcon decorationIcon = index.data(Qt::DecorationRole).value<QIcon>();
251 
252  if (index.data(d->roles[ColumnTypeRole]).toInt() == SecondaryActionColumn) {
253  if (hover) {
254  // Only draw on hover
255  const int delta = floor((qreal)(option.decorationSize.width() - DelegatePrivate::ACTION_ICON_SIZE) / 2.0);
256  decorationRect.adjust(delta, delta-1, -delta-1, -delta);
257  decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
258  }
259  } else {
260  // as default always draw as main column
261  decorationIcon.paint(painter, decorationRect, option.decorationAlignment);
262  }
263 
264  QPixmap buffer(option.rect.size());
265  buffer.fill(Qt::transparent);
266  QPainter p(&buffer);
267  // draw title
268  p.setFont(titleFont);
269  if (option.palette.color(QPalette::Base).alpha() > 0) {
270  p.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText), 1));
271  } else {
272  p.setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
273  }
274  p.drawText(titleRect, Qt::AlignLeft|Qt::AlignVCenter, titleText);
275 
276  // draw sub-title, BUT only if:
277  // * SubTitleMandatoryRole is defined and model returns 'true'
278  // * SubTitleMandatoryRole is not defined and the adjasent model indexes
279  // have the same contents of the Qt::DisplayRole
280  // * when model doesn't provide a valid data for SubTitleMandatory role
281  // we also show title on mouse hover
282  //
283  // the rationale for this is that subtitle text should in most cases not be
284  // required to understand the item itself and that showing all the subtexts in a
285  // listing makes the information density very high, impacting both the speed at
286  // which one can scan the list visually and the aesthetic qualities of the listing.
287  bool drawSubTitle = !subTitleText.isEmpty();
288 
289  if (drawSubTitle && !hover) {
290  // If the model wants to have exact control for subtitles showing
291  // it is expected to return a valid data for SubTitleMandatoryRole.
292  // If it doesn't return a valid data for this role
293  // then by default we well be showing a subtitles for
294  // adjasent items with the same content (see comments below too)
295  QVariant mandatoryRoleData = index.data(d->roles[SubTitleMandatoryRole]);
296  if (mandatoryRoleData.isValid()) {
297  drawSubTitle = mandatoryRoleData.value<bool>();
298  } else {
299  bool uniqueTitle = true;
300  QModelIndex sib = index.sibling(index.row() + 1, index.column());
301  if (sib.isValid()) {
302  uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
303  }
304 
305  if (uniqueTitle) {
306  sib = index.sibling(index.row() + -1, index.column());
307  if (sib.isValid()) {
308  uniqueTitle = sib.data(Qt::DisplayRole).value<QString>() != titleText;
309  }
310  }
311 
312  drawSubTitle = !uniqueTitle;
313  }
314  }
315 
316 
317  if (drawSubTitle) {
318  if (option.palette.color(QPalette::Base).alpha() > 0) {
319  p.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 1));
320  } else {
321  QColor textColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
322  textColor.setAlphaF(0.6);
323  p.setPen(textColor);
324  }
325 
326  const QFont subTitleFont = d->fontForSubTitle(option.font);
327  p.setFont(subTitleFont);
328  p.drawText(subTitleRect, Qt::AlignLeft|Qt::AlignVCenter, subTitleText);
329  }
330  p.end();
331 
332 
333  d->m_showToolTip = false;
334 
335  const QColor gradientColor = KColorScheme(QPalette::Active).background(KColorScheme::NormalBackground).color();
336 
337  if (option.direction == Qt::LeftToRight) {
338  if (((titleRect.width() + decorationRect.width() + 10) > option.rect.width() ||
339  (subTitleRect.width() + decorationRect.width() + 15) > option.rect.width()) &&
340  (titleRect.width() > 120 || subTitleRect.width() > 120)) {
341  QPainter p(&buffer);
342  p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
343  p.setPen(Qt::NoPen);
344  QLinearGradient gr;
345  QRect gradientRect(option.rect.width() - 60, titleRect.y(),
346  80, titleRect.height() + subTitleRect.height());
347  // draw it on the right side
348  gr.setStart(gradientRect.topLeft());
349  gr.setFinalStop(gradientRect.topRight());
350  gr.setColorAt(0.0, Qt::transparent);
351  gr.setColorAt(0.7, gradientColor);
352  p.setBrush(QBrush(gr));
353  p.drawRect(gradientRect);
354  d->m_showToolTip = true;
355  p.end();
356  }
357 
358  } else {
359  if (((titleRect.width() + decorationRect.width() + 10) > option.rect.width() ||
360  (subTitleRect.width() + decorationRect.width() + 15 )> option.rect.width()) &&
361  (titleRect.width() > 120 || subTitleRect.width() > 120)) {
362  buffer.fill(Qt::transparent);
363  QPainter p(&buffer);
364  p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
365  p.setPen(Qt::NoPen);
366  QLinearGradient gr;
367  QRect gradientRect(option.rect.x() - 55, titleRect.y(),
368  60, titleRect.height() + subTitleRect.height());
369  gr.setStart(gradientRect.topRight());
370  gr.setFinalStop(gradientRect.topLeft());
371  gr.setColorAt(0.0, Qt::transparent);
372  gr.setColorAt(0.6, gradientColor);
373  p.setBrush(QBrush(gr));
374  p.drawRect(gradientRect);
375 
376  d->m_showToolTip = true;
377  p.end();
378  }
379  }
380 
381  painter->drawPixmap(option.rect, buffer, buffer.rect());
382 
383  if (hover) {
384  painter->save();
385  painter->setRenderHint(QPainter::Antialiasing);
386 
387  const int column = index.column();
388  const int columns = index.model()->columnCount();
389  int roundedRadius = 5;
390  const bool useSvg = option.palette.color(QPalette::Base).alpha() == 0;
391 
392  // use a slightly translucent version of the palette's highlight color
393  // for the background
394  QColor backgroundColor = option.palette.color(QPalette::Highlight);
395  backgroundColor.setAlphaF(0.2);
396 
397  QColor backgroundColor2 = option.palette.color(QPalette::Highlight);
398  backgroundColor2.setAlphaF(0.5);
399 
400  QRect highlightRect = option.rect;
401  if (!useSvg) {
402  highlightRect.adjust(2, 2, -2, -2);
403  }
404 
405  QPen outlinePen(backgroundColor, 2);
406 
407  if (column == 0) {
408  //clip right (or left for rtl languages) to make the connection with the next column
409  if (columns > 1) {
410  if (useSvg) {
411  roundedRadius = d->svg->marginSize(Plasma::RightMargin);
412  }
413  painter->setClipRect(option.rect);
414  highlightRect.adjust(0, 0, roundedRadius, 0);
415  }
416 
417  QLinearGradient gradient(highlightRect.topLeft(), highlightRect.topRight());
418 
419  //reverse the gradient
420  if (option.direction == Qt::RightToLeft) {
421  gradient.setStart(highlightRect.topRight());
422  gradient.setFinalStop(highlightRect.topLeft());
423  }
424 
425  gradient.setColorAt(0, backgroundColor);
426  gradient.setColorAt(((qreal)titleRect.width()/3.0) / (qreal)highlightRect.width(), backgroundColor2);
427  gradient.setColorAt(0.7, backgroundColor);
428  outlinePen.setBrush(gradient);
429  //last column, clip left (right for rtl)
430  } else if (column == columns-1) {
431  if (useSvg) {
432  roundedRadius = d->svg->marginSize(Plasma::LeftMargin);
433  }
434  painter->setClipRect(option.rect);
435  highlightRect.adjust(-roundedRadius, 0, 0, 0);
436 
437  //column < columns-1; clip both ways
438  } else {
439  if (useSvg) {
440  roundedRadius = d->svg->marginSize(Plasma::LeftMargin);
441  }
442  painter->setClipRect(option.rect);
443  highlightRect.adjust(-roundedRadius, 0, +roundedRadius, 0);
444  }
445 
446  //if the view is transparent paint as plasma, otherwise paint with kde colors
447  if (useSvg) {
448  d->svg->resizeFrame(highlightRect.size());
449  d->svg->paintFrame(painter, highlightRect.topLeft());
450  } else {
451  painter->setPen(outlinePen);
452  painter->drawPath(PaintUtils::roundedRectangle(highlightRect, roundedRadius));
453  }
454 
455  painter->restore();
456  }
457 
458 
459 }
460 
461 QSize Delegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
462 {
463  Q_UNUSED(index)
464  QSize size = option.rect.size();
465 
466  QFontMetrics metrics(option.font);
467 
468  QFontMetrics subMetrics(d->fontForSubTitle(option.font));
469  size.setHeight(qMax(option.decorationSize.height(), qMax(size.height(), metrics.height() + subMetrics.ascent()) + 3) + 4);
470 // kDebug() << "size hint is" << size << (metrics.height() + subMetrics.ascent());
471 
472  const bool useSvg = option.palette.color(QPalette::Base).alpha() == 0;
473 
474  if (useSvg) {
475  qreal left, top, right, bottom;
476  d->svg->getMargins(left, top, right, bottom);
477  size += QSize(left+right, top+bottom);
478  } else {
479  size *= 1.1;
480  }
481 
482  return size;
483 }
484 
485 bool Delegate::showToolTip() const
486 {
487  return d->m_showToolTip;
488 }
489 
490 }
491 
492 #include "delegate.moc"
493 
QAbstractItemDelegate
Plasma::Delegate::SecondaryActionColumn
Definition: delegate.h:65
delegate.h
Plasma::Delegate::rectAfterTitle
QRect rectAfterTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
Returns the empty area after the title.
Definition: delegate.cpp:169
Plasma::Delegate::roleMapping
int roleMapping(SpecificRoles role) const
Definition: delegate.cpp:164
Plasma::Delegate::showToolTip
bool showToolTip() const
Definition: delegate.cpp:485
Plasma::Delegate::setRoleMapping
void setRoleMapping(SpecificRoles role, int actual)
Maps an arbitrary role to a role belonging to SpecificRoles.
Definition: delegate.cpp:159
Plasma::Theme::TextColor
the text color to be used by items resting on the background
Definition: theme.h:63
Plasma::Delegate::~Delegate
~Delegate()
Definition: delegate.cpp:154
Plasma::Delegate::Delegate
Delegate(QObject *parent=0)
Definition: delegate.cpp:145
Plasma::FrameSvg
Provides an SVG with borders.
Definition: framesvg.h:76
QObject
Plasma::Delegate::sizeHint
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
Definition: delegate.cpp:461
Plasma::PaintUtils::roundedRectangle
QPainterPath roundedRectangle(const QRectF &rect, qreal radius)
Returns a nicely rounded rectanglular path for painting.
Definition: paintutils.cpp:159
Plasma::Delegate::paint
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
Definition: delegate.cpp:218
paintutils.h
Plasma::Delegate::rectAfterSubTitle
QRect rectAfterSubTitle(const QStyleOptionViewItem &option, const QModelIndex &index) const
Returns the empty area after the subtitle.
Definition: delegate.cpp:188
Plasma::Delegate::ColumnTypeRole
Definition: delegate.h:60
Plasma::RightMargin
The right margin.
Definition: plasma.h:240
Plasma::Delegate::SubTitleRole
Definition: delegate.h:58
Plasma::Delegate::SubTitleMandatoryRole
Definition: delegate.h:59
Plasma::Theme::defaultTheme
static Theme * defaultTheme()
Singleton pattern accessor.
Definition: theme.cpp:544
framesvg.h
Plasma::Theme::color
Q_INVOKABLE QColor color(ColorRole role) const
Returns the text color to be used by items resting on the background.
Definition: theme.cpp:918
Plasma::Delegate::emptyRect
QRect emptyRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
Returns the empty area after both the title and the subtitle.
Definition: delegate.cpp:207
Plasma::Left
Display to the left.
Definition: plasma.h:92
Plasma::LeftMargin
The left margin.
Definition: plasma.h:239
Plasma::Delegate::SpecificRoles
SpecificRoles
Definition: delegate.h:57
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:33 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal