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

source code of qtbase/src/widgets/itemviews/qitemdelegate.cpp