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
qdesigner_promotiondialog.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
6#include "iconloader_p.h"
9
10#include <QtDesigner/abstractformeditor.h>
11#include <QtDesigner/abstractformwindow.h>
12#include <QtDesigner/abstractpromotioninterface.h>
13#include <QtDesigner/abstractwidgetdatabase.h>
14#include <QtDesigner/abstractintegration.h>
15#include <abstractdialoggui_p.h>
16
17#include <QtWidgets/qboxlayout.h>
18#include <QtWidgets/qformlayout.h>
19#include <QtWidgets/qdialogbuttonbox.h>
20#include <QtWidgets/qtreeview.h>
21#include <QtWidgets/qheaderview.h>
22#include <QtWidgets/qpushbutton.h>
23#include <QtWidgets/qcombobox.h>
24#include <QtWidgets/qlineedit.h>
25#include <QtWidgets/qcheckbox.h>
26#include <QtWidgets/qlabel.h>
27#include <QtWidgets/qlayoutitem.h>
28#include <QtWidgets/qmenu.h>
29
30#include <QtGui/qaction.h>
31#include <QtGui/qvalidator.h>
32
33#include <QtCore/qitemselectionmodel.h>
34#include <QtCore/qtimer.h>
35
37
38using namespace Qt::StringLiterals;
39
40namespace qdesigner_internal {
41 // PromotionParameters
47
48 // NewPromotedClassPanel
49 NewPromotedClassPanel::NewPromotedClassPanel(const QStringList &baseClasses,
50 int selectedBaseClass,
51 QWidget *parent) :
52 QGroupBox(parent),
53 m_baseClassCombo(new QComboBox),
54 m_classNameEdit(new QLineEdit),
55 m_includeFileEdit(new QLineEdit),
56 m_globalIncludeCheckBox(new QCheckBox),
57 m_addButton(new QPushButton(tr("Add")))
58 {
59 setTitle(tr("New Promoted Class"));
60 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
61 QHBoxLayout *hboxLayout = new QHBoxLayout(this);
62
63 m_classNameEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(u"^[_a-zA-Z:][:_a-zA-Z0-9]*$"_s), m_classNameEdit));
64 connect(m_classNameEdit, &QLineEdit::textChanged,
65 this, &NewPromotedClassPanel::slotNameChanged);
66 connect(m_includeFileEdit, &QLineEdit::textChanged,
67 this, &NewPromotedClassPanel::slotIncludeFileChanged);
68
69 m_baseClassCombo->setEditable(false);
70 m_baseClassCombo->addItems(baseClasses);
71 if (selectedBaseClass != -1)
72 m_baseClassCombo->setCurrentIndex(selectedBaseClass);
73
74 // Grid
75 QFormLayout *formLayout = new QFormLayout();
76 formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); // Mac
77 formLayout->addRow(tr("Base class name:"), m_baseClassCombo);
78 formLayout->addRow(tr("Promoted class name:"), m_classNameEdit);
79
80 QString toolTip = tr("Header file for C++ classes or module name for Qt for Python.");
81 auto *label = new QLabel(tr("Header file:"));
82 label->setToolTip(toolTip);
83 formLayout->addRow(label, m_includeFileEdit);
84 m_includeFileEdit->setToolTip(toolTip);
85
86 toolTip = tr("Indicates that the header file is a global header file. Does not have any effect on Qt for Python.");
87 label = new QLabel(tr("Global include"));
88 label->setToolTip(toolTip);
89 formLayout->addRow(label, m_globalIncludeCheckBox);
90 m_globalIncludeCheckBox->setToolTip(toolTip);
91
92 hboxLayout->addLayout(formLayout);
93 hboxLayout->addItem(new QSpacerItem(15, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
94 // Button box
95 QVBoxLayout *buttonLayout = new QVBoxLayout();
96
97 m_addButton->setAutoDefault(false);
98 connect(m_addButton, &QAbstractButton::clicked, this, &NewPromotedClassPanel::slotAdd);
99 m_addButton->setEnabled(false);
100 buttonLayout->addWidget(m_addButton);
101
102 QPushButton *resetButton = new QPushButton(tr("Reset"));
103 resetButton->setAutoDefault(false);
104 connect(resetButton, &QAbstractButton::clicked, this, &NewPromotedClassPanel::slotReset);
105
106 buttonLayout->addWidget(resetButton);
107 buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding));
108 hboxLayout->addLayout(buttonLayout);
109
110 enableButtons();
111 }
112
113 void NewPromotedClassPanel::slotAdd() {
114 bool ok = false;
115 emit newPromotedClass(promotionParameters(), &ok);
116 if (ok)
117 slotReset();
118 }
119
120 void NewPromotedClassPanel::slotReset() {
121 const QString empty;
122 m_classNameEdit->setText(empty);
123 m_includeFileEdit->setText(empty);
124 m_globalIncludeCheckBox->setCheckState(Qt::Unchecked);
125 }
126
127 void NewPromotedClassPanel::grabFocus() {
128 m_classNameEdit->setFocus(Qt::OtherFocusReason);
129 }
130
131 void NewPromotedClassPanel::slotNameChanged(const QString &className) {
132 // Suggest a name
133 if (!className.isEmpty()) {
134 QString suggestedHeader = m_promotedHeaderLowerCase ?
135 className.toLower() : className;
136 suggestedHeader.replace("::"_L1, "_"_L1);
137 if (!m_promotedHeaderSuffix.startsWith(u'.'))
138 suggestedHeader += u'.';
139 suggestedHeader += m_promotedHeaderSuffix;
140
141 const bool blocked = m_includeFileEdit->blockSignals(true);
142 m_includeFileEdit->setText(suggestedHeader);
143 m_includeFileEdit->blockSignals(blocked);
144 }
145 enableButtons();
146 }
147
148 void NewPromotedClassPanel::slotIncludeFileChanged(const QString &){
149 enableButtons();
150 }
151
152 void NewPromotedClassPanel::enableButtons() {
153 const bool enabled = !m_classNameEdit->text().isEmpty() && !m_includeFileEdit->text().isEmpty();
154 m_addButton->setEnabled(enabled);
155 m_addButton->setDefault(enabled);
156 }
157
158 PromotionParameters NewPromotedClassPanel::promotionParameters() const {
160 rc.m_baseClass = m_baseClassCombo->currentText();
161 rc.m_className = m_classNameEdit->text();
162 rc.m_includeFile = buildIncludeFile(m_includeFileEdit->text(),
163 m_globalIncludeCheckBox->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal);
164 return rc;
165 }
166
167 void NewPromotedClassPanel::chooseBaseClass(const QString &baseClass) {
168 const int index = m_baseClassCombo->findText (baseClass);
169 if (index != -1)
170 m_baseClassCombo->setCurrentIndex (index);
171 }
172
173 // --------------- QDesignerPromotionDialog
174 QDesignerPromotionDialog::QDesignerPromotionDialog(QDesignerFormEditorInterface *core,
175 QWidget *parent,
176 const QString &promotableWidgetClassName,
177 QString *promoteTo) :
181 m_core(core),
183 m_promotion(core->promotion()),
184 m_model(new PromotionModel(core)),
186 m_buttonBox(nullptr),
187 m_removeButton(new QPushButton(createIconSet("minus.png"_L1), QString()))
188 {
189 m_buttonBox = createButtonBox();
190 setModal(true);
191 setWindowTitle(tr("Promoted Widgets"));
192
193 QVBoxLayout *vboxLayout = new QVBoxLayout(this);
194
195 // tree view group
196 QGroupBox *treeViewGroup = new QGroupBox();
197 treeViewGroup->setTitle(tr("Promoted Classes"));
198 QVBoxLayout *treeViewVBoxLayout = new QVBoxLayout(treeViewGroup);
199 // tree view
200 m_treeView->setModel (m_model);
201 m_treeView->setMinimumWidth(450);
202 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
203
204 connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
205 this, &QDesignerPromotionDialog::slotSelectionChanged);
206
207 connect(m_treeView, &QWidget::customContextMenuRequested,
208 this, &QDesignerPromotionDialog::slotTreeViewContextMenu);
209
210 QHeaderView *headerView = m_treeView->header();
211 headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
212 treeViewVBoxLayout->addWidget(m_treeView);
213 // remove button
214 QHBoxLayout *hboxLayout = new QHBoxLayout();
215 hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
216
217 m_removeButton->setAutoDefault(false);
218 connect(m_removeButton, &QAbstractButton::clicked, this, &QDesignerPromotionDialog::slotRemove);
219 m_removeButton->setEnabled(false);
220 hboxLayout->addWidget(m_removeButton);
221 treeViewVBoxLayout->addLayout(hboxLayout);
222 vboxLayout->addWidget(treeViewGroup);
223 // Create new panel: Try to be smart and preselect a base class. Default to QFrame
224 const QStringList &baseClassNameList = baseClassNames(m_promotion);
225 int preselectedBaseClass = -1;
226 if (m_mode == ModeEditChooseClass) {
227 preselectedBaseClass = baseClassNameList.indexOf(m_promotableWidgetClassName);
228 }
229 if (preselectedBaseClass == -1)
230 preselectedBaseClass = baseClassNameList.indexOf("QFrame"_L1);
231
232 NewPromotedClassPanel *newPromotedClassPanel = new NewPromotedClassPanel(baseClassNameList, preselectedBaseClass);
233 newPromotedClassPanel->setPromotedHeaderSuffix(core->integration()->headerSuffix());
234 newPromotedClassPanel->setPromotedHeaderLowerCase(core->integration()->isHeaderLowercase());
235 connect(newPromotedClassPanel, &NewPromotedClassPanel::newPromotedClass,
236 this, &QDesignerPromotionDialog::slotNewPromotedClass);
237 connect(this, &QDesignerPromotionDialog::selectedBaseClassChanged,
238 newPromotedClassPanel, &NewPromotedClassPanel::chooseBaseClass);
239 vboxLayout->addWidget(newPromotedClassPanel);
240 // button box
241 vboxLayout->addWidget(m_buttonBox);
242 // connect model
243 connect(m_model, &PromotionModel::includeFileChanged,
244 this, &QDesignerPromotionDialog::slotIncludeFileChanged);
245
246 connect(m_model, &PromotionModel::classNameChanged,
247 this, &QDesignerPromotionDialog::slotClassNameChanged);
248
249 // focus
250 if (m_mode == ModeEditChooseClass)
251 newPromotedClassPanel->grabFocus();
252
253 slotUpdateFromWidgetDatabase();
254 }
255
256 QDialogButtonBox *QDesignerPromotionDialog::createButtonBox() {
257 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Close);
258
259 connect(buttonBox, &QDialogButtonBox::accepted,
260 this, &QDesignerPromotionDialog::slotAcceptPromoteTo);
261 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Promote"));
262 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
263
264 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
265 return buttonBox;
266 }
267
268 void QDesignerPromotionDialog::slotUpdateFromWidgetDatabase() {
270 m_treeView->expandAll();
271 m_removeButton->setEnabled(false);
272 }
273
274 void QDesignerPromotionDialog::delayedUpdateFromWidgetDatabase() {
275 QTimer::singleShot(0, this, &QDesignerPromotionDialog::slotUpdateFromWidgetDatabase);
276 }
277
278 const QStringList &QDesignerPromotionDialog::baseClassNames(const QDesignerPromotionInterface *promotion) {
279 using WidgetDataBaseItemList = QList<QDesignerWidgetDataBaseItemInterface *>;
280 static QStringList rc;
281 if (rc.isEmpty()) {
282 // Convert the item list into a string list.
283 const WidgetDataBaseItemList dbItems = promotion->promotionBaseClasses();
284 for (auto *item : dbItems)
285 rc.append(item->name());
286 }
287 return rc;
288 }
289
290 void QDesignerPromotionDialog::slotAcceptPromoteTo() {
291 Q_ASSERT(m_mode == ModeEditChooseClass);
292 unsigned flags;
293 // Ok pressed: Promote to selected class
294 if (QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags)) {
295 if (flags & CanPromote) {
296 *m_promoteTo = dbItem ->name();
297 accept();
298 }
299 }
300 }
301
302 void QDesignerPromotionDialog::slotRemove() {
303 unsigned flags;
304 QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
305 if (!dbItem || (flags & Referenced))
306 return;
307
308 QString errorMessage;
309 if (m_promotion->removePromotedClass(dbItem->name(), &errorMessage)) {
310 slotUpdateFromWidgetDatabase();
311 } else {
312 displayError(errorMessage);
313 }
314 }
315
316 void QDesignerPromotionDialog::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) {
317 // Enable deleting non-referenced items
318 unsigned flags;
319 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(selected, flags);
320 m_removeButton->setEnabled(dbItem && !(flags & Referenced));
321 // In choose mode, can we promote to the class?
322 if (m_mode == ModeEditChooseClass) {
323 const bool enablePromoted = flags & CanPromote;
324 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enablePromoted);
325 m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(enablePromoted);
326 }
327 // different base?
328 if (dbItem) {
329 const QString baseClass = dbItem->extends();
330 if (baseClass != m_lastSelectedBaseClass) {
331 m_lastSelectedBaseClass = baseClass;
332 emit selectedBaseClassChanged(m_lastSelectedBaseClass);
333 }
334 }
335 }
336
337 QDesignerWidgetDataBaseItemInterface *QDesignerPromotionDialog::databaseItemAt(const QItemSelection &selected, unsigned &flags) const {
338 flags = 0;
339 const QModelIndexList indexes = selected.indexes();
340 if (indexes.isEmpty())
341 return nullptr;
342 const PromotionModel::ModelData data = m_model->modelData(indexes.constFirst());
343 QDesignerWidgetDataBaseItemInterface *dbItem = data.promotedItem;
344
345 if (dbItem) {
346 if (data.referenced)
347 flags |= Referenced;
348 // In choose mode, can we promote to the class?
349 if (m_mode == ModeEditChooseClass && dbItem && dbItem->isPromoted() && dbItem->extends() == m_promotableWidgetClassName)
350 flags |= CanPromote;
351
352 }
353 return dbItem;
354 }
355
356 void QDesignerPromotionDialog::slotNewPromotedClass(const PromotionParameters &p, bool *ok) {
357 QString errorMessage;
358 *ok = m_promotion->addPromotedClass(p.m_baseClass, p.m_className, p.m_includeFile, &errorMessage);
359 if (*ok) {
360 // update and select
361 slotUpdateFromWidgetDatabase();
362 const QModelIndex newClassIndex = m_model->indexOfClass(p.m_className);
363 if (newClassIndex.isValid()) {
364 m_treeView->selectionModel()->select(newClassIndex, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
365 }
366 } else {
367 displayError(errorMessage);
368 }
369 }
370
372 if (includeFile.isEmpty()) {
374 return;
375 }
376
378 return;
379
384 }
385 }
386
388 if (newName.isEmpty()) {
390 return;
391 }
392 const QString oldName = dbItem->name();
393 if (newName == oldName)
394 return;
395
400 }
401 }
402
403 void QDesignerPromotionDialog::slotTreeViewContextMenu(const QPoint &pos) {
404 unsigned flags;
405 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
406 if (!dbItem)
407 return;
408
409 QMenu menu;
410 QAction *signalSlotAction = menu.addAction(tr("Change signals/slots..."));
411 connect(signalSlotAction, &QAction::triggered,
412 this, &QDesignerPromotionDialog::slotEditSignalsSlots);
413
414 menu.exec(m_treeView->viewport()->mapToGlobal(pos));
415 }
416
417 void QDesignerPromotionDialog::slotEditSignalsSlots() {
418 unsigned flags;
419 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
420 if (!dbItem)
421 return;
422
423 SignalSlotDialog::editPromotedClass(m_core, dbItem->name(), this);
424 }
425
426 void QDesignerPromotionDialog::displayError(const QString &message) {
427 m_core->dialogGui()->message(this, QDesignerDialogGuiInterface::PromotionErrorMessage, QMessageBox::Warning,
428 tr("%1 - Error").arg(windowTitle()), message, QMessageBox::Close);
429 }
430} // namespace qdesigner_internal
431
432QT_END_NAMESPACE
The QDesignerWidgetDataBaseItemInterface class provides an interface that is used to access individua...
friend class QWidget
Definition qpainter.h:421
PromotionModel(QDesignerFormEditorInterface *core)
QDesignerPromotionDialog(QDesignerFormEditorInterface *core, QWidget *parent=nullptr, const QString &promotableWidgetClassName=QString(), QString *promoteTo=nullptr)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.