1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtgroupboxpropertybrowser.h"
5
6#include <QtCore/QHash>
7#include <QtWidgets/QGridLayout>
8#include <QtWidgets/QGroupBox>
9#include <QtWidgets/QLabel>
10
11QT_BEGIN_NAMESPACE
12
13class QtGroupBoxPropertyBrowserPrivate
14{
15 QtGroupBoxPropertyBrowser *q_ptr;
16 Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser)
17public:
18
19 void init(QWidget *parent);
20
21 void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
22 void propertyRemoved(QtBrowserItem *index);
23 void propertyChanged(QtBrowserItem *index);
24 QWidget *createEditor(QtProperty *property, QWidget *parent) const
25 { return q_ptr->createEditor(property, parent); }
26
27 void slotEditorDestroyed();
28 void slotUpdate();
29
30 struct WidgetItem
31 {
32 QWidget *widget{nullptr}; // can be null
33 QLabel *label{nullptr};
34 QLabel *widgetLabel{nullptr};
35 QGroupBox *groupBox{nullptr};
36 QGridLayout *layout{nullptr};
37 QFrame *line{nullptr};
38 WidgetItem *parent{nullptr};
39 QList<WidgetItem *> children;
40 };
41private:
42 void updateLater();
43 void updateItem(WidgetItem *item);
44 void insertRow(QGridLayout *layout, int row) const;
45 void removeRow(QGridLayout *layout, int row) const;
46
47 bool hasHeader(WidgetItem *item) const;
48
49 QHash<QtBrowserItem *, WidgetItem *> m_indexToItem;
50 QHash<WidgetItem *, QtBrowserItem *> m_itemToIndex;
51 QHash<QWidget *, WidgetItem *> m_widgetToItem;
52 QGridLayout *m_mainLayout;
53 QList<WidgetItem *> m_children;
54 QList<WidgetItem *> m_recreateQueue;
55};
56
57void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent)
58{
59 m_mainLayout = new QGridLayout();
60 parent->setLayout(m_mainLayout);
61 auto *item = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
62 m_mainLayout->addItem(item, row: 0, column: 0);
63}
64
65void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed()
66{
67 auto *editor = qobject_cast<QWidget *>(o: q_ptr->sender());
68 if (!editor)
69 return;
70 if (!m_widgetToItem.contains(key: editor))
71 return;
72 m_widgetToItem[editor]->widget = nullptr;
73 m_widgetToItem.remove(key: editor);
74}
75
76void QtGroupBoxPropertyBrowserPrivate::slotUpdate()
77{
78 for (WidgetItem *item : std::as_const(t&: m_recreateQueue)) {
79 WidgetItem *par = item->parent;
80 QWidget *w = nullptr;
81 QGridLayout *l = nullptr;
82 int oldRow = -1;
83 if (!par) {
84 w = q_ptr;
85 l = m_mainLayout;
86 oldRow = m_children.indexOf(t: item);
87 } else {
88 w = par->groupBox;
89 l = par->layout;
90 oldRow = par->children.indexOf(t: item);
91 if (hasHeader(item: par))
92 oldRow += 2;
93 }
94
95 if (item->widget) {
96 item->widget->setParent(w);
97 } else if (item->widgetLabel) {
98 item->widgetLabel->setParent(w);
99 } else {
100 item->widgetLabel = new QLabel(w);
101 }
102 int span = 1;
103 if (item->widget)
104 l->addWidget(item->widget, row: oldRow, column: 1, rowSpan: 1, columnSpan: 1);
105 else if (item->widgetLabel)
106 l->addWidget(item->widgetLabel, row: oldRow, column: 1, rowSpan: 1, columnSpan: 1);
107 else
108 span = 2;
109 item->label = new QLabel(w);
110 item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
111 l->addWidget(item->label, row: oldRow, column: 0, rowSpan: 1, columnSpan: span);
112
113 updateItem(item);
114 }
115 m_recreateQueue.clear();
116}
117
118void QtGroupBoxPropertyBrowserPrivate::updateLater()
119{
120 QMetaObject::invokeMethod(object: q_ptr, function: [this] { slotUpdate(); }, type: Qt::QueuedConnection);
121}
122
123void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
124{
125 WidgetItem *afterItem = m_indexToItem.value(key: afterIndex);
126 WidgetItem *parentItem = m_indexToItem.value(key: index->parent());
127
128 auto *newItem = new WidgetItem();
129 newItem->parent = parentItem;
130
131 QGridLayout *layout = nullptr;
132 QWidget *parentWidget = nullptr;
133 int row = -1;
134 if (!afterItem) {
135 row = 0;
136 if (parentItem)
137 parentItem->children.insert(i: 0, t: newItem);
138 else
139 m_children.insert(i: 0, t: newItem);
140 } else {
141 if (parentItem) {
142 row = parentItem->children.indexOf(t: afterItem) + 1;
143 parentItem->children.insert(i: row, t: newItem);
144 } else {
145 row = m_children.indexOf(t: afterItem) + 1;
146 m_children.insert(i: row, t: newItem);
147 }
148 }
149 if (parentItem && hasHeader(item: parentItem))
150 row += 2;
151
152 if (!parentItem) {
153 layout = m_mainLayout;
154 parentWidget = q_ptr;;
155 } else {
156 if (!parentItem->groupBox) {
157 m_recreateQueue.removeAll(t: parentItem);
158 WidgetItem *par = parentItem->parent;
159 QWidget *w = nullptr;
160 QGridLayout *l = nullptr;
161 int oldRow = -1;
162 if (!par) {
163 w = q_ptr;
164 l = m_mainLayout;
165 oldRow = m_children.indexOf(t: parentItem);
166 } else {
167 w = par->groupBox;
168 l = par->layout;
169 oldRow = par->children.indexOf(t: parentItem);
170 if (hasHeader(item: par))
171 oldRow += 2;
172 }
173 parentItem->groupBox = new QGroupBox(w);
174 parentItem->layout = new QGridLayout();
175 parentItem->groupBox->setLayout(parentItem->layout);
176 if (parentItem->label) {
177 l->removeWidget(w: parentItem->label);
178 delete parentItem->label;
179 parentItem->label = nullptr;
180 }
181 if (parentItem->widget) {
182 l->removeWidget(w: parentItem->widget);
183 parentItem->widget->setParent(parentItem->groupBox);
184 parentItem->layout->addWidget(parentItem->widget, row: 0, column: 0, rowSpan: 1, columnSpan: 2);
185 parentItem->line = new QFrame(parentItem->groupBox);
186 } else if (parentItem->widgetLabel) {
187 l->removeWidget(w: parentItem->widgetLabel);
188 delete parentItem->widgetLabel;
189 parentItem->widgetLabel = nullptr;
190 }
191 if (parentItem->line) {
192 parentItem->line->setFrameShape(QFrame::HLine);
193 parentItem->line->setFrameShadow(QFrame::Sunken);
194 parentItem->layout->addWidget(parentItem->line, row: 1, column: 0, rowSpan: 1, columnSpan: 2);
195 }
196 l->addWidget(parentItem->groupBox, row: oldRow, column: 0, rowSpan: 1, columnSpan: 2);
197 updateItem(item: parentItem);
198 }
199 layout = parentItem->layout;
200 parentWidget = parentItem->groupBox;
201 }
202
203 newItem->label = new QLabel(parentWidget);
204 newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
205 newItem->widget = createEditor(property: index->property(), parent: parentWidget);
206 if (!newItem->widget) {
207 newItem->widgetLabel = new QLabel(parentWidget);
208 } else {
209 QObject::connect(sender: newItem->widget, signal: &QWidget::destroyed,
210 context: q_ptr, slot: [this] { slotEditorDestroyed(); });
211 m_widgetToItem[newItem->widget] = newItem;
212 }
213
214 insertRow(layout, row);
215 int span = 1;
216 if (newItem->widget)
217 layout->addWidget(newItem->widget, row, column: 1);
218 else if (newItem->widgetLabel)
219 layout->addWidget(newItem->widgetLabel, row, column: 1);
220 else
221 span = 2;
222 layout->addWidget(newItem->label, row, column: 0, rowSpan: 1, columnSpan: span);
223
224 m_itemToIndex[newItem] = index;
225 m_indexToItem[index] = newItem;
226
227 updateItem(item: newItem);
228}
229
230void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index)
231{
232 WidgetItem *item = m_indexToItem.value(key: index);
233
234 m_indexToItem.remove(key: index);
235 m_itemToIndex.remove(key: item);
236
237 WidgetItem *parentItem = item->parent;
238
239 int row = -1;
240
241 if (parentItem) {
242 row = parentItem->children.indexOf(t: item);
243 parentItem->children.removeAt(i: row);
244 if (hasHeader(item: parentItem))
245 row += 2;
246 } else {
247 row = m_children.indexOf(t: item);
248 m_children.removeAt(i: row);
249 }
250
251 if (item->widget)
252 delete item->widget;
253 if (item->label)
254 delete item->label;
255 if (item->widgetLabel)
256 delete item->widgetLabel;
257 if (item->groupBox)
258 delete item->groupBox;
259
260 if (!parentItem) {
261 removeRow(layout: m_mainLayout, row);
262 } else if (parentItem->children.size() != 0) {
263 removeRow(layout: parentItem->layout, row);
264 } else {
265 WidgetItem *par = parentItem->parent;
266 QGridLayout *l = (par ? par->layout : m_mainLayout);
267 if (parentItem->widget) {
268 parentItem->widget->hide();
269 parentItem->widget->setParent(0);
270 } else if (parentItem->widgetLabel) {
271 parentItem->widgetLabel->hide();
272 parentItem->widgetLabel->setParent(0);
273 } else {
274 //parentItem->widgetLabel = new QLabel(w);
275 }
276 l->removeWidget(w: parentItem->groupBox);
277 delete parentItem->groupBox;
278 parentItem->groupBox = nullptr;
279 parentItem->line = nullptr;
280 parentItem->layout = nullptr;
281 if (!m_recreateQueue.contains(t: parentItem))
282 m_recreateQueue.append(t: parentItem);
283 updateLater();
284 }
285 m_recreateQueue.removeAll(t: item);
286
287 delete item;
288}
289
290void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const
291{
292 QHash<QLayoutItem *, QRect> itemToPos;
293 int idx = 0;
294 while (idx < layout->count()) {
295 int r, c, rs, cs;
296 layout->getItemPosition(idx, row: &r, column: &c, rowSpan: &rs, columnSpan: &cs);
297 if (r >= row) {
298 itemToPos[layout->takeAt(index: idx)] = QRect(r + 1, c, rs, cs);
299 } else {
300 idx++;
301 }
302 }
303
304 for (auto it = itemToPos.cbegin(), icend = itemToPos.cend(); it != icend; ++it) {
305 const QRect r = it.value();
306 layout->addItem(item: it.key(), row: r.x(), column: r.y(), rowSpan: r.width(), columnSpan: r.height());
307 }
308}
309
310void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const
311{
312 QHash<QLayoutItem *, QRect> itemToPos;
313 int idx = 0;
314 while (idx < layout->count()) {
315 int r, c, rs, cs;
316 layout->getItemPosition(idx, row: &r, column: &c, rowSpan: &rs, columnSpan: &cs);
317 if (r > row) {
318 itemToPos[layout->takeAt(index: idx)] = QRect(r - 1, c, rs, cs);
319 } else {
320 idx++;
321 }
322 }
323
324 for (auto it = itemToPos.cbegin(), icend = itemToPos.cend(); it != icend; ++it) {
325 const QRect r = it.value();
326 layout->addItem(item: it.key(), row: r.x(), column: r.y(), rowSpan: r.width(), columnSpan: r.height());
327 }
328}
329
330bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const
331{
332 return item->widget;
333}
334
335void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index)
336{
337 WidgetItem *item = m_indexToItem.value(key: index);
338
339 updateItem(item);
340}
341
342void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item)
343{
344 QtProperty *property = m_itemToIndex[item]->property();
345 if (item->groupBox) {
346 QFont font = item->groupBox->font();
347 font.setUnderline(property->isModified());
348 item->groupBox->setFont(font);
349 item->groupBox->setTitle(property->propertyName());
350 item->groupBox->setToolTip(property->descriptionToolTip());
351 item->groupBox->setStatusTip(property->statusTip());
352 item->groupBox->setWhatsThis(property->whatsThis());
353 item->groupBox->setEnabled(property->isEnabled());
354 }
355 if (item->label) {
356 QFont font = item->label->font();
357 font.setUnderline(property->isModified());
358 item->label->setFont(font);
359 item->label->setText(property->propertyName());
360 item->label->setToolTip(property->descriptionToolTip());
361 item->label->setStatusTip(property->statusTip());
362 item->label->setWhatsThis(property->whatsThis());
363 item->label->setEnabled(property->isEnabled());
364 }
365 if (item->widgetLabel) {
366 QFont font = item->widgetLabel->font();
367 font.setUnderline(false);
368 item->widgetLabel->setFont(font);
369 item->widgetLabel->setText(property->valueText());
370 item->widgetLabel->setEnabled(property->isEnabled());
371 }
372 if (item->widget) {
373 QFont font = item->widget->font();
374 font.setUnderline(false);
375 item->widget->setFont(font);
376 item->widget->setEnabled(property->isEnabled());
377 const QString valueToolTip = property->valueToolTip();
378 item->widget->setToolTip(valueToolTip.isEmpty() ? property->valueText() : valueToolTip);
379 }
380 //item->setIcon(1, property->valueIcon());
381}
382
383
384
385/*!
386 \class QtGroupBoxPropertyBrowser
387 \internal
388 \inmodule QtDesigner
389 \since 4.4
390
391 \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox
392 based property browser.
393
394 A property browser is a widget that enables the user to edit a
395 given set of properties. Each property is represented by a label
396 specifying the property's name, and an editing widget (e.g. a line
397 edit or a combobox) holding its value. A property can have zero or
398 more subproperties.
399
400 QtGroupBoxPropertyBrowser provides group boxes for all nested
401 properties, i.e. subproperties are enclosed by a group box with
402 the parent property's name as its title. For example:
403
404 \image qtgroupboxpropertybrowser.png
405
406 Use the QtAbstractPropertyBrowser API to add, insert and remove
407 properties from an instance of the QtGroupBoxPropertyBrowser
408 class. The properties themselves are created and managed by
409 implementations of the QtAbstractPropertyManager class.
410
411 \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser
412*/
413
414/*!
415 Creates a property browser with the given \a parent.
416*/
417QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent)
418 : QtAbstractPropertyBrowser(parent), d_ptr(new QtGroupBoxPropertyBrowserPrivate)
419{
420 d_ptr->q_ptr = this;
421
422 d_ptr->init(parent: this);
423}
424
425/*!
426 Destroys this property browser.
427
428 Note that the properties that were inserted into this browser are
429 \e not destroyed since they may still be used in other
430 browsers. The properties are owned by the manager that created
431 them.
432
433 \sa QtProperty, QtAbstractPropertyManager
434*/
435QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser()
436{
437 for (auto it = d_ptr->m_itemToIndex.cbegin(), icend = d_ptr->m_itemToIndex.cend(); it != icend; ++it)
438 delete it.key();
439}
440
441/*!
442 \reimp
443*/
444void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem)
445{
446 d_ptr->propertyInserted(index: item, afterIndex: afterItem);
447}
448
449/*!
450 \reimp
451*/
452void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item)
453{
454 d_ptr->propertyRemoved(index: item);
455}
456
457/*!
458 \reimp
459*/
460void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item)
461{
462 d_ptr->propertyChanged(index: item);
463}
464
465QT_END_NAMESPACE
466
467#include "moc_qtgroupboxpropertybrowser.cpp"
468

source code of qttools/src/shared/qtpropertybrowser/qtgroupboxpropertybrowser.cpp