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 "qstyleditemdelegate.h"
5
6#include <qabstractitemmodel.h>
7#include <qapplication.h>
8#include <qbrush.h>
9#if QT_CONFIG(lineedit)
10#include <qlineedit.h>
11#endif
12#if QT_CONFIG(textedit)
13#include <qtextedit.h>
14#include <qplaintextedit.h>
15#endif
16#include <qpainter.h>
17#include <qpalette.h>
18#include <qpoint.h>
19#include <qrect.h>
20#include <qsize.h>
21#include <qstyle.h>
22#include <qdatetime.h>
23#include <qstyleoption.h>
24#include <qevent.h>
25#include <qpixmap.h>
26#include <qbitmap.h>
27#include <qpixmapcache.h>
28#include <qitemeditorfactory.h>
29#include <private/qitemeditorfactory_p.h>
30#include <qmetaobject.h>
31#include <qtextlayout.h>
32#include <private/qabstractitemdelegate_p.h>
33#include <private/qabstractitemmodel_p.h>
34#include <private/qtextengine_p.h>
35#include <private/qlayoutengine_p.h>
36#include <qdebug.h>
37#include <qlocale.h>
38#if QT_CONFIG(tableview)
39#include <qtableview.h>
40#endif
41
42#include <array>
43#include <limits.h>
44
45QT_BEGIN_NAMESPACE
46
47class QStyledItemDelegatePrivate : public QAbstractItemDelegatePrivate
48{
49 Q_DECLARE_PUBLIC(QStyledItemDelegate)
50
51public:
52 QStyledItemDelegatePrivate() : factory(nullptr) { }
53
54 static const QWidget *widget(const QStyleOptionViewItem &option)
55 {
56 return option.widget;
57 }
58
59 const QItemEditorFactory *editorFactory() const
60 {
61 return factory ? factory : QItemEditorFactory::defaultFactory();
62 }
63
64 QItemEditorFactory *factory;
65
66 mutable std::array<QModelRoleData, 7> modelRoleData = {
67 QModelRoleData(Qt::FontRole),
68 QModelRoleData(Qt::TextAlignmentRole),
69 QModelRoleData(Qt::ForegroundRole),
70 QModelRoleData(Qt::CheckStateRole),
71 QModelRoleData(Qt::DecorationRole),
72 QModelRoleData(Qt::DisplayRole),
73 QModelRoleData(Qt::BackgroundRole)
74 };
75};
76
77/*!
78 \class QStyledItemDelegate
79
80 \brief The QStyledItemDelegate class provides display and editing facilities for
81 data items from a model.
82
83 \ingroup model-view
84 \inmodule QtWidgets
85
86 When displaying data from models in Qt item views, e.g., a
87 QTableView, the individual items are drawn by a delegate. Also,
88 when an item is edited, it provides an editor widget, which is
89 placed on top of the item view while editing takes place.
90 QStyledItemDelegate is the default delegate for all Qt item
91 views, and is installed upon them when they are created.
92
93 The QStyledItemDelegate class is one of the \l{Model/View Classes}
94 and is part of Qt's \l{Model/View Programming}{model/view
95 framework}. The delegate allows the display and editing of items
96 to be developed independently from the model and view.
97
98 The data of items in models are assigned an
99 \l{Qt::}{ItemDataRole}; each item can store a QVariant for each
100 role. QStyledItemDelegate implements display and editing for the
101 most common datatypes expected by users, including booleans,
102 integers, and strings.
103
104 The data will be drawn differently depending on which role they
105 have in the model. The following table describes the roles and the
106 data types the delegate can handle for each of them. It is often
107 sufficient to ensure that the model returns appropriate data for
108 each of the roles to determine the appearance of items in views.
109
110 \table
111 \header \li Role \li Accepted Types
112 \omit
113 \row \li \l Qt::AccessibleDescriptionRole \li QString
114 \row \li \l Qt::AccessibleTextRole \li QString
115 \endomit
116 \row \li \l Qt::BackgroundRole \li QBrush
117 \row \li \l Qt::CheckStateRole \li Qt::CheckState
118 \row \li \l Qt::DecorationRole \li QIcon, QPixmap, QImage and QColor
119 \row \li \l Qt::DisplayRole \li QString and types with a string representation
120 \row \li \l Qt::EditRole \li See QItemEditorFactory for details
121 \row \li \l Qt::FontRole \li QFont
122 \row \li \l Qt::SizeHintRole \li QSize
123 \omit
124 \row \li \l Qt::StatusTipRole \li
125 \endomit
126 \row \li \l Qt::TextAlignmentRole \li Qt::Alignment
127 \row \li \l Qt::ForegroundRole \li QBrush
128 \omit
129 \row \li \l Qt::ToolTipRole
130 \row \li \l Qt::WhatsThisRole
131 \endomit
132 \endtable
133
134 Editors are created with a QItemEditorFactory; a default static
135 instance provided by QItemEditorFactory is installed on all item
136 delegates. You can set a custom factory using
137 setItemEditorFactory() or set a new default factory with
138 QItemEditorFactory::setDefaultFactory().
139
140 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp setDefaultFactory
141
142 After the new factory has been set, all standard item delegates
143 will use it (i.e, also delegates that were created before the new
144 default factory was set).
145
146 It is the data stored in the item model with the \l{Qt::}{EditRole}
147 that is edited. See the QItemEditorFactory class for a more
148 high-level introduction to item editor factories.
149
150 \section1 Subclassing QStyledItemDelegate
151
152 If the delegate does not support painting of the data types you
153 need or you want to customize the drawing of items, you need to
154 subclass QStyledItemDelegate, and reimplement paint() and possibly
155 sizeHint(). The paint() function is called individually for each
156 item, and with sizeHint(), you can specify the hint for each
157 of them.
158
159 When reimplementing paint(), one would typically handle the
160 datatypes one would like to draw and use the superclass
161 implementation for other types.
162
163 The painting of check box indicators are performed by the current
164 style. The style also specifies the size and the bounding
165 rectangles in which to draw the data for the different data roles.
166 The bounding rectangle of the item itself is also calculated by
167 the style. When drawing already supported datatypes, it is
168 therefore a good idea to ask the style for these bounding
169 rectangles. The QStyle class description describes this in
170 more detail.
171
172 If you wish to change any of the bounding rectangles calculated by
173 the style or the painting of check box indicators, you can
174 subclass QStyle. Note, however, that the size of the items can
175 also be affected by reimplementing sizeHint().
176
177 It is possible for a custom delegate to provide editors
178 without the use of an editor item factory. In this case, the
179 following virtual functions must be reimplemented:
180
181 \list
182 \li createEditor() returns the widget used to change data from the model
183 and can be reimplemented to customize editing behavior.
184 \li setEditorData() provides the widget with data to manipulate.
185 \li updateEditorGeometry() ensures that the editor is displayed correctly
186 with respect to the item view.
187 \li setModelData() returns updated data to the model.
188 \endlist
189
190 The \l{Star Delegate Example}{Star Delegate} example creates
191 editors by reimplementing these methods.
192
193 \section1 QStyledItemDelegate vs. QItemDelegate
194
195 Since Qt 4.4, there are two delegate classes: QItemDelegate and
196 QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
197 These two classes are independent alternatives to painting and providing
198 editors for items in views. The difference between them is that
199 QStyledItemDelegate uses the current style to paint its items. We therefore
200 recommend using QStyledItemDelegate as the base class when implementing
201 custom delegates or when working with Qt style sheets. The code required
202 for either class should be equal unless the custom delegate needs to use
203 the style for drawing.
204
205 If you wish to customize the painting of item views, you should
206 implement a custom style. Please see the QStyle class
207 documentation for details.
208
209 \sa {Delegate Classes}, QItemDelegate, QAbstractItemDelegate, QStyle,
210 {Star Delegate Example}
211*/
212
213
214/*!
215 Constructs an item delegate with the given \a parent.
216*/
217QStyledItemDelegate::QStyledItemDelegate(QObject *parent)
218 : QAbstractItemDelegate(*new QStyledItemDelegatePrivate(), parent)
219{
220}
221
222/*!
223 Destroys the item delegate.
224*/
225QStyledItemDelegate::~QStyledItemDelegate()
226{
227}
228
229/*!
230 This function returns the string that the delegate will use to display the
231 Qt::DisplayRole of the model in \a locale. \a value is the value of the Qt::DisplayRole
232 provided by the model.
233
234 The default implementation uses the QLocale::toString to convert \a value into
235 a QString.
236
237 This function is not called for empty model indices, i.e., indices for which
238 the model returns an invalid QVariant.
239
240 \sa QAbstractItemModel::data()
241*/
242QString QStyledItemDelegate::displayText(const QVariant &value, const QLocale& locale) const
243{
244 return d_func()->textForRole(role: Qt::DisplayRole, value, locale);
245}
246
247/*!
248 Initialize \a option with the values using the index \a index. This method
249 is useful for subclasses when they need a QStyleOptionViewItem, but don't want
250 to fill in all the information themselves.
251
252 \sa QStyleOption::initFrom()
253*/
254void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
255 const QModelIndex &index) const
256{
257 option->index = index;
258
259 Q_D(const QStyledItemDelegate);
260 QModelRoleDataSpan modelRoleDataSpan = d->modelRoleData;
261 index.multiData(roleDataSpan: modelRoleDataSpan);
262
263 const QVariant *value;
264 value = modelRoleDataSpan.dataForRole(role: Qt::FontRole);
265 if (value->isValid() && !value->isNull()) {
266 option->font = qvariant_cast<QFont>(v: *value).resolve(option->font);
267 option->fontMetrics = QFontMetrics(option->font);
268 }
269
270 value = modelRoleDataSpan.dataForRole(role: Qt::TextAlignmentRole);
271 if (value->isValid() && !value->isNull())
272 option->displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(data: *value);
273
274 value = modelRoleDataSpan.dataForRole(role: Qt::ForegroundRole);
275 if (value->canConvert<QBrush>())
276 option->palette.setBrush(acr: QPalette::Text, abrush: qvariant_cast<QBrush>(v: *value));
277
278 value = modelRoleDataSpan.dataForRole(role: Qt::CheckStateRole);
279 if (value->isValid() && !value->isNull()) {
280 option->features |= QStyleOptionViewItem::HasCheckIndicator;
281 option->checkState = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: *value);
282 }
283
284 value = modelRoleDataSpan.dataForRole(role: Qt::DecorationRole);
285 if (value->isValid() && !value->isNull()) {
286 option->features |= QStyleOptionViewItem::HasDecoration;
287 switch (value->userType()) {
288 case QMetaType::QIcon: {
289 option->icon = qvariant_cast<QIcon>(v: *value);
290 if (option->icon.isNull()) {
291 option->features &= ~QStyleOptionViewItem::HasDecoration;
292 break;
293 }
294 QIcon::Mode mode;
295 if (!(option->state & QStyle::State_Enabled))
296 mode = QIcon::Disabled;
297 else if (option->state & QStyle::State_Selected)
298 mode = QIcon::Selected;
299 else
300 mode = QIcon::Normal;
301 QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
302 QSize actualSize = option->icon.actualSize(size: option->decorationSize, mode, state);
303 // For highdpi icons actualSize might be larger than decorationSize, which we don't want. Clamp it to decorationSize.
304 option->decorationSize = QSize(qMin(a: option->decorationSize.width(), b: actualSize.width()),
305 qMin(a: option->decorationSize.height(), b: actualSize.height()));
306 break;
307 }
308 case QMetaType::QColor: {
309 QPixmap pixmap(option->decorationSize);
310 pixmap.fill(fillColor: qvariant_cast<QColor>(v: *value));
311 option->icon = QIcon(pixmap);
312 break;
313 }
314 case QMetaType::QImage: {
315 QImage image = qvariant_cast<QImage>(v: *value);
316 option->icon = QIcon(QPixmap::fromImage(image));
317 option->decorationSize = image.deviceIndependentSize().toSize();
318 break;
319 }
320 case QMetaType::QPixmap: {
321 QPixmap pixmap = qvariant_cast<QPixmap>(v: *value);
322 option->icon = QIcon(pixmap);
323 option->decorationSize = pixmap.deviceIndependentSize().toSize();
324 break;
325 }
326 default:
327 break;
328 }
329 }
330
331 value = modelRoleDataSpan.dataForRole(role: Qt::DisplayRole);
332 if (value->isValid() && !value->isNull()) {
333 option->features |= QStyleOptionViewItem::HasDisplay;
334 option->text = displayText(value: *value, locale: option->locale);
335 }
336
337 value = modelRoleDataSpan.dataForRole(role: Qt::BackgroundRole);
338 option->backgroundBrush = qvariant_cast<QBrush>(v: *value);
339
340 // disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
341 option->styleObject = nullptr;
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 This function paints the item using the view's QStyle.
349
350 When reimplementing paint in a subclass. Use the initStyleOption()
351 to set up the \a option in the same way as the
352 QStyledItemDelegate.
353
354 Whenever possible, use the \a option while painting.
355 Especially its \l{QStyleOption::}{rect} variable to decide
356 where to draw and its \l{QStyleOption::}{state} to determine
357 if it is enabled or selected.
358
359 After painting, you should ensure that the painter is returned to
360 the state it was supplied in when this function was called.
361 For example, it may be useful to call QPainter::save() before
362 painting and QPainter::restore() afterwards.
363
364 \sa QItemDelegate::paint(), QStyle::drawControl(), QStyle::CE_ItemViewItem
365*/
366void QStyledItemDelegate::paint(QPainter *painter,
367 const QStyleOptionViewItem &option, const QModelIndex &index) const
368{
369 Q_ASSERT(index.isValid());
370
371 QStyleOptionViewItem opt = option;
372 initStyleOption(option: &opt, index);
373
374 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
375 QStyle *style = widget ? widget->style() : QApplication::style();
376 style->drawControl(element: QStyle::CE_ItemViewItem, opt: &opt, p: painter, w: widget);
377}
378
379/*!
380 Returns the size needed by the delegate to display the item
381 specified by \a index, taking into account the style information
382 provided by \a option.
383
384 This function uses the view's QStyle to determine the size of the
385 item.
386
387 \sa QStyle::sizeFromContents(), QStyle::CT_ItemViewItem
388*/
389QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option,
390 const QModelIndex &index) const
391{
392 QVariant value = index.data(arole: Qt::SizeHintRole);
393 if (value.isValid())
394 return qvariant_cast<QSize>(v: value);
395
396 QStyleOptionViewItem opt = option;
397 initStyleOption(option: &opt, index);
398 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
399 QStyle *style = widget ? widget->style() : QApplication::style();
400 return style->sizeFromContents(ct: QStyle::CT_ItemViewItem, opt: &opt, contentsSize: QSize(), w: widget);
401}
402
403/*!
404 Returns the widget used to edit the item specified by \a index
405 for editing. The \a parent widget and style \a option are used to
406 control how the editor widget appears.
407
408 \sa QAbstractItemDelegate::createEditor()
409*/
410QWidget *QStyledItemDelegate::createEditor(QWidget *parent,
411 const QStyleOptionViewItem &option,
412 const QModelIndex &index) const
413{
414 Q_UNUSED(option);
415 Q_D(const QStyledItemDelegate);
416 if (!index.isValid())
417 return nullptr;
418 return d->editorFactory()->createEditor(userType: index.data(arole: Qt::EditRole).userType(), parent);
419}
420
421/*!
422 Sets the data to be displayed and edited by the \a editor from the
423 data model item specified by the model \a index.
424
425 The default implementation stores the data in the \a editor
426 widget's \l {Qt's Property System} {user property}.
427
428 \sa QMetaProperty::isUser()
429*/
430void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
431{
432 QVariant v = index.data(arole: Qt::EditRole);
433 QByteArray n = editor->metaObject()->userProperty().name();
434
435 if (!n.isEmpty()) {
436 if (!v.isValid())
437 v = QVariant(editor->property(name: n).metaType());
438 editor->setProperty(name: n, value: v);
439 }
440}
441
442/*!
443 Gets data from the \a editor widget and stores it in the specified
444 \a model at the item \a index.
445
446 The default implementation gets the value to be stored in the data
447 model from the \a editor widget's \l {Qt's Property System} {user
448 property}.
449
450 \sa QMetaProperty::isUser()
451*/
452void QStyledItemDelegate::setModelData(QWidget *editor,
453 QAbstractItemModel *model,
454 const QModelIndex &index) const
455{
456 Q_D(const QStyledItemDelegate);
457 Q_ASSERT(model);
458 Q_ASSERT(editor);
459 QByteArray n = editor->metaObject()->userProperty().name();
460 if (n.isEmpty())
461 n = d->editorFactory()->valuePropertyName(
462 userType: model->data(index, role: Qt::EditRole).userType());
463 if (!n.isEmpty())
464 model->setData(index, value: editor->property(name: n), role: Qt::EditRole);
465}
466
467/*!
468 Updates the \a editor for the item specified by \a index
469 according to the style \a option given.
470*/
471void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
472 const QStyleOptionViewItem &option,
473 const QModelIndex &index) const
474{
475 if (!editor)
476 return;
477 Q_ASSERT(index.isValid());
478 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
479
480 QStyleOptionViewItem opt = option;
481 initStyleOption(option: &opt, index);
482 opt.showDecorationSelected = editor->style()->styleHint(stylehint: QStyle::SH_ItemView_ShowDecorationSelected, opt: nullptr, widget: editor);
483
484 QStyle *style = widget ? widget->style() : QApplication::style();
485 QRect geom = style->subElementRect(subElement: QStyle::SE_ItemViewItemText, option: &opt, widget);
486 editor->setGeometry(geom);
487}
488
489/*!
490 Returns the editor factory used by the item delegate.
491 If no editor factory is set, the function will return null.
492
493 \sa setItemEditorFactory()
494*/
495QItemEditorFactory *QStyledItemDelegate::itemEditorFactory() const
496{
497 Q_D(const QStyledItemDelegate);
498 return d->factory;
499}
500
501/*!
502 Sets the editor factory to be used by the item delegate to be the \a factory
503 specified. If no editor factory is set, the item delegate will use the
504 default editor factory.
505
506 \sa itemEditorFactory()
507*/
508void QStyledItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
509{
510 Q_D(QStyledItemDelegate);
511 d->factory = factory;
512}
513
514/*!
515 \reimp
516
517 See details in QAbstractItemDelegate::handleEditorEvent().
518*/
519bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
520{
521 return handleEditorEvent(object, event);
522}
523
524/*!
525 \reimp
526*/
527bool QStyledItemDelegate::editorEvent(QEvent *event,
528 QAbstractItemModel *model,
529 const QStyleOptionViewItem &option,
530 const QModelIndex &index)
531{
532 Q_ASSERT(event);
533 Q_ASSERT(model);
534
535 // make sure that the item is checkable
536 Qt::ItemFlags flags = model->flags(index);
537 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
538 || !(flags & Qt::ItemIsEnabled))
539 return false;
540
541 // make sure that we have a check state
542 QVariant value = index.data(arole: Qt::CheckStateRole);
543 if (!value.isValid())
544 return false;
545
546 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
547 QStyle *style = widget ? widget->style() : QApplication::style();
548
549 // make sure that we have the right event type
550 if ((event->type() == QEvent::MouseButtonRelease)
551 || (event->type() == QEvent::MouseButtonDblClick)
552 || (event->type() == QEvent::MouseButtonPress)) {
553 QStyleOptionViewItem viewOpt(option);
554 initStyleOption(option: &viewOpt, index);
555 QRect checkRect = style->subElementRect(subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &viewOpt, widget);
556 QMouseEvent *me = static_cast<QMouseEvent*>(event);
557 if (me->button() != Qt::LeftButton || !checkRect.contains(p: me->position().toPoint()))
558 return false;
559
560 if ((event->type() == QEvent::MouseButtonPress)
561 || (event->type() == QEvent::MouseButtonDblClick))
562 return true;
563
564 } else if (event->type() == QEvent::KeyPress) {
565 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
566 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
567 return false;
568 } else {
569 return false;
570 }
571
572 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: value);
573 if (flags & Qt::ItemIsUserTristate)
574 state = ((Qt::CheckState)((state + 1) % 3));
575 else
576 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
577 return model->setData(index, value: state, role: Qt::CheckStateRole);
578}
579
580QT_END_NAMESPACE
581
582#include "moc_qstyleditemdelegate.cpp"
583

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