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
formbuilder.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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
5#include "formbuilder.h"
7#include "ui4_p.h"
8
9#include <QtUiPlugin/customwidget.h>
10#include <QtWidgets/QtWidgets>
11
12#ifdef QT_OPENGLWIDGETS_LIB
13# include <QtOpenGLWidgets/qopenglwidget.h>
14#endif
15
17
18using namespace Qt::StringLiterals;
19
20#ifdef QFORMINTERNAL_NAMESPACE
21namespace QFormInternal {
22#endif
23
24/*!
25 \class QFormBuilder
26
27 \brief The QFormBuilder class is used to dynamically construct
28 user interfaces from UI files at run-time.
29
30 \inmodule QtDesigner
31
32 The QFormBuilder class provides a mechanism for dynamically
33 creating user interfaces at run-time, based on UI files
34 created with \QD. For example:
35
36 \snippet lib/tools_designer_src_lib_uilib_formbuilder.cpp 0
37
38 By including the user interface in the example's resources (\c
39 myForm.qrc), we ensure that it will be present when the example is
40 run:
41
42 \snippet lib/tools_designer_src_lib_uilib_formbuilder.cpp 1
44 QFormBuilder extends the QAbstractFormBuilder base class with a
45 number of functions that are used to support custom widget
46 plugins:
47
48 \list
49 \li pluginPaths() returns the list of paths that the form builder
50 searches when loading custom widget plugins.
51 \li addPluginPath() allows additional paths to be registered with
52 the form builder.
53 \li setPluginPath() is used to replace the existing list of paths
54 with a list obtained from some other source.
55 \li clearPluginPaths() removes all paths registered with the form
56 builder.
57 \li customWidgets() returns a list of interfaces to plugins that
58 can be used to create new instances of registered custom widgets.
59 \endlist
60
61 The QFormBuilder class is typically used by custom components and
62 applications that embed \QD. Standalone applications that need to
63 dynamically generate user interfaces at run-time use the
64 QUiLoader class, found in the QtUiTools module.
65
66 \sa QAbstractFormBuilder, {Qt UI Tools}
67*/
68
69/*!
70 \fn QFormBuilder::QFormBuilder()
71
72 Constructs a new form builder.
73*/
74
75QFormBuilder::QFormBuilder() = default;
76
77/*!
78 Destroys the form builder.
79*/
80QFormBuilder::~QFormBuilder() = default;
81
82/*!
83 \internal
84*/
85QWidget *QFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget)
86{
87 if (!d->parentWidgetIsSet())
88 d->setParentWidget(parentWidget);
89 // Is this a QLayoutWidget with a margin of 0: Not a known page-based
90 // container and no method for adding pages registered.
91 d->setProcessingLayoutWidget(false);
92 if (ui_widget->attributeClass() == "QWidget"_L1 && !ui_widget->hasAttributeNative()
93 && parentWidget
94#if QT_CONFIG(mainwindow)
95 && !qobject_cast<QMainWindow *>(parentWidget)
96#endif
97#if QT_CONFIG(toolbox)
98 && !qobject_cast<QToolBox *>(parentWidget)
99#endif
100#if QT_CONFIG(stackedwidget)
101 && !qobject_cast<QStackedWidget *>(parentWidget)
102#endif
103#if QT_CONFIG(tabwidget)
104 && !qobject_cast<QTabWidget *>(parentWidget)
105#endif
106#if QT_CONFIG(scrollarea)
107 && !qobject_cast<QScrollArea *>(parentWidget)
108#endif
109#if QT_CONFIG(mdiarea)
110 && !qobject_cast<QMdiArea *>(parentWidget)
111#endif
112#if QT_CONFIG(dockwidget)
113 && !qobject_cast<QDockWidget *>(parentWidget)
114#endif
115 ) {
116 const QString parentClassName = QLatin1StringView(parentWidget->metaObject()->className());
117 if (!d->isCustomWidgetContainer(parentClassName))
118 d->setProcessingLayoutWidget(true);
119 }
120 return QAbstractFormBuilder::create(ui_widget, parentWidget);
121}
122
123
124/*!
125 \internal
126*/
127QWidget *QFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name)
128{
129 if (widgetName.isEmpty()) {
130 //: Empty class name passed to widget factory method
131 qWarning() << QCoreApplication::translate("QFormBuilder", "An empty class name was passed on to %1 (object name: '%2').").arg(QString::fromUtf8(Q_FUNC_INFO), name);
132 return nullptr;
133 }
134
135 QWidget *w = nullptr;
136
137#if QT_CONFIG(tabwidget)
138 if (qobject_cast<QTabWidget*>(parentWidget))
139 parentWidget = nullptr;
140#endif
141#if QT_CONFIG(stackedwidget)
142 if (qobject_cast<QStackedWidget*>(parentWidget))
143 parentWidget = nullptr;
144#endif
145#if QT_CONFIG(toolbox)
146 if (qobject_cast<QToolBox*>(parentWidget))
147 parentWidget = nullptr;
148#endif
149
150 // ### special-casing for Line (QFrame) -- fix for 4.2
151 do {
152 if (widgetName == "Line"_L1) {
153 w = new QFrame(parentWidget);
154 static_cast<QFrame*>(w)->setFrameStyle(QFrame::HLine | QFrame::Sunken);
155 break;
156 }
157 const QByteArray widgetNameBA = widgetName.toUtf8();
158 const char *widgetNameC = widgetNameBA.constData();
159 if (w) { // symmetry for macro
160 }
161
162#define DECLARE_LAYOUT(L, C)
163#define DECLARE_COMPAT_WIDGET(W, C)
164#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); }
165#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); }
166
167// AXVION DISABLE Style Qt-Generic-NoIrregularInclude Required functionality
168#include "widgets.table"
169// AXIVION ENABLE Style Qt-Generic-NoIrregularInclude
170
171#undef DECLARE_COMPAT_WIDGET
172#undef DECLARE_LAYOUT
173#undef DECLARE_WIDGET
174#undef DECLARE_WIDGET_1
175
176 if (w)
177 break;
178
179 // try with a registered custom widget
180 QDesignerCustomWidgetInterface *factory = d->m_customWidgets.value(widgetName);
181 if (factory != nullptr)
182 w = factory->createWidget(parentWidget);
183 } while(false);
184
185 if (w == nullptr) { // Attempt to instantiate base class of promoted/custom widgets
186 const QString baseClassName = d->customWidgetBaseClass(widgetName);
187 if (!baseClassName.isEmpty()) {
188 qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a custom widget of the class '%1'; defaulting to base class '%2'.").arg(widgetName, baseClassName);
189 return createWidget(baseClassName, parentWidget, name);
190 }
191 }
192
193 if (w == nullptr) { // nothing to do
194 qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a widget of the class '%1'.").arg(widgetName);
195 return nullptr;
196 }
197
198 w->setObjectName(name);
199
200 if (qobject_cast<QDialog *>(w))
201 w->setParent(parentWidget);
202
203 return w;
204}
205
206/*!
207 \internal
208*/
209QLayout *QFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name)
210{
211 QLayout *l = nullptr;
212
213 auto *parentWidget = qobject_cast<QWidget*>(parent);
214 auto *parentLayout = qobject_cast<QLayout*>(parent);
215
216 Q_ASSERT(parentWidget || parentLayout);
217
218#define DECLARE_WIDGET(W, C)
219#define DECLARE_COMPAT_WIDGET(W, C)
220
221#define DECLARE_LAYOUT(L, C)
222 if (layoutName == QLatin1StringView(#L)) {
223 Q_ASSERT(l == 0);
224 l = parentLayout
225 ? new L()
226 : new L(parentWidget);
227 }
228
229#include "widgets.table"
230
231#undef DECLARE_LAYOUT
232#undef DECLARE_COMPAT_WIDGET
233#undef DECLARE_WIDGET
234
235 if (l) {
236 l->setObjectName(name);
237 } else {
238 qWarning() << QCoreApplication::translate("QFormBuilder", "The layout type `%1' is not supported.").arg(layoutName);
239 }
240
241 return l;
242}
243
244/*!
245 \internal
246*/
247bool QFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout)
248{
249 return QAbstractFormBuilder::addItem(ui_item, item, layout);
250}
251
252/*!
253 \internal
254*/
255bool QFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget)
256{
257 return QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget);
258}
259
260/*!
261 \internal
262*/
263QWidget *QFormBuilder::widgetByName(QWidget *topLevel, const QString &name)
264{
265 Q_ASSERT(topLevel);
266 if (topLevel->objectName() == name)
267 return topLevel;
268
269 return topLevel->findChild<QWidget*>(name);
270}
271
272static QObject *objectByName(QWidget *topLevel, const QString &name)
273{
274 Q_ASSERT(topLevel);
275 if (topLevel->objectName() == name)
276 return topLevel;
277
278 return topLevel->findChild<QObject*>(name);
279}
280
281/*!
282 \internal
283*/
284void QFormBuilder::createConnections(DomConnections *ui_connections, QWidget *widget)
285{
286 Q_ASSERT(widget != nullptr);
287
288 if (ui_connections == nullptr)
289 return;
290
291 const auto &connections = ui_connections->elementConnection();
292 for (const DomConnection *c : connections) {
293 QObject *sender = objectByName(widget, c->elementSender());
294 QObject *receiver = objectByName(widget, c->elementReceiver());
295 if (!sender || !receiver)
296 continue;
297
298 QByteArray sig = c->elementSignal().toUtf8();
299 sig.prepend("2");
300 QByteArray sl = c->elementSlot().toUtf8();
301 sl.prepend("1");
302 QObject::connect(sender, sig.constData(), receiver, sl.constData());
303 }
304}
305
306/*!
307 \internal
308*/
309QWidget *QFormBuilder::create(DomUI *ui, QWidget *parentWidget)
310{
311 return QAbstractFormBuilder::create(ui, parentWidget);
312}
313
314/*!
315 \internal
316*/
317QLayout *QFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget)
318{
319 // Is this a temporary layout widget used to represent QLayout hierarchies in Designer?
320 // Set its margins to 0.
321 bool layoutWidget = d->processingLayoutWidget();
322 QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget);
323 if (layoutWidget) {
324 int left = 0, top = 0, right = 0, bottom = 0;
325 QFormBuilderExtra::getLayoutMargins(ui_layout->elementProperty(),
326 &left, &top, &right, &bottom);
327 l->setContentsMargins(left, top, right, bottom);
328 d->setProcessingLayoutWidget(false);
329 }
330 return l;
331}
332
333/*!
334 \internal
335*/
336QLayoutItem *QFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget)
337{
338 return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget);
339}
340
341/*!
342 \internal
343*/
344QAction *QFormBuilder::create(DomAction *ui_action, QObject *parent)
345{
346 return QAbstractFormBuilder::create(ui_action, parent);
347}
348
349/*!
350 \internal
351*/
352QActionGroup *QFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent)
353{
354 return QAbstractFormBuilder::create(ui_action_group, parent);
355}
356
357/*!
358 Returns the list of paths the form builder searches for plugins.
359
360 \sa addPluginPath()
361*/
362QStringList QFormBuilder::pluginPaths() const
363{
364 return d->m_pluginPaths;
365}
366
367/*!
368 Clears the list of paths that the form builder uses to search for
369 custom widget plugins.
370
371 \sa pluginPaths()
372*/
373void QFormBuilder::clearPluginPaths()
374{
375 d->m_pluginPaths.clear();
376 updateCustomWidgets();
377}
378
379/*!
380 Adds a new plugin path specified by \a pluginPath to the list of
381 paths that will be searched by the form builder when loading a
382 custom widget plugin.
383
384 \sa setPluginPath(), clearPluginPaths()
385*/
386void QFormBuilder::addPluginPath(const QString &pluginPath)
387{
388 d->m_pluginPaths.append(pluginPath);
389 updateCustomWidgets();
390}
391
392/*!
393 Sets the list of plugin paths to the list specified by \a pluginPaths.
394
395 \sa addPluginPath()
396*/
397void QFormBuilder::setPluginPath(const QStringList &pluginPaths)
398{
399 d->m_pluginPaths = pluginPaths;
400 updateCustomWidgets();
401}
402
403static void insertPlugins(QObject *o, QMap<QString, QDesignerCustomWidgetInterface*> *customWidgets)
404{
405 // step 1) try with a normal plugin
406 if (auto *iface = qobject_cast<QDesignerCustomWidgetInterface *>(o)) {
407 customWidgets->insert(iface->name(), iface);
408 return;
409 }
410 // step 2) try with a collection of plugins
411 if (auto *c = qobject_cast<QDesignerCustomWidgetCollectionInterface *>(o)) {
412 const auto &collectionCustomWidgets = c->customWidgets();
413 for (QDesignerCustomWidgetInterface *iface : collectionCustomWidgets)
414 customWidgets->insert(iface->name(), iface);
415 }
416}
417
418/*!
419 \internal
420*/
421void QFormBuilder::updateCustomWidgets()
422{
423 d->m_customWidgets.clear();
424
425#if QT_CONFIG(library)
426 for (const QString &path : std::as_const(d->m_pluginPaths)) {
427 const QDir dir(path);
428 const QStringList candidates = dir.entryList(QDir::Files);
429
430 for (const QString &plugin : candidates) {
431 if (!QLibrary::isLibrary(plugin))
432 continue;
433
434 QPluginLoader loader(path + u'/' + plugin);
435 if (loader.load())
436 insertPlugins(loader.instance(), &d->m_customWidgets);
437 }
438 }
439#endif // QT_CONFIG(library)
440
441 // Check statically linked plugins
442 const QObjectList staticPlugins = QPluginLoader::staticInstances();
443 for (QObject *o : staticPlugins)
444 insertPlugins(o, &d->m_customWidgets);
445}
446
447/*!
448 \fn QList<QDesignerCustomWidgetInterface*> QFormBuilder::customWidgets() const
449
450 Returns a list of the available plugins.
451*/
452QList<QDesignerCustomWidgetInterface*> QFormBuilder::customWidgets() const
453{
454 return d->m_customWidgets.values();
455}
456
457/*!
458 \internal
459*/
460
461void QFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties)
462{
463
464 if (properties.isEmpty())
465 return;
466
467 for (DomProperty *p : properties) {
468 const QVariant v = toVariant(o->metaObject(), p);
469 if (!v.isValid()) // QTBUG-33130, do not fall for QVariant(QString()).isNull() == true.
470 continue;
471
472 const QString attributeName = p->attributeName();
473 const bool isWidget = o->isWidgetType();
474 if (isWidget && o->parent() == d->parentWidget() && attributeName == "geometry"_L1) {
475 // apply only the size part of a geometry for the root widget
476 static_cast<QWidget*>(o)->resize(qvariant_cast<QRect>(v).size());
477 } else if (d->applyPropertyInternally(o, attributeName, v)) {
478 } else if (isWidget && qstrcmp("QFrame", o->metaObject()->className()) == 0
479 && attributeName == "orientation"_L1) {
480 // ### special-casing for Line (QFrame) -- try to fix me
481 o->setProperty("frameShape", v); // v is of QFrame::Shape enum
482 } else {
483 o->setProperty(attributeName.toUtf8().constData(), v);
484 }
485 }
486}
487
488#ifdef QFORMINTERNAL_NAMESPACE
489} // namespace QFormInternal
490#endif
491
492QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:432
static void insertPlugins(QObject *o, QMap< QString, QDesignerCustomWidgetInterface * > *customWidgets)
static QObject * objectByName(QWidget *topLevel, const QString &name)
Combined button and popup list for selecting options.