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 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | class QStyledItemDelegatePrivate : public QAbstractItemDelegatePrivate |
48 | { |
49 | Q_DECLARE_PUBLIC(QStyledItemDelegate) |
50 | |
51 | public: |
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 | */ |
215 | QStyledItemDelegate::QStyledItemDelegate(QObject *parent) |
216 | : QAbstractItemDelegate(*new QStyledItemDelegatePrivate(), parent) |
217 | { |
218 | } |
219 | |
220 | /*! |
221 | Destroys the item delegate. |
222 | */ |
223 | QStyledItemDelegate::~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 | */ |
240 | QString 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 | */ |
252 | void 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 | */ |
364 | void 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 | */ |
387 | QSize 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 | */ |
408 | QWidget *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 | */ |
428 | void 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 | */ |
450 | void 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 | */ |
469 | void 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 | */ |
501 | QItemEditorFactory *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 | */ |
514 | void 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 | */ |
552 | bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event) |
553 | { |
554 | Q_D(QStyledItemDelegate); |
555 | return d->editorEventFilter(object, event); |
556 | } |
557 | |
558 | /*! |
559 | \reimp |
560 | */ |
561 | bool 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 | |
614 | QT_END_NAMESPACE |
615 | |
616 | #include "moc_qstyleditemdelegate.cpp" |
617 | |