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 \since 4.4
87
88 When displaying data from models in Qt item views, e.g., a
89 QTableView, the individual items are drawn by a delegate. Also,
90 when an item is edited, it provides an editor widget, which is
91 placed on top of the item view while editing takes place.
92 QStyledItemDelegate is the default delegate for all Qt item
93 views, and is installed upon them when they are created.
94
95 The QStyledItemDelegate class is one of the \l{Model/View Classes}
96 and is part of Qt's \l{Model/View Programming}{model/view
97 framework}. The delegate allows the display and editing of items
98 to be developed independently from the model and view.
99
100 The data of items in models are assigned an
101 \l{Qt::}{ItemDataRole}; each item can store a QVariant for each
102 role. QStyledItemDelegate implements display and editing for the
103 most common datatypes expected by users, including booleans,
104 integers, and strings.
105
106 The data will be drawn differently depending on which role they
107 have in the model. The following table describes the roles and the
108 data types the delegate can handle for each of them. It is often
109 sufficient to ensure that the model returns appropriate data for
110 each of the roles to determine the appearance of items in views.
111
112 \table
113 \header \li Role \li Accepted Types
114 \omit
115 \row \li \l Qt::AccessibleDescriptionRole \li QString
116 \row \li \l Qt::AccessibleTextRole \li QString
117 \endomit
118 \row \li \l Qt::BackgroundRole \li QBrush \since 4.2
119 \row \li \l Qt::CheckStateRole \li Qt::CheckState
120 \row \li \l Qt::DecorationRole \li QIcon, QPixmap, QImage and QColor
121 \row \li \l Qt::DisplayRole \li QString and types with a string representation
122 \row \li \l Qt::EditRole \li See QItemEditorFactory for details
123 \row \li \l Qt::FontRole \li QFont
124 \row \li \l Qt::SizeHintRole \li QSize
125 \omit
126 \row \li \l Qt::StatusTipRole \li
127 \endomit
128 \row \li \l Qt::TextAlignmentRole \li Qt::Alignment
129 \row \li \l Qt::ForegroundRole \li QBrush \since 4.2
130 \omit
131 \row \li \l Qt::ToolTipRole
132 \row \li \l Qt::WhatsThisRole
133 \endomit
134 \endtable
135
136 Editors are created with a QItemEditorFactory; a default static
137 instance provided by QItemEditorFactory is installed on all item
138 delegates. You can set a custom factory using
139 setItemEditorFactory() or set a new default factory with
140 QItemEditorFactory::setDefaultFactory(). It is the data stored in
141 the item model with the \l{Qt::}{EditRole} that is edited. See the
142 QItemEditorFactory class for a more high-level introduction to
143 item editor factories. The \l{Color Editor Factory Example}{Color
144 Editor Factory} example shows how to create custom editors with a
145 factory.
146
147 \section1 Subclassing QStyledItemDelegate
148
149 If the delegate does not support painting of the data types you
150 need or you want to customize the drawing of items, you need to
151 subclass QStyledItemDelegate, and reimplement paint() and possibly
152 sizeHint(). The paint() function is called individually for each
153 item, and with sizeHint(), you can specify the hint for each
154 of them.
155
156 When reimplementing paint(), one would typically handle the
157 datatypes one would like to draw and use the superclass
158 implementation for other types.
159
160 The painting of check box indicators are performed by the current
161 style. The style also specifies the size and the bounding
162 rectangles in which to draw the data for the different data roles.
163 The bounding rectangle of the item itself is also calculated by
164 the style. When drawing already supported datatypes, it is
165 therefore a good idea to ask the style for these bounding
166 rectangles. The QStyle class description describes this in
167 more detail.
168
169 If you wish to change any of the bounding rectangles calculated by
170 the style or the painting of check box indicators, you can
171 subclass QStyle. Note, however, that the size of the items can
172 also be affected by reimplementing sizeHint().
173
174 It is possible for a custom delegate to provide editors
175 without the use of an editor item factory. In this case, the
176 following virtual functions must be reimplemented:
177
178 \list
179 \li createEditor() returns the widget used to change data from the model
180 and can be reimplemented to customize editing behavior.
181 \li setEditorData() provides the widget with data to manipulate.
182 \li updateEditorGeometry() ensures that the editor is displayed correctly
183 with respect to the item view.
184 \li setModelData() returns updated data to the model.
185 \endlist
186
187 The \l{Star Delegate Example}{Star Delegate} example creates
188 editors by reimplementing these methods.
189
190 \section1 QStyledItemDelegate vs. QItemDelegate
191
192 Since Qt 4.4, there are two delegate classes: QItemDelegate and
193 QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
194 These two classes are independent alternatives to painting and providing
195 editors for items in views. The difference between them is that
196 QStyledItemDelegate uses the current style to paint its items. We therefore
197 recommend using QStyledItemDelegate as the base class when implementing
198 custom delegates or when working with Qt style sheets. The code required
199 for either class should be equal unless the custom delegate needs to use
200 the style for drawing.
201
202 If you wish to customize the painting of item views, you should
203 implement a custom style. Please see the QStyle class
204 documentation for details.
205
206 \sa {Delegate Classes}, QItemDelegate, QAbstractItemDelegate, QStyle,
207 {Spin Box Delegate Example}, {Star Delegate Example}, {Color
208 Editor Factory 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(role: 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(roleDataSpan: modelRoleDataSpan);
260
261 const QVariant *value;
262 value = modelRoleDataSpan.dataForRole(role: Qt::FontRole);
263 if (value->isValid() && !value->isNull()) {
264 option->font = qvariant_cast<QFont>(v: *value).resolve(option->font);
265 option->fontMetrics = QFontMetrics(option->font);
266 }
267
268 value = modelRoleDataSpan.dataForRole(role: Qt::TextAlignmentRole);
269 if (value->isValid() && !value->isNull())
270 option->displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(data: *value);
271
272 value = modelRoleDataSpan.dataForRole(role: Qt::ForegroundRole);
273 if (value->canConvert<QBrush>())
274 option->palette.setBrush(acr: QPalette::Text, abrush: qvariant_cast<QBrush>(v: *value));
275
276 value = modelRoleDataSpan.dataForRole(role: Qt::CheckStateRole);
277 if (value->isValid() && !value->isNull()) {
278 option->features |= QStyleOptionViewItem::HasCheckIndicator;
279 option->checkState = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: *value);
280 }
281
282 value = modelRoleDataSpan.dataForRole(role: 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>(v: *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(size: 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(a: option->decorationSize.width(), b: actualSize.width()),
303 qMin(a: option->decorationSize.height(), b: actualSize.height()));
304 break;
305 }
306 case QMetaType::QColor: {
307 QPixmap pixmap(option->decorationSize);
308 pixmap.fill(fillColor: qvariant_cast<QColor>(v: *value));
309 option->icon = QIcon(pixmap);
310 break;
311 }
312 case QMetaType::QImage: {
313 QImage image = qvariant_cast<QImage>(v: *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>(v: *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(role: Qt::DisplayRole);
330 if (value->isValid() && !value->isNull()) {
331 option->features |= QStyleOptionViewItem::HasDisplay;
332 option->text = displayText(value: *value, locale: option->locale);
333 }
334
335 value = modelRoleDataSpan.dataForRole(role: Qt::BackgroundRole);
336 option->backgroundBrush = qvariant_cast<QBrush>(v: *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(option: &opt, index);
371
372 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
373 QStyle *style = widget ? widget->style() : QApplication::style();
374 style->drawControl(element: QStyle::CE_ItemViewItem, opt: &opt, p: painter, w: 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(arole: Qt::SizeHintRole);
391 if (value.isValid())
392 return qvariant_cast<QSize>(v: value);
393
394 QStyleOptionViewItem opt = option;
395 initStyleOption(option: &opt, index);
396 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
397 QStyle *style = widget ? widget->style() : QApplication::style();
398 return style->sizeFromContents(ct: QStyle::CT_ItemViewItem, opt: &opt, contentsSize: QSize(), w: 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(userType: index.data(arole: 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(arole: Qt::EditRole);
431 QByteArray n = editor->metaObject()->userProperty().name();
432
433 if (!n.isEmpty()) {
434 if (!v.isValid())
435 v = QVariant(editor->property(name: n).metaType());
436 editor->setProperty(name: n, value: 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 userType: model->data(index, role: Qt::EditRole).userType());
461 if (!n.isEmpty())
462 model->setData(index, value: editor->property(name: n), role: 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(option: &opt, index);
480 // let the editor take up all available space
481 //if the editor is not a QLineEdit
482 //or it is in a QTableView
483#if QT_CONFIG(tableview) && QT_CONFIG(lineedit)
484 if (qobject_cast<QExpandingLineEdit*>(object: editor) && !qobject_cast<const QTableView*>(object: widget))
485 opt.showDecorationSelected = editor->style()->styleHint(stylehint: QStyle::SH_ItemView_ShowDecorationSelected, opt: nullptr, widget: editor);
486 else
487#endif
488 opt.showDecorationSelected = true;
489
490 QStyle *style = widget ? widget->style() : QApplication::style();
491 QRect geom = style->subElementRect(subElement: QStyle::SE_ItemViewItemText, option: &opt, widget);
492 editor->setGeometry(geom);
493}
494
495/*!
496 Returns the editor factory used by the item delegate.
497 If no editor factory is set, the function will return null.
498
499 \sa setItemEditorFactory()
500*/
501QItemEditorFactory *QStyledItemDelegate::itemEditorFactory() const
502{
503 Q_D(const QStyledItemDelegate);
504 return d->factory;
505}
506
507/*!
508 Sets the editor factory to be used by the item delegate to be the \a factory
509 specified. If no editor factory is set, the item delegate will use the
510 default editor factory.
511
512 \sa itemEditorFactory()
513*/
514void QStyledItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
515{
516 Q_D(QStyledItemDelegate);
517 d->factory = factory;
518}
519
520
521/*!
522 \fn bool QStyledItemDelegate::eventFilter(QObject *editor, QEvent *event)
523
524 Returns \c true if the given \a editor is a valid QWidget and the
525 given \a event is handled; otherwise returns \c false. The following
526 key press events are handled by default:
527
528 \list
529 \li \uicontrol Tab
530 \li \uicontrol Backtab
531 \li \uicontrol Enter
532 \li \uicontrol Return
533 \li \uicontrol Esc
534 \endlist
535
536 If the \a editor's type is QTextEdit or QPlainTextEdit then \uicontrol Tab,
537 \uicontrol Backtab, \uicontrol Enter and \uicontrol Return keys are \e not
538 handled.
539
540 In the case of \uicontrol Tab, \uicontrol Backtab, \uicontrol Enter and \uicontrol Return
541 key press events, the \a editor's data is committed to the model
542 and the editor is closed. If the \a event is a \uicontrol Tab key press
543 the view will open an editor on the next item in the
544 view. Likewise, if the \a event is a \uicontrol Backtab key press the
545 view will open an editor on the \e previous item in the view.
546
547 If the event is a \uicontrol Esc key press event, the \a editor is
548 closed \e without committing its data.
549
550 \sa commitData(), closeEditor()
551*/
552bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
553{
554 Q_D(QStyledItemDelegate);
555 return d->editorEventFilter(object, event);
556}
557
558/*!
559 \reimp
560*/
561bool QStyledItemDelegate::editorEvent(QEvent *event,
562 QAbstractItemModel *model,
563 const QStyleOptionViewItem &option,
564 const QModelIndex &index)
565{
566 Q_ASSERT(event);
567 Q_ASSERT(model);
568
569 // make sure that the item is checkable
570 Qt::ItemFlags flags = model->flags(index);
571 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
572 || !(flags & Qt::ItemIsEnabled))
573 return false;
574
575 // make sure that we have a check state
576 QVariant value = index.data(arole: Qt::CheckStateRole);
577 if (!value.isValid())
578 return false;
579
580 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
581 QStyle *style = widget ? widget->style() : QApplication::style();
582
583 // make sure that we have the right event type
584 if ((event->type() == QEvent::MouseButtonRelease)
585 || (event->type() == QEvent::MouseButtonDblClick)
586 || (event->type() == QEvent::MouseButtonPress)) {
587 QStyleOptionViewItem viewOpt(option);
588 initStyleOption(option: &viewOpt, index);
589 QRect checkRect = style->subElementRect(subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &viewOpt, widget);
590 QMouseEvent *me = static_cast<QMouseEvent*>(event);
591 if (me->button() != Qt::LeftButton || !checkRect.contains(p: me->position().toPoint()))
592 return false;
593
594 if ((event->type() == QEvent::MouseButtonPress)
595 || (event->type() == QEvent::MouseButtonDblClick))
596 return true;
597
598 } else if (event->type() == QEvent::KeyPress) {
599 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
600 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
601 return false;
602 } else {
603 return false;
604 }
605
606 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: value);
607 if (flags & Qt::ItemIsUserTristate)
608 state = ((Qt::CheckState)((state + 1) % 3));
609 else
610 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
611 return model->setData(index, value: state, role: Qt::CheckStateRole);
612}
613
614QT_END_NAMESPACE
615
616#include "moc_qstyleditemdelegate.cpp"
617

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