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
34
35using namespace Qt::StringLiterals;
36
38enum { debugNewFormWidget = 0 };
39
41 // File name (templates from resources, paths)
43 // Class name (widgets from Widget data base)
45};
46
47static constexpr auto newFormObjectNameC = "Form"_L1;
48
49// Create a form name for an arbitrary class. If it is Qt, qtify it,
50// else return "Form".
51static QString formName(const QString &className)
52{
53 if (!className.startsWith(u'Q'))
54 return newFormObjectNameC;
55 QString rc = className;
56 rc.remove(0, 1);
57 return rc;
58}
59
60namespace qdesigner_internal {
61
63 const char *name;
64 int width;
65 int height;
66};
67
68static const struct TemplateSize templateSizes[] =
69{
70 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "Default size"), 0, 0 },
71 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA portrait (240x320)"), 240, 320 },
72 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA landscape (320x240)"), 320, 240 },
73 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA portrait (480x640)"), 480, 640 },
74 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA landscape (640x480)"), 640, 480 }
75};
76
77/* -------------- NewForm dialog.
78 * Designer takes new form templates from:
79 * 1) Files located in directories specified in resources
80 * 2) Files located in directories specified as user templates
81 * 3) XML from container widgets deemed usable for form templates by the widget
82 * database
83 * 4) XML from custom container widgets deemed usable for form templates by the
84 * widget database
85 *
86 * The widget database provides helper functions to obtain lists of names
87 * and xml for 3,4.
88 *
89 * Fixed-size forms for embedded platforms are obtained as follows:
90 * 1) If the origin is a file:
91 * - Check if the file exists in the subdirectory "/<width>x<height>/" of
92 * the path (currently the case for the dialog box because the button box
93 * needs to be positioned)
94 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
95 * 2) If the origin is XML:
96 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
97 *
98 * The tree widget item roles indicate which type of entry it is
99 * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4)
100 */
101
104 m_core(core),
106 m_currentItem(nullptr),
107 m_acceptedItem(nullptr)
108{
109 // ### FIXME Qt 8: Remove (QTBUG-96005)
110#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
112#endif
113
114 m_ui->setupUi(this);
116 m_ui->treeWidget->header()->hide();
119
126
128
129 QString uiExtension = u"ui"_s;
130 QString templatePath = u":/qt-project.org/designer/templates/forms"_s;
131
133 if (lang) {
134 templatePath = u":/templates/forms"_s;
136 }
137
138 // Resource templates
140 QTreeWidgetItem *selectedItem = nullptr;
142 // Additional template paths
144 for (const auto &ftp : formTemplatePaths)
146
147 // Widgets/custom widgets
148 if (!lang) {
149 //: New Form Dialog Categories
152 }
153
154 // Still no selection - default to first item
155 if (selectedItem == nullptr && m_ui->treeWidget->topLevelItemCount() != 0) {
157 if (firstTopLevel->childCount() > 0)
159 }
160
161 // Open parent, select and make visible
162 if (selectedItem) {
166 }
167 // Fill profile combo
169 m_ui->profileComboBox->addItem(tr("None"));
175 } else {
176 for (const auto &deviceProfile : std::as_const(m_deviceProfiles))
179 if (ci >= 0)
181 }
182 // Fill size combo
183 for (const TemplateSize &t : templateSizes)
185
187
189 qDebug() << Q_FUNC_INFO << "Leaving";
190}
191
193{
196 // Do not change previously stored item if dialog was rejected
197 if (m_acceptedItem)
199 delete m_ui;
200}
201
203{
205 qDebug() << Q_FUNC_INFO << current;
206 if (!current)
207 return;
208
209 if (!current->parent()) { // Top level item: Ensure expanded when browsing down
210 return;
211 }
212
214
216}
217
219{
220 bool rc = false;
221 if (m_currentItem) {
223 if (pixmap.isNull()) {
224 m_ui->lblPreview->setText(tr("Error loading form"));
225 } else {
227 rc = true;
228 }
229 }
230 return rc;
231}
232
234{
236 qDebug() << Q_FUNC_INFO << item;
237
240}
241
243{
244 // Cache pixmaps per item/device profile
247 if (it == m_itemPixmapCache.end()) {
248 // file or string?
250 QPixmap rc;
251 if (fileName.metaType().id() == QMetaType::QString) {
253 } else {
261 }
262 if (rc.isNull()) // Retry invalid ones
263 return rc;
265 }
266 return it.value();
267}
268
270{
272 if (f.open(QFile::ReadOnly)) {
275 f.close();
276 return rc;
277 }
278 qWarning() << "The file " << fileName << " could not be opened: " << f.errorString();
279 return QPixmap();
280}
281
300
302{
303 const QSizeF screenSize(screen()->geometry().size());
304 const int previewSize = qRound(screenSize.width() / 7.5); // 256 on 1920px screens.
305 const int margin = previewSize / 32 - 1; // 7 on 1920px screens.
306 const int shadow = margin;
307
309 if (wimage.isNull())
310 return QPixmap();
316
320 dest.fill(0);
321
322 QPainter p(&dest);
324
326
327 p.drawRect(QRectF(margin - 1, margin - 1, imageSize.width() + 1.5, imageSize.height() + 1.5));
328
329 const QColor dark(Qt::darkGray);
330 const QColor light(Qt::transparent);
331
332 // right shadow
333 {
336 lg.setColorAt(0, dark);
337 lg.setColorAt(1, light);
338 p.fillRect(rect, lg);
339 }
340
341 // bottom shadow
342 {
345 lg.setColorAt(0, dark);
346 lg.setColorAt(1, light);
347 p.fillRect(rect, lg);
348 }
349
350 // bottom/right corner shadow
351 {
354 g.setColorAt(0, dark);
355 g.setColorAt(1, light);
356 p.fillRect(rect, g);
357 }
358
359 // top/right corner
360 {
363 g.setColorAt(0, dark);
364 g.setColorAt(1, light);
365 p.fillRect(rect, g);
366 }
367
368 // bottom/left corner
369 {
372 g.setColorAt(0, dark);
373 g.setColorAt(1, light);
374 p.fillRect(rect, g);
375 }
376
377 p.end();
378
379 return QPixmap::fromImage(dest);
380}
381
384{
385 const QDir dir(path);
386
387 if (!dir.exists())
388 return;
389
390 // Iterate through the directory and add the templates
392 QDir::Files);
393
394 if (list.isEmpty())
395 return;
396
397 const QChar separator = resourceFile ? QChar(u'/')
398 : QDir::separator();
401 // Try to get something that is easy to read.
404 if (index != -1) {
405 // try to find a second slash, just to be a bit better.
406 const int index2 = visiblePath.lastIndexOf(separator, index - 1);
407 if (index2 != -1)
408 index = index2;
411 }
412
413 root->setText(0, visiblePath.replace(u'_', u' '));
414 root->setToolTip(0, path);
415
416 for (const auto &fi : list) {
417 if (!fi.isFile())
418 continue;
419
421 const QString text = fi.baseName().replace(u'_', u' ');
422 if (selectedItemFound == nullptr && text == selectedItem)
424 item->setText(0, text);
426 }
427}
428
431{
432 if (nameList.isEmpty())
433 return;
436 root->setText(0, title);
437 for (const auto &text : nameList) {
439 item->setText(0, text);
440 if (selectedItemFound == nullptr && text == selectedItem)
443 }
444}
445
447{
448 if (item && !item->parent())
450}
451
453{
455}
456
458{
459 const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s);
460 if (index != -1)
462}
463
464static QString readAll(const QString &fileName, QString *errorMessage)
465{
466 QFile file(fileName);
467 if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
468 *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString());
469 return QString();
470 }
471 return QString::fromUtf8(file.readAll());
472}
473
475{
476 const QSize size = templateSize();
477 // file name or string contents?
481 // No fixed size: just open.
482 if (size.isNull())
484 // try to find a file matching the size, like "../640x480/xx.ui"
488 << size.width() << 'x' << size.height() << QDir::separator()
489 << fiBase.fileName();
492 // Nothing found, scale via DOM/temporary file
494 if (!contents.isEmpty())
496 return contents;
497 }
498 // Content.
501 if (!size.isNull())
503 return contents;
504}
505
507{
508 // Store index for form windows to take effect and refresh pixmap
512}
513
515{
517}
518
520{
521 const int ci = profileComboIndex();
522 if (ci > 0)
525}
526
528{
529 return m_currentItem != nullptr;
530}
531
533{
534 if (m_currentItem == nullptr) {
535 *ptrToErrorMessage = tr("Internal error: No template selected.");
536 return QString();
537 }
539 if (contents.isEmpty())
540 return contents;
541
543 return contents;
544}
545
557
558}
559
560QT_END_NAMESPACE
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static QString readAll(const QString &fileName, QString *errorMessage)
static const struct TemplateSize templateSizes[]
static constexpr auto newFormObjectNameC
@ profileComboIndexOffset
@ debugNewFormWidget
static QString formName(const QString &className)
NewForm_CustomRole
@ TemplateNameRole
@ ClassNameRole