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
itemlisteditor.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 <abstractformbuilder.h>
6#include <iconloader_p.h>
7#include <formwindowbase_p.h>
8#include <designerpropertymanager.h>
9
10#include <QtDesigner/abstractformwindow.h>
11
12#include <qttreepropertybrowser_p.h>
13
14#include <QtWidgets/qsplitter.h>
15#include <QtCore/qcoreapplication.h>
16
18
19using namespace Qt::StringLiterals;
20
21namespace qdesigner_internal {
22
24{
25public:
27 {
28 setResizeMode(Interactive);
29 //: Sample string to determinate the width for the first column of the list item property browser
30 const QString widthSampleString = QCoreApplication::translate("ItemPropertyBrowser", "XX Icon Selected off");
31 m_width = fontMetrics().horizontalAdvance(widthSampleString);
32 setSplitterPosition(m_width);
33 m_width += fontMetrics().horizontalAdvance(u"/this/is/some/random/path"_s);
34 }
35
36 QSize sizeHint() const override
37 {
38 return QSize(m_width, 1);
39 }
40
41private:
42 int m_width;
43};
44
45////////////////// Item editor ///////////////
46AbstractItemEditor::AbstractItemEditor(QDesignerFormWindowInterface *form, QWidget *parent)
47 : QWidget(parent),
48 m_iconCache(qobject_cast<FormWindowBase *>(form)->iconCache())
49{
50 m_propertyManager = new DesignerPropertyManager(form->core(), this);
51 m_editorFactory = new DesignerEditorFactory(form->core(), this);
54 m_propertyBrowser->setFactoryForManager(static_cast<QtVariantPropertyManager *>(m_propertyManager),
55 m_editorFactory);
56
57 connect(m_editorFactory, &DesignerEditorFactory::resetProperty,
58 this, &AbstractItemEditor::resetProperty);
59 connect(m_propertyManager, &DesignerPropertyManager::valueChanged,
60 this, &AbstractItemEditor::propertyChanged);
61 connect(iconCache(), &DesignerIconCache::reloaded, this, &AbstractItemEditor::cacheReloaded);
62}
63
68
69static const char * const itemFlagNames[] = {
70 QT_TRANSLATE_NOOP("AbstractItemEditor", "Selectable"),
71 QT_TRANSLATE_NOOP("AbstractItemEditor", "Editable"),
72 QT_TRANSLATE_NOOP("AbstractItemEditor", "DragEnabled"),
73 QT_TRANSLATE_NOOP("AbstractItemEditor", "DropEnabled"),
74 QT_TRANSLATE_NOOP("AbstractItemEditor", "UserCheckable"),
75 QT_TRANSLATE_NOOP("AbstractItemEditor", "Enabled"),
76 QT_TRANSLATE_NOOP("AbstractItemEditor", "Tristate"),
77 nullptr
78};
79
80static const char * const checkStateNames[] = {
81 QT_TRANSLATE_NOOP("AbstractItemEditor", "Unchecked"),
82 QT_TRANSLATE_NOOP("AbstractItemEditor", "PartiallyChecked"),
83 QT_TRANSLATE_NOOP("AbstractItemEditor", "Checked"),
84 nullptr
85};
86
87static QStringList c2qStringList(const char * const in[])
88{
89 QStringList out;
90 for (int i = 0; in[i]; i++)
91 out << AbstractItemEditor::tr(in[i]);
92 return out;
93}
94
96 Qt::Alignment alignDefault)
97{
98 for (int i = 0; propList[i].name; i++) {
99 int type = propList[i].typeFunc ? propList[i].typeFunc() : propList[i].type;
100 int role = propList[i].role;
101 QtVariantProperty *prop = m_propertyManager->addProperty(type, QLatin1StringView(propList[i].name));
102 if (role == Qt::TextAlignmentRole) {
104 QVariant(uint(alignDefault)));
105 }
106 Q_ASSERT(prop);
107 if (role == Qt::ToolTipPropertyRole || role == Qt::WhatsThisPropertyRole)
108 prop->setAttribute(u"validationMode"_s, ValidationRichText);
109 else if (role == Qt::DisplayPropertyRole)
110 prop->setAttribute(u"validationMode"_s, ValidationMultiLine);
111 else if (role == Qt::StatusTipPropertyRole)
112 prop->setAttribute(u"validationMode"_s, ValidationSingleLine);
113 else if (role == ItemFlagsShadowRole)
114 prop->setAttribute(u"flagNames"_s, c2qStringList(itemFlagNames));
115 else if (role == Qt::CheckStateRole)
116 prop->setAttribute(u"enumNames"_s, c2qStringList(checkStateNames));
117 prop->setAttribute(u"resettable"_s, true);
118 m_properties.append(prop);
119 m_rootProperties.append(prop);
120 m_propertyToRole.insert(prop, role);
121 }
122}
123
124void AbstractItemEditor::setupObject(QWidget *object)
125{
126 m_propertyManager->setObject(object);
127 QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(object);
128 FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow);
130}
131
132void AbstractItemEditor::setupEditor(QWidget *object,
133 const PropertyDefinition *propList,
134 Qt::Alignment alignDefault)
135{
136 setupProperties(propList, alignDefault);
137 setupObject(object);
138}
139
140void AbstractItemEditor::propertyChanged(QtProperty *property)
141{
143 return;
144
145
148 int role;
149 if ((role = m_propertyToRole.value(prop, -1)) == -1)
150 // Subproperty
151 return;
152
153 if ((role == ItemFlagsShadowRole && prop->value().toInt() == defaultItemFlags())
154 || (role == Qt::DecorationPropertyRole && !qvariant_cast<PropertySheetIconValue>(prop->value()).mask())
155 || (role == Qt::FontRole && !qvariant_cast<QFont>(prop->value()).resolveMask())) {
156 prop->setModified(false);
157 setItemData(role, QVariant());
158 } else {
159 prop->setModified(true);
160 setItemData(role, prop->value());
161 }
162
163 switch (role) {
164 case Qt::DecorationPropertyRole:
165 setItemData(Qt::DecorationRole, QVariant::fromValue(iconCache()->icon(qvariant_cast<PropertySheetIconValue>(prop->value()))));
166 break;
167 case Qt::DisplayPropertyRole:
168 setItemData(Qt::EditRole, QVariant::fromValue(qvariant_cast<PropertySheetStringValue>(prop->value()).value()));
169 break;
170 case Qt::ToolTipPropertyRole:
171 setItemData(Qt::ToolTipRole, QVariant::fromValue(qvariant_cast<PropertySheetStringValue>(prop->value()).value()));
172 break;
173 case Qt::StatusTipPropertyRole:
174 setItemData(Qt::StatusTipRole, QVariant::fromValue(qvariant_cast<PropertySheetStringValue>(prop->value()).value()));
175 break;
176 case Qt::WhatsThisPropertyRole:
177 setItemData(Qt::WhatsThisRole, QVariant::fromValue(qvariant_cast<PropertySheetStringValue>(prop->value()).value()));
178 break;
179 default:
180 break;
181 }
182
183 prop->setValue(getItemData(role));
184}
185
186void AbstractItemEditor::resetProperty(QtProperty *property)
187{
189 return;
190
192 return;
193
195 return;
196
198
200 int role = m_propertyToRole.value(prop);
201 if (role == ItemFlagsShadowRole)
202 prop->setValue(QVariant::fromValue(defaultItemFlags()));
203 else
204 prop->setValue(QVariant(QMetaType(prop->valueType()), nullptr));
205 prop->setModified(false);
206
207 setItemData(role, QVariant());
208 if (role == Qt::DecorationPropertyRole)
209 setItemData(Qt::DecorationRole, QVariant::fromValue(QIcon()));
210 if (role == Qt::DisplayPropertyRole)
211 setItemData(Qt::EditRole, QVariant::fromValue(QString()));
212 if (role == Qt::ToolTipPropertyRole)
213 setItemData(Qt::ToolTipRole, QVariant::fromValue(QString()));
214 if (role == Qt::StatusTipPropertyRole)
215 setItemData(Qt::StatusTipRole, QVariant::fromValue(QString()));
216 if (role == Qt::WhatsThisPropertyRole)
217 setItemData(Qt::WhatsThisRole, QVariant::fromValue(QString()));
218}
219
225
227{
229 for (QtVariantProperty *prop : std::as_const(m_properties)) {
230 int role = m_propertyToRole.value(prop);
231 QVariant val = getItemData(role);
232
233 bool modified = false;
234 if (!val.isValid()) {
235 if (role == ItemFlagsShadowRole)
236 val = QVariant::fromValue(defaultItemFlags());
237 else
238 val = QVariant(QMetaType(prop->value().userType()), nullptr);
239 } else {
240 modified = role != Qt::TextAlignmentRole
241 || val.toUInt() != DesignerPropertyManager::alignDefault(prop);
242 }
243 prop->setModified(modified);
244 prop->setValue(val);
245 }
246
247 if (m_propertyBrowser->topLevelItems().isEmpty()) {
248 for (QtVariantProperty *prop : std::as_const(m_rootProperties))
249 m_propertyBrowser->addProperty(prop);
250 }
251}
252
253void AbstractItemEditor::injectPropertyBrowser(QWidget *parent, QWidget *widget)
254{
255 // It is impossible to design a splitter with just one widget, so we do it by hand.
256 m_propertySplitter = new QSplitter;
257 m_propertySplitter->addWidget(widget);
259 m_propertySplitter->setStretchFactor(0, 1);
260 m_propertySplitter->setStretchFactor(1, 0);
261 parent->layout()->addWidget(m_propertySplitter);
262}
263
264////////////////// List editor ///////////////
265ItemListEditor::ItemListEditor(QDesignerFormWindowInterface *form, QWidget *parent)
266 : AbstractItemEditor(form, parent),
267 m_updating(false)
268{
269 ui.setupUi(this);
270
271 injectPropertyBrowser(this, ui.widget);
272 connect(ui.showPropertiesButton, &QAbstractButton::clicked,
273 this, &ItemListEditor::togglePropertyBrowser);
274
275 connect(ui.newListItemButton, &QAbstractButton::clicked,
276 this, &ItemListEditor::newListItemButtonClicked);
277 connect(ui.deleteListItemButton, &QAbstractButton::clicked,
278 this, &ItemListEditor::deleteListItemButtonClicked);
279 connect(ui.moveListItemUpButton, &QAbstractButton::clicked,
280 this, &ItemListEditor::moveListItemUpButtonClicked);
281 connect(ui.moveListItemDownButton, &QAbstractButton::clicked,
282 this, &ItemListEditor::moveListItemDownButtonClicked);
283 connect(ui.listWidget, &QListWidget::currentRowChanged,
284 this, &ItemListEditor::listWidgetCurrentRowChanged);
285 connect(ui.listWidget, &QListWidget::itemChanged,
286 this, &ItemListEditor::listWidgetItemChanged);
287
288 setPropertyBrowserVisible(false);
289
290 QIcon upIcon = createIconSet("up.png"_L1);
291 QIcon downIcon = createIconSet("down.png"_L1);
292 QIcon minusIcon = createIconSet("minus.png"_L1);
293 QIcon plusIcon = createIconSet("plus.png"_L1);
294 ui.moveListItemUpButton->setIcon(upIcon);
295 ui.moveListItemDownButton->setIcon(downIcon);
296 ui.newListItemButton->setIcon(plusIcon);
297 ui.deleteListItemButton->setIcon(minusIcon);
298
299 connect(iconCache(), &DesignerIconCache::reloaded, this, &AbstractItemEditor::cacheReloaded);
300}
301
302void ItemListEditor::setupEditor(QWidget *object,
303 const PropertyDefinition *propList,
304 Qt::Alignment alignDefault)
305{
306 AbstractItemEditor::setupEditor(object, propList, alignDefault);
307
308 if (ui.listWidget->count() > 0)
309 ui.listWidget->setCurrentRow(0);
310 else
311 updateEditor();
312}
313
315{
316 m_updating = true;
317 ui.listWidget->setCurrentRow(idx);
318 m_updating = false;
319}
320
321void ItemListEditor::newListItemButtonClicked()
322{
323 int row = ui.listWidget->currentRow() + 1;
324
325 QListWidgetItem *item = new QListWidgetItem(m_newItemText);
326 item->setData(Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(m_newItemText)));
327 if (m_alignDefault != 0)
328 item->setTextAlignment(Qt::Alignment(m_alignDefault));
329 item->setFlags(item->flags() | Qt::ItemIsEditable);
330 if (row < ui.listWidget->count())
331 ui.listWidget->insertItem(row, item);
332 else
333 ui.listWidget->addItem(item);
334 emit itemInserted(row);
335
336 ui.listWidget->setCurrentItem(item);
337 ui.listWidget->editItem(item);
338}
339
340void ItemListEditor::deleteListItemButtonClicked()
341{
342 int row = ui.listWidget->currentRow();
343
344 if (row != -1) {
345 delete ui.listWidget->takeItem(row);
346 emit itemDeleted(row);
347 }
348
349 if (row == ui.listWidget->count())
350 row--;
351 if (row < 0)
352 updateEditor();
353 else
354 ui.listWidget->setCurrentRow(row);
355}
356
357void ItemListEditor::moveListItemUpButtonClicked()
358{
359 int row = ui.listWidget->currentRow();
360 if (row <= 0)
361 return; // nothing to do
362
363 ui.listWidget->insertItem(row - 1, ui.listWidget->takeItem(row));
364 ui.listWidget->setCurrentRow(row - 1);
365 emit itemMovedUp(row);
366}
367
368void ItemListEditor::moveListItemDownButtonClicked()
369{
370 int row = ui.listWidget->currentRow();
371 if (row == -1 || row == ui.listWidget->count() - 1)
372 return; // nothing to do
373
374 ui.listWidget->insertItem(row + 1, ui.listWidget->takeItem(row));
375 ui.listWidget->setCurrentRow(row + 1);
376 emit itemMovedDown(row);
377}
378
379void ItemListEditor::listWidgetCurrentRowChanged()
380{
381 updateEditor();
382 if (!m_updating)
383 emit indexChanged(ui.listWidget->currentRow());
384}
385
386void ItemListEditor::listWidgetItemChanged(QListWidgetItem *item)
387{
389 return;
390
391 PropertySheetStringValue val = qvariant_cast<PropertySheetStringValue>(item->data(Qt::DisplayPropertyRole));
392 val.setValue(item->text());
394 item->setData(Qt::DisplayPropertyRole, QVariant::fromValue(val));
395
396 // The checkState could change, too, but if this signal is connected,
397 // checkState is not in the list anyway, as we are editing a header item.
398 emit itemChanged(ui.listWidget->currentRow(), Qt::DisplayPropertyRole,
399 QVariant::fromValue(val));
401}
402
403void ItemListEditor::togglePropertyBrowser()
404{
405 setPropertyBrowserVisible(!m_propertyBrowser->isVisible());
406}
407
408void ItemListEditor::setPropertyBrowserVisible(bool v)
409{
410 ui.showPropertiesButton->setText(v ? tr("Properties &>>") : tr("Properties &<<"));
411 m_propertyBrowser->setVisible(v);
412}
413
414void ItemListEditor::setItemData(int role, const QVariant &v)
415{
416 QListWidgetItem *item = ui.listWidget->currentItem();
417 bool reLayout = false;
418 if ((role == Qt::EditRole
419 && (v.toString().count(u'\n') != item->data(role).toString().count(u'\n')))
420 || role == Qt::FontRole) {
421 reLayout = true;
422 }
423 QVariant newValue = v;
424 if (role == Qt::FontRole && newValue.metaType().id() == QMetaType::QFont) {
425 QFont oldFont = ui.listWidget->font();
426 QFont newFont = qvariant_cast<QFont>(newValue).resolve(oldFont);
427 newValue = QVariant::fromValue(newFont);
428 item->setData(role, QVariant()); // force the right font with the current resolve mask is set (item view bug)
429 }
430 item->setData(role, newValue);
431 if (reLayout)
432 ui.listWidget->doItemsLayout();
433 emit itemChanged(ui.listWidget->currentRow(), role, newValue);
434}
435
436QVariant ItemListEditor::getItemData(int role) const
437{
438 return ui.listWidget->currentItem()->data(role);
439}
440
442{
443 static const int flags = QListWidgetItem().flags();
444 return flags;
445}
446
447void ItemListEditor::cacheReloaded()
448{
449 reloadIconResources(iconCache(), ui.listWidget);
450}
451
452void ItemListEditor::updateEditor()
453{
454 bool currentItemEnabled = false;
455
456 bool moveRowUpEnabled = false;
457 bool moveRowDownEnabled = false;
458
459 QListWidgetItem *item = ui.listWidget->currentItem();
460 if (item) {
461 currentItemEnabled = true;
462 int currentRow = ui.listWidget->currentRow();
463 if (currentRow > 0)
464 moveRowUpEnabled = true;
465 if (currentRow < ui.listWidget->count() - 1)
466 moveRowDownEnabled = true;
467 }
468
469 ui.moveListItemUpButton->setEnabled(moveRowUpEnabled);
470 ui.moveListItemDownButton->setEnabled(moveRowDownEnabled);
471 ui.deleteListItemButton->setEnabled(currentItemEnabled);
472
473 if (item)
475 else
477}
478
480{
481 return m_alignDefault;
482}
483
484void ItemListEditor::setAlignDefault(uint newAlignDefault)
485{
486 m_alignDefault = newAlignDefault;
487}
488} // namespace qdesigner_internal
489
490QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:431
void unsetFactoryForManager(QtAbstractPropertyManager *manager)
Removes the association between the given manager and the factory bound to it, automatically calling ...
void clear()
Removes all the properties from the editor, but does not delete them since they can still be used in ...
The QtProperty class encapsulates an instance of a property.
void setModified(bool modified)
Sets the property's modified state according to the passed modified value.
The QtTreePropertyBrowser class provides QTreeWidget based property browser.
void setSplitterPosition(int position)
QtVariantProperty * variantProperty(const QtProperty *property) const
Returns the given property converted into a QtVariantProperty.
The QtVariantProperty class is a convenience class handling QVariant based properties.
int valueType() const
Returns the type of this property's value.
QtTreePropertyBrowser * m_propertyBrowser
DesignerPropertyManager * m_propertyManager
DesignerEditorFactory * m_editorFactory
void setupProperties(const PropertyDefinition *propList, Qt::Alignment alignDefault=Qt::AlignLeading|Qt::AlignVCenter)
QVariant getItemData(int role) const override
void setAlignDefault(uint newAlignDefault)
Auxiliary methods to store/retrieve settings.
static const char *const checkStateNames[]
static QStringList c2qStringList(const char *const in[])
static const char *const itemFlagNames[]