Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qitemdelegate.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <qabstractitemmodel.h>
8#include <qapplication.h>
9#include <qbrush.h>
10#include <qpainter.h>
11#include <qpainterstateguard.h>
12#include <qpalette.h>
13#include <qpoint.h>
14#include <qrect.h>
15#include <qsize.h>
16#include <qstyle.h>
17#include <qdatetime.h>
18#include <qstyleoption.h>
19#include <qevent.h>
20#include <qpixmap.h>
21#include <qbitmap.h>
22#include <qpixmapcache.h>
23#include <qitemeditorfactory.h>
24#include <qmetaobject.h>
25#include <qtextlayout.h>
26#include <private/qabstractitemdelegate_p.h>
27#include <private/qabstractitemmodel_p.h>
28#include <private/qstylehelper_p.h>
29#include <private/qtextengine_p.h>
30#include <qdebug.h>
31#include <qlocale.h>
32#include <qmath.h>
33
34#include <limits.h>
35
36// keep in sync with QAbstractItemDelegate::helpEvent()
37#ifndef DBL_DIG
38# define DBL_DIG 10
39#endif
40
42
44{
45 Q_DECLARE_PUBLIC(QItemDelegate)
46
47public:
48 QItemDelegatePrivate() : f(nullptr), clipPainting(true) {}
49
50 inline const QItemEditorFactory *editorFactory() const
51 { return f ? f : QItemEditorFactory::defaultFactory(); }
52
54 {
55 if (!(state & QStyle::State_Enabled)) return QIcon::Disabled;
57 return QIcon::Normal;
58 }
59
61 { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; }
62
64 {
66 return text;
67 }
68
70
71 QItemEditorFactory *f;
73
74 QRect displayRect(const QModelIndex &index, const QStyleOptionViewItem &option,
75 const QRect &decorationRect, const QRect &checkRect) const;
76 QRect textLayoutBounds(const QStyleOptionViewItem &option,
77 const QRect &decorationRect, const QRect &checkRect) const;
78 QSizeF doTextLayout(int lineWidth) const;
80 mutable QTextOption textOption;
81
82 const QWidget *widget(const QStyleOptionViewItem &option) const
83 {
84 return option.widget;
85 }
86
87 // ### temporary hack until we have QStandardItemDelegate
88 mutable struct Icon {
89 QIcon icon;
92 } tmp;
93};
94
95QRect QItemDelegatePrivate::displayRect(const QModelIndex &index, const QStyleOptionViewItem &option,
96 const QRect &decorationRect, const QRect &checkRect) const
97{
98 Q_Q(const QItemDelegate);
99 const QVariant value = index.data(Qt::DisplayRole);
100 if (!value.isValid() || value.isNull())
101 return QRect();
102
103 const QString text = valueToText(value, option);
104 const QVariant fontVal = index.data(Qt::FontRole);
105 const QFont fnt = qvariant_cast<QFont>(fontVal).resolve(option.font);
106 return q->textRectangle(nullptr,
107 textLayoutBounds(option, decorationRect, checkRect),
108 fnt, text);
109}
110
111// similar to QCommonStylePrivate::viewItemSize(Qt::DisplayRole)
112QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItem &option,
113 const QRect &decorationRect, const QRect &checkRect) const
114{
115 QRect rect = option.rect;
116 const QWidget *w = widget(option);
117 QStyle *style = w ? w->style() : QApplication::style();
118 const bool wrapText = option.features & QStyleOptionViewItem::WrapText;
119 // see QItemDelegate::drawDisplay
120 const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, w) + 1;
121 switch (option.decorationPosition) {
122 case QStyleOptionViewItem::Left:
123 case QStyleOptionViewItem::Right:
124 rect.setWidth(wrapText && rect.isValid() ? rect.width() - 2 * textMargin : (QFIXED_MAX));
125 break;
126 case QStyleOptionViewItem::Top:
127 case QStyleOptionViewItem::Bottom:
128 rect.setWidth(wrapText ? option.decorationSize.width() - 2 * textMargin : (QFIXED_MAX));
129 break;
130 }
131
132 if (wrapText) {
133 if (!decorationRect.isNull())
134 rect.setWidth(rect.width() - decorationRect.width() - 2 * textMargin);
135 if (!checkRect.isNull())
136 rect.setWidth(rect.width() - checkRect.width() - 2 * textMargin);
137 // adjust height to be sure that the text fits
138 const QSizeF size = doTextLayout(rect.width());
139 rect.setHeight(qCeil(size.height()));
140 }
141
142 return rect;
143}
144
145QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const
146{
147 qreal height = 0;
148 qreal widthUsed = 0;
149 textLayout.beginLayout();
150 while (true) {
151 QTextLine line = textLayout.createLine();
152 if (!line.isValid())
153 break;
154 line.setLineWidth(lineWidth);
155 line.setPosition(QPointF(0, height));
156 height += line.height();
157 widthUsed = qMax(widthUsed, line.naturalTextWidth());
158 }
159 textLayout.endLayout();
160 return QSizeF(widthUsed, height);
161}
162
163/*!
164 \class QItemDelegate
165
166 \brief The QItemDelegate class provides display and editing facilities for
167 data items from a model.
168
169 \ingroup model-view
170 \inmodule QtWidgets
171
172 QItemDelegate can be used to provide custom display features and editor
173 widgets for item views based on QAbstractItemView subclasses. Using a
174 delegate for this purpose allows the display and editing mechanisms to be
175 customized and developed independently from the model and view.
176
177 The QItemDelegate class is one of the \l{Model/View Classes} and
178 is part of Qt's \l{Model/View Programming}{model/view framework}.
179 Note that QStyledItemDelegate has taken over the job of drawing
180 Qt's item views. We recommend the use of QStyledItemDelegate when
181 creating new delegates.
182
183 When displaying items from a custom model in a standard view, it is
184 often sufficient to simply ensure that the model returns appropriate
185 data for each of the \l{Qt::ItemDataRole}{roles} that determine the
186 appearance of items in views. The default delegate used by Qt's
187 standard views uses this role information to display items in most
188 of the common forms expected by users. However, it is sometimes
189 necessary to have even more control over the appearance of items than
190 the default delegate can provide.
191
192 This class provides default implementations of the functions for
193 painting item data in a view and editing data from item models.
194 Default implementations of the paint() and sizeHint() virtual
195 functions, defined in QAbstractItemDelegate, are provided to
196 ensure that the delegate implements the correct basic behavior
197 expected by views. You can reimplement these functions in
198 subclasses to customize the appearance of items.
199
200 When editing data in an item view, QItemDelegate provides an
201 editor widget, which is a widget that is placed on top of the view
202 while editing takes place. Editors are created with a
203 QItemEditorFactory; a default static instance provided by
204 QItemEditorFactory is installed on all item delegates. You can set
205 a custom factory using setItemEditorFactory() or set a new default
206 factory with QItemEditorFactory::setDefaultFactory(). It is the
207 data stored in the item model with the Qt::EditRole that is edited.
208
209 Only the standard editing functions for widget-based delegates are
210 reimplemented here:
211
212 \list
213 \li createEditor() returns the widget used to change data from the model
214 and can be reimplemented to customize editing behavior.
215 \li setEditorData() provides the widget with data to manipulate.
216 \li updateEditorGeometry() ensures that the editor is displayed correctly
217 with respect to the item view.
218 \li setModelData() returns updated data to the model.
219 \endlist
220
221 The closeEditor() signal indicates that the user has completed editing the data,
222 and that the editor widget can be destroyed.
223
224 \section1 Standard Roles and Data Types
225
226 The default delegate used by the standard views supplied with Qt
227 associates each standard role (defined by Qt::ItemDataRole) with certain
228 data types. Models that return data in these types can influence the
229 appearance of the delegate as described in the following table.
230
231 \table
232 \header \li Role \li Accepted Types
233 \omit
234 \row \li \l Qt::AccessibleDescriptionRole \li QString
235 \row \li \l Qt::AccessibleTextRole \li QString
236 \endomit
237 \row \li \l Qt::BackgroundRole \li QBrush
238 \row \li \l Qt::CheckStateRole \li Qt::CheckState
239 \row \li \l Qt::DecorationRole \li QIcon, QPixmap and QColor
240 \row \li \l Qt::DisplayRole \li QString and types with a string representation
241 \row \li \l Qt::EditRole \li See QItemEditorFactory for details
242 \row \li \l Qt::FontRole \li QFont
243 \row \li \l Qt::SizeHintRole \li QSize
244 \omit
245 \row \li \l Qt::StatusTipRole \li
246 \endomit
247 \row \li \l Qt::TextAlignmentRole \li Qt::Alignment
248 \row \li \l Qt::ForegroundRole \li QBrush
249 \omit
250 \row \li \l Qt::ToolTipRole
251 \row \li \l Qt::WhatsThisRole
252 \endomit
253 \endtable
254
255 If the default delegate does not allow the level of customization that
256 you need, either for display purposes or for editing data, it is possible to
257 subclass QItemDelegate to implement the desired behavior.
258
259 \section1 Subclassing
260
261 When subclassing QItemDelegate to create a delegate that displays items
262 using a custom renderer, it is important to ensure that the delegate can
263 render items suitably for all the required states; such as selected,
264 disabled, checked. The documentation for the paint() function contains
265 some hints to show how this can be achieved.
266
267 You can provide custom editors by using a QItemEditorFactory. The following
268 code shows how a custom editor can be made available to delegates with the
269 default item editor factory.
270
271 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp setDefaultFactory
272
273 After the default factory has been set, all standard item delegates
274 will use it (also the delegates that were created before setting the
275 default factory).
276
277 This way, you can avoid subclassing QItemDelegate, and all values of the
278 specified type (for example QMetaType::QDateTime) will be edited using the
279 provided editor (like \c{MyFancyDateTimeEdit} in the above example).
280
281 An alternative is to reimplement createEditor(), setEditorData(),
282 setModelData(), and updateEditorGeometry(). This process is described
283 in the \l{A simple delegate}{Model/View Programming overview documentation}.
284
285 \section1 QStyledItemDelegate vs. QItemDelegate
286
287 Since Qt 4.4, there are two delegate classes: QItemDelegate and
288 QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
289 These two classes are independent alternatives to painting and providing
290 editors for items in views. The difference between them is that
291 QStyledItemDelegate uses the current style to paint its items. We therefore
292 recommend using QStyledItemDelegate as the base class when implementing
293 custom delegates or when working with Qt style sheets. The code required
294 for either class should be equal unless the custom delegate needs to use
295 the style for drawing.
296
297 \sa {Delegate Classes}, QStyledItemDelegate, QAbstractItemDelegate
298*/
299
300/*!
301 Constructs an item delegate with the given \a parent.
302*/
303
304QItemDelegate::QItemDelegate(QObject *parent)
305 : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent)
306{
307
308}
309
310/*!
311 Destroys the item delegate.
312*/
313
314QItemDelegate::~QItemDelegate()
315{
316}
317
318/*!
319 \property QItemDelegate::clipping
320 \brief if the delegate should clip the paint events
321
322 This property will set the paint clip to the size of the item.
323 The default value is on. It is useful for cases such
324 as when images are larger than the size of the item.
325*/
326
327bool QItemDelegate::hasClipping() const
328{
329 Q_D(const QItemDelegate);
330 return d->clipPainting;
331}
332
333void QItemDelegate::setClipping(bool clip)
334{
335 Q_D(QItemDelegate);
336 d->clipPainting = clip;
337}
338
339QString QItemDelegatePrivate::valueToText(const QVariant &value, const QStyleOptionViewItem &option) const
340{
341 return textForRole(Qt::DisplayRole, value, option.locale, DBL_DIG);
342}
343
344/*!
345 Renders the delegate using the given \a painter and style \a option for
346 the item specified by \a index.
347
348 When reimplementing this function in a subclass, you should update the area
349 held by the option's \l{QStyleOption::rect}{rect} variable, using the
350 option's \l{QStyleOption::state}{state} variable to determine the state of
351 the item to be displayed, and adjust the way it is painted accordingly.
352
353 For example, a selected item may need to be displayed differently to
354 unselected items, as shown in the following code:
355
356 \code
357 if (option.state & QStyle::State_Selected)
358 painter->fillRect(option.rect, option.palette.highlight());
359 \endcode
360
361 After painting, you should ensure that the painter is returned to its
362 the state it was supplied in when this function was called. For example,
363 it may be useful to call QPainter::save() before painting and
364 QPainter::restore() afterwards.
365
366 \sa QStyle::State
367*/
368void QItemDelegate::paint(QPainter *painter,
369 const QStyleOptionViewItem &option,
370 const QModelIndex &index) const
371{
372 Q_D(const QItemDelegate);
373 Q_ASSERT(index.isValid());
374
375 QStyleOptionViewItem opt = setOptions(index, option);
376
377 // prepare
378 painter->save();
379 if (d->clipPainting)
380 painter->setClipRect(opt.rect);
381
382 // get the data and the rectangles
383
384 QVariant value;
385
386 QPixmap pixmap;
387 QRect decorationRect;
388 value = index.data(Qt::DecorationRole);
389 if (value.isValid()) {
390 // ### we need the pixmap to call the virtual function
391 pixmap = decoration(opt, value);
392 if (value.userType() == QMetaType::QIcon) {
393 d->tmp.icon = qvariant_cast<QIcon>(value);
394 d->tmp.mode = d->iconMode(option.state);
395 d->tmp.state = d->iconState(option.state);
396 const QSize size = d->tmp.icon.actualSize(option.decorationSize,
397 d->tmp.mode, d->tmp.state);
398 decorationRect = QRect(QPoint(0, 0), size);
399 } else {
400 d->tmp.icon = QIcon();
401 decorationRect = QRect(QPoint(0, 0), pixmap.size());
402 }
403 } else {
404 d->tmp.icon = QIcon();
405 decorationRect = QRect();
406 }
407
408 QRect checkRect;
409 Qt::CheckState checkState = Qt::Unchecked;
410 value = index.data(Qt::CheckStateRole);
411 if (value.isValid()) {
412 checkState = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(value);
413 checkRect = doCheck(opt, opt.rect, value);
414 }
415
416 QString text;
417 QRect displayRect;
418 value = index.data(Qt::DisplayRole);
419 if (value.isValid() && !value.isNull()) {
420 text = d->valueToText(value, opt);
421 displayRect = d->displayRect(index, opt, decorationRect, checkRect);
422 }
423
424 // do the layout
425
426 doLayout(opt, &checkRect, &decorationRect, &displayRect, false);
427
428 // draw the item
429
430 drawBackground(painter, opt, index);
431 drawCheck(painter, opt, checkRect, checkState);
432 drawDecoration(painter, opt, decorationRect, pixmap);
433 drawDisplay(painter, opt, displayRect, text);
434 drawFocus(painter, opt, displayRect);
435
436 // done
437 painter->restore();
438}
439
440/*!
441 Returns the size needed by the delegate to display the item
442 specified by \a index, taking into account the style information
443 provided by \a option.
444
445 When reimplementing this function, note that in case of text
446 items, QItemDelegate adds a margin (i.e. 2 *
447 QStyle::PM_FocusFrameHMargin) to the length of the text.
448*/
449
450QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option,
451 const QModelIndex &index) const
452{
453 Q_D(const QItemDelegate);
454 QVariant value = index.data(Qt::SizeHintRole);
455 if (value.isValid())
456 return qvariant_cast<QSize>(value);
457 QRect decorationRect = rect(option, index, Qt::DecorationRole);
458 QRect checkRect = rect(option, index, Qt::CheckStateRole);
459 QRect displayRect = d->displayRect(index, option, decorationRect, checkRect);
460
461 doLayout(option, &checkRect, &decorationRect, &displayRect, true);
462
463 return (decorationRect|displayRect|checkRect).size();
464}
465
466/*!
467 Returns the widget used to edit the item specified by \a index
468 for editing. The \a parent widget and style \a option are used to
469 control how the editor widget appears.
470
471 \sa QAbstractItemDelegate::createEditor()
472*/
473
474QWidget *QItemDelegate::createEditor(QWidget *parent,
475 const QStyleOptionViewItem &,
476 const QModelIndex &index) const
477{
478 Q_D(const QItemDelegate);
479 if (!index.isValid())
480 return nullptr;
481 const QItemEditorFactory *factory = d->f;
482 if (factory == nullptr)
483 factory = QItemEditorFactory::defaultFactory();
484 QWidget *w = factory->createEditor(index.data(Qt::EditRole).userType(), parent);
485 if (w)
486 w->setFocusPolicy(Qt::WheelFocus);
487 return w;
488}
489
490/*!
491 Sets the data to be displayed and edited by the \a editor from the
492 data model item specified by the model \a index.
493
494 The default implementation stores the data in the \a editor
495 widget's \l {Qt's Property System} {user property}.
496
497 \sa QMetaProperty::isUser()
498*/
499
500void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
501{
502 QVariant v = index.data(Qt::EditRole);
503 QByteArray n = editor->metaObject()->userProperty().name();
504
505 if (!n.isEmpty()) {
506 if (!v.isValid())
507 v = QVariant(editor->property(n).metaType());
508 editor->setProperty(n, v);
509 }
510}
511
512/*!
513 Gets data from the \a editor widget and stores it in the specified
514 \a model at the item \a index.
515
516 The default implementation gets the value to be stored in the data
517 model from the \a editor widget's \l {Qt's Property System} {user
518 property}.
519
520 \sa QMetaProperty::isUser()
521*/
522
523void QItemDelegate::setModelData(QWidget *editor,
524 QAbstractItemModel *model,
525 const QModelIndex &index) const
526{
527 Q_D(const QItemDelegate);
528 Q_ASSERT(model);
529 Q_ASSERT(editor);
530 QByteArray n = editor->metaObject()->userProperty().name();
531 if (n.isEmpty())
532 n = d->editorFactory()->valuePropertyName(
533 model->data(index, Qt::EditRole).userType());
534 if (!n.isEmpty())
535 model->setData(index, editor->property(n), Qt::EditRole);
536}
537
538/*!
539 Updates the \a editor for the item specified by \a index
540 according to the style \a option given.
541*/
542
543void QItemDelegate::updateEditorGeometry(QWidget *editor,
544 const QStyleOptionViewItem &option,
545 const QModelIndex &index) const
546{
547 if (!editor)
548 return;
549 Q_ASSERT(index.isValid());
550 QPixmap pixmap = decoration(option, index.data(Qt::DecorationRole));
551 QString text = QItemDelegatePrivate::replaceNewLine(index.data(Qt::DisplayRole).toString());
552 QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
553 QRect textRect = textRectangle(nullptr, option.rect, option.font, text);
554 QRect checkRect = doCheck(option, textRect, index.data(Qt::CheckStateRole));
555 QStyleOptionViewItem opt = option;
556 opt.showDecorationSelected = true; // let the editor take up all available space
557 doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
558 editor->setGeometry(textRect);
559}
560
561/*!
562 Returns the editor factory used by the item delegate.
563 If no editor factory is set, the function will return null.
564
565 \sa setItemEditorFactory()
566*/
567QItemEditorFactory *QItemDelegate::itemEditorFactory() const
568{
569 Q_D(const QItemDelegate);
570 return d->f;
571}
572
573/*!
574 Sets the editor factory to be used by the item delegate to be the \a factory
575 specified. If no editor factory is set, the item delegate will use the
576 default editor factory.
577
578 \sa itemEditorFactory()
579*/
580void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
581{
582 Q_D(QItemDelegate);
583 d->f = factory;
584}
585
586/*!
587 Renders the item view \a text within the rectangle specified by \a rect
588 using the given \a painter and style \a option.
589*/
590
591void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
592 const QRect &rect, const QString &text) const
593{
594 Q_D(const QItemDelegate);
595
596 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
597 ? QPalette::Normal : QPalette::Disabled;
598 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
599 cg = QPalette::Inactive;
600 if (option.state & QStyle::State_Selected) {
601 painter->fillRect(rect, option.palette.brush(cg, QPalette::Highlight));
602 painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
603 } else {
604 painter->setPen(option.palette.color(cg, QPalette::Text));
605 }
606
607 if (text.isEmpty())
608 return;
609
610 if (option.state & QStyle::State_Editing) {
611 painter->save();
612 painter->setPen(option.palette.color(cg, QPalette::Text));
613 painter->drawRect(rect.adjusted(0, 0, -1, -1));
614 painter->restore();
615 }
616
617 const QStyleOptionViewItem opt = option;
618
619 const QWidget *widget = d->widget(option);
620 QStyle *style = widget ? widget->style() : QApplication::style();
621 const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
622 QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
623 const bool wrapText = opt.features & QStyleOptionViewItem::WrapText;
624 d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
625 d->textOption.setTextDirection(option.direction);
626 d->textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
627 d->textLayout.setTextOption(d->textOption);
628 d->textLayout.setFont(option.font);
629 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
630
631 QSizeF textLayoutSize = d->doTextLayout(textRect.width());
632
633 if (textRect.width() < textLayoutSize.width()
634 || textRect.height() < textLayoutSize.height()) {
635 QString elided;
636 int start = 0;
637 int end = text.indexOf(QChar::LineSeparator, start);
638 if (end == -1) {
639 elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
640 } else {
641 while (end != -1) {
642 elided += option.fontMetrics.elidedText(text.mid(start, end - start),
643 option.textElideMode, textRect.width());
644 elided += QChar::LineSeparator;
645 start = end + 1;
646 end = text.indexOf(QChar::LineSeparator, start);
647 }
648 //let's add the last line (after the last QChar::LineSeparator)
649 elided += option.fontMetrics.elidedText(text.mid(start),
650 option.textElideMode, textRect.width());
651 }
652 d->textLayout.setText(elided);
653 textLayoutSize = d->doTextLayout(textRect.width());
654 }
655
656 const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
657 const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
658 layoutSize, textRect);
659 // if we still overflow even after eliding the text, enable clipping
660 if (!hasClipping() && (textRect.width() < textLayoutSize.width()
661 || textRect.height() < textLayoutSize.height())) {
662 painter->save();
663 painter->setClipRect(layoutRect);
664 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
665 layoutRect);
666 painter->restore();
667 } else {
668 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
669 layoutRect);
670 }
671}
672
673/*!
674 Renders the decoration \a pixmap within the rectangle specified by
675 \a rect using the given \a painter and style \a option.
676*/
677void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
678 const QRect &rect, const QPixmap &pixmap) const
679{
680 Q_D(const QItemDelegate);
681 // if we have an icon, we ignore the pixmap
682 if (!d->tmp.icon.isNull()) {
683 d->tmp.icon.paint(painter, rect, option.decorationAlignment,
684 d->tmp.mode, d->tmp.state);
685 return;
686 }
687
688 if (pixmap.isNull() || !rect.isValid())
689 return;
690 QPoint p = QStyle::alignedRect(option.direction, option.decorationAlignment,
691 pixmap.size(), rect).topLeft();
692 if (option.state & QStyle::State_Selected) {
693 const QPixmap pm = selectedPixmap(pixmap, option.palette, option.state & QStyle::State_Enabled);
694 painter->drawPixmap(p, pm);
695 } else {
696 painter->drawPixmap(p, pixmap);
697 }
698}
699
700/*!
701 Renders the region within the rectangle specified by \a rect, indicating
702 that it has the focus, using the given \a painter and style \a option.
703*/
704
705void QItemDelegate::drawFocus(QPainter *painter,
706 const QStyleOptionViewItem &option,
707 const QRect &rect) const
708{
709 Q_D(const QItemDelegate);
710 if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
711 return;
712 QStyleOptionFocusRect o;
713 o.QStyleOption::operator=(option);
714 o.rect = rect;
715 o.state |= QStyle::State_KeyboardFocusChange;
716 o.state |= QStyle::State_Item;
717 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
718 ? QPalette::Normal : QPalette::Disabled;
719 o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
720 ? QPalette::Highlight : QPalette::Window);
721 const QWidget *widget = d->widget(option);
722 QStyle *style = widget ? widget->style() : QApplication::style();
723 style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
724}
725
726/*!
727 Renders a check indicator within the rectangle specified by \a
728 rect, using the given \a painter and style \a option, using the
729 given \a state.
730*/
731
732void QItemDelegate::drawCheck(QPainter *painter,
733 const QStyleOptionViewItem &option,
734 const QRect &rect, Qt::CheckState state) const
735{
736 Q_D(const QItemDelegate);
737 if (!rect.isValid())
738 return;
739
740 QStyleOptionViewItem opt(option);
741 opt.rect = rect;
742 opt.state = opt.state & ~QStyle::State_HasFocus;
743
744 switch (state) {
745 case Qt::Unchecked:
746 opt.state |= QStyle::State_Off;
747 break;
748 case Qt::PartiallyChecked:
749 opt.state |= QStyle::State_NoChange;
750 break;
751 case Qt::Checked:
752 opt.state |= QStyle::State_On;
753 break;
754 }
755
756 const QWidget *widget = d->widget(option);
757 QStyle *style = widget ? widget->style() : QApplication::style();
758 style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget);
759}
760
761/*!
762 Renders the item background for the given \a index,
763 using the given \a painter and style \a option.
764*/
765
766void QItemDelegate::drawBackground(QPainter *painter,
767 const QStyleOptionViewItem &option,
768 const QModelIndex &index) const
769{
770 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
771 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
772 ? QPalette::Normal : QPalette::Disabled;
773 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
774 cg = QPalette::Inactive;
775
776 painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
777 } else {
778 QVariant value = index.data(Qt::BackgroundRole);
779 if (value.canConvert<QBrush>()) {
780 QPainterStateGuard psg(painter);
781 painter->setBrushOrigin(option.rect.topLeft());
782 painter->fillRect(option.rect, qvariant_cast<QBrush>(value));
783 }
784 }
785}
786
787
788/*!
789 \internal
790
791 Code duplicated in QCommonStylePrivate::viewItemLayout
792*/
793
794void QItemDelegate::doLayout(const QStyleOptionViewItem &option,
795 QRect *checkRect, QRect *pixmapRect, QRect *textRect,
796 bool hint) const
797{
798 Q_ASSERT(checkRect && pixmapRect && textRect);
799 Q_D(const QItemDelegate);
800 const QWidget *widget = d->widget(option);
801 QStyle *style = widget ? widget->style() : QApplication::style();
802 const bool hasCheck = checkRect->isValid();
803 const bool hasPixmap = pixmapRect->isValid();
804 const bool hasText = textRect->isValid();
805 const bool hasMargin = (hasText | hasPixmap | hasCheck);
806 const int frameHMargin = hasMargin ?
807 style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1 : 0;
808 const int textMargin = hasText ? frameHMargin : 0;
809 const int pixmapMargin = hasPixmap ? frameHMargin : 0;
810 const int checkMargin = hasCheck ? frameHMargin : 0;
811 const int x = option.rect.left();
812 const int y = option.rect.top();
813 int w, h;
814
815 textRect->adjust(-textMargin, 0, textMargin, 0); // add width padding
816 if (textRect->height() == 0 && (!hasPixmap || !hint)) {
817 //if there is no text, we still want to have a decent height for the item sizeHint and the editor size
818 textRect->setHeight(option.fontMetrics.height());
819 }
820
821 QSize pm(0, 0);
822 if (hasPixmap) {
823 pm = pixmapRect->size();
824 pm.rwidth() += 2 * pixmapMargin;
825 }
826 if (hint) {
827 h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
828 if (option.decorationPosition == QStyleOptionViewItem::Left
829 || option.decorationPosition == QStyleOptionViewItem::Right) {
830 w = textRect->width() + pm.width();
831 } else {
832 w = qMax(textRect->width(), pm.width());
833 }
834 } else {
835 w = option.rect.width();
836 h = option.rect.height();
837 }
838
839 int cw = 0;
840 QRect check;
841 if (hasCheck) {
842 cw = checkRect->width() + 2 * checkMargin;
843 if (hint) w += cw;
844 if (option.direction == Qt::RightToLeft) {
845 check.setRect(x + w - cw, y, cw, h);
846 } else {
847 check.setRect(x, y, cw, h);
848 }
849 }
850
851 // at this point w should be the *total* width
852
853 QRect display;
854 QRect decoration;
855 switch (option.decorationPosition) {
856 case QStyleOptionViewItem::Top: {
857 if (hasPixmap)
858 pm.setHeight(pm.height() + pixmapMargin); // add space
859 h = hint ? textRect->height() : h - pm.height();
860
861 if (option.direction == Qt::RightToLeft) {
862 decoration.setRect(x, y, w - cw, pm.height());
863 display.setRect(x, y + pm.height(), w - cw, h);
864 } else {
865 decoration.setRect(x + cw, y, w - cw, pm.height());
866 display.setRect(x + cw, y + pm.height(), w - cw, h);
867 }
868 break; }
869 case QStyleOptionViewItem::Bottom: {
870 if (hasText)
871 textRect->setHeight(textRect->height() + textMargin); // add space
872 h = hint ? textRect->height() + pm.height() : h;
873
874 if (option.direction == Qt::RightToLeft) {
875 display.setRect(x, y, w - cw, textRect->height());
876 decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
877 } else {
878 display.setRect(x + cw, y, w - cw, textRect->height());
879 decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
880 }
881 break; }
882 case QStyleOptionViewItem::Left: {
883 if (option.direction == Qt::LeftToRight) {
884 decoration.setRect(x + cw, y, pm.width(), h);
885 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
886 } else {
887 display.setRect(x, y, w - pm.width() - cw, h);
888 decoration.setRect(display.right() + 1, y, pm.width(), h);
889 }
890 break; }
891 case QStyleOptionViewItem::Right: {
892 if (option.direction == Qt::LeftToRight) {
893 display.setRect(x + cw, y, w - pm.width() - cw, h);
894 decoration.setRect(display.right() + 1, y, pm.width(), h);
895 } else {
896 decoration.setRect(x, y, pm.width(), h);
897 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
898 }
899 break; }
900 default:
901 qWarning("doLayout: decoration position is invalid");
902 decoration = *pixmapRect;
903 break;
904 }
905
906 if (!hint) { // we only need to do the internal layout if we are going to paint
907 *checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
908 checkRect->size(), check);
909 *pixmapRect = QStyle::alignedRect(option.direction, option.decorationAlignment,
910 pixmapRect->size(), decoration);
911 // the text takes up all available space, unless the decoration is not shown as selected
912 if (option.showDecorationSelected)
913 *textRect = display;
914 else
915 *textRect = QStyle::alignedRect(option.direction, option.displayAlignment,
916 textRect->size().boundedTo(display.size()), display);
917 } else {
918 *checkRect = check;
919 *pixmapRect = decoration;
920 *textRect = display;
921 }
922}
923
924/*!
925 \internal
926
927 Returns the pixmap used to decorate the root of the item view.
928 The style \a option controls the appearance of the root; the \a variant
929 refers to the data associated with an item.
930*/
931
932QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const
933{
934 Q_D(const QItemDelegate);
935 switch (variant.userType()) {
936 case QMetaType::QIcon: {
937 QIcon::Mode mode = d->iconMode(option.state);
938 QIcon::State state = d->iconState(option.state);
939 const auto dpr = QStyleHelper::getDpr(option.widget);
940 return qvariant_cast<QIcon>(variant).pixmap(option.decorationSize, dpr, mode, state); }
941 case QMetaType::QColor: {
942 static QPixmap pixmap(option.decorationSize);
943 pixmap.fill(qvariant_cast<QColor>(variant));
944 return pixmap; }
945 default:
946 break;
947 }
948
949 return qvariant_cast<QPixmap>(variant);
950}
951
952// hacky but faster version of "QString::asprintf("%d-%d", i, enabled)"
953static QString qPixmapSerial(quint64 i, bool enabled)
954{
955 ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) };
956 ushort *ptr = &arr[16];
957
958 while (i > 0) {
959 // hey - it's our internal representation, so use the ascii character after '9'
960 // instead of 'a' for hex
961 *(--ptr) = '0' + i % 16;
962 i >>= 4;
963 }
964
965 return QString((const QChar *)ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr));
966}
967
968
969/*!
970 \internal
971 Returns the selected version of the given \a pixmap using the given \a palette.
972 The \a enabled argument decides whether the normal or disabled highlight color of
973 the palette is used.
974*/
975QPixmap QItemDelegate::selectedPixmap(const QPixmap &pixmap, const QPalette &palette, bool enabled)
976{
977 const QString key = qPixmapSerial(pixmap.cacheKey(), enabled);
978 QPixmap pm;
979 if (!QPixmapCache::find(key, &pm)) {
980 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
981
982 QColor color = palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
983 QPalette::Highlight);
984 color.setAlphaF(0.3f);
985
986 QPainter painter(&img);
987 painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
988 painter.fillRect(0, 0, img.width(), img.height(), color);
989 painter.end();
990
991 pm = QPixmap(QPixmap::fromImage(img));
992 const int n = (img.sizeInBytes() >> 10) + 1;
993 if (QPixmapCache::cacheLimit() < n)
994 QPixmapCache::setCacheLimit(n);
995
996 QPixmapCache::insert(key, pm);
997 }
998 return pm;
999}
1000
1001/*!
1002 \internal
1003 Only used (and usable) for Qt::DecorationRole and Qt::CheckStateRole
1004*/
1005QRect QItemDelegate::rect(const QStyleOptionViewItem &option,
1006 const QModelIndex &index, int role) const
1007{
1008 Q_D(const QItemDelegate);
1009 QVariant value = index.data(role);
1010 if (role == Qt::CheckStateRole)
1011 return doCheck(option, option.rect, value);
1012 if (value.isValid() && !value.isNull()) {
1013 switch (value.userType()) {
1014 case QMetaType::UnknownType:
1015 break;
1016 case QMetaType::QPixmap: {
1017 const QPixmap &pixmap = qvariant_cast<QPixmap>(value);
1018 return QRect(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); }
1019 case QMetaType::QImage: {
1020 const QImage &image = qvariant_cast<QImage>(value);
1021 return QRect(QPoint(0, 0), image.deviceIndependentSize().toSize()); }
1022 case QMetaType::QIcon: {
1023 QIcon::Mode mode = d->iconMode(option.state);
1024 QIcon::State state = d->iconState(option.state);
1025 QIcon icon = qvariant_cast<QIcon>(value);
1026 QSize size = icon.actualSize(option.decorationSize, mode, state);
1027 return QRect(QPoint(0, 0), size); }
1028 case QMetaType::QColor:
1029 return QRect(QPoint(0, 0), option.decorationSize);
1030 case QMetaType::QString:
1031 default: {
1032 const QString text = d->valueToText(value, option);
1033 value = index.data(Qt::FontRole);
1034 QFont fnt = qvariant_cast<QFont>(value).resolve(option.font);
1035 return textRectangle(nullptr,
1036 d->textLayoutBounds(option, QRect(), QRect()),
1037 fnt, text); }
1038 }
1039 }
1040 return QRect();
1041}
1042
1043/*!
1044 \internal
1045*/
1046QRect QItemDelegate::doCheck(const QStyleOptionViewItem &option,
1047 const QRect &bounding, const QVariant &value) const
1048{
1049 if (value.isValid()) {
1050 Q_D(const QItemDelegate);
1051 QStyleOptionButton opt;
1052 opt.QStyleOption::operator=(option);
1053 opt.rect = bounding;
1054 const QWidget *widget = d->widget(option); // cast
1055 QStyle *style = widget ? widget->style() : QApplication::style();
1056 return style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
1057 }
1058 return QRect();
1059}
1060
1061/*!
1062 \internal
1063*/
1064QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect,
1065 const QFont &font, const QString &text) const
1066{
1067 Q_D(const QItemDelegate);
1068 d->textOption.setWrapMode(QTextOption::WordWrap);
1069 d->textLayout.setTextOption(d->textOption);
1070 d->textLayout.setFont(font);
1071 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
1072 QSizeF fpSize = d->doTextLayout(rect.width());
1073 const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height()));
1074 // ###: textRectangle should take style option as argument
1075 const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr) + 1;
1076 return QRect(0, 0, size.width() + 2 * textMargin, size.height());
1077}
1078
1079/*!
1080 \reimp
1081
1082 See details in QAbstractItemDelegate::handleEditorEvent().
1083*/
1084
1085bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
1086{
1087 return handleEditorEvent(object, event);
1088}
1089
1090/*!
1091 \reimp
1092*/
1093
1094bool QItemDelegate::editorEvent(QEvent *event,
1095 QAbstractItemModel *model,
1096 const QStyleOptionViewItem &option,
1097 const QModelIndex &index)
1098{
1099 Q_ASSERT(event);
1100 Q_ASSERT(model);
1101
1102 // make sure that the item is checkable
1103 Qt::ItemFlags flags = model->flags(index);
1104 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
1105 || !(flags & Qt::ItemIsEnabled))
1106 return false;
1107
1108 // make sure that we have a check state
1109 QVariant value = index.data(Qt::CheckStateRole);
1110 if (!value.isValid())
1111 return false;
1112
1113 // make sure that we have the right event type
1114 if ((event->type() == QEvent::MouseButtonRelease)
1115 || (event->type() == QEvent::MouseButtonDblClick)
1116 || (event->type() == QEvent::MouseButtonPress)) {
1117 QRect checkRect = doCheck(option, option.rect, Qt::Checked);
1118 QRect emptyRect;
1119 doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
1120 QMouseEvent *me = static_cast<QMouseEvent*>(event);
1121 if (me->button() != Qt::LeftButton || !checkRect.contains(me->position().toPoint()))
1122 return false;
1123
1124 // eat the double click events inside the check rect
1125 if ((event->type() == QEvent::MouseButtonPress)
1126 || (event->type() == QEvent::MouseButtonDblClick))
1127 return true;
1128
1129 } else if (event->type() == QEvent::KeyPress) {
1130 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
1131 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
1132 return false;
1133 } else {
1134 return false;
1135 }
1136
1137 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(value);
1138 if (flags & Qt::ItemIsUserTristate)
1139 state = ((Qt::CheckState)((state + 1) % 3));
1140 else
1141 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
1142 return model->setData(index, state, Qt::CheckStateRole);
1143}
1144
1145/*!
1146 \internal
1147*/
1148
1149QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index,
1150 const QStyleOptionViewItem &option) const
1151{
1152 QStyleOptionViewItem opt = option;
1153
1154 // set font
1155 QVariant value = index.data(Qt::FontRole);
1156 if (value.isValid()){
1157 opt.font = qvariant_cast<QFont>(value).resolve(opt.font);
1158 opt.fontMetrics = QFontMetrics(opt.font);
1159 }
1160
1161 // set text alignment
1162 value = index.data(Qt::TextAlignmentRole);
1163 if (value.isValid())
1164 opt.displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(value);
1165
1166 // set foreground brush
1167 value = index.data(Qt::ForegroundRole);
1168 if (value.canConvert<QBrush>())
1169 opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
1170
1171 // disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
1172 opt.styleObject = nullptr;
1173
1174 return opt;
1175}
1176
1177QT_END_NAMESPACE
1178
1179#include "moc_qitemdelegate.cpp"
const QWidget * widget(const QStyleOptionViewItem &option) const
QItemEditorFactory * f
QSizeF doTextLayout(int lineWidth) const
QRect displayRect(const QModelIndex &index, const QStyleOptionViewItem &option, const QRect &decorationRect, const QRect &checkRect) const
QRect textLayoutBounds(const QStyleOptionViewItem &option, const QRect &decorationRect, const QRect &checkRect) const
\inmodule QtCore
friend class QWidget
Definition qpainter.h:431
\inmodule QtCore\reentrant
Definition qpoint.h:231
static QString qPixmapSerial(quint64 i, bool enabled)
#define DBL_DIG