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 const QStyleOptionViewItem opt = option;
611
612 const QWidget *widget = d->widget(option);
613 QStyle *style = widget ? widget->style() : QApplication::style();
614 const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
615 QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
616 const bool wrapText = opt.features & QStyleOptionViewItem::WrapText;
617 d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
618 d->textOption.setTextDirection(option.direction);
619 d->textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
620 d->textLayout.setTextOption(d->textOption);
621 d->textLayout.setFont(option.font);
622 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
623
624 QSizeF textLayoutSize = d->doTextLayout(textRect.width());
625
626 if (textRect.width() < textLayoutSize.width()
627 || textRect.height() < textLayoutSize.height()) {
628 QString elided;
629 int start = 0;
630 int end = text.indexOf(QChar::LineSeparator, start);
631 if (end == -1) {
632 elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
633 } else {
634 while (end != -1) {
635 elided += option.fontMetrics.elidedText(text.mid(start, end - start),
636 option.textElideMode, textRect.width());
637 elided += QChar::LineSeparator;
638 start = end + 1;
639 end = text.indexOf(QChar::LineSeparator, start);
640 }
641 //let's add the last line (after the last QChar::LineSeparator)
642 elided += option.fontMetrics.elidedText(text.mid(start),
643 option.textElideMode, textRect.width());
644 }
645 d->textLayout.setText(elided);
646 textLayoutSize = d->doTextLayout(textRect.width());
647 }
648
649 const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
650 const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
651 layoutSize, textRect);
652 // if we still overflow even after eliding the text, enable clipping
653 if (!hasClipping() && (textRect.width() < textLayoutSize.width()
654 || textRect.height() < textLayoutSize.height())) {
655 painter->save();
656 painter->setClipRect(layoutRect);
657 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
658 layoutRect);
659 painter->restore();
660 } else {
661 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
662 layoutRect);
663 }
664}
665
666/*!
667 Renders the decoration \a pixmap within the rectangle specified by
668 \a rect using the given \a painter and style \a option.
669*/
670void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
671 const QRect &rect, const QPixmap &pixmap) const
672{
673 Q_D(const QItemDelegate);
674 // if we have an icon, we ignore the pixmap
675 if (!d->tmp.icon.isNull()) {
676 d->tmp.icon.paint(painter, rect, option.decorationAlignment,
677 d->tmp.mode, d->tmp.state);
678 return;
679 }
680
681 if (pixmap.isNull() || !rect.isValid())
682 return;
683 QPoint p = QStyle::alignedRect(option.direction, option.decorationAlignment,
684 pixmap.size(), rect).topLeft();
685 if (option.state & QStyle::State_Selected) {
686 const QPixmap pm = selectedPixmap(pixmap, option.palette, option.state & QStyle::State_Enabled);
687 painter->drawPixmap(p, pm);
688 } else {
689 painter->drawPixmap(p, pixmap);
690 }
691}
692
693/*!
694 Renders the region within the rectangle specified by \a rect, indicating
695 that it has the focus, using the given \a painter and style \a option.
696*/
697
698void QItemDelegate::drawFocus(QPainter *painter,
699 const QStyleOptionViewItem &option,
700 const QRect &rect) const
701{
702 Q_D(const QItemDelegate);
703 if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
704 return;
705 QStyleOptionFocusRect o;
706 o.QStyleOption::operator=(option);
707 o.rect = rect;
708 o.state |= QStyle::State_KeyboardFocusChange;
709 o.state |= QStyle::State_Item;
710 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
711 ? QPalette::Normal : QPalette::Disabled;
712 o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
713 ? QPalette::Highlight : QPalette::Window);
714 const QWidget *widget = d->widget(option);
715 QStyle *style = widget ? widget->style() : QApplication::style();
716 style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
717}
718
719/*!
720 Renders a check indicator within the rectangle specified by \a
721 rect, using the given \a painter and style \a option, using the
722 given \a state.
723*/
724
725void QItemDelegate::drawCheck(QPainter *painter,
726 const QStyleOptionViewItem &option,
727 const QRect &rect, Qt::CheckState state) const
728{
729 Q_D(const QItemDelegate);
730 if (!rect.isValid())
731 return;
732
733 QStyleOptionViewItem opt(option);
734 opt.rect = rect;
735 opt.state = opt.state & ~QStyle::State_HasFocus;
736
737 switch (state) {
738 case Qt::Unchecked:
739 opt.state |= QStyle::State_Off;
740 break;
741 case Qt::PartiallyChecked:
742 opt.state |= QStyle::State_NoChange;
743 break;
744 case Qt::Checked:
745 opt.state |= QStyle::State_On;
746 break;
747 }
748
749 const QWidget *widget = d->widget(option);
750 QStyle *style = widget ? widget->style() : QApplication::style();
751 style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget);
752}
753
754/*!
755 Renders the item background for the given \a index,
756 using the given \a painter and style \a option.
757*/
758
759void QItemDelegate::drawBackground(QPainter *painter,
760 const QStyleOptionViewItem &option,
761 const QModelIndex &index) const
762{
763 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
764 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
765 ? QPalette::Normal : QPalette::Disabled;
766 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
767 cg = QPalette::Inactive;
768
769 painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
770 } else {
771 QVariant value = index.data(Qt::BackgroundRole);
772 if (value.canConvert<QBrush>()) {
773 QPainterStateGuard psg(painter);
774 painter->setBrushOrigin(option.rect.topLeft());
775 painter->fillRect(option.rect, qvariant_cast<QBrush>(value));
776 }
777 }
778}
779
780
781/*!
782 \internal
783
784 Code duplicated in QCommonStylePrivate::viewItemLayout
785*/
786
787void QItemDelegate::doLayout(const QStyleOptionViewItem &option,
788 QRect *checkRect, QRect *pixmapRect, QRect *textRect,
789 bool hint) const
790{
791 Q_ASSERT(checkRect && pixmapRect && textRect);
792 Q_D(const QItemDelegate);
793 const QWidget *widget = d->widget(option);
794 QStyle *style = widget ? widget->style() : QApplication::style();
795 const bool hasCheck = checkRect->isValid();
796 const bool hasPixmap = pixmapRect->isValid();
797 const bool hasText = textRect->isValid();
798 const bool hasMargin = (hasText | hasPixmap | hasCheck);
799 const int frameHMargin = hasMargin ?
800 style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1 : 0;
801 const int textMargin = hasText ? frameHMargin : 0;
802 const int pixmapMargin = hasPixmap ? frameHMargin : 0;
803 const int checkMargin = hasCheck ? frameHMargin : 0;
804 const int x = option.rect.left();
805 const int y = option.rect.top();
806 int w, h;
807
808 textRect->adjust(-textMargin, 0, textMargin, 0); // add width padding
809 if (textRect->height() == 0 && (!hasPixmap || !hint)) {
810 //if there is no text, we still want to have a decent height for the item sizeHint and the editor size
811 textRect->setHeight(option.fontMetrics.height());
812 }
813
814 QSize pm(0, 0);
815 if (hasPixmap) {
816 pm = pixmapRect->size();
817 pm.rwidth() += 2 * pixmapMargin;
818 }
819 if (hint) {
820 h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
821 if (option.decorationPosition == QStyleOptionViewItem::Left
822 || option.decorationPosition == QStyleOptionViewItem::Right) {
823 w = textRect->width() + pm.width();
824 } else {
825 w = qMax(textRect->width(), pm.width());
826 }
827 } else {
828 w = option.rect.width();
829 h = option.rect.height();
830 }
831
832 int cw = 0;
833 QRect check;
834 if (hasCheck) {
835 cw = checkRect->width() + 2 * checkMargin;
836 if (hint) w += cw;
837 if (option.direction == Qt::RightToLeft) {
838 check.setRect(x + w - cw, y, cw, h);
839 } else {
840 check.setRect(x, y, cw, h);
841 }
842 }
843
844 // at this point w should be the *total* width
845
846 QRect display;
847 QRect decoration;
848 switch (option.decorationPosition) {
849 case QStyleOptionViewItem::Top: {
850 if (hasPixmap)
851 pm.setHeight(pm.height() + pixmapMargin); // add space
852 h = hint ? textRect->height() : h - pm.height();
853
854 if (option.direction == Qt::RightToLeft) {
855 decoration.setRect(x, y, w - cw, pm.height());
856 display.setRect(x, y + pm.height(), w - cw, h);
857 } else {
858 decoration.setRect(x + cw, y, w - cw, pm.height());
859 display.setRect(x + cw, y + pm.height(), w - cw, h);
860 }
861 break; }
862 case QStyleOptionViewItem::Bottom: {
863 if (hasText)
864 textRect->setHeight(textRect->height() + textMargin); // add space
865 h = hint ? textRect->height() + pm.height() : h;
866
867 if (option.direction == Qt::RightToLeft) {
868 display.setRect(x, y, w - cw, textRect->height());
869 decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
870 } else {
871 display.setRect(x + cw, y, w - cw, textRect->height());
872 decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
873 }
874 break; }
875 case QStyleOptionViewItem::Left: {
876 if (option.direction == Qt::LeftToRight) {
877 decoration.setRect(x + cw, y, pm.width(), h);
878 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
879 } else {
880 display.setRect(x, y, w - pm.width() - cw, h);
881 decoration.setRect(display.right() + 1, y, pm.width(), h);
882 }
883 break; }
884 case QStyleOptionViewItem::Right: {
885 if (option.direction == Qt::LeftToRight) {
886 display.setRect(x + cw, y, w - pm.width() - cw, h);
887 decoration.setRect(display.right() + 1, y, pm.width(), h);
888 } else {
889 decoration.setRect(x, y, pm.width(), h);
890 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
891 }
892 break; }
893 default:
894 qWarning("doLayout: decoration position is invalid");
895 decoration = *pixmapRect;
896 break;
897 }
898
899 if (!hint) { // we only need to do the internal layout if we are going to paint
900 *checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
901 checkRect->size(), check);
902 *pixmapRect = QStyle::alignedRect(option.direction, option.decorationAlignment,
903 pixmapRect->size(), decoration);
904 // the text takes up all available space, unless the decoration is not shown as selected
905 if (option.showDecorationSelected)
906 *textRect = display;
907 else
908 *textRect = QStyle::alignedRect(option.direction, option.displayAlignment,
909 textRect->size().boundedTo(display.size()), display);
910 } else {
911 *checkRect = check;
912 *pixmapRect = decoration;
913 *textRect = display;
914 }
915}
916
917/*!
918 \internal
919
920 Returns the pixmap used to decorate the root of the item view.
921 The style \a option controls the appearance of the root; the \a variant
922 refers to the data associated with an item.
923*/
924
925QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const
926{
927 Q_D(const QItemDelegate);
928 switch (variant.userType()) {
929 case QMetaType::QIcon: {
930 QIcon::Mode mode = d->iconMode(option.state);
931 QIcon::State state = d->iconState(option.state);
932 const auto dpr = QStyleHelper::getDpr(option.widget);
933 return qvariant_cast<QIcon>(variant).pixmap(option.decorationSize, dpr, mode, state); }
934 case QMetaType::QColor: {
935 static QPixmap pixmap(option.decorationSize);
936 pixmap.fill(qvariant_cast<QColor>(variant));
937 return pixmap; }
938 default:
939 break;
940 }
941
942 return qvariant_cast<QPixmap>(variant);
943}
944
945// hacky but faster version of "QString::asprintf("%d-%d", i, enabled)"
946static QString qPixmapSerial(quint64 i, bool enabled)
947{
948 ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) };
949 ushort *ptr = &arr[16];
950
951 while (i > 0) {
952 // hey - it's our internal representation, so use the ascii character after '9'
953 // instead of 'a' for hex
954 *(--ptr) = '0' + i % 16;
955 i >>= 4;
956 }
957
958 return QString((const QChar *)ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr));
959}
960
961
962/*!
963 \internal
964 Returns the selected version of the given \a pixmap using the given \a palette.
965 The \a enabled argument decides whether the normal or disabled highlight color of
966 the palette is used.
967*/
968QPixmap QItemDelegate::selectedPixmap(const QPixmap &pixmap, const QPalette &palette, bool enabled)
969{
970 const QString key = qPixmapSerial(pixmap.cacheKey(), enabled);
971 QPixmap pm;
972 if (!QPixmapCache::find(key, &pm)) {
973 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
974
975 QColor color = palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
976 QPalette::Highlight);
977 color.setAlphaF(0.3f);
978
979 QPainter painter(&img);
980 painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
981 painter.fillRect(0, 0, img.width(), img.height(), color);
982 painter.end();
983
984 pm = QPixmap(QPixmap::fromImage(img));
985 const int n = (img.sizeInBytes() >> 10) + 1;
986 if (QPixmapCache::cacheLimit() < n)
987 QPixmapCache::setCacheLimit(n);
988
989 QPixmapCache::insert(key, pm);
990 }
991 return pm;
992}
993
994/*!
995 \internal
996 Only used (and usable) for Qt::DecorationRole and Qt::CheckStateRole
997*/
998QRect QItemDelegate::rect(const QStyleOptionViewItem &option,
999 const QModelIndex &index, int role) const
1000{
1001 Q_D(const QItemDelegate);
1002 QVariant value = index.data(role);
1003 if (role == Qt::CheckStateRole)
1004 return doCheck(option, option.rect, value);
1005 if (value.isValid() && !value.isNull()) {
1006 switch (value.userType()) {
1007 case QMetaType::UnknownType:
1008 break;
1009 case QMetaType::QPixmap: {
1010 const QPixmap &pixmap = qvariant_cast<QPixmap>(value);
1011 return QRect(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); }
1012 case QMetaType::QImage: {
1013 const QImage &image = qvariant_cast<QImage>(value);
1014 return QRect(QPoint(0, 0), image.deviceIndependentSize().toSize()); }
1015 case QMetaType::QIcon: {
1016 QIcon::Mode mode = d->iconMode(option.state);
1017 QIcon::State state = d->iconState(option.state);
1018 QIcon icon = qvariant_cast<QIcon>(value);
1019 QSize size = icon.actualSize(option.decorationSize, mode, state);
1020 return QRect(QPoint(0, 0), size); }
1021 case QMetaType::QColor:
1022 return QRect(QPoint(0, 0), option.decorationSize);
1023 case QMetaType::QString:
1024 default: {
1025 const QString text = d->valueToText(value, option);
1026 value = index.data(Qt::FontRole);
1027 QFont fnt = qvariant_cast<QFont>(value).resolve(option.font);
1028 return textRectangle(nullptr,
1029 d->textLayoutBounds(option, QRect(), QRect()),
1030 fnt, text); }
1031 }
1032 }
1033 return QRect();
1034}
1035
1036/*!
1037 \internal
1038*/
1039QRect QItemDelegate::doCheck(const QStyleOptionViewItem &option,
1040 const QRect &bounding, const QVariant &value) const
1041{
1042 if (value.isValid()) {
1043 Q_D(const QItemDelegate);
1044 QStyleOptionButton opt;
1045 opt.QStyleOption::operator=(option);
1046 opt.rect = bounding;
1047 const QWidget *widget = d->widget(option); // cast
1048 QStyle *style = widget ? widget->style() : QApplication::style();
1049 return style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
1050 }
1051 return QRect();
1052}
1053
1054/*!
1055 \internal
1056*/
1057QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect,
1058 const QFont &font, const QString &text) const
1059{
1060 Q_D(const QItemDelegate);
1061 d->textOption.setWrapMode(QTextOption::WordWrap);
1062 d->textLayout.setTextOption(d->textOption);
1063 d->textLayout.setFont(font);
1064 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
1065 QSizeF fpSize = d->doTextLayout(rect.width());
1066 const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height()));
1067 // ###: textRectangle should take style option as argument
1068 const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr) + 1;
1069 return QRect(0, 0, size.width() + 2 * textMargin, size.height());
1070}
1071
1072/*!
1073 \reimp
1074
1075 See details in QAbstractItemDelegate::handleEditorEvent().
1076*/
1077
1078bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
1079{
1080 return handleEditorEvent(object, event);
1081}
1082
1083/*!
1084 \reimp
1085*/
1086
1087bool QItemDelegate::editorEvent(QEvent *event,
1088 QAbstractItemModel *model,
1089 const QStyleOptionViewItem &option,
1090 const QModelIndex &index)
1091{
1092 Q_ASSERT(event);
1093 Q_ASSERT(model);
1094
1095 // make sure that the item is checkable
1096 Qt::ItemFlags flags = model->flags(index);
1097 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
1098 || !(flags & Qt::ItemIsEnabled))
1099 return false;
1100
1101 // make sure that we have a check state
1102 QVariant value = index.data(Qt::CheckStateRole);
1103 if (!value.isValid())
1104 return false;
1105
1106 // make sure that we have the right event type
1107 if ((event->type() == QEvent::MouseButtonRelease)
1108 || (event->type() == QEvent::MouseButtonDblClick)
1109 || (event->type() == QEvent::MouseButtonPress)) {
1110 QRect checkRect = doCheck(option, option.rect, Qt::Checked);
1111 QRect emptyRect;
1112 doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
1113 QMouseEvent *me = static_cast<QMouseEvent*>(event);
1114 if (me->button() != Qt::LeftButton || !checkRect.contains(me->position().toPoint()))
1115 return false;
1116
1117 // eat the double click events inside the check rect
1118 if ((event->type() == QEvent::MouseButtonPress)
1119 || (event->type() == QEvent::MouseButtonDblClick))
1120 return true;
1121
1122 } else if (event->type() == QEvent::KeyPress) {
1123 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
1124 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
1125 return false;
1126 } else {
1127 return false;
1128 }
1129
1130 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(value);
1131 if (flags & Qt::ItemIsUserTristate)
1132 state = ((Qt::CheckState)((state + 1) % 3));
1133 else
1134 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
1135 return model->setData(index, state, Qt::CheckStateRole);
1136}
1137
1138/*!
1139 \internal
1140*/
1141
1142QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index,
1143 const QStyleOptionViewItem &option) const
1144{
1145 QStyleOptionViewItem opt = option;
1146
1147 // set font
1148 QVariant value = index.data(Qt::FontRole);
1149 if (value.isValid()){
1150 opt.font = qvariant_cast<QFont>(value).resolve(opt.font);
1151 opt.fontMetrics = QFontMetrics(opt.font);
1152 }
1153
1154 // set text alignment
1155 value = index.data(Qt::TextAlignmentRole);
1156 if (value.isValid())
1157 opt.displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(value);
1158
1159 // set foreground brush
1160 value = index.data(Qt::ForegroundRole);
1161 if (value.canConvert<QBrush>())
1162 opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
1163
1164 // disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
1165 opt.styleObject = nullptr;
1166
1167 return opt;
1168}
1169
1170QT_END_NAMESPACE
1171
1172#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:232
Combined button and popup list for selecting options.
static QString qPixmapSerial(quint64 i, bool enabled)
#define DBL_DIG