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
newformwidget.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// Qt-Security score:significant reason:default
4
6#include "ui_newformwidget.h"
11
12#include <QtDesigner/abstractformeditor.h>
13#include <QtDesigner/abstractformwindow.h>
14#include <QtDesigner/qextensionmanager.h>
15#include <QtDesigner/abstractlanguage.h>
16#include <QtDesigner/abstractwidgetdatabase.h>
17
18#include <QtCore/qdir.h>
19#include <QtCore/qfile.h>
20#include <QtCore/qfileinfo.h>
21#include <QtCore/qdebug.h>
22#include <QtCore/qbytearray.h>
23#include <QtCore/qbuffer.h>
24#include <QtCore/qdir.h>
25#include <QtCore/qtextstream.h>
26
27#include <QtWidgets/qapplication.h>
28#include <QtWidgets/qheaderview.h>
29#include <QtWidgets/qtreewidget.h>
30#include <QtGui/qpainter.h>
31#include <QtGui/qscreen.h>
32#include <QtWidgets/qpushbutton.h>
33
34#include <array>
35
36QT_BEGIN_NAMESPACE
37
38using namespace Qt::StringLiterals;
39
41enum { debugNewFormWidget = 0 };
42
44 // File name (templates from resources, paths)
46 // Class name (widgets from Widget data base)
48};
49
50static constexpr auto newFormObjectNameC = "Form"_L1;
51
52// Create a form name for an arbitrary class. If it is Qt, qtify it,
53// else return "Form".
54static QString formName(const QString &className)
55{
56 if (!className.startsWith(u'Q'))
57 return newFormObjectNameC;
58 QString rc = className;
59 rc.remove(0, 1);
60 return rc;
61}
62
63namespace qdesigner_internal {
64
70
71static constexpr std::array standardScreenSizes = {
72 StandardScreenSize{ "QVGA"_L1, {320, 240} },
73 StandardScreenSize{ "MIMXRT1050-EVKB"_L1, {480,272} },
74 StandardScreenSize{ "VGA"_L1, {640, 480} },
75 StandardScreenSize{ "WVGA"_L1, {800, 480} },
76 StandardScreenSize{ "WSVGA"_L1, {1024, 600} },
77 StandardScreenSize{ "XGA"_L1, {1024, 768} },
78 StandardScreenSize{ "MIMXRT1170-EVKB"_L1, {1280, 720} },
79 StandardScreenSize{ "WXGA"_L1, {1280, 800} }
80};
81
82static void populateSizeCombo(QComboBox *cb)
83{
84 cb->addItem(NewFormWidget::tr("Default size"), QSize{ 0, 0 });
85 for (const auto &size : standardScreenSizes) {
86 //: Standard screen size: %1 Name, %2 screen width, %3 screen height
87 QString portraitName = NewFormWidget::tr("%1 portrait (%2x%3)").arg(size.name)
88 .arg(size.size.height()).arg(size.size.width());
89 cb->addItem(portraitName, size.size.transposed());
90 //: Standard screen size: %1 Name, %2 screen width, %3 screen height
91 QString landscapeName = NewFormWidget::tr("%1 landscape (%2x%3)").arg(size.name)
92 .arg(size.size.width()).arg(size.size.height());
93 cb->addItem(landscapeName, size.size);
94 }
95}
96
97/* -------------- NewForm dialog.
98 * Designer takes new form templates from:
99 * 1) Files located in directories specified in resources
100 * 2) Files located in directories specified as user templates
101 * 3) XML from container widgets deemed usable for form templates by the widget
102 * database
103 * 4) XML from custom container widgets deemed usable for form templates by the
104 * widget database
105 *
106 * The widget database provides helper functions to obtain lists of names
107 * and xml for 3,4.
108 *
109 * Fixed-size forms for embedded platforms are obtained as follows:
110 * 1) If the origin is a file:
111 * - Check if the file exists in the subdirectory "/<width>x<height>/" of
112 * the path (currently the case for the dialog box because the button box
113 * needs to be positioned)
114 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
115 * 2) If the origin is XML:
116 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
117 *
118 * The tree widget item roles indicate which type of entry it is
119 * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4)
120 */
121
124 m_core(core),
126 m_currentItem(nullptr),
127 m_acceptedItem(nullptr)
128{
129 // ### FIXME Qt 8: Remove (QTBUG-96005)
130#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
132#endif
133
134 m_ui->setupUi(this);
136 m_ui->treeWidget->header()->hide();
139
146
148
149 QString uiExtension = u"ui"_s;
150 QString templatePath = u":/qt-project.org/designer/templates/forms"_s;
151
153 if (lang) {
154 templatePath = u":/templates/forms"_s;
156 }
157
158 // Resource templates
160 QTreeWidgetItem *selectedItem = nullptr;
162 // Additional template paths
164 for (const auto &ftp : formTemplatePaths)
166
167 // Widgets/custom widgets
168 if (!lang) {
169 //: New Form Dialog Categories
172 }
173
174 // Still no selection - default to first item
175 if (selectedItem == nullptr && m_ui->treeWidget->topLevelItemCount() != 0) {
177 if (firstTopLevel->childCount() > 0)
179 }
180
181 // Open parent, select and make visible
182 if (selectedItem) {
186 }
187 // Fill profile combo
189 m_ui->profileComboBox->addItem(tr("None"));
195 } else {
196 for (const auto &deviceProfile : std::as_const(m_deviceProfiles))
199 if (ci >= 0)
201 }
202
204
206
208 qDebug() << Q_FUNC_INFO << "Leaving";
209}
210
212{
215 // Do not change previously stored item if dialog was rejected
216 if (m_acceptedItem)
218 delete m_ui;
219}
220
222{
224 qDebug() << Q_FUNC_INFO << current;
225 if (!current)
226 return;
227
228 if (!current->parent()) { // Top level item: Ensure expanded when browsing down
229 return;
230 }
231
233
235}
236
238{
239 bool rc = false;
240 if (m_currentItem) {
242 if (pixmap.isNull()) {
243 m_ui->lblPreview->setText(tr("Error loading form"));
244 } else {
246 rc = true;
247 }
248 }
249 return rc;
250}
251
253{
255 qDebug() << Q_FUNC_INFO << item;
256
259}
260
262{
263 // Cache pixmaps per item/device profile
266 if (it == m_itemPixmapCache.end()) {
267 // file or string?
269 QPixmap rc;
270 if (fileName.metaType().id() == QMetaType::QString) {
272 } else {
280 }
281 if (rc.isNull()) // Retry invalid ones
282 return rc;
284 }
285 return it.value();
286}
287
289{
291 if (f.open(QFile::ReadOnly)) {
294 f.close();
295 return rc;
296 }
297 qWarning() << "The file " << fileName << " could not be opened: " << f.errorString();
298 return QPixmap();
299}
300
319
321{
322 const QSizeF screenSize(screen()->geometry().size());
323 const int previewSize = qRound(screenSize.width() / 7.5); // 256 on 1920px screens.
324 const int margin = previewSize / 32 - 1; // 7 on 1920px screens.
325 const int shadow = margin;
326
328 if (wimage.isNull())
329 return QPixmap();
335
339 dest.fill(0);
340
341 QPainter p(&dest);
343
345
346 p.drawRect(QRectF(margin - 1, margin - 1, imageSize.width() + 1.5, imageSize.height() + 1.5));
347
348 const QColor dark(Qt::darkGray);
349 const QColor light(Qt::transparent);
350
351 // right shadow
352 {
355 lg.setColorAt(0, dark);
356 lg.setColorAt(1, light);
357 p.fillRect(rect, lg);
358 }
359
360 // bottom shadow
361 {
364 lg.setColorAt(0, dark);
365 lg.setColorAt(1, light);
366 p.fillRect(rect, lg);
367 }
368
369 // bottom/right corner shadow
370 {
373 g.setColorAt(0, dark);
374 g.setColorAt(1, light);
375 p.fillRect(rect, g);
376 }
377
378 // top/right corner
379 {
382 g.setColorAt(0, dark);
383 g.setColorAt(1, light);
384 p.fillRect(rect, g);
385 }
386
387 // bottom/left corner
388 {
391 g.setColorAt(0, dark);
392 g.setColorAt(1, light);
393 p.fillRect(rect, g);
394 }
395
396 p.end();
397
398 return QPixmap::fromImage(dest);
399}
400
403{
404 const QDir dir(path);
405
406 if (!dir.exists())
407 return;
408
409 // Iterate through the directory and add the templates
411 QDir::Files);
412
413 if (list.isEmpty())
414 return;
415
416 const QChar separator = resourceFile ? QChar(u'/')
417 : QDir::separator();
420 // Try to get something that is easy to read.
423 if (index != -1) {
424 // try to find a second slash, just to be a bit better.
425 const int index2 = visiblePath.lastIndexOf(separator, index - 1);
426 if (index2 != -1)
427 index = index2;
430 }
431
432 root->setText(0, visiblePath.replace(u'_', u' '));
433 root->setToolTip(0, path);
434
435 for (const auto &fi : list) {
436 if (!fi.isFile())
437 continue;
438
440 const QString text = fi.baseName().replace(u'_', u' ');
441 if (selectedItemFound == nullptr && text == selectedItem)
443 item->setText(0, text);
445 }
446}
447
450{
451 if (nameList.isEmpty())
452 return;
455 root->setText(0, title);
456 for (const auto &text : nameList) {
458 item->setText(0, text);
459 if (selectedItemFound == nullptr && text == selectedItem)
462 }
463}
464
466{
467 if (item && !item->parent())
469}
470
472{
474}
475
477{
478 const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s);
479 if (index != -1)
481}
482
483static QString readAll(const QString &fileName, QString *errorMessage)
484{
485 QFile file(fileName);
486 if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
487 *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString());
488 return QString();
489 }
490 return QString::fromUtf8(file.readAll());
491}
492
494{
495 const QSize size = templateSize();
496 // file name or string contents?
500 // No fixed size: just open.
501 if (size.isNull())
503 // try to find a file matching the size, like "../640x480/xx.ui"
507 << size.width() << 'x' << size.height() << QDir::separator()
508 << fiBase.fileName();
511 // Nothing found, scale via DOM/temporary file
513 if (!contents.isEmpty())
515 return contents;
516 }
517 // Content.
520 if (!size.isNull())
522 return contents;
523}
524
526{
527 // Store index for form windows to take effect and refresh pixmap
531}
532
534{
536}
537
539{
540 const int ci = profileComboIndex();
541 if (ci > 0)
544}
545
547{
548 return m_currentItem != nullptr;
549}
550
552{
553 if (m_currentItem == nullptr) {
554 *ptrToErrorMessage = tr("Internal error: No template selected.");
555 return QString();
556 }
558 if (contents.isEmpty())
559 return contents;
560
562 return contents;
563}
564
576
585
586} // namespace qdesigner_internal
587
588QT_END_NAMESPACE
Auxiliary methods to store/retrieve settings.
static QString readAll(const QString &fileName, QString *errorMessage)
static void populateSizeCombo(QComboBox *cb)
static constexpr std::array standardScreenSizes
static constexpr auto newFormObjectNameC
@ profileComboIndexOffset
@ debugNewFormWidget
static QString formName(const QString &className)
NewForm_CustomRole
@ TemplateNameRole
@ ClassNameRole