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

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