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
widgetdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
11#include <QtDesigner/private/ui4_p.h>
12
13#include <QtDesigner/propertysheet.h>
14#include <QtDesigner/qextensionmanager.h>
15#include <QtDesigner/abstractformeditor.h>
16
17#include <QtUiPlugin/customwidget.h>
18#include <QtWidgets/QtWidgets>
19#ifdef QT_OPENGLWIDGETS_LIB
20#include <QtOpenGLWidgets/qopenglwidget.h>
21#endif
22
23#include <QtCore/qxmlstream.h>
24
25#include <QtCore/qcoreapplication.h>
26#include <QtCore/qdebug.h>
27#include <QtCore/qmetaobject.h>
28#include <QtCore/qset.h>
29#include <QtCore/qstring.h>
30#include <QtCore/qtextstream.h>
31#include <QtCore/qcoreapplication.h>
32
33#include <memory>
34
35QT_BEGIN_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
39namespace {
40 enum { debugWidgetDataBase = 0 };
41}
42
43namespace qdesigner_internal {
44
45using namespace Qt::StringLiterals;
46
47// ----------------------------------------------------------
57
59{
60 return m_name;
61}
62
64{
65 m_name = name;
66}
67
69{
70 return m_group;
71}
72
77
79{
80 return m_toolTip;
81}
82
87
89{
90 return m_whatsThis;
91}
92
97
102
107
109{
110 return m_icon;
111}
112
114{
115 m_icon = icon;
116}
117
119{
120 return m_compat;
121}
122
124{
125 m_compat = b;
126}
127
129{
130 return m_container;
131}
132
134{
135 m_container = b;
136}
137
139{
140 return m_custom;
141}
142
144{
145 m_custom = b;
146}
147
149{
150 return m_pluginPath;
151}
152
157
159{
160 return m_promoted;
161}
162
164{
165 m_promoted = b;
166}
167
169{
170 return m_extends;
171}
172
174{
175 m_extends = s;
176}
177
182
187
192
197
202
207
212
217
236
241
246
247static void addWidgetItem(WidgetDataBase *wdb, const char *name, const QMetaObject &mo,
248 const char *comment)
249{
251 if (auto *base = mo.superClass())
253 if (comment[0])
255 wdb->append(item);
256}
257
258// ----------------------------------------------------------
261 m_core(core)
262{
263#define DECLARE_LAYOUT(L, C)
264#define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C)
265#define DECLARE_WIDGET(W, C) addWidgetItem(this, #W, W::staticMetaObject, C);
266
267#include <widgets.table>
268
269#undef DECLARE_COMPAT_WIDGET
270#undef DECLARE_LAYOUT
271#undef DECLARE_WIDGET
272#undef DECLARE_WIDGET_1
273
275 QCoreApplication::translate("WidgetDataBase",
276 "Abstract base class that cannot be instantiated. For promotion/custom widget usage only.");
277
278#if QT_CONFIG(abstractbutton)
279 auto *abItem = new WidgetDataBaseItem(u"QAbstractButton"_s);
281 abItem->setBaseClassName(u"QWidget"_s);
282 append(abItem);
283#endif // QT_CONFIG(abstractbutton)
284
285#if QT_CONFIG(itemviews)
286 auto *aivItem = new WidgetDataBaseItem(u"QAbstractItemView"_s);
287 aivItem->setBaseClassName(u"QAbstractScrollArea"_s);
290#endif // QT_CONFIG(itemviews)
291
292 append(new WidgetDataBaseItem(u"Line"_s));
293 append(new WidgetDataBaseItem(u"Spacer"_s));
294 append(new WidgetDataBaseItem(u"QSplitter"_s));
295 append(new WidgetDataBaseItem(u"QLayoutWidget"_s));
296 // QDesignerWidget is used as central widget and as container for tab widgets, etc.
297 WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(u"QDesignerWidget"_s);
300 append(new WidgetDataBaseItem(u"QDesignerDialog"_s));
301 append(new WidgetDataBaseItem(u"QDesignerMenu"_s));
302 append(new WidgetDataBaseItem(u"QDesignerMenuBar"_s));
303 append(new WidgetDataBaseItem(u"QDesignerDockWidget"_s));
304 append(new WidgetDataBaseItem(u"QAction"_s));
305 append(new WidgetDataBaseItem(u"QButtonGroup"_s));
306
307 // ### remove me
308 // ### check the casts
309
310#if 0 // ### enable me after 4.1
311 item(indexOfClassName(u"QToolBar"_s))->setContainer(true);
312#endif
313
314 item(indexOfClassName(u"QTabWidget"_s))->setContainer(true);
315 item(indexOfClassName(u"QGroupBox"_s))->setContainer(true);
316 item(indexOfClassName(u"QScrollArea"_s))->setContainer(true);
317 item(indexOfClassName(u"QStackedWidget"_s))->setContainer(true);
318 item(indexOfClassName(u"QToolBox"_s))->setContainer(true);
319 item(indexOfClassName(u"QFrame"_s))->setContainer(true);
320 item(indexOfClassName(u"QLayoutWidget"_s))->setContainer(true);
321 item(indexOfClassName(u"QDesignerWidget"_s))->setContainer(true);
322 item(indexOfClassName(u"QDesignerDialog"_s))->setContainer(true);
323 item(indexOfClassName(u"QSplitter"_s))->setContainer(true);
324 item(indexOfClassName(u"QMainWindow"_s))->setContainer(true);
325 item(indexOfClassName(u"QDockWidget"_s))->setContainer(true);
326 item(indexOfClassName(u"QDesignerDockWidget"_s))->setContainer(true);
327 item(indexOfClassName(u"QMdiArea"_s))->setContainer(true);
328 item(indexOfClassName(u"QWizard"_s))->setContainer(true);
329 item(indexOfClassName(u"QWizardPage"_s))->setContainer(true);
330
331 item(indexOfClassName(u"QWidget"_s))->setContainer(true);
332 item(indexOfClassName(u"QDialog"_s))->setContainer(true);
333}
334
335WidgetDataBase::~WidgetDataBase() = default;
336
341
357
359 const QDesignerCustomWidgetData &data)
360{
361 WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group());
362 item->setContainer(c->isContainer());
363 item->setCustom(true);
364 item->setIcon(c->icon());
365 item->setIncludeFile(c->includeFile());
366 item->setToolTip(c->toolTip());
367 item->setWhatsThis(c->whatsThis());
368 item->setPluginPath(data.pluginPath());
369 item->setAddPageMethod(data.xmlAddPageMethod());
370 item->setExtends(data.xmlExtends());
371 return item;
372}
373
375{
376 // 1) create a map of existing custom classes
379 for (qsizetype i = 0, count = m_items.size(); i < count; ++i) {
381 if (item->isCustom() && !item->isPromoted())
383 else
385 }
386 // 2) create a list plugins
392
393 // 3) replace custom classes or add new ones, remove them from existingCustomClasses,
394 // leaving behind deleted items
395 unsigned replacedPlugins = 0;
396 unsigned addedPlugins = 0;
397 unsigned removedPlugins = 0;
398 if (!pluginList.isEmpty()) {
403 // Add new class.
405 designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName));
406 } else {
408 addedPlugins++;
409 }
410 } else {
411 // replace existing info
412 const auto existingIndex = existingIt.value();
413 delete m_items[existingIndex];
417
418 }
419 }
420 }
421 // 4) remove classes that have not been matched. The stored indexes become invalid while deleting.
423 const int index = indexOfClassName(it.key());
424 if (index != -1) {
425 remove(index);
427 }
428 }
429
431 qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted.";
432}
433
435{
437 delete m_items.takeAt(index);
438}
439
441{
444 // Create non-widgets, widgets in order
445 QObject* object = factory->createObject(name, nullptr);
446 if (!object)
447 object = factory->createWidget(name, nullptr);
448 if (!object) {
449 qDebug() << "** WARNING Factory failed to create " << name;
450 return {};
451 }
452 // Get properties from sheet.
455 const int propertyCount = sheet->count();
456 for (int i = 0; i < propertyCount; ++i) {
458 }
459 }
460 delete object;
461 return result;
462}
463
473
475{
476 // At this point, grab the default icons for the non-custom widgets from
477 // the widget box. They will show up in the object inspector.
479 const int itemCount = count();
480 for (int i = 0; i < itemCount; ++i) {
482 if (!dbItem->isCustom() && dbItem->icon().isNull()) {
483 // Careful not to catch the layout icons when looking for
484 // QWidget
485 const QString name = dbItem->name();
486 if (name == "QWidget"_L1) {
487 dbItem->setIcon(wb->iconForWidget(name, u"Containers"_s));
488 } else {
490 }
491 }
492 }
493 }
494}
495
496// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates)
497
498enum { NewFormWidth = 400, NewFormHeight = 300 };
499
500// Check if class is suitable to generate a form from
501static inline bool isExistingTemplate(const QString &className)
502{
503 return className == "QWidget"_L1 || className == "QDialog"_L1 || className == "QMainWindow"_L1;
504}
505
506// Check if class is suitable to generate a form from
507static inline bool suitableForNewForm(const QString &className)
508{
509 if (className.isEmpty()) // Missing custom widget information
510 return false;
511 if (className == "QSplitter"_L1)
512 return false;
513 if (className.startsWith("QDesigner"_L1) || className.startsWith("QLayout"_L1))
514 return false;
515 return true;
516}
517
518// Return a list of widget classes from which new forms can be generated.
519// Suitable for 'New form' wizards in integrations.
521{
522 static QStringList rc;
523 if (rc.isEmpty()) {
525 const int widgetCount = wdb->count();
526 for (int i = 0; i < widgetCount; i++) {
528 if (item->isContainer() && !item->isCustom() && !item->isPromoted()) {
529 const QString name = item->name(); // Standard Widgets: no existing templates
531 rc += name;
532 }
533 }
534 }
535 return rc;
536}
537
538// Return a list of custom widget classes from which new forms can be generated.
539// Suitable for 'New form' wizards in integrations.
541{
544 const int widgetCount = wdb->count();
545 for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class.
547 if (item->isContainer() && item->isCustom() && !item->isPromoted()) {
549 rc += item->name();
550 }
551 }
552 return rc;
553}
554
555// Get XML for a new form from the widget box. Change objectName/geometry
556// properties to be suitable for new forms
557static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
558{
559 QDesignerWidgetBoxInterface::Widget widget;
560 const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget);
561 if (!found)
562 return QString();
563 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false));
564 if (!domUI)
565 return QString();
566 domUI->setAttributeVersion(u"4.0"_s);
567 DomWidget *domWidget = domUI->elementWidget();
568 if (!domWidget)
569 return QString();
570 // Properties: Remove the "objectName" property in favour of the name attribute and check geometry.
571 domWidget->setAttributeName(objectName);
572 QList<DomProperty *> properties = domWidget->elementProperty();
573 for (auto it = properties.begin(); it != properties.end(); ) {
574 DomProperty *property = *it;
575 if (property->attributeName() == "objectName"_L1) { // remove "objectName"
576 it = properties.erase(it);
577 delete property;
578 } else {
579 if (property->attributeName() == "geometry"_L1) { // Make sure form is at least 400, 300
580 if (DomRect *geom = property->elementRect()) {
581 if (geom->elementWidth() < NewFormWidth)
582 geom->setElementWidth(NewFormWidth);
583 if (geom->elementHeight() < NewFormHeight)
584 geom->setElementHeight(NewFormHeight);
585 }
586 }
587 ++it;
588 }
589 }
590 // Add a window title property
591 DomString *windowTitleString = new DomString;
592 windowTitleString->setText(objectName);
593 DomProperty *windowTitleProperty = new DomProperty;
594 windowTitleProperty->setAttributeName(u"windowTitle"_s);
595 windowTitleProperty->setElementString(windowTitleString);
596 properties.push_back(windowTitleProperty);
597 // ------
598 domWidget->setElementProperty(properties);
599 // Embed in in DomUI and get string. Omit the version number.
600 domUI->setElementClass(objectName);
601
602 QString rc;
603 { // Serialize domUI
604 QXmlStreamWriter writer(&rc);
605 writer.setAutoFormatting(true);
606 writer.setAutoFormattingIndent(1);
607 writer.writeStartDocument();
608 domUI->write(writer);
609 writer.writeEndDocument();
610 }
611 return rc;
612}
613
614// Generate default standard ui new form xml based on the class passed on as similarClassName.
615static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
616{
617 QString rc;
618 QTextStream str(&rc);
619 str << R"(<ui version="4.0"><class>)" << name << "</class>"
620 << R"(<widget class=")" << className << R"(" name=")" << name << R"(">)"
621 << R"(<property name="geometry" ><rect><x>0</x><y>0</y><width>)"
622 << NewFormWidth << "</width><height>" << NewFormHeight << "</height></rect></property>"
623 << R"(<property name="windowTitle"><string>)" << name << "</string></property>\n";
624
625 if (similarClassName == "QMainWindow"_L1) {
626 str << R"(<widget class="QWidget" name="centralwidget"/>)";
627 } else if (similarClassName == "QWizard"_L1) {
628 str << R"(<widget class="QWizardPage" name="wizardPage1"/><widget class="QWizardPage" name="wizardPage2"/>)";
629 } else if (similarClassName == "QDockWidget"_L1) {
630 str << R"(<widget class="QWidget" name="dockWidgetContents"/>)";
631 }
632 str << "</widget></ui>\n";
633 return rc;
634}
635
636// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses().
638{
639 // How to find suitable XML for a class:
640 // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there).
642 if (!widgetBoxXml.isEmpty())
643 return widgetBoxXml;
644 // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should
645 // be left over. Generate something that is similar to the default templates. Find a similar class.
647 QString similarClass = u"QWidget"_s;
648 const int index = wdb->indexOfClassName(className);
649 if (index != -1) {
652 }
653 // Generate standard ui based on the class passed on as baseClassName.
655 return rc;
656}
657
658// Set a fixed size on a XML template
660{
661 std::unique_ptr<DomUI> domUI(QDesignerWidgetBox::xmlToUi(u"Form"_s, xml, false));
662 if (!domUI)
663 return QString();
665 if (!domWidget)
666 return QString();
667 // Properties: Find/Ensure the geometry, minimum and maximum sizes properties
668 const QString geometryPropertyName = u"geometry"_s;
669 const QString minimumSizePropertyName = u"minimumSize"_s;
670 const QString maximumSizePropertyName = u"maximumSize"_s;
671 DomProperty *geomProperty = nullptr;
674
676 for (DomProperty *p : std::as_const(properties)) {
677 const QString name = p->attributeName();
678 if (name == geometryPropertyName) {
679 geomProperty = p;
680 } else if (name == minimumSizePropertyName) {
682 } else if (name == maximumSizePropertyName) {
684 }
685 }
686 if (!geomProperty) {
691 }
692 if (fixed) {
693 if (!minimumSizeProperty) {
698 }
699 if (!maximumSizeProperty) {
704 }
705 }
706 // Set values of geometry, minimum and maximum sizes properties
707 const int width = size.width();
708 const int height = size.height();
712 }
713 if (fixed) {
717 }
721 }
722 }
723 // write back
725
726 QString rc;
727 { // serialize domUI
734 }
735
736 return rc;
737}
738
739// ---- free functions
749
758
759
760/* Appends a derived class to the database inheriting the data of the base class. Used
761 for custom and promoted widgets.
762
763 Depending on whether an entry exists, the existing or a newly created entry is
764 returned. A return value of 0 indicates that the base class could not be found. */
765
766QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface *
768 const QString &className, const QString &group,
769 const QString &baseClassName,
770 const QString &includeFile,
771 bool promoted, bool custom)
772{
774 qDebug() << "appendDerived " << className << " derived from " << baseClassName;
775 // Check.
777 qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.",
779 return nullptr;
780 }
781 // Check whether item already exists.
784 if ( existingIndex != -1)
786 if (derivedItem) {
787 // Check the existing item for base class mismatch. This will likely
788 // happen when loading a file written by an instance with missing plugins.
789 // In that case, just warn and ignore the file properties.
790 //
791 // An empty base class indicates that it is not known (for example, for custom plugins).
792 // In this case, the widget DB is later updated once the widget is created
793 // by DOM (by querying the metaobject). Suppress the warning.
796 return derivedItem;
797
798 // Warn about mismatches
800 "The file contains a custom widget '%1' whose base class (%2)"
801 " differs from the current entry in the widget database (%3)."
802 " The widget database is left unchanged.").
804 return derivedItem;
805 }
806 // Create this item, inheriting its base properties
808 if (baseIndex == -1) {
810 qDebug() << "appendDerived failed due to missing base class";
811 return nullptr;
812 }
815 // Sort of hack: If base class is QWidget, we most likely
816 // do not want to inherit the container attribute.
817 if (baseItem->name() == "QWidget"_L1)
819 // set new props
827 return derivedItem;
828}
829
830/* Return a list of database items to which a class can be promoted to. */
831
832QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList
834 const QString &baseClassName)
835{
837 // find existing promoted widgets deriving from base.
838 const int count = db->count();
839 for (int i = 0; i < count; ++i) {
841 if (item->isPromoted() && item->extends() == baseClassName) {
843 }
844 }
845 return rc;
846}
847} // namespace qdesigner_internal
848
849QT_END_NAMESPACE
Auxiliary methods to store/retrieve settings.
static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName)
static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name)
static bool isExistingTemplate(const QString &className)
static WidgetDataBaseItem * createCustomWidgetItem(const QDesignerCustomWidgetInterface *c, const QDesignerCustomWidgetData &data)
static void addWidgetItem(WidgetDataBase *wdb, const char *name, const QMetaObject &mo, const char *comment)
static bool suitableForNewForm(const QString &className)
#define QDESIGNER_SHARED_EXPORT