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().
141
142 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp setDefaultFactory
143
144 After the new factory has been set, all standard item delegates
145 will use it (i.e, also delegates that were created before the new
146 default factory was set).
147
148 It is the data stored in the item model with the \l{Qt::}{EditRole}
149 that is edited. See the QItemEditorFactory class for a more
150 high-level introduction to item editor factories.
151
152 \section1 Subclassing QStyledItemDelegate
153
154 If the delegate does not support painting of the data types you
155 need or you want to customize the drawing of items, you need to
156 subclass QStyledItemDelegate, and reimplement paint() and possibly
157 sizeHint(). The paint() function is called individually for each
158 item, and with sizeHint(), you can specify the hint for each
159 of them.
160
161 When reimplementing paint(), one would typically handle the
162 datatypes one would like to draw and use the superclass
163 implementation for other types.
164
165 The painting of check box indicators are performed by the current
166 style. The style also specifies the size and the bounding
167 rectangles in which to draw the data for the different data roles.
168 The bounding rectangle of the item itself is also calculated by
169 the style. When drawing already supported datatypes, it is
170 therefore a good idea to ask the style for these bounding
171 rectangles. The QStyle class description describes this in
172 more detail.
173
174 If you wish to change any of the bounding rectangles calculated by
175 the style or the painting of check box indicators, you can
176 subclass QStyle. Note, however, that the size of the items can
177 also be affected by reimplementing sizeHint().
178
179 It is possible for a custom delegate to provide editors
180 without the use of an editor item factory. In this case, the
181 following virtual functions must be reimplemented:
182
183 \list
184 \li createEditor() returns the widget used to change data from the model
185 and can be reimplemented to customize editing behavior.
186 \li setEditorData() provides the widget with data to manipulate.
187 \li updateEditorGeometry() ensures that the editor is displayed correctly
188 with respect to the item view.
189 \li setModelData() returns updated data to the model.
190 \endlist
191
192 The \l{Star Delegate Example}{Star Delegate} example creates
193 editors by reimplementing these methods.
194
195 \section1 QStyledItemDelegate vs. QItemDelegate
196
197 Since Qt 4.4, there are two delegate classes: QItemDelegate and
198 QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
199 These two classes are independent alternatives to painting and providing
200 editors for items in views. The difference between them is that
201 QStyledItemDelegate uses the current style to paint its items. We therefore
202 recommend using QStyledItemDelegate as the base class when implementing
203 custom delegates or when working with Qt style sheets. The code required
204 for either class should be equal unless the custom delegate needs to use
205 the style for drawing.
206
207 If you wish to customize the painting of item views, you should
208 implement a custom style. Please see the QStyle class
209 documentation for details.
210
211 \sa {Delegate Classes}, QItemDelegate, QAbstractItemDelegate, QStyle,
212 {Star Delegate Example}
213*/
214
215
216/*!
217 Constructs an item delegate with the given \a parent.
218*/
219QStyledItemDelegate::QStyledItemDelegate(QObject *parent)
220 : QAbstractItemDelegate(*new QStyledItemDelegatePrivate(), parent)
221{
222}
223
224/*!
225 Destroys the item delegate.
226*/
227QStyledItemDelegate::~QStyledItemDelegate()
228{
229}
230
231/*!
232 This function returns the string that the delegate will use to display the
233 Qt::DisplayRole of the model in \a locale. \a value is the value of the Qt::DisplayRole
234 provided by the model.
235
236 The default implementation uses the QLocale::toString to convert \a value into
237 a QString.
238
239 This function is not called for empty model indices, i.e., indices for which
240 the model returns an invalid QVariant.
241
242 \sa QAbstractItemModel::data()
243*/
244QString QStyledItemDelegate::displayText(const QVariant &value, const QLocale& locale) const
245{
246 return d_func()->textForRole(role: Qt::DisplayRole, value, locale);
247}
248
249/*!
250 Initialize \a option with the values using the index \a index. This method
251 is useful for subclasses when they need a QStyleOptionViewItem, but don't want
252 to fill in all the information themselves.
253
254 \sa QStyleOption::initFrom()
255*/
256void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option,
257 const QModelIndex &index) const
258{
259 option->index = index;
260
261 Q_D(const QStyledItemDelegate);
262 QModelRoleDataSpan modelRoleDataSpan = d->modelRoleData;
263 index.multiData(roleDataSpan: modelRoleDataSpan);
264
265 const QVariant *value;
266 value = modelRoleDataSpan.dataForRole(role: Qt::FontRole);
267 if (value->isValid() && !value->isNull()) {
268 option->font = qvariant_cast<QFont>(v: *value).resolve(option->font);
269 option->fontMetrics = QFontMetrics(option->font);
270 }
271
272 value = modelRoleDataSpan.dataForRole(role: Qt::TextAlignmentRole);
273 if (value->isValid() && !value->isNull())
274 option->displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(data: *value);
275
276 value = modelRoleDataSpan.dataForRole(role: Qt::ForegroundRole);
277 if (value->canConvert<QBrush>())
278 option->palette.setBrush(acr: QPalette::Text, abrush: qvariant_cast<QBrush>(v: *value));
279
280 value = modelRoleDataSpan.dataForRole(role: Qt::CheckStateRole);
281 if (value->isValid() && !value->isNull()) {
282 option->features |= QStyleOptionViewItem::HasCheckIndicator;
283 option->checkState = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: *value);
284 }
285
286 value = modelRoleDataSpan.dataForRole(role: Qt::DecorationRole);
287 if (value->isValid() && !value->isNull()) {
288 option->features |= QStyleOptionViewItem::HasDecoration;
289 switch (value->userType()) {
290 case QMetaType::QIcon: {
291 option->icon = qvariant_cast<QIcon>(v: *value);
292 if (option->icon.isNull()) {
293 option->features &= ~QStyleOptionViewItem::HasDecoration;
294 break;
295 }
296 QIcon::Mode mode;
297 if (!(option->state & QStyle::State_Enabled))
298 mode = QIcon::Disabled;
299 else if (option->state & QStyle::State_Selected)
300 mode = QIcon::Selected;
301 else
302 mode = QIcon::Normal;
303 QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
304 QSize actualSize = option->icon.actualSize(size: option->decorationSize, mode, state);
305 // For highdpi icons actualSize might be larger than decorationSize, which we don't want. Clamp it to decorationSize.
306 option->decorationSize = QSize(qMin(a: option->decorationSize.width(), b: actualSize.width()),
307 qMin(a: option->decorationSize.height(), b: actualSize.height()));
308 break;
309 }
310 case QMetaType::QColor: {
311 QPixmap pixmap(option->decorationSize);
312 pixmap.fill(fillColor: qvariant_cast<QColor>(v: *value));
313 option->icon = QIcon(pixmap);
314 break;
315 }
316 case QMetaType::QImage: {
317 QImage image = qvariant_cast<QImage>(v: *value);
318 option->icon = QIcon(QPixmap::fromImage(image));
319 option->decorationSize = image.deviceIndependentSize().toSize();
320 break;
321 }
322 case QMetaType::QPixmap: {
323 QPixmap pixmap = qvariant_cast<QPixmap>(v: *value);
324 option->icon = QIcon(pixmap);
325 option->decorationSize = pixmap.deviceIndependentSize().toSize();
326 break;
327 }
328 default:
329 break;
330 }
331 }
332
333 value = modelRoleDataSpan.dataForRole(role: Qt::DisplayRole);
334 if (value->isValid() && !value->isNull()) {
335 option->features |= QStyleOptionViewItem::HasDisplay;
336 option->text = displayText(value: *value, locale: option->locale);
337 }
338
339 value = modelRoleDataSpan.dataForRole(role: Qt::BackgroundRole);
340 option->backgroundBrush = qvariant_cast<QBrush>(v: *value);
341
342 // disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
343 option->styleObject = nullptr;
344}
345
346/*!
347 Renders the delegate using the given \a painter and style \a option for
348 the item specified by \a index.
349
350 This function paints the item using the view's QStyle.
351
352 When reimplementing paint in a subclass. Use the initStyleOption()
353 to set up the \a option in the same way as the
354 QStyledItemDelegate.
355
356 Whenever possible, use the \a option while painting.
357 Especially its \l{QStyleOption::}{rect} variable to decide
358 where to draw and its \l{QStyleOption::}{state} to determine
359 if it is enabled or selected.
360
361 After painting, you should ensure that the painter is returned to
362 the state it was supplied in when this function was called.
363 For example, it may be useful to call QPainter::save() before
364 painting and QPainter::restore() afterwards.
365
366 \sa QItemDelegate::paint(), QStyle::drawControl(), QStyle::CE_ItemViewItem
367*/
368void QStyledItemDelegate::paint(QPainter *painter,
369 const QStyleOptionViewItem &option, const QModelIndex &index) const
370{
371 Q_ASSERT(index.isValid());
372
373 QStyleOptionViewItem opt = option;
374 initStyleOption(option: &opt, index);
375
376 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
377 QStyle *style = widget ? widget->style() : QApplication::style();
378 style->drawControl(element: QStyle::CE_ItemViewItem, opt: &opt, p: painter, w: widget);
379}
380
381/*!
382 Returns the size needed by the delegate to display the item
383 specified by \a index, taking into account the style information
384 provided by \a option.
385
386 This function uses the view's QStyle to determine the size of the
387 item.
388
389 \sa QStyle::sizeFromContents(), QStyle::CT_ItemViewItem
390*/
391QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option,
392 const QModelIndex &index) const
393{
394 QVariant value = index.data(arole: Qt::SizeHintRole);
395 if (value.isValid())
396 return qvariant_cast<QSize>(v: value);
397
398 QStyleOptionViewItem opt = option;
399 initStyleOption(option: &opt, index);
400 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
401 QStyle *style = widget ? widget->style() : QApplication::style();
402 return style->sizeFromContents(ct: QStyle::CT_ItemViewItem, opt: &opt, contentsSize: QSize(), w: widget);
403}
404
405/*!
406 Returns the widget used to edit the item specified by \a index
407 for editing. The \a parent widget and style \a option are used to
408 control how the editor widget appears.
409
410 \sa QAbstractItemDelegate::createEditor()
411*/
412QWidget *QStyledItemDelegate::createEditor(QWidget *parent,
413 const QStyleOptionViewItem &option,
414 const QModelIndex &index) const
415{
416 Q_UNUSED(option);
417 Q_D(const QStyledItemDelegate);
418 if (!index.isValid())
419 return nullptr;
420 return d->editorFactory()->createEditor(userType: index.data(arole: Qt::EditRole).userType(), parent);
421}
422
423/*!
424 Sets the data to be displayed and edited by the \a editor from the
425 data model item specified by the model \a index.
426
427 The default implementation stores the data in the \a editor
428 widget's \l {Qt's Property System} {user property}.
429
430 \sa QMetaProperty::isUser()
431*/
432void QStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
433{
434 QVariant v = index.data(arole: Qt::EditRole);
435 QByteArray n = editor->metaObject()->userProperty().name();
436
437 if (!n.isEmpty()) {
438 if (!v.isValid())
439 v = QVariant(editor->property(name: n).metaType());
440 editor->setProperty(name: n, value: v);
441 }
442}
443
444/*!
445 Gets data from the \a editor widget and stores it in the specified
446 \a model at the item \a index.
447
448 The default implementation gets the value to be stored in the data
449 model from the \a editor widget's \l {Qt's Property System} {user
450 property}.
451
452 \sa QMetaProperty::isUser()
453*/
454void QStyledItemDelegate::setModelData(QWidget *editor,
455 QAbstractItemModel *model,
456 const QModelIndex &index) const
457{
458 Q_D(const QStyledItemDelegate);
459 Q_ASSERT(model);
460 Q_ASSERT(editor);
461 QByteArray n = editor->metaObject()->userProperty().name();
462 if (n.isEmpty())
463 n = d->editorFactory()->valuePropertyName(
464 userType: model->data(index, role: Qt::EditRole).userType());
465 if (!n.isEmpty())
466 model->setData(index, value: editor->property(name: n), role: Qt::EditRole);
467}
468
469/*!
470 Updates the \a editor for the item specified by \a index
471 according to the style \a option given.
472*/
473void QStyledItemDelegate::updateEditorGeometry(QWidget *editor,
474 const QStyleOptionViewItem &option,
475 const QModelIndex &index) const
476{
477 if (!editor)
478 return;
479 Q_ASSERT(index.isValid());
480 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
481
482 QStyleOptionViewItem opt = option;
483 initStyleOption(option: &opt, index);
484 opt.showDecorationSelected = editor->style()->styleHint(stylehint: QStyle::SH_ItemView_ShowDecorationSelected, opt: nullptr, widget: editor);
485
486 QStyle *style = widget ? widget->style() : QApplication::style();
487 QRect geom = style->subElementRect(subElement: QStyle::SE_ItemViewItemText, option: &opt, widget);
488 editor->setGeometry(geom);
489}
490
491/*!
492 Returns the editor factory used by the item delegate.
493 If no editor factory is set, the function will return null.
494
495 \sa setItemEditorFactory()
496*/
497QItemEditorFactory *QStyledItemDelegate::itemEditorFactory() const
498{
499 Q_D(const QStyledItemDelegate);
500 return d->factory;
501}
502
503/*!
504 Sets the editor factory to be used by the item delegate to be the \a factory
505 specified. If no editor factory is set, the item delegate will use the
506 default editor factory.
507
508 \sa itemEditorFactory()
509*/
510void QStyledItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
511{
512 Q_D(QStyledItemDelegate);
513 d->factory = factory;
514}
515
516
517/*!
518 \fn bool QStyledItemDelegate::eventFilter(QObject *editor, QEvent *event)
519
520 Returns \c true if the given \a editor is a valid QWidget and the
521 given \a event is handled; otherwise returns \c false. The following
522 key press events are handled by default:
523
524 \list
525 \li \uicontrol Tab
526 \li \uicontrol Backtab
527 \li \uicontrol Enter
528 \li \uicontrol Return
529 \li \uicontrol Esc
530 \endlist
531
532 If the \a editor's type is QTextEdit or QPlainTextEdit then \uicontrol Tab,
533 \uicontrol Backtab, \uicontrol Enter and \uicontrol Return keys are \e not
534 handled.
535
536 In the case of \uicontrol Tab, \uicontrol Backtab, \uicontrol Enter and \uicontrol Return
537 key press events, the \a editor's data is committed to the model
538 and the editor is closed. If the \a event is a \uicontrol Tab key press
539 the view will open an editor on the next item in the
540 view. Likewise, if the \a event is a \uicontrol Backtab key press the
541 view will open an editor on the \e previous item in the view.
542
543 If the event is a \uicontrol Esc key press event, the \a editor is
544 closed \e without committing its data.
545
546 \sa commitData(), closeEditor()
547*/
548bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event)
549{
550 Q_D(QStyledItemDelegate);
551 return d->editorEventFilter(object, event);
552}
553
554/*!
555 \reimp
556*/
557bool QStyledItemDelegate::editorEvent(QEvent *event,
558 QAbstractItemModel *model,
559 const QStyleOptionViewItem &option,
560 const QModelIndex &index)
561{
562 Q_ASSERT(event);
563 Q_ASSERT(model);
564
565 // make sure that the item is checkable
566 Qt::ItemFlags flags = model->flags(index);
567 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
568 || !(flags & Qt::ItemIsEnabled))
569 return false;
570
571 // make sure that we have a check state
572 QVariant value = index.data(arole: Qt::CheckStateRole);
573 if (!value.isValid())
574 return false;
575
576 const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
577 QStyle *style = widget ? widget->style() : QApplication::style();
578
579 // make sure that we have the right event type
580 if ((event->type() == QEvent::MouseButtonRelease)
581 || (event->type() == QEvent::MouseButtonDblClick)
582 || (event->type() == QEvent::MouseButtonPress)) {
583 QStyleOptionViewItem viewOpt(option);
584 initStyleOption(option: &viewOpt, index);
585 QRect checkRect = style->subElementRect(subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &viewOpt, widget);
586 QMouseEvent *me = static_cast<QMouseEvent*>(event);
587 if (me->button() != Qt::LeftButton || !checkRect.contains(p: me->position().toPoint()))
588 return false;
589
590 if ((event->type() == QEvent::MouseButtonPress)
591 || (event->type() == QEvent::MouseButtonDblClick))
592 return true;
593
594 } else if (event->type() == QEvent::KeyPress) {
595 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
596 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
597 return false;
598 } else {
599 return false;
600 }
601
602 Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: value);
603 if (flags & Qt::ItemIsUserTristate)
604 state = ((Qt::CheckState)((state + 1) % 3));
605 else
606 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
607 return model->setData(index, value: state, role: Qt::CheckStateRole);
608}
609
610QT_END_NAMESPACE
611
612#include "moc_qstyleditemdelegate.cpp"
613

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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