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
5#include "ui_newformwidget.h"
10
11#include <QtDesigner/abstractformeditor.h>
12#include <QtDesigner/abstractformwindow.h>
13#include <QtDesigner/qextensionmanager.h>
14#include <QtDesigner/abstractlanguage.h>
15#include <QtDesigner/abstractwidgetdatabase.h>
16
17#include <QtCore/qdir.h>
18#include <QtCore/qfile.h>
19#include <QtCore/qfileinfo.h>
20#include <QtCore/qdebug.h>
21#include <QtCore/qbytearray.h>
22#include <QtCore/qbuffer.h>
23#include <QtCore/qdir.h>
24#include <QtCore/qtextstream.h>
25
26#include <QtWidgets/qapplication.h>
27#include <QtWidgets/qheaderview.h>
28#include <QtWidgets/qtreewidget.h>
29#include <QtGui/qpainter.h>
30#include <QtGui/qscreen.h>
31#include <QtWidgets/qpushbutton.h>
32
33#include <array>
34
35QT_BEGIN_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
40enum { debugNewFormWidget = 0 };
41
43 // File name (templates from resources, paths)
45 // Class name (widgets from Widget data base)
47};
48
49static constexpr auto newFormObjectNameC = "Form"_L1;
50
51// Create a form name for an arbitrary class. If it is Qt, qtify it,
52// else return "Form".
53static QString formName(const QString &className)
54{
55 if (!className.startsWith(u'Q'))
56 return newFormObjectNameC;
57 QString rc = className;
58 rc.remove(0, 1);
59 return rc;
60}
61
62namespace qdesigner_internal {
63
69
70static constexpr std::array standardScreenSizes = {
71 StandardScreenSize{ "QVGA"_L1, {320, 240} },
72 StandardScreenSize{ "MIMXRT1050-EVKB"_L1, {480,272} },
73 StandardScreenSize{ "VGA"_L1, {640, 480} },
74 StandardScreenSize{ "WVGA"_L1, {800, 480} },
75 StandardScreenSize{ "WSVGA"_L1, {1024, 600} },
76 StandardScreenSize{ "XGA"_L1, {1024, 768} },
77 StandardScreenSize{ "MIMXRT1170-EVKB"_L1, {1280, 720} },
78 StandardScreenSize{ "WXGA"_L1, {1280, 800} }
79};
80
81static void populateSizeCombo(QComboBox *cb)
82{
83 cb->addItem(NewFormWidget::tr("Default size"), QSize{ 0, 0 });
84 for (const auto &size : standardScreenSizes) {
85 //: Standard screen size: %1 Name, %2 screen width, %3 screen height
86 QString portraitName = NewFormWidget::tr("%1 portrait (%2x%3)").arg(size.name)
87 .arg(size.size.height()).arg(size.size.width());
88 cb->addItem(portraitName, size.size.transposed());
89 //: Standard screen size: %1 Name, %2 screen width, %3 screen height
90 QString landscapeName = NewFormWidget::tr("%1 landscape (%2x%3)").arg(size.name)
91 .arg(size.size.width()).arg(size.size.height());
92 cb->addItem(landscapeName, size.size);
93 }
94}
95
96/* -------------- NewForm dialog.
97 * Designer takes new form templates from:
98 * 1) Files located in directories specified in resources
99 * 2) Files located in directories specified as user templates
100 * 3) XML from container widgets deemed usable for form templates by the widget
101 * database
102 * 4) XML from custom container widgets deemed usable for form templates by the
103 * widget database
104 *
105 * The widget database provides helper functions to obtain lists of names
106 * and xml for 3,4.
107 *
108 * Fixed-size forms for embedded platforms are obtained as follows:
109 * 1) If the origin is a file:
110 * - Check if the file exists in the subdirectory "/<width>x<height>/" of
111 * the path (currently the case for the dialog box because the button box
112 * needs to be positioned)
113 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
114 * 2) If the origin is XML:
115 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
116 *
117 * The tree widget item roles indicate which type of entry it is
118 * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4)
119 */
120
123 m_core(core),
125 m_currentItem(nullptr),
126 m_acceptedItem(nullptr)
127{
128 // ### FIXME Qt 8: Remove (QTBUG-96005)
129#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
131#endif
132
133 m_ui->setupUi(this);
135 m_ui->treeWidget->header()->hide();
138
145
147
148 QString uiExtension = u"ui"_s;
149 QString templatePath = u":/qt-project.org/designer/templates/forms"_s;
150
152 if (lang) {
153 templatePath = u":/templates/forms"_s;
155 }
156
157 // Resource templates
159 QTreeWidgetItem *selectedItem = nullptr;
161 // Additional template paths
163 for (const auto &ftp : formTemplatePaths)
165
166 // Widgets/custom widgets
167 if (!lang) {
168 //: New Form Dialog Categories
171 }
172
173 // Still no selection - default to first item
174 if (selectedItem == nullptr && m_ui->treeWidget->topLevelItemCount() != 0) {
176 if (firstTopLevel->childCount() > 0)
178 }
179
180 // Open parent, select and make visible
181 if (selectedItem) {
185 }
186 // Fill profile combo
188 m_ui->profileComboBox->addItem(tr("None"));
194 } else {
195 for (const auto &deviceProfile : std::as_const(m_deviceProfiles))
198 if (ci >= 0)
200 }
201
203
205
207 qDebug() << Q_FUNC_INFO << "Leaving";
208}
209
211{
214 // Do not change previously stored item if dialog was rejected
215 if (m_acceptedItem)
217 delete m_ui;
218}
219
221{
223 qDebug() << Q_FUNC_INFO << current;
224 if (!current)
225 return;
226
227 if (!current->parent()) { // Top level item: Ensure expanded when browsing down
228 return;
229 }
230
232
234}
235
237{
238 bool rc = false;
239 if (m_currentItem) {
241 if (pixmap.isNull()) {
242 m_ui->lblPreview->setText(tr("Error loading form"));
243 } else {
245 rc = true;
246 }
247 }
248 return rc;
249}
250
252{
254 qDebug() << Q_FUNC_INFO << item;
255
258}
259
261{
262 // Cache pixmaps per item/device profile
265 if (it == m_itemPixmapCache.end()) {
266 // file or string?
268 QPixmap rc;
269 if (fileName.metaType().id() == QMetaType::QString) {
271 } else {
279 }
280 if (rc.isNull()) // Retry invalid ones
281 return rc;
283 }
284 return it.value();
285}
286
288{
290 if (f.open(QFile::ReadOnly)) {
293 f.close();
294 return rc;
295 }
296 qWarning() << "The file " << fileName << " could not be opened: " << f.errorString();
297 return QPixmap();
298}
299
318
320{
321 const QSizeF screenSize(screen()->geometry().size());
322 const int previewSize = qRound(screenSize.width() / 7.5); // 256 on 1920px screens.
323 const int margin = previewSize / 32 - 1; // 7 on 1920px screens.
324 const int shadow = margin;
325
327 if (wimage.isNull())
328 return QPixmap();
334
338 dest.fill(0);
339
340 QPainter p(&dest);
342
344
345 p.drawRect(QRectF(margin - 1, margin - 1, imageSize.width() + 1.5, imageSize.height() + 1.5));
346
347 const QColor dark(Qt::darkGray);
348 const QColor light(Qt::transparent);
349
350 // right shadow
351 {
354 lg.setColorAt(0, dark);
355 lg.setColorAt(1, light);
356 p.fillRect(rect, lg);
357 }
358
359 // bottom shadow
360 {
363 lg.setColorAt(0, dark);
364 lg.setColorAt(1, light);
365 p.fillRect(rect, lg);
366 }
367
368 // bottom/right corner shadow
369 {
372 g.setColorAt(0, dark);
373 g.setColorAt(1, light);
374 p.fillRect(rect, g);
375 }
376
377 // top/right corner
378 {
381 g.setColorAt(0, dark);
382 g.setColorAt(1, light);
383 p.fillRect(rect, g);
384 }
385
386 // bottom/left corner
387 {
390 g.setColorAt(0, dark);
391 g.setColorAt(1, light);
392 p.fillRect(rect, g);
393 }
394
395 p.end();
396
397 return QPixmap::fromImage(dest);
398}
399
402{
403 const QDir dir(path);
404
405 if (!dir.exists())
406 return;
407
408 // Iterate through the directory and add the templates
410 QDir::Files);
411
412 if (list.isEmpty())
413 return;
414
415 const QChar separator = resourceFile ? QChar(u'/')
416 : QDir::separator();
419 // Try to get something that is easy to read.
422 if (index != -1) {
423 // try to find a second slash, just to be a bit better.
424 const int index2 = visiblePath.lastIndexOf(separator, index - 1);
425 if (index2 != -1)
426 index = index2;
429 }
430
431 root->setText(0, visiblePath.replace(u'_', u' '));
432 root->setToolTip(0, path);
433
434 for (const auto &fi : list) {
435 if (!fi.isFile())
436 continue;
437
439 const QString text = fi.baseName().replace(u'_', u' ');
440 if (selectedItemFound == nullptr && text == selectedItem)
442 item->setText(0, text);
444 }
445}
446
449{
450 if (nameList.isEmpty())
451 return;
454 root->setText(0, title);
455 for (const auto &text : nameList) {
457 item->setText(0, text);
458 if (selectedItemFound == nullptr && text == selectedItem)
461 }
462}
463
465{
466 if (item && !item->parent())
468}
469
471{
473}
474
476{
477 const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s);
478 if (index != -1)
480}
481
482static QString readAll(const QString &fileName, QString *errorMessage)
483{
484 QFile file(fileName);
485 if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
486 *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString());
487 return QString();
488 }
489 return QString::fromUtf8(file.readAll());
490}
491
493{
494 const QSize size = templateSize();
495 // file name or string contents?
499 // No fixed size: just open.
500 if (size.isNull())
502 // try to find a file matching the size, like "../640x480/xx.ui"
506 << size.width() << 'x' << size.height() << QDir::separator()
507 << fiBase.fileName();
510 // Nothing found, scale via DOM/temporary file
512 if (!contents.isEmpty())
514 return contents;
515 }
516 // Content.
519 if (!size.isNull())
521 return contents;
522}
523
525{
526 // Store index for form windows to take effect and refresh pixmap
530}
531
533{
535}
536
538{
539 const int ci = profileComboIndex();
540 if (ci > 0)
543}
544
546{
547 return m_currentItem != nullptr;
548}
549
551{
552 if (m_currentItem == nullptr) {
553 *ptrToErrorMessage = tr("Internal error: No template selected.");
554 return QString();
555 }
557 if (contents.isEmpty())
558 return contents;
559
561 return contents;
562}
563
575
584
585} // namespace qdesigner_internal
586
587QT_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