Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qabstractitemdelegate.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <qabstractitemmodel.h>
8#include <qabstractitemview.h>
9#include <qfontmetrics.h>
10#if QT_CONFIG(whatsthis)
11#include <qwhatsthis.h>
12#endif
13#if QT_CONFIG(tooltip)
14#include <qtooltip.h>
15#endif
16#include <qevent.h>
17#include <qstring.h>
18#include <qdebug.h>
19#if QT_CONFIG(lineedit)
20#include <qlineedit.h>
21#endif
22#if QT_CONFIG(textedit)
23#include <qtextedit.h>
24#include <qplaintextedit.h>
25#endif
26#include <qapplication.h>
27#include <qvalidator.h>
28#include <qjsonvalue.h>
29#include <private/qtextengine_p.h>
30#include <private/qabstractitemdelegate_p.h>
31
32#include <qpa/qplatformintegration.h>
33#if QT_CONFIG(draganddrop)
34#include <qpa/qplatformdrag.h>
35#include <private/qdnd_p.h>
36#endif
37#include <private/qguiapplication_p.h>
38
40
41/*!
42 \class QAbstractItemDelegate
43
44 \brief The QAbstractItemDelegate class is used to display and edit
45 data items from a model.
46
47 \ingroup model-view
48 \inmodule QtWidgets
49
50 A QAbstractItemDelegate provides the interface and common functionality
51 for delegates in the model/view architecture. Delegates display
52 individual items in views, and handle the editing of model data.
53
54 The QAbstractItemDelegate class is one of the \l{Model/View Classes}
55 and is part of Qt's \l{Model/View Programming}{model/view framework}.
56
57 To render an item in a custom way, you must implement paint() and
58 sizeHint(). The QStyledItemDelegate class provides default implementations for
59 these functions; if you do not need custom rendering, subclass that
60 class instead.
61
62 We give an example of drawing a progress bar in items; in our case
63 for a package management program.
64
65 \image widgetdelegate.png {Package manager showing download progress}
66
67 We create the \c WidgetDelegate class, which inherits from
68 QStyledItemDelegate. We do the drawing in the paint() function:
69
70 \snippet widgetdelegate.cpp 0
71
72 Notice that we use a QStyleOptionProgressBar and initialize its
73 members. We can then use the current QStyle to draw it.
74
75 To provide custom editing, there are two approaches that can be
76 used. The first approach is to create an editor widget and display
77 it directly on top of the item. To do this you must reimplement
78 createEditor() to provide an editor widget, setEditorData() to populate
79 the editor with the data from the model, and setModelData() so that the
80 delegate can update the model with data from the editor.
81
82 The second approach is to handle user events directly by reimplementing
83 editorEvent().
84
85 \sa {model-view-programming}{Model/View Programming},
86 QStyledItemDelegate, QStyle
87*/
88
89/*!
90 \enum QAbstractItemDelegate::EndEditHint
91
92 This enum describes the different hints that the delegate can give to the
93 model and view components to make editing data in a model a comfortable
94 experience for the user.
95
96 \value NoHint There is no recommended action to be performed.
97
98 These hints let the delegate influence the behavior of the view:
99
100 \value EditNextItem The view should use the delegate to open an
101 editor on the next item in the view.
102 \value EditPreviousItem The view should use the delegate to open an
103 editor on the previous item in the view.
104
105 Note that custom views may interpret the concepts of next and previous
106 differently.
107
108 The following hints are most useful when models are used that cache
109 data, such as those that manipulate data locally in order to increase
110 performance or conserve network bandwidth.
111
112 \value SubmitModelCache If the model caches data, it should write out
113 cached data to the underlying data store.
114 \value RevertModelCache If the model caches data, it should discard
115 cached data and replace it with data from the
116 underlying data store.
117
118 Although models and views should respond to these hints in appropriate
119 ways, custom components may ignore any or all of them if they are not
120 relevant.
121*/
122
123/*!
124 \fn void QAbstractItemDelegate::commitData(QWidget *editor)
125
126 This signal must be emitted when the \a editor widget has completed
127 editing the data, and wants to write it back into the model.
128*/
129
130/*!
131 \fn void QAbstractItemDelegate::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
132
133 This signal is emitted when the user has finished editing an item using
134 the specified \a editor.
135
136 The \a hint provides a way for the delegate to influence how the model and
137 view behave after editing is completed. It indicates to these components
138 what action should be performed next to provide a comfortable editing
139 experience for the user. For example, if \c EditNextItem is specified,
140 the view should use a delegate to open an editor on the next item in the
141 model.
142
143 \sa EndEditHint
144*/
145
146/*!
147 \fn void QAbstractItemDelegate::sizeHintChanged(const QModelIndex &index)
148
149 This signal must be emitted when the sizeHint() of \a index changed.
150
151 Views automatically connect to this signal and relayout items as necessary.
152*/
153
154
155/*!
156 Creates a new abstract item delegate with the given \a parent.
157*/
158QAbstractItemDelegate::QAbstractItemDelegate(QObject *parent)
159 : QObject(*new QAbstractItemDelegatePrivate, parent)
160{
161
162}
163
164/*!
165 \internal
166
167 Creates a new abstract item delegate with the given \a parent.
168*/
169QAbstractItemDelegate::QAbstractItemDelegate(QObjectPrivate &dd, QObject *parent)
170 : QObject(dd, parent)
171{
172
173}
174
175/*!
176 Destroys the abstract item delegate.
177*/
178QAbstractItemDelegate::~QAbstractItemDelegate()
179{
180
181}
182
183/*!
184 \fn void QAbstractItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const = 0;
185
186 This pure abstract function must be reimplemented if you want to
187 provide custom rendering. Use the \a painter and style \a option to
188 render the item specified by the item \a index.
189
190 If you reimplement this you must also reimplement sizeHint().
191*/
192
193/*!
194 \fn QSize QAbstractItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const = 0
195
196 This pure abstract function must be reimplemented if you want to
197 provide custom rendering. The options are specified by \a option
198 and the model item by \a index.
199
200 If you reimplement this you must also reimplement paint().
201*/
202
203/*!
204 Returns the editor to be used for editing the data item with the
205 given \a index. Note that the index contains information about the
206 model being used. The editor's parent widget is specified by \a parent,
207 and the item options by \a option.
208
209 The base implementation returns \nullptr. If you want custom editing you
210 will need to reimplement this function.
211
212 The returned editor widget should have Qt::StrongFocus;
213 otherwise, \l{QMouseEvent}s received by the widget will propagate
214 to the view. The view's background will shine through unless the
215 editor paints its own background (e.g., with
216 \l{QWidget::}{setAutoFillBackground()}).
217
218 \sa destroyEditor(), setModelData(), setEditorData()
219*/
220QWidget *QAbstractItemDelegate::createEditor(QWidget *,
221 const QStyleOptionViewItem &,
222 const QModelIndex &) const
223{
224 return nullptr;
225}
226
227
228/*!
229 Called when the \a editor is no longer needed for editing the data item
230 with the given \a index and should be destroyed. The default behavior is a
231 call to deleteLater on the editor. It is possible e.g. to avoid this delete by
232 reimplementing this function.
233
234 \since 5.0
235 \sa createEditor()
236*/
237void QAbstractItemDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const
238{
239 Q_UNUSED(index);
240 editor->deleteLater();
241}
242
243/*!
244 Sets the contents of the given \a editor to the data for the item
245 at the given \a index. Note that the index contains information
246 about the model being used.
247
248 The base implementation does nothing. If you want custom editing
249 you will need to reimplement this function.
250
251 \sa setModelData()
252*/
253void QAbstractItemDelegate::setEditorData(QWidget *,
254 const QModelIndex &) const
255{
256 // do nothing
257}
258
259/*!
260 Sets the data for the item at the given \a index in the \a model
261 to the contents of the given \a editor.
262
263 The base implementation does nothing. If you want custom editing
264 you will need to reimplement this function.
265
266 \sa setEditorData()
267*/
268void QAbstractItemDelegate::setModelData(QWidget *,
269 QAbstractItemModel *,
270 const QModelIndex &) const
271{
272 // do nothing
273}
274
275/*!
276 Updates the geometry of the \a editor for the item with the given
277 \a index, according to the rectangle specified in the \a option.
278 If the item has an internal layout, the editor will be laid out
279 accordingly. Note that the index contains information about the
280 model being used.
281
282 The base implementation does nothing. If you want custom editing
283 you must reimplement this function.
284*/
285void QAbstractItemDelegate::updateEditorGeometry(QWidget *,
286 const QStyleOptionViewItem &,
287 const QModelIndex &) const
288{
289 // do nothing
290}
291
292/*!
293 When editing of an item starts, this function is called with the
294 \a event that triggered the editing, the \a model, the \a index of
295 the item, and the \a option used for rendering the item.
296
297 Mouse events are sent to editorEvent() even if they don't start
298 editing of the item. This can, for instance, be useful if you wish
299 to open a context menu when the right mouse button is pressed on
300 an item.
301
302 The base implementation returns \c false (indicating that it has not
303 handled the event).
304*/
305bool QAbstractItemDelegate::editorEvent(QEvent *,
306 QAbstractItemModel *,
307 const QStyleOptionViewItem &,
308 const QModelIndex &)
309{
310 // do nothing
311 return false;
312}
313
314/*!
315 Whenever a help event occurs, this function is called with the \a event
316 \a view \a option and the \a index that corresponds to the item where the
317 event occurs.
318
319 Returns \c true if the delegate can handle the event; otherwise returns \c false.
320 A return value of true indicates that the data obtained using the index had
321 the required role.
322
323 For QEvent::ToolTip and QEvent::WhatsThis events that were handled successfully,
324 the relevant popup may be shown depending on the user's system configuration.
325
326 \sa QHelpEvent
327*/
328bool QAbstractItemDelegate::helpEvent(QHelpEvent *event,
329 QAbstractItemView *view,
330 const QStyleOptionViewItem &option,
331 const QModelIndex &index)
332{
333 if (!event || !view)
334 return false;
335 Q_UNUSED(index);
336 Q_UNUSED(option);
337 switch (event->type()) {
338#if QT_CONFIG(tooltip)
339 case QEvent::ToolTip: {
340 Q_D(QAbstractItemDelegate);
341 QHelpEvent *he = static_cast<QHelpEvent*>(event);
342 const int precision = inherits("QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
343 const QString tooltip = index.isValid() ?
344 d->textForRole(Qt::ToolTipRole, index.data(Qt::ToolTipRole), option.locale, precision) :
345 QString();
346 QToolTip::showText(he->globalPos(), tooltip, view->viewport(), option.rect);
347 event->setAccepted(!tooltip.isEmpty());
348 break;
349 }
350#endif
351#if QT_CONFIG(whatsthis)
352 case QEvent::QueryWhatsThis:
353 event->setAccepted(index.data(Qt::WhatsThisRole).isValid());
354 break;
355 case QEvent::WhatsThis: {
356 Q_D(QAbstractItemDelegate);
357 QHelpEvent *he = static_cast<QHelpEvent*>(event);
358 const int precision = inherits("QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
359 const QString whatsthis = index.isValid() ?
360 d->textForRole(Qt::WhatsThisRole, index.data(Qt::WhatsThisRole), option.locale, precision) :
361 QString();
362 QWhatsThis::showText(he->globalPos(), whatsthis, view);
363 event->setAccepted(!whatsthis.isEmpty());
364 break;
365 }
366#endif
367 case QEvent::None:
368 default:
369 break;
370 }
371 return event->isAccepted();
372}
373
374/*!
375 \internal
376
377 This virtual method is reserved and will be used in Qt 5.1.
378*/
379QList<int> QAbstractItemDelegate::paintingRoles() const
380{
381 return QList<int>();
382}
383
384QAbstractItemDelegatePrivate::QAbstractItemDelegatePrivate()
385 : QObjectPrivate()
386{
387}
388
389/*!
390 \fn bool QAbstractItemDelegate::handleEditorEvent(QObject *editor, QEvent *event)
391
392 Implements standard handling of events on behalf of the currently active \a editor.
393 Call this function from an override of eventFilter() in a QAbstractItemModel subclass,
394 and return its result. To avoid duplicate event processing, do not call
395 the parent class implementation of eventFilter() after calling this function.
396
397 Returns \c true if the given \a editor is a valid QWidget and the
398 given \a event is handled; otherwise returns \c false. The following
399 key press events are handled by default:
400
401 \list
402 \li \uicontrol Tab
403 \li \uicontrol Backtab
404 \li \uicontrol Enter
405 \li \uicontrol Return
406 \li \uicontrol Esc
407 \endlist
408
409 If the \a editor's type is QTextEdit or QPlainTextEdit then \uicontrol Tab,
410 \uicontrol Backtab, \uicontrol Enter and \uicontrol Return keys are \e not
411 handled.
412
413 In the case of \uicontrol Tab, \uicontrol Backtab, \uicontrol Enter and \uicontrol Return
414 key press events, the \a editor's data is committed to the model
415 and the editor is closed. If the \a event is a \uicontrol Tab key press
416 the view will open an editor on the next item in the
417 view. Likewise, if the \a event is a \uicontrol Backtab key press the
418 view will open an editor on the \e previous item in the view.
419
420 If the event is a \uicontrol Esc key press event, the \a editor is
421 closed \e without committing its data.
422
423 \sa commitData(), closeEditor()
424 \since 6.10
425*/
426bool QAbstractItemDelegate::handleEditorEvent(QObject *object, QEvent *event)
427{
428 Q_D(QAbstractItemDelegate);
429 return d->handleEditorEvent(object, event);
430}
431
432static bool editorHandlesKeyEvent(QWidget *editor, const QKeyEvent *event)
433{
434#if QT_CONFIG(textedit)
435 // do not filter enter / return / tab / backtab for QTextEdit or QPlainTextEdit
436 if (qobject_cast<QTextEdit *>(editor) || qobject_cast<QPlainTextEdit *>(editor)) {
437 switch (event->key()) {
438 case Qt::Key_Tab:
439 case Qt::Key_Backtab:
440 case Qt::Key_Enter:
441 case Qt::Key_Return:
442 return true;
443
444 default:
445 break;
446 }
447 }
448#endif // QT_CONFIG(textedit)
449
450 Q_UNUSED(editor);
451 Q_UNUSED(event);
452 return false;
453}
454
455bool QAbstractItemDelegatePrivate::handleEditorEvent(QObject *object, QEvent *event)
456{
457 Q_Q(QAbstractItemDelegate);
458
459 QWidget *editor = qobject_cast<QWidget*>(object);
460 if (!editor)
461 return false;
462 if (event->type() == QEvent::KeyPress) {
463 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
464 if (editorHandlesKeyEvent(editor, keyEvent))
465 return false;
466
467#ifndef QT_NO_SHORTCUT
468 if (keyEvent->matches(QKeySequence::Cancel)) {
469 // don't commit data
470 emit q->closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
471 return true;
472 }
473#endif
474
475 switch (keyEvent->key()) {
476 case Qt::Key_Tab:
477 if (tryFixup(editor)) {
478 emit q->commitData(editor);
479 emit q->closeEditor(editor, QAbstractItemDelegate::EditNextItem);
480 }
481 return true;
482 case Qt::Key_Backtab:
483 if (tryFixup(editor)) {
484 emit q->commitData(editor);
485 emit q->closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
486 }
487 return true;
488 case Qt::Key_Enter:
489 case Qt::Key_Return:
490 // We want the editor to be able to process the key press
491 // before committing the data (e.g. so it can do
492 // validation/fixup of the input).
493 if (!tryFixup(editor))
494 return true;
495
496 QMetaObject::invokeMethod(q, "_q_commitDataAndCloseEditor",
497 Qt::QueuedConnection, Q_ARG(QWidget*, editor));
498 return false;
499 default:
500 return false;
501 }
502 } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
503 //the Hide event will take care of he editors that are in fact complete dialogs
504 if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
505 QWidget *w = QApplication::focusWidget();
506 while (w) { // don't worry about focus changes internally in the editor
507 if (w == editor)
508 return false;
509 w = w->parentWidget();
510 }
511#if QT_CONFIG(draganddrop)
512 // The window may lose focus during an drag operation.
513 // i.e when dragging involves the taskbar on Windows.
514 QPlatformDrag *platformDrag = QGuiApplicationPrivate::instance()->platformIntegration()->drag();
515 if (platformDrag && platformDrag->currentDrag()) {
516 return false;
517 }
518#endif
519 if (tryFixup(editor))
520 emit q->commitData(editor);
521
522 // If the application loses focus while editing, then the focus needs to go back
523 // to the itemview when the editor closes. This ensures that when the application
524 // is active again it will have the focus on the itemview as expected.
525 QWidget *editorParent = editor->parentWidget();
526 const bool manuallyFixFocus = (event->type() == QEvent::FocusOut) && !editor->hasFocus() &&
527 editorParent &&
528 (static_cast<QFocusEvent *>(event)->reason() == Qt::ActiveWindowFocusReason);
529 emit q->closeEditor(editor, QAbstractItemDelegate::NoHint);
530 if (manuallyFixFocus)
531 editorParent->setFocus();
532 }
533#ifndef QT_NO_SHORTCUT
534 } else if (event->type() == QEvent::ShortcutOverride) {
535 if (static_cast<QKeyEvent*>(event)->matches(QKeySequence::Cancel)) {
536 event->accept();
537 return true;
538 }
539#endif
540 }
541 return false;
542}
543
544bool QAbstractItemDelegatePrivate::tryFixup(QWidget *editor)
545{
546#if QT_CONFIG(lineedit)
547 if (QLineEdit *e = qobject_cast<QLineEdit*>(editor)) {
548 if (!e->hasAcceptableInput()) {
549#if QT_CONFIG(validator)
550 if (const QValidator *validator = e->validator()) {
551 QString text = e->text();
552 validator->fixup(text);
553 e->setText(text);
554 }
555#endif
556 return e->hasAcceptableInput();
557 }
558 }
559#endif
560#if QT_CONFIG(spinbox)
561 // Give a chance to the spinbox to interpret the text and emit
562 // the appropriate signals before committing data.
563 if (QAbstractSpinBox *sb = qobject_cast<QAbstractSpinBox *>(editor)) {
564 if (!sb->keyboardTracking())
565 sb->interpretText();
566 }
567#else
568 Q_UNUSED(editor);
569#endif // QT_CONFIG(lineedit)
570
571 return true;
572}
573
574QString QAbstractItemDelegatePrivate::textForRole(Qt::ItemDataRole role, const QVariant &value, const QLocale &locale, int precision) const
575{
576 const QLocale::FormatType formatType = (role == Qt::DisplayRole) ? QLocale::ShortFormat : QLocale::LongFormat;
577 QString text;
578 switch (value.userType()) {
579 case QMetaType::Float:
580 text = locale.toString(value.toFloat());
581 break;
582 case QMetaType::Double:
583 text = locale.toString(value.toDouble(), 'g', precision);
584 break;
585 case QMetaType::Int:
586 case QMetaType::LongLong:
587 text = locale.toString(value.toLongLong());
588 break;
589 case QMetaType::UInt:
590 case QMetaType::ULongLong:
591 text = locale.toString(value.toULongLong());
592 break;
593 case QMetaType::QDate:
594 text = locale.toString(value.toDate(), formatType);
595 break;
596 case QMetaType::QTime:
597 text = locale.toString(value.toTime(), formatType);
598 break;
599 case QMetaType::QDateTime:
600 text = locale.toString(value.toDateTime(), formatType);
601 break;
602 case QMetaType::QJsonValue: {
603 const QJsonValue val = value.toJsonValue();
604 if (val.isBool()) {
605 text = QVariant(val.toBool()).toString();
606 break;
607 }
608 if (val.isDouble()) {
609 text = locale.toString(val.toDouble(), 'g', precision);
610 break;
611 }
612 // val is a string (or null) here
613 Q_FALLTHROUGH();
614 }
615 default: {
616 text = value.toString();
617 if (role == Qt::DisplayRole)
618 text.replace(u'\n', QChar::LineSeparator);
619 break;
620 }
621 }
622 return text;
623}
624
625void QAbstractItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor)
626{
627 Q_Q(QAbstractItemDelegate);
628 emit q->commitData(editor);
629 emit q->closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
630}
631
632QT_END_NAMESPACE
633
634#include "moc_qabstractitemdelegate.cpp"
static bool editorHandlesKeyEvent(QWidget *editor, const QKeyEvent *event)