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 "qitemdelegate.h" |
5 | |
6 | #include <qabstractitemmodel.h> |
7 | #include <qapplication.h> |
8 | #include <qbrush.h> |
9 | #include <qpainter.h> |
10 | #include <qpalette.h> |
11 | #include <qpoint.h> |
12 | #include <qrect.h> |
13 | #include <qsize.h> |
14 | #include <qstyle.h> |
15 | #include <qdatetime.h> |
16 | #include <qstyleoption.h> |
17 | #include <qevent.h> |
18 | #include <qpixmap.h> |
19 | #include <qbitmap.h> |
20 | #include <qpixmapcache.h> |
21 | #include <qitemeditorfactory.h> |
22 | #include <qmetaobject.h> |
23 | #include <qtextlayout.h> |
24 | #include <private/qabstractitemdelegate_p.h> |
25 | #include <private/qabstractitemmodel_p.h> |
26 | #include <private/qtextengine_p.h> |
27 | #include <qdebug.h> |
28 | #include <qlocale.h> |
29 | #include <qmath.h> |
30 | |
31 | #include <limits.h> |
32 | |
33 | // keep in sync with QAbstractItemDelegate::helpEvent() |
34 | #ifndef DBL_DIG |
35 | # define DBL_DIG 10 |
36 | #endif |
37 | |
38 | QT_BEGIN_NAMESPACE |
39 | |
40 | class QItemDelegatePrivate : public QAbstractItemDelegatePrivate |
41 | { |
42 | Q_DECLARE_PUBLIC(QItemDelegate) |
43 | |
44 | public: |
45 | QItemDelegatePrivate() : f(nullptr), clipPainting(true) {} |
46 | |
47 | inline const QItemEditorFactory *editorFactory() const |
48 | { return f ? f : QItemEditorFactory::defaultFactory(); } |
49 | |
50 | inline QIcon::Mode iconMode(QStyle::State state) const |
51 | { |
52 | if (!(state & QStyle::State_Enabled)) return QIcon::Disabled; |
53 | if (state & QStyle::State_Selected) return QIcon::Selected; |
54 | return QIcon::Normal; |
55 | } |
56 | |
57 | inline QIcon::State iconState(QStyle::State state) const |
58 | { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; } |
59 | |
60 | inline static QString replaceNewLine(QString text) |
61 | { |
62 | text.replace(before: u'\n', after: QChar::LineSeparator); |
63 | return text; |
64 | } |
65 | |
66 | QString valueToText(const QVariant &value, const QStyleOptionViewItem &option) const; |
67 | |
68 | QItemEditorFactory *f; |
69 | bool clipPainting; |
70 | |
71 | QRect displayRect(const QModelIndex &index, const QStyleOptionViewItem &option, |
72 | const QRect &decorationRect, const QRect &checkRect) const; |
73 | QRect textLayoutBounds(const QStyleOptionViewItem &option, |
74 | const QRect &decorationRect, const QRect &checkRect) const; |
75 | QSizeF doTextLayout(int lineWidth) const; |
76 | mutable QTextLayout textLayout; |
77 | mutable QTextOption textOption; |
78 | |
79 | const QWidget *widget(const QStyleOptionViewItem &option) const |
80 | { |
81 | return option.widget; |
82 | } |
83 | |
84 | // ### temporary hack until we have QStandardItemDelegate |
85 | mutable struct Icon { |
86 | QIcon icon; |
87 | QIcon::Mode mode; |
88 | QIcon::State state; |
89 | } tmp; |
90 | }; |
91 | |
92 | QRect QItemDelegatePrivate::displayRect(const QModelIndex &index, const QStyleOptionViewItem &option, |
93 | const QRect &decorationRect, const QRect &checkRect) const |
94 | { |
95 | Q_Q(const QItemDelegate); |
96 | const QVariant value = index.data(arole: Qt::DisplayRole); |
97 | if (!value.isValid() || value.isNull()) |
98 | return QRect(); |
99 | |
100 | const QString text = valueToText(value, option); |
101 | const QVariant fontVal = index.data(arole: Qt::FontRole); |
102 | const QFont fnt = qvariant_cast<QFont>(v: fontVal).resolve(option.font); |
103 | return q->textRectangle(painter: nullptr, |
104 | rect: textLayoutBounds(option, decorationRect, checkRect), |
105 | font: fnt, text); |
106 | } |
107 | |
108 | // similar to QCommonStylePrivate::viewItemSize(Qt::DisplayRole) |
109 | QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItem &option, |
110 | const QRect &decorationRect, const QRect &checkRect) const |
111 | { |
112 | QRect rect = option.rect; |
113 | const QWidget *w = widget(option); |
114 | QStyle *style = w ? w->style() : QApplication::style(); |
115 | const bool wrapText = option.features & QStyleOptionViewItem::WrapText; |
116 | // see QItemDelegate::drawDisplay |
117 | const int textMargin = style->pixelMetric(metric: QStyle::PM_FocusFrameHMargin, option: nullptr, widget: w) + 1; |
118 | switch (option.decorationPosition) { |
119 | case QStyleOptionViewItem::Left: |
120 | case QStyleOptionViewItem::Right: |
121 | rect.setWidth(wrapText && rect.isValid() ? rect.width() - 2 * textMargin : (QFIXED_MAX)); |
122 | break; |
123 | case QStyleOptionViewItem::Top: |
124 | case QStyleOptionViewItem::Bottom: |
125 | rect.setWidth(wrapText ? option.decorationSize.width() - 2 * textMargin : (QFIXED_MAX)); |
126 | break; |
127 | } |
128 | |
129 | if (wrapText) { |
130 | if (!decorationRect.isNull()) |
131 | rect.setWidth(rect.width() - decorationRect.width() - 2 * textMargin); |
132 | if (!checkRect.isNull()) |
133 | rect.setWidth(rect.width() - checkRect.width() - 2 * textMargin); |
134 | // adjust height to be sure that the text fits |
135 | const QSizeF size = doTextLayout(lineWidth: rect.width()); |
136 | rect.setHeight(qCeil(v: size.height())); |
137 | } |
138 | |
139 | return rect; |
140 | } |
141 | |
142 | QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const |
143 | { |
144 | qreal height = 0; |
145 | qreal widthUsed = 0; |
146 | textLayout.beginLayout(); |
147 | while (true) { |
148 | QTextLine line = textLayout.createLine(); |
149 | if (!line.isValid()) |
150 | break; |
151 | line.setLineWidth(lineWidth); |
152 | line.setPosition(QPointF(0, height)); |
153 | height += line.height(); |
154 | widthUsed = qMax(a: widthUsed, b: line.naturalTextWidth()); |
155 | } |
156 | textLayout.endLayout(); |
157 | return QSizeF(widthUsed, height); |
158 | } |
159 | |
160 | /*! |
161 | \class QItemDelegate |
162 | |
163 | \brief The QItemDelegate class provides display and editing facilities for |
164 | data items from a model. |
165 | |
166 | \ingroup model-view |
167 | \inmodule QtWidgets |
168 | |
169 | QItemDelegate can be used to provide custom display features and editor |
170 | widgets for item views based on QAbstractItemView subclasses. Using a |
171 | delegate for this purpose allows the display and editing mechanisms to be |
172 | customized and developed independently from the model and view. |
173 | |
174 | The QItemDelegate class is one of the \l{Model/View Classes} and |
175 | is part of Qt's \l{Model/View Programming}{model/view framework}. |
176 | Note that QStyledItemDelegate has taken over the job of drawing |
177 | Qt's item views. We recommend the use of QStyledItemDelegate when |
178 | creating new delegates. |
179 | |
180 | When displaying items from a custom model in a standard view, it is |
181 | often sufficient to simply ensure that the model returns appropriate |
182 | data for each of the \l{Qt::ItemDataRole}{roles} that determine the |
183 | appearance of items in views. The default delegate used by Qt's |
184 | standard views uses this role information to display items in most |
185 | of the common forms expected by users. However, it is sometimes |
186 | necessary to have even more control over the appearance of items than |
187 | the default delegate can provide. |
188 | |
189 | This class provides default implementations of the functions for |
190 | painting item data in a view and editing data from item models. |
191 | Default implementations of the paint() and sizeHint() virtual |
192 | functions, defined in QAbstractItemDelegate, are provided to |
193 | ensure that the delegate implements the correct basic behavior |
194 | expected by views. You can reimplement these functions in |
195 | subclasses to customize the appearance of items. |
196 | |
197 | When editing data in an item view, QItemDelegate provides an |
198 | editor widget, which is a widget that is placed on top of the view |
199 | while editing takes place. Editors are created with a |
200 | QItemEditorFactory; a default static instance provided by |
201 | QItemEditorFactory is installed on all item delegates. You can set |
202 | a custom factory using setItemEditorFactory() or set a new default |
203 | factory with QItemEditorFactory::setDefaultFactory(). It is the |
204 | data stored in the item model with the Qt::EditRole that is edited. |
205 | |
206 | Only the standard editing functions for widget-based delegates are |
207 | reimplemented here: |
208 | |
209 | \list |
210 | \li createEditor() returns the widget used to change data from the model |
211 | and can be reimplemented to customize editing behavior. |
212 | \li setEditorData() provides the widget with data to manipulate. |
213 | \li updateEditorGeometry() ensures that the editor is displayed correctly |
214 | with respect to the item view. |
215 | \li setModelData() returns updated data to the model. |
216 | \endlist |
217 | |
218 | The closeEditor() signal indicates that the user has completed editing the data, |
219 | and that the editor widget can be destroyed. |
220 | |
221 | \section1 Standard Roles and Data Types |
222 | |
223 | The default delegate used by the standard views supplied with Qt |
224 | associates each standard role (defined by Qt::ItemDataRole) with certain |
225 | data types. Models that return data in these types can influence the |
226 | appearance of the delegate as described in the following table. |
227 | |
228 | \table |
229 | \header \li Role \li Accepted Types |
230 | \omit |
231 | \row \li \l Qt::AccessibleDescriptionRole \li QString |
232 | \row \li \l Qt::AccessibleTextRole \li QString |
233 | \endomit |
234 | \row \li \l Qt::BackgroundRole \li QBrush (\since 4.2) |
235 | \row \li \l Qt::CheckStateRole \li Qt::CheckState |
236 | \row \li \l Qt::DecorationRole \li QIcon, QPixmap and QColor |
237 | \row \li \l Qt::DisplayRole \li QString and types with a string representation |
238 | \row \li \l Qt::EditRole \li See QItemEditorFactory for details |
239 | \row \li \l Qt::FontRole \li QFont |
240 | \row \li \l Qt::SizeHintRole \li QSize |
241 | \omit |
242 | \row \li \l Qt::StatusTipRole \li |
243 | \endomit |
244 | \row \li \l Qt::TextAlignmentRole \li Qt::Alignment |
245 | \row \li \l Qt::ForegroundRole \li QBrush (\since 4.2) |
246 | \omit |
247 | \row \li \l Qt::ToolTipRole |
248 | \row \li \l Qt::WhatsThisRole |
249 | \endomit |
250 | \endtable |
251 | |
252 | If the default delegate does not allow the level of customization that |
253 | you need, either for display purposes or for editing data, it is possible to |
254 | subclass QItemDelegate to implement the desired behavior. |
255 | |
256 | \section1 Subclassing |
257 | |
258 | When subclassing QItemDelegate to create a delegate that displays items |
259 | using a custom renderer, it is important to ensure that the delegate can |
260 | render items suitably for all the required states; such as selected, |
261 | disabled, checked. The documentation for the paint() function contains |
262 | some hints to show how this can be achieved. |
263 | |
264 | You can provide custom editors by using a QItemEditorFactory. The following |
265 | code shows how a custom editor can be made available to delegates with the |
266 | default item editor factory. |
267 | |
268 | \snippet code/src_gui_itemviews_qitemeditorfactory.cpp setDefaultFactory |
269 | |
270 | After the default factory has been set, all standard item delegates |
271 | will use it (also the delegates that were created before setting the |
272 | default factory). |
273 | |
274 | This way, you can avoid subclassing QItemDelegate, and all values of the |
275 | specified type (for example QMetaType::QDateTime) will be edited using the |
276 | provided editor (like \c{MyFancyDateTimeEdit} in the above example). |
277 | |
278 | An alternative is to reimplement createEditor(), setEditorData(), |
279 | setModelData(), and updateEditorGeometry(). This process is described |
280 | in the \l{A simple delegate}{Model/View Programming overview documentation}. |
281 | |
282 | \section1 QStyledItemDelegate vs. QItemDelegate |
283 | |
284 | Since Qt 4.4, there are two delegate classes: QItemDelegate and |
285 | QStyledItemDelegate. However, the default delegate is QStyledItemDelegate. |
286 | These two classes are independent alternatives to painting and providing |
287 | editors for items in views. The difference between them is that |
288 | QStyledItemDelegate uses the current style to paint its items. We therefore |
289 | recommend using QStyledItemDelegate as the base class when implementing |
290 | custom delegates or when working with Qt style sheets. The code required |
291 | for either class should be equal unless the custom delegate needs to use |
292 | the style for drawing. |
293 | |
294 | \sa {Delegate Classes}, QStyledItemDelegate, QAbstractItemDelegate |
295 | */ |
296 | |
297 | /*! |
298 | Constructs an item delegate with the given \a parent. |
299 | */ |
300 | |
301 | QItemDelegate::QItemDelegate(QObject *parent) |
302 | : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent) |
303 | { |
304 | |
305 | } |
306 | |
307 | /*! |
308 | Destroys the item delegate. |
309 | */ |
310 | |
311 | QItemDelegate::~QItemDelegate() |
312 | { |
313 | } |
314 | |
315 | /*! |
316 | \property QItemDelegate::clipping |
317 | \brief if the delegate should clip the paint events |
318 | \since 4.2 |
319 | |
320 | This property will set the paint clip to the size of the item. |
321 | The default value is on. It is useful for cases such |
322 | as when images are larger than the size of the item. |
323 | */ |
324 | |
325 | bool QItemDelegate::hasClipping() const |
326 | { |
327 | Q_D(const QItemDelegate); |
328 | return d->clipPainting; |
329 | } |
330 | |
331 | void QItemDelegate::setClipping(bool clip) |
332 | { |
333 | Q_D(QItemDelegate); |
334 | d->clipPainting = clip; |
335 | } |
336 | |
337 | QString QItemDelegatePrivate::valueToText(const QVariant &value, const QStyleOptionViewItem &option) const |
338 | { |
339 | return textForRole(role: Qt::DisplayRole, value, locale: option.locale, DBL_DIG); |
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 | When reimplementing this function in a subclass, you should update the area |
347 | held by the option's \l{QStyleOption::rect}{rect} variable, using the |
348 | option's \l{QStyleOption::state}{state} variable to determine the state of |
349 | the item to be displayed, and adjust the way it is painted accordingly. |
350 | |
351 | For example, a selected item may need to be displayed differently to |
352 | unselected items, as shown in the following code: |
353 | |
354 | \code |
355 | if (option.state & QStyle::State_Selected) |
356 | painter->fillRect(option.rect, option.palette.highlight()); |
357 | \endcode |
358 | |
359 | After painting, you should ensure that the painter is returned to its |
360 | the state it was supplied in when this function was called. For example, |
361 | it may be useful to call QPainter::save() before painting and |
362 | QPainter::restore() afterwards. |
363 | |
364 | \sa QStyle::State |
365 | */ |
366 | void QItemDelegate::paint(QPainter *painter, |
367 | const QStyleOptionViewItem &option, |
368 | const QModelIndex &index) const |
369 | { |
370 | Q_D(const QItemDelegate); |
371 | Q_ASSERT(index.isValid()); |
372 | |
373 | QStyleOptionViewItem opt = setOptions(index, option); |
374 | |
375 | // prepare |
376 | painter->save(); |
377 | if (d->clipPainting) |
378 | painter->setClipRect(opt.rect); |
379 | |
380 | // get the data and the rectangles |
381 | |
382 | QVariant value; |
383 | |
384 | QPixmap pixmap; |
385 | QRect decorationRect; |
386 | value = index.data(arole: Qt::DecorationRole); |
387 | if (value.isValid()) { |
388 | // ### we need the pixmap to call the virtual function |
389 | pixmap = decoration(option: opt, variant: value); |
390 | if (value.userType() == QMetaType::QIcon) { |
391 | d->tmp.icon = qvariant_cast<QIcon>(v: value); |
392 | d->tmp.mode = d->iconMode(state: option.state); |
393 | d->tmp.state = d->iconState(state: option.state); |
394 | const QSize size = d->tmp.icon.actualSize(size: option.decorationSize, |
395 | mode: d->tmp.mode, state: d->tmp.state); |
396 | decorationRect = QRect(QPoint(0, 0), size); |
397 | } else { |
398 | d->tmp.icon = QIcon(); |
399 | decorationRect = QRect(QPoint(0, 0), pixmap.size()); |
400 | } |
401 | } else { |
402 | d->tmp.icon = QIcon(); |
403 | decorationRect = QRect(); |
404 | } |
405 | |
406 | QRect checkRect; |
407 | Qt::CheckState checkState = Qt::Unchecked; |
408 | value = index.data(arole: Qt::CheckStateRole); |
409 | if (value.isValid()) { |
410 | checkState = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: value); |
411 | checkRect = doCheck(option: opt, bounding: opt.rect, variant: value); |
412 | } |
413 | |
414 | QString text; |
415 | QRect displayRect; |
416 | value = index.data(arole: Qt::DisplayRole); |
417 | if (value.isValid() && !value.isNull()) { |
418 | text = d->valueToText(value, option: opt); |
419 | displayRect = d->displayRect(index, option: opt, decorationRect, checkRect); |
420 | } |
421 | |
422 | // do the layout |
423 | |
424 | doLayout(option: opt, checkRect: &checkRect, iconRect: &decorationRect, textRect: &displayRect, hint: false); |
425 | |
426 | // draw the item |
427 | |
428 | drawBackground(painter, option: opt, index); |
429 | drawCheck(painter, option: opt, rect: checkRect, state: checkState); |
430 | drawDecoration(painter, option: opt, rect: decorationRect, pixmap); |
431 | drawDisplay(painter, option: opt, rect: displayRect, text); |
432 | drawFocus(painter, option: opt, rect: displayRect); |
433 | |
434 | // done |
435 | painter->restore(); |
436 | } |
437 | |
438 | /*! |
439 | Returns the size needed by the delegate to display the item |
440 | specified by \a index, taking into account the style information |
441 | provided by \a option. |
442 | |
443 | When reimplementing this function, note that in case of text |
444 | items, QItemDelegate adds a margin (i.e. 2 * |
445 | QStyle::PM_FocusFrameHMargin) to the length of the text. |
446 | */ |
447 | |
448 | QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option, |
449 | const QModelIndex &index) const |
450 | { |
451 | Q_D(const QItemDelegate); |
452 | QVariant value = index.data(arole: Qt::SizeHintRole); |
453 | if (value.isValid()) |
454 | return qvariant_cast<QSize>(v: value); |
455 | QRect decorationRect = rect(option, index, role: Qt::DecorationRole); |
456 | QRect checkRect = rect(option, index, role: Qt::CheckStateRole); |
457 | QRect displayRect = d->displayRect(index, option, decorationRect, checkRect); |
458 | |
459 | doLayout(option, checkRect: &checkRect, iconRect: &decorationRect, textRect: &displayRect, hint: true); |
460 | |
461 | return (decorationRect|displayRect|checkRect).size(); |
462 | } |
463 | |
464 | /*! |
465 | Returns the widget used to edit the item specified by \a index |
466 | for editing. The \a parent widget and style \a option are used to |
467 | control how the editor widget appears. |
468 | |
469 | \sa QAbstractItemDelegate::createEditor() |
470 | */ |
471 | |
472 | QWidget *QItemDelegate::createEditor(QWidget *parent, |
473 | const QStyleOptionViewItem &, |
474 | const QModelIndex &index) const |
475 | { |
476 | Q_D(const QItemDelegate); |
477 | if (!index.isValid()) |
478 | return nullptr; |
479 | const QItemEditorFactory *factory = d->f; |
480 | if (factory == nullptr) |
481 | factory = QItemEditorFactory::defaultFactory(); |
482 | QWidget *w = factory->createEditor(userType: index.data(arole: Qt::EditRole).userType(), parent); |
483 | if (w) |
484 | w->setFocusPolicy(Qt::WheelFocus); |
485 | return w; |
486 | } |
487 | |
488 | /*! |
489 | Sets the data to be displayed and edited by the \a editor from the |
490 | data model item specified by the model \a index. |
491 | |
492 | The default implementation stores the data in the \a editor |
493 | widget's \l {Qt's Property System} {user property}. |
494 | |
495 | \sa QMetaProperty::isUser() |
496 | */ |
497 | |
498 | void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const |
499 | { |
500 | QVariant v = index.data(arole: Qt::EditRole); |
501 | QByteArray n = editor->metaObject()->userProperty().name(); |
502 | |
503 | if (!n.isEmpty()) { |
504 | if (!v.isValid()) |
505 | v = QVariant(editor->property(name: n).metaType()); |
506 | editor->setProperty(name: n, value: v); |
507 | } |
508 | } |
509 | |
510 | /*! |
511 | Gets data from the \a editor widget and stores it in the specified |
512 | \a model at the item \a index. |
513 | |
514 | The default implementation gets the value to be stored in the data |
515 | model from the \a editor widget's \l {Qt's Property System} {user |
516 | property}. |
517 | |
518 | \sa QMetaProperty::isUser() |
519 | */ |
520 | |
521 | void QItemDelegate::setModelData(QWidget *editor, |
522 | QAbstractItemModel *model, |
523 | const QModelIndex &index) const |
524 | { |
525 | Q_D(const QItemDelegate); |
526 | Q_ASSERT(model); |
527 | Q_ASSERT(editor); |
528 | QByteArray n = editor->metaObject()->userProperty().name(); |
529 | if (n.isEmpty()) |
530 | n = d->editorFactory()->valuePropertyName( |
531 | userType: model->data(index, role: Qt::EditRole).userType()); |
532 | if (!n.isEmpty()) |
533 | model->setData(index, value: editor->property(name: n), role: Qt::EditRole); |
534 | } |
535 | |
536 | /*! |
537 | Updates the \a editor for the item specified by \a index |
538 | according to the style \a option given. |
539 | */ |
540 | |
541 | void QItemDelegate::updateEditorGeometry(QWidget *editor, |
542 | const QStyleOptionViewItem &option, |
543 | const QModelIndex &index) const |
544 | { |
545 | if (!editor) |
546 | return; |
547 | Q_ASSERT(index.isValid()); |
548 | QPixmap pixmap = decoration(option, variant: index.data(arole: Qt::DecorationRole)); |
549 | QString text = QItemDelegatePrivate::replaceNewLine(text: index.data(arole: Qt::DisplayRole).toString()); |
550 | QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(other: pixmap.rect()); |
551 | QRect textRect = textRectangle(painter: nullptr, rect: option.rect, font: option.font, text); |
552 | QRect checkRect = doCheck(option, bounding: textRect, variant: index.data(arole: Qt::CheckStateRole)); |
553 | QStyleOptionViewItem opt = option; |
554 | opt.showDecorationSelected = true; // let the editor take up all available space |
555 | doLayout(option: opt, checkRect: &checkRect, iconRect: &pixmapRect, textRect: &textRect, hint: false); |
556 | editor->setGeometry(textRect); |
557 | } |
558 | |
559 | /*! |
560 | Returns the editor factory used by the item delegate. |
561 | If no editor factory is set, the function will return null. |
562 | |
563 | \sa setItemEditorFactory() |
564 | */ |
565 | QItemEditorFactory *QItemDelegate::itemEditorFactory() const |
566 | { |
567 | Q_D(const QItemDelegate); |
568 | return d->f; |
569 | } |
570 | |
571 | /*! |
572 | Sets the editor factory to be used by the item delegate to be the \a factory |
573 | specified. If no editor factory is set, the item delegate will use the |
574 | default editor factory. |
575 | |
576 | \sa itemEditorFactory() |
577 | */ |
578 | void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory) |
579 | { |
580 | Q_D(QItemDelegate); |
581 | d->f = factory; |
582 | } |
583 | |
584 | /*! |
585 | Renders the item view \a text within the rectangle specified by \a rect |
586 | using the given \a painter and style \a option. |
587 | */ |
588 | |
589 | void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, |
590 | const QRect &rect, const QString &text) const |
591 | { |
592 | Q_D(const QItemDelegate); |
593 | |
594 | QPalette::ColorGroup cg = option.state & QStyle::State_Enabled |
595 | ? QPalette::Normal : QPalette::Disabled; |
596 | if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) |
597 | cg = QPalette::Inactive; |
598 | if (option.state & QStyle::State_Selected) { |
599 | painter->fillRect(rect, option.palette.brush(cg, cr: QPalette::Highlight)); |
600 | painter->setPen(option.palette.color(cg, cr: QPalette::HighlightedText)); |
601 | } else { |
602 | painter->setPen(option.palette.color(cg, cr: QPalette::Text)); |
603 | } |
604 | |
605 | if (text.isEmpty()) |
606 | return; |
607 | |
608 | if (option.state & QStyle::State_Editing) { |
609 | painter->save(); |
610 | painter->setPen(option.palette.color(cg, cr: QPalette::Text)); |
611 | painter->drawRect(r: rect.adjusted(xp1: 0, yp1: 0, xp2: -1, yp2: -1)); |
612 | painter->restore(); |
613 | } |
614 | |
615 | const QStyleOptionViewItem opt = option; |
616 | |
617 | const QWidget *widget = d->widget(option); |
618 | QStyle *style = widget ? widget->style() : QApplication::style(); |
619 | const int textMargin = style->pixelMetric(metric: QStyle::PM_FocusFrameHMargin, option: nullptr, widget) + 1; |
620 | QRect textRect = rect.adjusted(xp1: textMargin, yp1: 0, xp2: -textMargin, yp2: 0); // remove width padding |
621 | const bool wrapText = opt.features & QStyleOptionViewItem::WrapText; |
622 | d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap); |
623 | d->textOption.setTextDirection(option.direction); |
624 | d->textOption.setAlignment(QStyle::visualAlignment(direction: option.direction, alignment: option.displayAlignment)); |
625 | d->textLayout.setTextOption(d->textOption); |
626 | d->textLayout.setFont(option.font); |
627 | d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text)); |
628 | |
629 | QSizeF textLayoutSize = d->doTextLayout(lineWidth: textRect.width()); |
630 | |
631 | if (textRect.width() < textLayoutSize.width() |
632 | || textRect.height() < textLayoutSize.height()) { |
633 | QString elided; |
634 | int start = 0; |
635 | int end = text.indexOf(ch: QChar::LineSeparator, from: start); |
636 | if (end == -1) { |
637 | elided += option.fontMetrics.elidedText(text, mode: option.textElideMode, width: textRect.width()); |
638 | } else { |
639 | while (end != -1) { |
640 | elided += option.fontMetrics.elidedText(text: text.mid(position: start, n: end - start), |
641 | mode: option.textElideMode, width: textRect.width()); |
642 | elided += QChar::LineSeparator; |
643 | start = end + 1; |
644 | end = text.indexOf(ch: QChar::LineSeparator, from: start); |
645 | } |
646 | //let's add the last line (after the last QChar::LineSeparator) |
647 | elided += option.fontMetrics.elidedText(text: text.mid(position: start), |
648 | mode: option.textElideMode, width: textRect.width()); |
649 | } |
650 | d->textLayout.setText(elided); |
651 | textLayoutSize = d->doTextLayout(lineWidth: textRect.width()); |
652 | } |
653 | |
654 | const QSize layoutSize(textRect.width(), int(textLayoutSize.height())); |
655 | const QRect layoutRect = QStyle::alignedRect(direction: option.direction, alignment: option.displayAlignment, |
656 | size: layoutSize, rectangle: textRect); |
657 | // if we still overflow even after eliding the text, enable clipping |
658 | if (!hasClipping() && (textRect.width() < textLayoutSize.width() |
659 | || textRect.height() < textLayoutSize.height())) { |
660 | painter->save(); |
661 | painter->setClipRect(layoutRect); |
662 | d->textLayout.draw(p: painter, pos: layoutRect.topLeft(), selections: QList<QTextLayout::FormatRange>(), |
663 | clip: layoutRect); |
664 | painter->restore(); |
665 | } else { |
666 | d->textLayout.draw(p: painter, pos: layoutRect.topLeft(), selections: QList<QTextLayout::FormatRange>(), |
667 | clip: layoutRect); |
668 | } |
669 | } |
670 | |
671 | /*! |
672 | Renders the decoration \a pixmap within the rectangle specified by |
673 | \a rect using the given \a painter and style \a option. |
674 | */ |
675 | void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, |
676 | const QRect &rect, const QPixmap &pixmap) const |
677 | { |
678 | Q_D(const QItemDelegate); |
679 | // if we have an icon, we ignore the pixmap |
680 | if (!d->tmp.icon.isNull()) { |
681 | d->tmp.icon.paint(painter, rect, alignment: option.decorationAlignment, |
682 | mode: d->tmp.mode, state: d->tmp.state); |
683 | return; |
684 | } |
685 | |
686 | if (pixmap.isNull() || !rect.isValid()) |
687 | return; |
688 | QPoint p = QStyle::alignedRect(direction: option.direction, alignment: option.decorationAlignment, |
689 | size: pixmap.size(), rectangle: rect).topLeft(); |
690 | if (option.state & QStyle::State_Selected) { |
691 | const QPixmap pm = selectedPixmap(pixmap, palette: option.palette, enabled: option.state & QStyle::State_Enabled); |
692 | painter->drawPixmap(p, pm); |
693 | } else { |
694 | painter->drawPixmap(p, pm: pixmap); |
695 | } |
696 | } |
697 | |
698 | /*! |
699 | Renders the region within the rectangle specified by \a rect, indicating |
700 | that it has the focus, using the given \a painter and style \a option. |
701 | */ |
702 | |
703 | void QItemDelegate::drawFocus(QPainter *painter, |
704 | const QStyleOptionViewItem &option, |
705 | const QRect &rect) const |
706 | { |
707 | Q_D(const QItemDelegate); |
708 | if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid()) |
709 | return; |
710 | QStyleOptionFocusRect o; |
711 | o.QStyleOption::operator=(other: option); |
712 | o.rect = rect; |
713 | o.state |= QStyle::State_KeyboardFocusChange; |
714 | o.state |= QStyle::State_Item; |
715 | QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) |
716 | ? QPalette::Normal : QPalette::Disabled; |
717 | o.backgroundColor = option.palette.color(cg, cr: (option.state & QStyle::State_Selected) |
718 | ? QPalette::Highlight : QPalette::Window); |
719 | const QWidget *widget = d->widget(option); |
720 | QStyle *style = widget ? widget->style() : QApplication::style(); |
721 | style->drawPrimitive(pe: QStyle::PE_FrameFocusRect, opt: &o, p: painter, w: widget); |
722 | } |
723 | |
724 | /*! |
725 | Renders a check indicator within the rectangle specified by \a |
726 | rect, using the given \a painter and style \a option, using the |
727 | given \a state. |
728 | */ |
729 | |
730 | void QItemDelegate::drawCheck(QPainter *painter, |
731 | const QStyleOptionViewItem &option, |
732 | const QRect &rect, Qt::CheckState state) const |
733 | { |
734 | Q_D(const QItemDelegate); |
735 | if (!rect.isValid()) |
736 | return; |
737 | |
738 | QStyleOptionViewItem opt(option); |
739 | opt.rect = rect; |
740 | opt.state = opt.state & ~QStyle::State_HasFocus; |
741 | |
742 | switch (state) { |
743 | case Qt::Unchecked: |
744 | opt.state |= QStyle::State_Off; |
745 | break; |
746 | case Qt::PartiallyChecked: |
747 | opt.state |= QStyle::State_NoChange; |
748 | break; |
749 | case Qt::Checked: |
750 | opt.state |= QStyle::State_On; |
751 | break; |
752 | } |
753 | |
754 | const QWidget *widget = d->widget(option); |
755 | QStyle *style = widget ? widget->style() : QApplication::style(); |
756 | style->drawPrimitive(pe: QStyle::PE_IndicatorItemViewItemCheck, opt: &opt, p: painter, w: widget); |
757 | } |
758 | |
759 | /*! |
760 | \since 4.2 |
761 | |
762 | Renders the item background for the given \a index, |
763 | using the given \a painter and style \a option. |
764 | */ |
765 | |
766 | void QItemDelegate::drawBackground(QPainter *painter, |
767 | const QStyleOptionViewItem &option, |
768 | const QModelIndex &index) const |
769 | { |
770 | if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { |
771 | QPalette::ColorGroup cg = option.state & QStyle::State_Enabled |
772 | ? QPalette::Normal : QPalette::Disabled; |
773 | if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) |
774 | cg = QPalette::Inactive; |
775 | |
776 | painter->fillRect(option.rect, option.palette.brush(cg, cr: QPalette::Highlight)); |
777 | } else { |
778 | QVariant value = index.data(arole: Qt::BackgroundRole); |
779 | if (value.canConvert<QBrush>()) { |
780 | QPointF oldBO = painter->brushOrigin(); |
781 | painter->setBrushOrigin(option.rect.topLeft()); |
782 | painter->fillRect(option.rect, qvariant_cast<QBrush>(v: value)); |
783 | painter->setBrushOrigin(oldBO); |
784 | } |
785 | } |
786 | } |
787 | |
788 | |
789 | /*! |
790 | \internal |
791 | |
792 | Code duplicated in QCommonStylePrivate::viewItemLayout |
793 | */ |
794 | |
795 | void QItemDelegate::doLayout(const QStyleOptionViewItem &option, |
796 | QRect *checkRect, QRect *pixmapRect, QRect *textRect, |
797 | bool hint) const |
798 | { |
799 | Q_ASSERT(checkRect && pixmapRect && textRect); |
800 | Q_D(const QItemDelegate); |
801 | const QWidget *widget = d->widget(option); |
802 | QStyle *style = widget ? widget->style() : QApplication::style(); |
803 | const bool hasCheck = checkRect->isValid(); |
804 | const bool hasPixmap = pixmapRect->isValid(); |
805 | const bool hasText = textRect->isValid(); |
806 | const bool hasMargin = (hasText | hasPixmap | hasCheck); |
807 | const int frameHMargin = hasMargin ? |
808 | style->pixelMetric(metric: QStyle::PM_FocusFrameHMargin, option: nullptr, widget) + 1 : 0; |
809 | const int textMargin = hasText ? frameHMargin : 0; |
810 | const int pixmapMargin = hasPixmap ? frameHMargin : 0; |
811 | const int checkMargin = hasCheck ? frameHMargin : 0; |
812 | const int x = option.rect.left(); |
813 | const int y = option.rect.top(); |
814 | int w, h; |
815 | |
816 | textRect->adjust(dx1: -textMargin, dy1: 0, dx2: textMargin, dy2: 0); // add width padding |
817 | if (textRect->height() == 0 && (!hasPixmap || !hint)) { |
818 | //if there is no text, we still want to have a decent height for the item sizeHint and the editor size |
819 | textRect->setHeight(option.fontMetrics.height()); |
820 | } |
821 | |
822 | QSize pm(0, 0); |
823 | if (hasPixmap) { |
824 | pm = pixmapRect->size(); |
825 | pm.rwidth() += 2 * pixmapMargin; |
826 | } |
827 | if (hint) { |
828 | h = qMax(a: checkRect->height(), b: qMax(a: textRect->height(), b: pm.height())); |
829 | if (option.decorationPosition == QStyleOptionViewItem::Left |
830 | || option.decorationPosition == QStyleOptionViewItem::Right) { |
831 | w = textRect->width() + pm.width(); |
832 | } else { |
833 | w = qMax(a: textRect->width(), b: pm.width()); |
834 | } |
835 | } else { |
836 | w = option.rect.width(); |
837 | h = option.rect.height(); |
838 | } |
839 | |
840 | int cw = 0; |
841 | QRect check; |
842 | if (hasCheck) { |
843 | cw = checkRect->width() + 2 * checkMargin; |
844 | if (hint) w += cw; |
845 | if (option.direction == Qt::RightToLeft) { |
846 | check.setRect(ax: x + w - cw, ay: y, aw: cw, ah: h); |
847 | } else { |
848 | check.setRect(ax: x, ay: y, aw: cw, ah: h); |
849 | } |
850 | } |
851 | |
852 | // at this point w should be the *total* width |
853 | |
854 | QRect display; |
855 | QRect decoration; |
856 | switch (option.decorationPosition) { |
857 | case QStyleOptionViewItem::Top: { |
858 | if (hasPixmap) |
859 | pm.setHeight(pm.height() + pixmapMargin); // add space |
860 | h = hint ? textRect->height() : h - pm.height(); |
861 | |
862 | if (option.direction == Qt::RightToLeft) { |
863 | decoration.setRect(ax: x, ay: y, aw: w - cw, ah: pm.height()); |
864 | display.setRect(ax: x, ay: y + pm.height(), aw: w - cw, ah: h); |
865 | } else { |
866 | decoration.setRect(ax: x + cw, ay: y, aw: w - cw, ah: pm.height()); |
867 | display.setRect(ax: x + cw, ay: y + pm.height(), aw: w - cw, ah: h); |
868 | } |
869 | break; } |
870 | case QStyleOptionViewItem::Bottom: { |
871 | if (hasText) |
872 | textRect->setHeight(textRect->height() + textMargin); // add space |
873 | h = hint ? textRect->height() + pm.height() : h; |
874 | |
875 | if (option.direction == Qt::RightToLeft) { |
876 | display.setRect(ax: x, ay: y, aw: w - cw, ah: textRect->height()); |
877 | decoration.setRect(ax: x, ay: y + textRect->height(), aw: w - cw, ah: h - textRect->height()); |
878 | } else { |
879 | display.setRect(ax: x + cw, ay: y, aw: w - cw, ah: textRect->height()); |
880 | decoration.setRect(ax: x + cw, ay: y + textRect->height(), aw: w - cw, ah: h - textRect->height()); |
881 | } |
882 | break; } |
883 | case QStyleOptionViewItem::Left: { |
884 | if (option.direction == Qt::LeftToRight) { |
885 | decoration.setRect(ax: x + cw, ay: y, aw: pm.width(), ah: h); |
886 | display.setRect(ax: decoration.right() + 1, ay: y, aw: w - pm.width() - cw, ah: h); |
887 | } else { |
888 | display.setRect(ax: x, ay: y, aw: w - pm.width() - cw, ah: h); |
889 | decoration.setRect(ax: display.right() + 1, ay: y, aw: pm.width(), ah: h); |
890 | } |
891 | break; } |
892 | case QStyleOptionViewItem::Right: { |
893 | if (option.direction == Qt::LeftToRight) { |
894 | display.setRect(ax: x + cw, ay: y, aw: w - pm.width() - cw, ah: h); |
895 | decoration.setRect(ax: display.right() + 1, ay: y, aw: pm.width(), ah: h); |
896 | } else { |
897 | decoration.setRect(ax: x, ay: y, aw: pm.width(), ah: h); |
898 | display.setRect(ax: decoration.right() + 1, ay: y, aw: w - pm.width() - cw, ah: h); |
899 | } |
900 | break; } |
901 | default: |
902 | qWarning(msg: "doLayout: decoration position is invalid"); |
903 | decoration = *pixmapRect; |
904 | break; |
905 | } |
906 | |
907 | if (!hint) { // we only need to do the internal layout if we are going to paint |
908 | *checkRect = QStyle::alignedRect(direction: option.direction, alignment: Qt::AlignCenter, |
909 | size: checkRect->size(), rectangle: check); |
910 | *pixmapRect = QStyle::alignedRect(direction: option.direction, alignment: option.decorationAlignment, |
911 | size: pixmapRect->size(), rectangle: decoration); |
912 | // the text takes up all available space, unless the decoration is not shown as selected |
913 | if (option.showDecorationSelected) |
914 | *textRect = display; |
915 | else |
916 | *textRect = QStyle::alignedRect(direction: option.direction, alignment: option.displayAlignment, |
917 | size: textRect->size().boundedTo(otherSize: display.size()), rectangle: display); |
918 | } else { |
919 | *checkRect = check; |
920 | *pixmapRect = decoration; |
921 | *textRect = display; |
922 | } |
923 | } |
924 | |
925 | /*! |
926 | \internal |
927 | |
928 | Returns the pixmap used to decorate the root of the item view. |
929 | The style \a option controls the appearance of the root; the \a variant |
930 | refers to the data associated with an item. |
931 | */ |
932 | |
933 | QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const |
934 | { |
935 | Q_D(const QItemDelegate); |
936 | switch (variant.userType()) { |
937 | case QMetaType::QIcon: { |
938 | QIcon::Mode mode = d->iconMode(state: option.state); |
939 | QIcon::State state = d->iconState(state: option.state); |
940 | return qvariant_cast<QIcon>(v: variant).pixmap(size: option.decorationSize, mode, state); } |
941 | case QMetaType::QColor: { |
942 | static QPixmap pixmap(option.decorationSize); |
943 | pixmap.fill(fillColor: qvariant_cast<QColor>(v: variant)); |
944 | return pixmap; } |
945 | default: |
946 | break; |
947 | } |
948 | |
949 | return qvariant_cast<QPixmap>(v: variant); |
950 | } |
951 | |
952 | // hacky but faster version of "QString::asprintf("%d-%d", i, enabled)" |
953 | static QString qPixmapSerial(quint64 i, bool enabled) |
954 | { |
955 | ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) }; |
956 | ushort *ptr = &arr[16]; |
957 | |
958 | while (i > 0) { |
959 | // hey - it's our internal representation, so use the ascii character after '9' |
960 | // instead of 'a' for hex |
961 | *(--ptr) = '0' + i % 16; |
962 | i >>= 4; |
963 | } |
964 | |
965 | return QString((const QChar *)ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr)); |
966 | } |
967 | |
968 | |
969 | /*! |
970 | \internal |
971 | Returns the selected version of the given \a pixmap using the given \a palette. |
972 | The \a enabled argument decides whether the normal or disabled highlight color of |
973 | the palette is used. |
974 | */ |
975 | QPixmap QItemDelegate::selectedPixmap(const QPixmap &pixmap, const QPalette &palette, bool enabled) |
976 | { |
977 | const QString key = qPixmapSerial(i: pixmap.cacheKey(), enabled); |
978 | QPixmap pm; |
979 | if (!QPixmapCache::find(key, pixmap: &pm)) { |
980 | QImage img = pixmap.toImage().convertToFormat(f: QImage::Format_ARGB32_Premultiplied); |
981 | |
982 | QColor color = palette.color(cg: enabled ? QPalette::Normal : QPalette::Disabled, |
983 | cr: QPalette::Highlight); |
984 | color.setAlphaF(0.3f); |
985 | |
986 | QPainter painter(&img); |
987 | painter.setCompositionMode(QPainter::CompositionMode_SourceAtop); |
988 | painter.fillRect(x: 0, y: 0, w: img.width(), h: img.height(), b: color); |
989 | painter.end(); |
990 | |
991 | pm = QPixmap(QPixmap::fromImage(image: img)); |
992 | const int n = (img.sizeInBytes() >> 10) + 1; |
993 | if (QPixmapCache::cacheLimit() < n) |
994 | QPixmapCache::setCacheLimit(n); |
995 | |
996 | QPixmapCache::insert(key, pixmap: pm); |
997 | } |
998 | return pm; |
999 | } |
1000 | |
1001 | /*! |
1002 | \internal |
1003 | Only used (and usable) for Qt::DecorationRole and Qt::CheckStateRole |
1004 | */ |
1005 | QRect QItemDelegate::rect(const QStyleOptionViewItem &option, |
1006 | const QModelIndex &index, int role) const |
1007 | { |
1008 | Q_D(const QItemDelegate); |
1009 | QVariant value = index.data(arole: role); |
1010 | if (role == Qt::CheckStateRole) |
1011 | return doCheck(option, bounding: option.rect, variant: value); |
1012 | if (value.isValid() && !value.isNull()) { |
1013 | switch (value.userType()) { |
1014 | case QMetaType::UnknownType: |
1015 | break; |
1016 | case QMetaType::QPixmap: { |
1017 | const QPixmap &pixmap = qvariant_cast<QPixmap>(v: value); |
1018 | return QRect(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); } |
1019 | case QMetaType::QImage: { |
1020 | const QImage &image = qvariant_cast<QImage>(v: value); |
1021 | return QRect(QPoint(0, 0), image.deviceIndependentSize().toSize()); } |
1022 | case QMetaType::QIcon: { |
1023 | QIcon::Mode mode = d->iconMode(state: option.state); |
1024 | QIcon::State state = d->iconState(state: option.state); |
1025 | QIcon icon = qvariant_cast<QIcon>(v: value); |
1026 | QSize size = icon.actualSize(size: option.decorationSize, mode, state); |
1027 | return QRect(QPoint(0, 0), size); } |
1028 | case QMetaType::QColor: |
1029 | return QRect(QPoint(0, 0), option.decorationSize); |
1030 | case QMetaType::QString: |
1031 | default: { |
1032 | const QString text = d->valueToText(value, option); |
1033 | value = index.data(arole: Qt::FontRole); |
1034 | QFont fnt = qvariant_cast<QFont>(v: value).resolve(option.font); |
1035 | return textRectangle(painter: nullptr, |
1036 | rect: d->textLayoutBounds(option, decorationRect: QRect(), checkRect: QRect()), |
1037 | font: fnt, text); } |
1038 | } |
1039 | } |
1040 | return QRect(); |
1041 | } |
1042 | |
1043 | /*! |
1044 | \internal |
1045 | */ |
1046 | QRect QItemDelegate::doCheck(const QStyleOptionViewItem &option, |
1047 | const QRect &bounding, const QVariant &value) const |
1048 | { |
1049 | if (value.isValid()) { |
1050 | Q_D(const QItemDelegate); |
1051 | QStyleOptionButton opt; |
1052 | opt.QStyleOption::operator=(other: option); |
1053 | opt.rect = bounding; |
1054 | const QWidget *widget = d->widget(option); // cast |
1055 | QStyle *style = widget ? widget->style() : QApplication::style(); |
1056 | return style->subElementRect(subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &opt, widget); |
1057 | } |
1058 | return QRect(); |
1059 | } |
1060 | |
1061 | /*! |
1062 | \internal |
1063 | */ |
1064 | QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect, |
1065 | const QFont &font, const QString &text) const |
1066 | { |
1067 | Q_D(const QItemDelegate); |
1068 | d->textOption.setWrapMode(QTextOption::WordWrap); |
1069 | d->textLayout.setTextOption(d->textOption); |
1070 | d->textLayout.setFont(font); |
1071 | d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text)); |
1072 | QSizeF fpSize = d->doTextLayout(lineWidth: rect.width()); |
1073 | const QSize size = QSize(qCeil(v: fpSize.width()), qCeil(v: fpSize.height())); |
1074 | // ###: textRectangle should take style option as argument |
1075 | const int textMargin = QApplication::style()->pixelMetric(metric: QStyle::PM_FocusFrameHMargin, option: nullptr) + 1; |
1076 | return QRect(0, 0, size.width() + 2 * textMargin, size.height()); |
1077 | } |
1078 | |
1079 | /*! |
1080 | \fn bool QItemDelegate::eventFilter(QObject *editor, QEvent *event) |
1081 | |
1082 | Returns \c true if the given \a editor is a valid QWidget and the |
1083 | given \a event is handled; otherwise returns \c false. The following |
1084 | key press events are handled by default: |
1085 | |
1086 | \list |
1087 | \li \uicontrol Tab |
1088 | \li \uicontrol Backtab |
1089 | \li \uicontrol Enter |
1090 | \li \uicontrol Return |
1091 | \li \uicontrol Esc |
1092 | \endlist |
1093 | |
1094 | In the case of \uicontrol Tab, \uicontrol Backtab, \uicontrol Enter and \uicontrol Return |
1095 | key press events, the \a editor's data is committed to the model |
1096 | and the editor is closed. If the \a event is a \uicontrol Tab key press |
1097 | the view will open an editor on the next item in the |
1098 | view. Likewise, if the \a event is a \uicontrol Backtab key press the |
1099 | view will open an editor on the \e previous item in the view. |
1100 | |
1101 | If the event is a \uicontrol Esc key press event, the \a editor is |
1102 | closed \e without committing its data. |
1103 | |
1104 | \sa commitData(), closeEditor() |
1105 | */ |
1106 | |
1107 | bool QItemDelegate::eventFilter(QObject *object, QEvent *event) |
1108 | { |
1109 | Q_D(QItemDelegate); |
1110 | return d->editorEventFilter(object, event); |
1111 | } |
1112 | |
1113 | /*! |
1114 | \reimp |
1115 | */ |
1116 | |
1117 | bool QItemDelegate::editorEvent(QEvent *event, |
1118 | QAbstractItemModel *model, |
1119 | const QStyleOptionViewItem &option, |
1120 | const QModelIndex &index) |
1121 | { |
1122 | Q_ASSERT(event); |
1123 | Q_ASSERT(model); |
1124 | |
1125 | // make sure that the item is checkable |
1126 | Qt::ItemFlags flags = model->flags(index); |
1127 | if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) |
1128 | || !(flags & Qt::ItemIsEnabled)) |
1129 | return false; |
1130 | |
1131 | // make sure that we have a check state |
1132 | QVariant value = index.data(arole: Qt::CheckStateRole); |
1133 | if (!value.isValid()) |
1134 | return false; |
1135 | |
1136 | // make sure that we have the right event type |
1137 | if ((event->type() == QEvent::MouseButtonRelease) |
1138 | || (event->type() == QEvent::MouseButtonDblClick) |
1139 | || (event->type() == QEvent::MouseButtonPress)) { |
1140 | QRect checkRect = doCheck(option, bounding: option.rect, value: Qt::Checked); |
1141 | QRect emptyRect; |
1142 | doLayout(option, checkRect: &checkRect, pixmapRect: &emptyRect, textRect: &emptyRect, hint: false); |
1143 | QMouseEvent *me = static_cast<QMouseEvent*>(event); |
1144 | if (me->button() != Qt::LeftButton || !checkRect.contains(p: me->position().toPoint())) |
1145 | return false; |
1146 | |
1147 | // eat the double click events inside the check rect |
1148 | if ((event->type() == QEvent::MouseButtonPress) |
1149 | || (event->type() == QEvent::MouseButtonDblClick)) |
1150 | return true; |
1151 | |
1152 | } else if (event->type() == QEvent::KeyPress) { |
1153 | if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space |
1154 | && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select) |
1155 | return false; |
1156 | } else { |
1157 | return false; |
1158 | } |
1159 | |
1160 | Qt::CheckState state = QtPrivate::legacyEnumValueFromModelData<Qt::CheckState>(data: value); |
1161 | if (flags & Qt::ItemIsUserTristate) |
1162 | state = ((Qt::CheckState)((state + 1) % 3)); |
1163 | else |
1164 | state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked; |
1165 | return model->setData(index, value: state, role: Qt::CheckStateRole); |
1166 | } |
1167 | |
1168 | /*! |
1169 | \internal |
1170 | */ |
1171 | |
1172 | QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index, |
1173 | const QStyleOptionViewItem &option) const |
1174 | { |
1175 | QStyleOptionViewItem opt = option; |
1176 | |
1177 | // set font |
1178 | QVariant value = index.data(arole: Qt::FontRole); |
1179 | if (value.isValid()){ |
1180 | opt.font = qvariant_cast<QFont>(v: value).resolve(opt.font); |
1181 | opt.fontMetrics = QFontMetrics(opt.font); |
1182 | } |
1183 | |
1184 | // set text alignment |
1185 | value = index.data(arole: Qt::TextAlignmentRole); |
1186 | if (value.isValid()) |
1187 | opt.displayAlignment = QtPrivate::legacyFlagValueFromModelData<Qt::Alignment>(data: value); |
1188 | |
1189 | // set foreground brush |
1190 | value = index.data(arole: Qt::ForegroundRole); |
1191 | if (value.canConvert<QBrush>()) |
1192 | opt.palette.setBrush(acr: QPalette::Text, abrush: qvariant_cast<QBrush>(v: value)); |
1193 | |
1194 | // disable style animations for checkboxes etc. within itemviews (QTBUG-30146) |
1195 | opt.styleObject = nullptr; |
1196 | |
1197 | return opt; |
1198 | } |
1199 | |
1200 | QT_END_NAMESPACE |
1201 | |
1202 | #include "moc_qitemdelegate.cpp" |
1203 |
Definitions
- QItemDelegatePrivate
- QItemDelegatePrivate
- editorFactory
- iconMode
- iconState
- replaceNewLine
- widget
- Icon
- displayRect
- textLayoutBounds
- doTextLayout
- QItemDelegate
- ~QItemDelegate
- hasClipping
- setClipping
- valueToText
- paint
- sizeHint
- createEditor
- setEditorData
- setModelData
- updateEditorGeometry
- itemEditorFactory
- setItemEditorFactory
- drawDisplay
- drawDecoration
- drawFocus
- drawCheck
- drawBackground
- doLayout
- decoration
- qPixmapSerial
- selectedPixmap
- rect
- doCheck
- textRectangle
- eventFilter
- editorEvent
Start learning QML with our Intro Training
Find out more