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 \since 4.4
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 \since 4.3
316 Whenever a help event occurs, this function is called with the \a event
317 \a view \a option and the \a index that corresponds to the item where the
318 event occurs.
319
320 Returns \c true if the delegate can handle the event; otherwise returns \c false.
321 A return value of true indicates that the data obtained using the index had
322 the required role.
323
324 For QEvent::ToolTip and QEvent::WhatsThis events that were handled successfully,
325 the relevant popup may be shown depending on the user's system configuration.
326
327 \sa QHelpEvent
328*/
329bool QAbstractItemDelegate::helpEvent(QHelpEvent *event,
330 QAbstractItemView *view,
331 const QStyleOptionViewItem &option,
332 const QModelIndex &index)
333{
334 if (!event || !view)
335 return false;
336 Q_UNUSED(index);
337 Q_UNUSED(option);
338 switch (event->type()) {
339#if QT_CONFIG(tooltip)
340 case QEvent::ToolTip: {
341 Q_D(QAbstractItemDelegate);
342 QHelpEvent *he = static_cast<QHelpEvent*>(event);
343 const int precision = inherits(classname: "QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
344 const QString tooltip = index.isValid() ?
345 d->textForRole(role: Qt::ToolTipRole, value: index.data(arole: Qt::ToolTipRole), locale: option.locale, precision) :
346 QString();
347 QToolTip::showText(pos: he->globalPos(), text: tooltip, w: view->viewport(), rect: option.rect);
348 event->setAccepted(!tooltip.isEmpty());
349 break;
350 }
351#endif
352#if QT_CONFIG(whatsthis)
353 case QEvent::QueryWhatsThis:
354 event->setAccepted(index.data(arole: Qt::WhatsThisRole).isValid());
355 break;
356 case QEvent::WhatsThis: {
357 Q_D(QAbstractItemDelegate);
358 QHelpEvent *he = static_cast<QHelpEvent*>(event);
359 const int precision = inherits(classname: "QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
360 const QString whatsthis = index.isValid() ?
361 d->textForRole(role: Qt::WhatsThisRole, value: index.data(arole: Qt::WhatsThisRole), locale: option.locale, precision) :
362 QString();
363 QWhatsThis::showText(pos: he->globalPos(), text: whatsthis, w: view);
364 event->setAccepted(!whatsthis.isEmpty());
365 break;
366 }
367#endif
368 case QEvent::None:
369 default:
370 break;
371 }
372 return event->isAccepted();
373}
374
375/*!
376 \internal
377
378 This virtual method is reserved and will be used in Qt 5.1.
379*/
380QList<int> QAbstractItemDelegate::paintingRoles() const
381{
382 return QList<int>();
383}
384
385QAbstractItemDelegatePrivate::QAbstractItemDelegatePrivate()
386 : QObjectPrivate()
387{
388}
389
390static bool editorHandlesKeyEvent(QWidget *editor, const QKeyEvent *event)
391{
392#if QT_CONFIG(textedit)
393 // do not filter enter / return / tab / backtab for QTextEdit or QPlainTextEdit
394 if (qobject_cast<QTextEdit *>(object: editor) || qobject_cast<QPlainTextEdit *>(object: editor)) {
395 switch (event->key()) {
396 case Qt::Key_Tab:
397 case Qt::Key_Backtab:
398 case Qt::Key_Enter:
399 case Qt::Key_Return:
400 return true;
401
402 default:
403 break;
404 }
405 }
406#endif // QT_CONFIG(textedit)
407
408 Q_UNUSED(editor);
409 Q_UNUSED(event);
410 return false;
411}
412
413bool QAbstractItemDelegatePrivate::editorEventFilter(QObject *object, QEvent *event)
414{
415 Q_Q(QAbstractItemDelegate);
416
417 QWidget *editor = qobject_cast<QWidget*>(o: object);
418 if (!editor)
419 return false;
420 if (event->type() == QEvent::KeyPress) {
421 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
422 if (editorHandlesKeyEvent(editor, event: keyEvent))
423 return false;
424
425#ifndef QT_NO_SHORTCUT
426 if (keyEvent->matches(key: QKeySequence::Cancel)) {
427 // don't commit data
428 emit q->closeEditor(editor, hint: QAbstractItemDelegate::RevertModelCache);
429 return true;
430 }
431#endif
432
433 switch (keyEvent->key()) {
434 case Qt::Key_Tab:
435 if (tryFixup(editor)) {
436 emit q->commitData(editor);
437 emit q->closeEditor(editor, hint: QAbstractItemDelegate::EditNextItem);
438 }
439 return true;
440 case Qt::Key_Backtab:
441 if (tryFixup(editor)) {
442 emit q->commitData(editor);
443 emit q->closeEditor(editor, hint: QAbstractItemDelegate::EditPreviousItem);
444 }
445 return true;
446 case Qt::Key_Enter:
447 case Qt::Key_Return:
448 // We want the editor to be able to process the key press
449 // before committing the data (e.g. so it can do
450 // validation/fixup of the input).
451 if (!tryFixup(editor))
452 return true;
453
454 QMetaObject::invokeMethod(obj: q, member: "_q_commitDataAndCloseEditor",
455 c: Qt::QueuedConnection, Q_ARG(QWidget*, editor));
456 return false;
457 default:
458 return false;
459 }
460 } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
461 //the Hide event will take care of he editors that are in fact complete dialogs
462 if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
463 QWidget *w = QApplication::focusWidget();
464 while (w) { // don't worry about focus changes internally in the editor
465 if (w == editor)
466 return false;
467 w = w->parentWidget();
468 }
469#if QT_CONFIG(draganddrop)
470 // The window may lose focus during an drag operation.
471 // i.e when dragging involves the taskbar on Windows.
472 QPlatformDrag *platformDrag = QGuiApplicationPrivate::instance()->platformIntegration()->drag();
473 if (platformDrag && platformDrag->currentDrag()) {
474 return false;
475 }
476#endif
477 if (tryFixup(editor))
478 emit q->commitData(editor);
479
480 // If the application loses focus while editing, then the focus needs to go back
481 // to the itemview when the editor closes. This ensures that when the application
482 // is active again it will have the focus on the itemview as expected.
483 QWidget *editorParent = editor->parentWidget();
484 const bool manuallyFixFocus = (event->type() == QEvent::FocusOut) && !editor->hasFocus() &&
485 editorParent &&
486 (static_cast<QFocusEvent *>(event)->reason() == Qt::ActiveWindowFocusReason);
487 emit q->closeEditor(editor, hint: QAbstractItemDelegate::NoHint);
488 if (manuallyFixFocus)
489 editorParent->setFocus();
490 }
491#ifndef QT_NO_SHORTCUT
492 } else if (event->type() == QEvent::ShortcutOverride) {
493 if (static_cast<QKeyEvent*>(event)->matches(key: QKeySequence::Cancel)) {
494 event->accept();
495 return true;
496 }
497#endif
498 }
499 return false;
500}
501
502bool QAbstractItemDelegatePrivate::tryFixup(QWidget *editor)
503{
504#if QT_CONFIG(lineedit)
505 if (QLineEdit *e = qobject_cast<QLineEdit*>(object: editor)) {
506 if (!e->hasAcceptableInput()) {
507#if QT_CONFIG(validator)
508 if (const QValidator *validator = e->validator()) {
509 QString text = e->text();
510 validator->fixup(text);
511 e->setText(text);
512 }
513#endif
514 return e->hasAcceptableInput();
515 }
516 }
517#else
518 Q_UNUSED(editor);
519#endif // QT_CONFIG(lineedit)
520
521 return true;
522}
523
524QString QAbstractItemDelegatePrivate::textForRole(Qt::ItemDataRole role, const QVariant &value, const QLocale &locale, int precision) const
525{
526 const QLocale::FormatType formatType = (role == Qt::DisplayRole) ? QLocale::ShortFormat : QLocale::LongFormat;
527 QString text;
528 switch (value.userType()) {
529 case QMetaType::Float:
530 text = locale.toString(f: value.toFloat());
531 break;
532 case QMetaType::Double:
533 text = locale.toString(f: value.toDouble(), format: 'g', precision);
534 break;
535 case QMetaType::Int:
536 case QMetaType::LongLong:
537 text = locale.toString(i: value.toLongLong());
538 break;
539 case QMetaType::UInt:
540 case QMetaType::ULongLong:
541 text = locale.toString(i: value.toULongLong());
542 break;
543 case QMetaType::QDate:
544 text = locale.toString(date: value.toDate(), format: formatType);
545 break;
546 case QMetaType::QTime:
547 text = locale.toString(time: value.toTime(), format: formatType);
548 break;
549 case QMetaType::QDateTime:
550 text = locale.toString(dateTime: value.toDateTime(), format: formatType);
551 break;
552 case QMetaType::QJsonValue: {
553 const QJsonValue val = value.toJsonValue();
554 if (val.isBool()) {
555 text = QVariant(val.toBool()).toString();
556 break;
557 }
558 if (val.isDouble()) {
559 text = locale.toString(f: val.toDouble(), format: 'g', precision);
560 break;
561 }
562 // val is a string (or null) here
563 Q_FALLTHROUGH();
564 }
565 default: {
566 text = value.toString();
567 if (role == Qt::DisplayRole)
568 text.replace(before: u'\n', after: QChar::LineSeparator);
569 break;
570 }
571 }
572 return text;
573}
574
575void QAbstractItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor)
576{
577 Q_Q(QAbstractItemDelegate);
578 emit q->commitData(editor);
579 emit q->closeEditor(editor, hint: QAbstractItemDelegate::SubmitModelCache);
580}
581
582QT_END_NAMESPACE
583
584#include "moc_qabstractitemdelegate.cpp"
585

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