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 <qplatformdefs.h> |
5 | #include "qitemeditorfactory.h" |
6 | #include "qitemeditorfactory_p.h" |
7 | |
8 | #if QT_CONFIG(combobox) |
9 | #include <qcombobox.h> |
10 | #endif |
11 | #if QT_CONFIG(datetimeedit) |
12 | #include <qdatetimeedit.h> |
13 | #endif |
14 | #if QT_CONFIG(label) |
15 | #include <qlabel.h> |
16 | #endif |
17 | #if QT_CONFIG(lineedit) |
18 | #include <qlineedit.h> |
19 | #endif |
20 | #if QT_CONFIG(spinbox) |
21 | #include <qspinbox.h> |
22 | #endif |
23 | #include <qstyle.h> |
24 | #include <qstyleoption.h> |
25 | #include <limits.h> |
26 | #include <float.h> |
27 | #include <qapplication.h> |
28 | #include <qdebug.h> |
29 | |
30 | #include <vector> |
31 | #include <algorithm> |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | |
35 | #if QT_CONFIG(combobox) |
36 | |
37 | class QBooleanComboBox : public QComboBox |
38 | { |
39 | Q_OBJECT |
40 | Q_PROPERTY(bool value READ value WRITE setValue USER true) |
41 | |
42 | public: |
43 | QBooleanComboBox(QWidget *parent); |
44 | void setValue(bool); |
45 | bool value() const; |
46 | }; |
47 | |
48 | #endif // QT_CONFIG(combobox) |
49 | |
50 | |
51 | #if QT_CONFIG(spinbox) |
52 | |
53 | class QUIntSpinBox : public QSpinBox |
54 | { |
55 | Q_OBJECT |
56 | Q_PROPERTY(uint value READ uintValue WRITE setUIntValue NOTIFY uintValueChanged USER true) |
57 | public: |
58 | explicit QUIntSpinBox(QWidget *parent = nullptr) |
59 | : QSpinBox(parent) |
60 | { |
61 | connect(asender: this, SIGNAL(valueChanged(int)), SIGNAL(uintValueChanged())); |
62 | } |
63 | |
64 | uint uintValue() |
65 | { |
66 | return value(); |
67 | } |
68 | |
69 | void setUIntValue(uint value_) |
70 | { |
71 | return setValue(value_); |
72 | } |
73 | |
74 | Q_SIGNALS: |
75 | void uintValueChanged(); |
76 | }; |
77 | |
78 | #endif // QT_CONFIG(spinbox) |
79 | |
80 | /*! |
81 | \class QItemEditorFactory |
82 | \brief The QItemEditorFactory class provides widgets for editing item data |
83 | in views and delegates. |
84 | \since 4.2 |
85 | \ingroup model-view |
86 | \inmodule QtWidgets |
87 | |
88 | When editing data in an item view, editors are created and |
89 | displayed by a delegate. QStyledItemDelegate, which is the delegate by |
90 | default installed on Qt's item views, uses a QItemEditorFactory to |
91 | create editors for it. A default unique instance provided by |
92 | QItemEditorFactory is used by all item delegates. If you set a |
93 | new default factory with setDefaultFactory(), the new factory will |
94 | be used by existing and new delegates. |
95 | |
96 | A factory keeps a collection of QItemEditorCreatorBase |
97 | instances, which are specialized editors that produce editors |
98 | for one particular QVariant data type (All Qt models store |
99 | their data in \l{QVariant}s). |
100 | |
101 | \section1 Standard Editing Widgets |
102 | |
103 | The standard factory implementation provides editors for a variety of data |
104 | types. These are created whenever a delegate needs to provide an editor for |
105 | data supplied by a model. The following table shows the relationship between |
106 | types and the standard editors provided. |
107 | |
108 | \table |
109 | \header \li Type \li Editor Widget |
110 | \row \li bool \li QComboBox |
111 | \row \li double \li QDoubleSpinBox |
112 | \row \li int \li{1,2} QSpinBox |
113 | \row \li unsigned int |
114 | \row \li QDate \li QDateEdit |
115 | \row \li QDateTime \li QDateTimeEdit |
116 | \row \li QPixmap \li QLabel |
117 | \row \li QString \li QLineEdit |
118 | \row \li QTime \li QTimeEdit |
119 | \endtable |
120 | |
121 | Additional editors can be registered with the registerEditor() function. |
122 | |
123 | \sa QStyledItemDelegate, {Model/View Programming}, {Color Editor Factory Example} |
124 | */ |
125 | |
126 | /*! |
127 | \fn QItemEditorFactory::QItemEditorFactory() |
128 | |
129 | Constructs a new item editor factory. |
130 | */ |
131 | |
132 | /*! |
133 | Creates an editor widget with the given \a parent for the specified \a userType of data, |
134 | and returns it as a QWidget. |
135 | |
136 | \sa registerEditor() |
137 | */ |
138 | QWidget *QItemEditorFactory::createEditor(int userType, QWidget *parent) const |
139 | { |
140 | QItemEditorCreatorBase *creator = creatorMap.value(key: userType, defaultValue: 0); |
141 | if (!creator) { |
142 | const QItemEditorFactory *dfactory = defaultFactory(); |
143 | return dfactory == this ? nullptr : dfactory->createEditor(userType, parent); |
144 | } |
145 | return creator->createWidget(parent); |
146 | } |
147 | |
148 | /*! |
149 | Returns the property name used to access data for the given \a userType of data. |
150 | */ |
151 | QByteArray QItemEditorFactory::valuePropertyName(int userType) const |
152 | { |
153 | QItemEditorCreatorBase *creator = creatorMap.value(key: userType, defaultValue: 0); |
154 | if (!creator) { |
155 | const QItemEditorFactory *dfactory = defaultFactory(); |
156 | return dfactory == this ? QByteArray() : dfactory->valuePropertyName(userType); |
157 | } |
158 | return creator->valuePropertyName(); |
159 | } |
160 | |
161 | /*! |
162 | Destroys the item editor factory. |
163 | */ |
164 | QItemEditorFactory::~QItemEditorFactory() |
165 | { |
166 | //we make sure we delete all the QItemEditorCreatorBase |
167 | //this has to be done only once, hence the sort-unique idiom |
168 | std::vector<QItemEditorCreatorBase*> creators(creatorMap.cbegin(), creatorMap.cend()); |
169 | std::sort(first: creators.begin(), last: creators.end()); |
170 | const auto it = std::unique(first: creators.begin(), last: creators.end()); |
171 | qDeleteAll(begin: creators.begin(), end: it); |
172 | } |
173 | |
174 | /*! |
175 | Registers an item editor creator specified by \a creator for the given \a userType of data. |
176 | |
177 | \b{Note:} The factory takes ownership of the item editor creator and will destroy |
178 | it if a new creator for the same type is registered later. |
179 | |
180 | \sa createEditor() |
181 | */ |
182 | void QItemEditorFactory::registerEditor(int userType, QItemEditorCreatorBase *creator) |
183 | { |
184 | const auto it = creatorMap.constFind(key: userType); |
185 | if (it != creatorMap.cend()) { |
186 | QItemEditorCreatorBase *oldCreator = it.value(); |
187 | Q_ASSERT(oldCreator); |
188 | creatorMap.erase(it); |
189 | if (std::find(first: creatorMap.cbegin(), last: creatorMap.cend(), val: oldCreator) == creatorMap.cend()) |
190 | delete oldCreator; // if it is no more in use we can delete it |
191 | } |
192 | |
193 | creatorMap[userType] = creator; |
194 | } |
195 | |
196 | class QDefaultItemEditorFactory : public QItemEditorFactory |
197 | { |
198 | public: |
199 | inline QDefaultItemEditorFactory() {} |
200 | QWidget *createEditor(int userType, QWidget *parent) const override; |
201 | QByteArray valuePropertyName(int) const override; |
202 | }; |
203 | |
204 | QWidget *QDefaultItemEditorFactory::createEditor(int userType, QWidget *parent) const |
205 | { |
206 | switch (userType) { |
207 | #if QT_CONFIG(combobox) |
208 | case QMetaType::Bool: { |
209 | QBooleanComboBox *cb = new QBooleanComboBox(parent); |
210 | cb->setFrame(false); |
211 | cb->setSizePolicy(hor: QSizePolicy::Ignored, ver: cb->sizePolicy().verticalPolicy()); |
212 | return cb; } |
213 | #endif |
214 | #if QT_CONFIG(spinbox) |
215 | case QMetaType::UInt: { |
216 | QSpinBox *sb = new QUIntSpinBox(parent); |
217 | sb->setFrame(false); |
218 | sb->setMinimum(0); |
219 | sb->setMaximum(INT_MAX); |
220 | sb->setSizePolicy(hor: QSizePolicy::Ignored, ver: sb->sizePolicy().verticalPolicy()); |
221 | return sb; } |
222 | case QMetaType::Int: { |
223 | QSpinBox *sb = new QSpinBox(parent); |
224 | sb->setFrame(false); |
225 | sb->setMinimum(INT_MIN); |
226 | sb->setMaximum(INT_MAX); |
227 | sb->setSizePolicy(hor: QSizePolicy::Ignored, ver: sb->sizePolicy().verticalPolicy()); |
228 | return sb; } |
229 | #endif |
230 | #if QT_CONFIG(datetimeedit) |
231 | case QMetaType::QDate: { |
232 | QDateTimeEdit *ed = new QDateEdit(parent); |
233 | ed->setFrame(false); |
234 | return ed; } |
235 | case QMetaType::QTime: { |
236 | QDateTimeEdit *ed = new QTimeEdit(parent); |
237 | ed->setFrame(false); |
238 | return ed; } |
239 | case QMetaType::QDateTime: { |
240 | QDateTimeEdit *ed = new QDateTimeEdit(parent); |
241 | ed->setFrame(false); |
242 | return ed; } |
243 | #endif |
244 | #if QT_CONFIG(label) |
245 | case QMetaType::QPixmap: |
246 | return new QLabel(parent); |
247 | #endif |
248 | #if QT_CONFIG(spinbox) |
249 | case QMetaType::Double: { |
250 | QDoubleSpinBox *sb = new QDoubleSpinBox(parent); |
251 | sb->setFrame(false); |
252 | sb->setMinimum(-DBL_MAX); |
253 | sb->setMaximum(DBL_MAX); |
254 | sb->setSizePolicy(hor: QSizePolicy::Ignored, ver: sb->sizePolicy().verticalPolicy()); |
255 | return sb; } |
256 | #endif |
257 | #if QT_CONFIG(lineedit) |
258 | case QMetaType::QString: |
259 | default: { |
260 | // the default editor is a lineedit |
261 | QExpandingLineEdit *le = new QExpandingLineEdit(parent); |
262 | le->setFrame(le->style()->styleHint(stylehint: QStyle::SH_ItemView_DrawDelegateFrame, opt: nullptr, widget: le)); |
263 | if (!le->style()->styleHint(stylehint: QStyle::SH_ItemView_ShowDecorationSelected, opt: nullptr, widget: le)) |
264 | le->setWidgetOwnsGeometry(true); |
265 | return le; } |
266 | #else |
267 | default: |
268 | break; |
269 | #endif |
270 | } |
271 | return nullptr; |
272 | } |
273 | |
274 | QByteArray QDefaultItemEditorFactory::valuePropertyName(int userType) const |
275 | { |
276 | switch (userType) { |
277 | #if QT_CONFIG(combobox) |
278 | case QMetaType::Bool: |
279 | return "currentIndex" ; |
280 | #endif |
281 | #if QT_CONFIG(spinbox) |
282 | case QMetaType::UInt: |
283 | case QMetaType::Int: |
284 | case QMetaType::Double: |
285 | return "value" ; |
286 | #endif |
287 | #if QT_CONFIG(datetimeedit) |
288 | case QMetaType::QDate: |
289 | return "date" ; |
290 | case QMetaType::QTime: |
291 | return "time" ; |
292 | case QMetaType::QDateTime: |
293 | return "dateTime" ; |
294 | #endif |
295 | case QMetaType::QString: |
296 | default: |
297 | // the default editor is a lineedit |
298 | return "text" ; |
299 | } |
300 | } |
301 | |
302 | static QItemEditorFactory *q_default_factory = nullptr; |
303 | struct QDefaultFactoryCleaner |
304 | { |
305 | inline QDefaultFactoryCleaner() {} |
306 | ~QDefaultFactoryCleaner() { delete q_default_factory; q_default_factory = nullptr; } |
307 | }; |
308 | |
309 | /*! |
310 | Returns the default item editor factory. |
311 | |
312 | \sa setDefaultFactory() |
313 | */ |
314 | const QItemEditorFactory *QItemEditorFactory::defaultFactory() |
315 | { |
316 | static const QDefaultItemEditorFactory factory; |
317 | if (q_default_factory) |
318 | return q_default_factory; |
319 | return &factory; |
320 | } |
321 | |
322 | /*! |
323 | Sets the default item editor factory to the given \a factory. |
324 | Both new and existing delegates will use the new factory. |
325 | |
326 | \sa defaultFactory() |
327 | */ |
328 | void QItemEditorFactory::setDefaultFactory(QItemEditorFactory *factory) |
329 | { |
330 | static const QDefaultFactoryCleaner cleaner; |
331 | delete q_default_factory; |
332 | q_default_factory = factory; |
333 | } |
334 | |
335 | /*! |
336 | \class QItemEditorCreatorBase |
337 | \brief The QItemEditorCreatorBase class provides an abstract base class that |
338 | must be subclassed when implementing new item editor creators. |
339 | \since 4.2 |
340 | \ingroup model-view |
341 | \inmodule QtWidgets |
342 | |
343 | QItemEditorCreatorBase objects are specialized widget factories that |
344 | provide editor widgets for one particular QVariant data type. They |
345 | are used by QItemEditorFactory to create editors for |
346 | \l{QStyledItemDelegate}s. Creator bases must be registered with |
347 | QItemEditorFactory::registerEditor(). |
348 | |
349 | An editor should provide a user property for the data it edits. |
350 | QItemDelagates can then access the property using Qt's |
351 | \l{Meta-Object System}{meta-object system} to set and retrieve the |
352 | editing data. A property is set as the user property with the USER |
353 | keyword: |
354 | |
355 | \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 0 |
356 | |
357 | If the editor does not provide a user property, it must return the |
358 | name of the property from valuePropertyName(); delegates will then |
359 | use the name to access the property. If a user property exists, |
360 | item delegates will not call valuePropertyName(). |
361 | |
362 | QStandardItemEditorCreator is a convenience template class that can be used |
363 | to register widgets without the need to subclass QItemEditorCreatorBase. |
364 | |
365 | \sa QStandardItemEditorCreator, QItemEditorFactory, |
366 | {Model/View Programming}, {Color Editor Factory Example} |
367 | */ |
368 | |
369 | /*! |
370 | \fn QItemEditorCreatorBase::~QItemEditorCreatorBase() |
371 | |
372 | Destroys the editor creator object. |
373 | */ |
374 | QItemEditorCreatorBase::~QItemEditorCreatorBase() |
375 | { |
376 | |
377 | } |
378 | |
379 | /*! |
380 | \fn QWidget *QItemEditorCreatorBase::createWidget(QWidget *parent) const |
381 | |
382 | Returns an editor widget with the given \a parent. |
383 | |
384 | When implementing this function in subclasses of this class, you must |
385 | construct and return new editor widgets with the parent widget specified. |
386 | */ |
387 | |
388 | /*! |
389 | \fn QByteArray QItemEditorCreatorBase::valuePropertyName() const |
390 | |
391 | Returns the name of the property used to get and set values in the creator's |
392 | editor widgets. |
393 | |
394 | When implementing this function in subclasses, you must ensure that the |
395 | editor widget's property specified by this function can accept the type |
396 | the creator is registered for. For example, a creator which constructs |
397 | QCheckBox widgets to edit boolean values would return the |
398 | \l{QCheckBox::checkable}{checkable} property name from this function, |
399 | and must be registered in the item editor factory for the QMetaType::Bool |
400 | type. |
401 | |
402 | Note: Since Qt 4.2 the item delegates query the user property of widgets, |
403 | and only call this function if the widget has no user property. You can |
404 | override this behavior by reimplementing QAbstractItemDelegate::setModelData() |
405 | and QAbstractItemDelegate::setEditorData(). |
406 | |
407 | \sa QMetaObject::userProperty(), QItemEditorFactory::registerEditor() |
408 | */ |
409 | |
410 | /*! |
411 | \class QItemEditorCreator |
412 | \brief The QItemEditorCreator class makes it possible to create |
413 | item editor creator bases without subclassing |
414 | QItemEditorCreatorBase. |
415 | |
416 | \since 4.2 |
417 | \ingroup model-view |
418 | \inmodule QtWidgets |
419 | |
420 | QItemEditorCreator is a convenience template class. It uses |
421 | the template class to create editors for QItemEditorFactory. |
422 | This way, it is not necessary to subclass |
423 | QItemEditorCreatorBase. |
424 | |
425 | \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 1 |
426 | |
427 | The constructor takes the name of the property that contains the |
428 | editing data. QStyledItemDelegate can then access the property by name |
429 | when it sets and retrieves editing data. Only use this class if |
430 | your editor does not define a user property (using the USER |
431 | keyword in the Q_PROPERTY macro). If the widget has a user |
432 | property, you should use QStandardItemEditorCreator instead. |
433 | |
434 | \sa QItemEditorCreatorBase, QStandardItemEditorCreator, |
435 | QItemEditorFactory, {Color Editor Factory Example} |
436 | */ |
437 | |
438 | /*! |
439 | \fn template <class T> QItemEditorCreator<T>::QItemEditorCreator(const QByteArray &valuePropertyName) |
440 | |
441 | Constructs an editor creator object using \a valuePropertyName |
442 | as the name of the property to be used for editing. The |
443 | property name is used by QStyledItemDelegate when setting and |
444 | getting editor data. |
445 | |
446 | Note that the \a valuePropertyName is only used if the editor |
447 | widget does not have a user property defined. |
448 | */ |
449 | |
450 | /*! |
451 | \fn template <class T> QWidget *QItemEditorCreator<T>::createWidget(QWidget *parent) const |
452 | \reimp |
453 | */ |
454 | |
455 | /*! |
456 | \fn template <class T> QByteArray QItemEditorCreator<T>::valuePropertyName() const |
457 | \reimp |
458 | */ |
459 | |
460 | /*! |
461 | \class QStandardItemEditorCreator |
462 | |
463 | \brief The QStandardItemEditorCreator class provides the |
464 | possibility to register widgets without having to subclass |
465 | QItemEditorCreatorBase. |
466 | |
467 | \since 4.2 |
468 | \ingroup model-view |
469 | \inmodule QtWidgets |
470 | |
471 | This convenience template class makes it possible to register widgets without |
472 | having to subclass QItemEditorCreatorBase. |
473 | |
474 | Example: |
475 | |
476 | \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 2 |
477 | |
478 | Setting the \c editorFactory created above in an item delegate via |
479 | QStyledItemDelegate::setItemEditorFactory() makes sure that all values of type |
480 | QMetaType::QDateTime will be edited in \c{MyFancyDateTimeEdit}. |
481 | |
482 | The editor must provide a user property that will contain the |
483 | editing data. The property is used by \l{QStyledItemDelegate}s to set |
484 | and retrieve the data (using Qt's \l{Meta-Object |
485 | System}{meta-object system}). You set the user property with |
486 | the USER keyword: |
487 | |
488 | \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 3 |
489 | |
490 | \sa QItemEditorCreatorBase, QItemEditorCreator, |
491 | QItemEditorFactory, QStyledItemDelegate, {Color Editor Factory Example} |
492 | */ |
493 | |
494 | /*! |
495 | \fn template <class T> QStandardItemEditorCreator<T>::QStandardItemEditorCreator() |
496 | |
497 | Constructs an editor creator object. |
498 | */ |
499 | |
500 | /*! |
501 | \fn template <class T> QWidget *QStandardItemEditorCreator<T>::createWidget(QWidget *parent) const |
502 | \reimp |
503 | */ |
504 | |
505 | /*! |
506 | \fn template <class T> QByteArray QStandardItemEditorCreator<T>::valuePropertyName() const |
507 | \reimp |
508 | */ |
509 | |
510 | #if QT_CONFIG(lineedit) |
511 | |
512 | QExpandingLineEdit::QExpandingLineEdit(QWidget *parent) |
513 | : QLineEdit(parent), originalWidth(-1), widgetOwnsGeometry(false) |
514 | { |
515 | connect(sender: this, SIGNAL(textChanged(QString)), receiver: this, SLOT(resizeToContents())); |
516 | updateMinimumWidth(); |
517 | } |
518 | |
519 | void QExpandingLineEdit::changeEvent(QEvent *e) |
520 | { |
521 | switch (e->type()) |
522 | { |
523 | case QEvent::FontChange: |
524 | case QEvent::StyleChange: |
525 | case QEvent::ContentsRectChange: |
526 | updateMinimumWidth(); |
527 | break; |
528 | default: |
529 | break; |
530 | } |
531 | |
532 | QLineEdit::changeEvent(e); |
533 | } |
534 | |
535 | void QExpandingLineEdit::updateMinimumWidth() |
536 | { |
537 | const QMargins tm = textMargins(); |
538 | const QMargins cm = contentsMargins(); |
539 | const int width = tm.left() + tm.right() + cm.left() + cm.right() + 4 /*horizontalMargin in qlineedit.cpp*/; |
540 | |
541 | QStyleOptionFrame opt; |
542 | initStyleOption(option: &opt); |
543 | |
544 | int minWidth = style()->sizeFromContents(ct: QStyle::CT_LineEdit, opt: &opt, contentsSize: QSize(width, 0), w: this).width(); |
545 | setMinimumWidth(minWidth); |
546 | } |
547 | |
548 | void QExpandingLineEdit::resizeToContents() |
549 | { |
550 | int oldWidth = width(); |
551 | if (originalWidth == -1) |
552 | originalWidth = oldWidth; |
553 | if (QWidget *parent = parentWidget()) { |
554 | QPoint position = pos(); |
555 | int hintWidth = minimumWidth() + fontMetrics().horizontalAdvance(displayText()); |
556 | int parentWidth = parent->width(); |
557 | int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x(); |
558 | int newWidth = qBound(min: qMin(a: originalWidth, b: maxWidth), val: hintWidth, max: maxWidth); |
559 | if (widgetOwnsGeometry) |
560 | setMaximumWidth(newWidth); |
561 | if (isRightToLeft()) |
562 | move(ax: position.x() - newWidth + oldWidth, ay: position.y()); |
563 | resize(w: newWidth, h: height()); |
564 | } |
565 | } |
566 | |
567 | #endif // QT_CONFIG(lineedit) |
568 | |
569 | #if QT_CONFIG(combobox) |
570 | |
571 | QBooleanComboBox::QBooleanComboBox(QWidget *parent) |
572 | : QComboBox(parent) |
573 | { |
574 | addItem(atext: QComboBox::tr(s: "False" )); |
575 | addItem(atext: QComboBox::tr(s: "True" )); |
576 | } |
577 | |
578 | void QBooleanComboBox::setValue(bool value) |
579 | { |
580 | setCurrentIndex(value ? 1 : 0); |
581 | } |
582 | |
583 | bool QBooleanComboBox::value() const |
584 | { |
585 | return (currentIndex() == 1); |
586 | } |
587 | |
588 | #endif // QT_CONFIG(combobox) |
589 | |
590 | QT_END_NAMESPACE |
591 | |
592 | #if QT_CONFIG(lineedit) || QT_CONFIG(combobox) |
593 | #include "qitemeditorfactory.moc" |
594 | #endif |
595 | |
596 | #include "moc_qitemeditorfactory_p.cpp" |
597 | |