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