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
qtbuttonpropertybrowser.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
6
7#include <QtWidgets/qgridlayout.h>
8#include <QtWidgets/qlabel.h>
9#include <QtWidgets/qtoolbutton.h>
10
11#include <QtCore/qhash.h>
12
14
16{
17 QtButtonPropertyBrowser *q_ptr = nullptr;
18 Q_DECLARE_PUBLIC(QtButtonPropertyBrowser)
19public:
20
22
23 void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
26 QWidget *createEditor(QtProperty *property, QWidget *parent) const
27 { return q_ptr->createEditor(property, parent); }
28
30 void slotUpdate();
31 void slotToggled(bool checked);
32
34 {
35 QWidget *widget{nullptr}; // can be null
36 QLabel *label{nullptr}; // main label with property name
37 QLabel *widgetLabel{nullptr}; // label substitute showing the current value if there is no widget
38 QToolButton *button{nullptr}; // expandable button for items with children
39 QWidget *container{nullptr}; // container which is expanded when the button is clicked
40 QGridLayout *layout{nullptr}; // layout in container
41 WidgetItem *parent{nullptr};
43 bool expanded{false};
44 };
45private:
46 void updateLater();
47 void updateItem(WidgetItem *item);
48 static void insertRow(QGridLayout *layout, int row);
49 static void removeRow(QGridLayout *layout, int row);
50 int gridRow(WidgetItem *item) const;
51 static int gridSpan(WidgetItem *item);
52 void setExpanded(WidgetItem *item, bool expanded);
53 static QToolButton *createButton(QWidget *parent = nullptr);
54
55 QHash<QtBrowserItem *, WidgetItem *> m_indexToItem;
56 QHash<WidgetItem *, QtBrowserItem *> m_itemToIndex;
57 QHash<QWidget *, WidgetItem *> m_widgetToItem;
58 QHash<QObject *, WidgetItem *> m_buttonToItem;
59 QGridLayout *m_mainLayout = nullptr;
60 QList<WidgetItem *> m_children;
61 QList<WidgetItem *> m_recreateQueue;
62};
63
64QToolButton *QtButtonPropertyBrowserPrivate::createButton(QWidget *parent)
65{
66 auto *button = new QToolButton(parent);
67 button->setCheckable(true);
68 button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
69 button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
70 button->setArrowType(Qt::DownArrow);
71 button->setIconSize(QSize(3, 16));
72 /*
73 QIcon icon;
74 icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowDown), QIcon::Normal, QIcon::Off);
75 icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowUp), QIcon::Normal, QIcon::On);
76 button->setIcon(icon);
77 */
78 return button;
79}
80
81int QtButtonPropertyBrowserPrivate::gridRow(WidgetItem *item) const
82{
83 QList<WidgetItem *> siblings;
84 if (item->parent)
85 siblings = item->parent->children;
86 else
87 siblings = m_children;
88
89 int row = 0;
90 for (WidgetItem *sibling : std::as_const(siblings)) {
91 if (sibling == item)
92 return row;
93 row += gridSpan(sibling);
94 }
95 return -1;
96}
97
99{
100 if (item->container && item->expanded)
101 return 2;
102 return 1;
103}
104
105void QtButtonPropertyBrowserPrivate::init(QWidget *parent)
106{
107 m_mainLayout = new QGridLayout();
108 parent->setLayout(m_mainLayout);
109 auto *item = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
110 m_mainLayout->addItem(item, 0, 0);
111}
112
114{
115 auto *editor = qobject_cast<QWidget *>(q_ptr->sender());
116 if (!editor)
117 return;
118 const auto it = m_widgetToItem.find(editor);
119 if (it != m_widgetToItem.end()) {
120 it.value()->widget = nullptr;
121 m_widgetToItem.erase(it);
122 }
123}
124
126{
127 for (WidgetItem *item : std::as_const(m_recreateQueue)) {
128 WidgetItem *parent = item->parent;
129 QWidget *w = nullptr;
130 QGridLayout *l = nullptr;
131 const int oldRow = gridRow(item);
132 if (parent) {
133 w = parent->container;
134 l = parent->layout;
135 } else {
136 w = q_ptr;
137 l = m_mainLayout;
138 }
139
140 int span = 1;
141 if (!item->widget && !item->widgetLabel)
142 span = 2;
143 item->label = new QLabel(w);
144 item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
145 l->addWidget(item->label, oldRow, 0, 1, span);
146
147 updateItem(item);
148 }
149 m_recreateQueue.clear();
150}
151
152void QtButtonPropertyBrowserPrivate::setExpanded(WidgetItem *item, bool expanded)
153{
154 if (item->expanded == expanded)
155 return;
156
157 if (!item->container)
158 return;
159
160 item->expanded = expanded;
161 const int row = gridRow(item);
162 WidgetItem *parent = item->parent;
163 QGridLayout *l = nullptr;
164 if (parent)
165 l = parent->layout;
166 else
167 l = m_mainLayout;
168
169 if (expanded) {
170 insertRow(l, row + 1);
171 l->addWidget(item->container, row + 1, 0, 1, 2);
172 item->container->show();
173 } else {
174 l->removeWidget(item->container);
175 item->container->hide();
176 removeRow(l, row + 1);
177 }
178
179 item->button->setChecked(expanded);
180 item->button->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow);
181}
182
184{
185 WidgetItem *item = m_buttonToItem.value(q_ptr->sender());
186 if (!item)
187 return;
188
189 setExpanded(item, checked);
190
191 if (checked)
192 emit q_ptr->expanded(m_itemToIndex.value(item));
193 else
194 emit q_ptr->collapsed(m_itemToIndex.value(item));
195}
196
197void QtButtonPropertyBrowserPrivate::updateLater()
198{
199 QMetaObject::invokeMethod(q_ptr, [this] { slotUpdate(); }, Qt::QueuedConnection);
200}
201
203{
204 WidgetItem *afterItem = m_indexToItem.value(afterIndex);
205 WidgetItem *parentItem = m_indexToItem.value(index->parent());
206
207 auto *newItem = new WidgetItem();
208 newItem->parent = parentItem;
209
210 QGridLayout *layout = nullptr;
211 QWidget *parentWidget = nullptr;
212 int row = -1;
213 if (!afterItem) {
214 row = 0;
215 if (parentItem)
216 parentItem->children.insert(0, newItem);
217 else
218 m_children.insert(0, newItem);
219 } else {
220 row = gridRow(afterItem) + gridSpan(afterItem);
221 if (parentItem)
222 parentItem->children.insert(parentItem->children.indexOf(afterItem) + 1, newItem);
223 else
224 m_children.insert(m_children.indexOf(afterItem) + 1, newItem);
225 }
226
227 if (!parentItem) {
228 layout = m_mainLayout;
229 parentWidget = q_ptr;
230 } else {
231 if (!parentItem->container) {
232 m_recreateQueue.removeAll(parentItem);
233 WidgetItem *grandParent = parentItem->parent;
234 QGridLayout *l = nullptr;
235 const int oldRow = gridRow(parentItem);
236 if (grandParent) {
237 l = grandParent->layout;
238 } else {
239 l = m_mainLayout;
240 }
241 auto *container = new QFrame();
242 container->setFrameShape(QFrame::Panel);
243 container->setFrameShadow(QFrame::Raised);
244 parentItem->container = container;
245 parentItem->button = createButton();
246 m_buttonToItem[parentItem->button] = parentItem;
247 QObject::connect(parentItem->button, &QAbstractButton::toggled,
248 q_ptr, [this](bool checked) { slotToggled(checked); });
249 parentItem->layout = new QGridLayout();
250 container->setLayout(parentItem->layout);
251 if (parentItem->label) {
252 l->removeWidget(parentItem->label);
253 delete parentItem->label;
254 parentItem->label = nullptr;
255 }
256 int span = 1;
257 if (!parentItem->widget && !parentItem->widgetLabel)
258 span = 2;
259 l->addWidget(parentItem->button, oldRow, 0, 1, span);
260 updateItem(parentItem);
261 }
262 layout = parentItem->layout;
263 parentWidget = parentItem->container;
264 }
265
266 newItem->label = new QLabel(parentWidget);
267 newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
268 newItem->widget = createEditor(index->property(), parentWidget);
269 if (newItem->widget) {
270 QObject::connect(newItem->widget, &QWidget::destroyed,
271 q_ptr, [this] { slotEditorDestroyed(); });
272 m_widgetToItem[newItem->widget] = newItem;
273 } else if (index->property()->hasValue()) {
274 newItem->widgetLabel = new QLabel(parentWidget);
275 newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
276 }
277
278 insertRow(layout, row);
279 int span = 1;
280 if (newItem->widget)
281 layout->addWidget(newItem->widget, row, 1);
282 else if (newItem->widgetLabel)
283 layout->addWidget(newItem->widgetLabel, row, 1);
284 else
285 span = 2;
286 layout->addWidget(newItem->label, row, 0, span, 1);
287
288 m_itemToIndex[newItem] = index;
289 m_indexToItem[index] = newItem;
290
291 updateItem(newItem);
292}
293
295{
296 WidgetItem *item = m_indexToItem.value(index);
297
298 m_indexToItem.remove(index);
299 m_itemToIndex.remove(item);
300
301 WidgetItem *parentItem = item->parent;
302
303 const int row = gridRow(item);
304
305 if (parentItem)
306 parentItem->children.removeAt(parentItem->children.indexOf(item));
307 else
308 m_children.removeAt(m_children.indexOf(item));
309
310 const int colSpan = gridSpan(item);
311
312 m_buttonToItem.remove(item->button);
313
314 delete item->widget;
315 delete item->label;
316 delete item->widgetLabel;
317 delete item->button;
318 delete item->container;
319
320 if (!parentItem) {
321 removeRow(m_mainLayout, row);
322 if (colSpan > 1)
323 removeRow(m_mainLayout, row);
324 } else if (!parentItem->children.empty()) {
325 removeRow(parentItem->layout, row);
326 if (colSpan > 1)
327 removeRow(parentItem->layout, row);
328 } else {
329 const WidgetItem *grandParent = parentItem->parent;
330 QGridLayout *l = nullptr;
331 if (grandParent) {
332 l = grandParent->layout;
333 } else {
334 l = m_mainLayout;
335 }
336
337 const int parentRow = gridRow(parentItem);
338 const int parentSpan = gridSpan(parentItem);
339
340 l->removeWidget(parentItem->button);
341 l->removeWidget(parentItem->container);
342 delete parentItem->button;
343 delete parentItem->container;
344 parentItem->button = nullptr;
345 parentItem->container = nullptr;
346 parentItem->layout = nullptr;
347 if (!m_recreateQueue.contains(parentItem))
348 m_recreateQueue.append(parentItem);
349 if (parentSpan > 1)
350 removeRow(l, parentRow + 1);
351
352 updateLater();
353 }
354 m_recreateQueue.removeAll(item);
355
356 delete item;
357}
358
359void QtButtonPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row)
360{
361 QHash<QLayoutItem *, QRect> itemToPos;
362 int idx = 0;
363 while (idx < layout->count()) {
364 int r, c, rs, cs;
365 layout->getItemPosition(idx, &r, &c, &rs, &cs);
366 if (r >= row) {
367 itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs);
368 } else {
369 idx++;
370 }
371 }
372
373 for (auto it = itemToPos.constBegin(), icend = itemToPos.constEnd(); it != icend; ++it) {
374 const QRect r = it.value();
375 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
376 }
377}
378
379void QtButtonPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row)
380{
381 QHash<QLayoutItem *, QRect> itemToPos;
382 int idx = 0;
383 while (idx < layout->count()) {
384 int r, c, rs, cs;
385 layout->getItemPosition(idx, &r, &c, &rs, &cs);
386 if (r > row) {
387 itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs);
388 } else {
389 idx++;
390 }
391 }
392
393 for (auto it = itemToPos.constBegin(), icend = itemToPos.constEnd(); it != icend; ++it) {
394 const QRect r = it.value();
395 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
396 }
397}
398
400{
401 WidgetItem *item = m_indexToItem.value(index);
402
403 updateItem(item);
404}
405
406void QtButtonPropertyBrowserPrivate::updateItem(WidgetItem *item)
407{
408 QtProperty *property = m_itemToIndex[item]->property();
409 if (item->button) {
410 QFont font = item->button->font();
411 font.setUnderline(property->isModified());
412 item->button->setFont(font);
413 item->button->setText(property->propertyName());
414 item->button->setToolTip(property->descriptionToolTip());
415 item->button->setStatusTip(property->statusTip());
416 item->button->setWhatsThis(property->whatsThis());
417 item->button->setEnabled(property->isEnabled());
418 }
419 if (item->label) {
420 QFont font = item->label->font();
421 font.setUnderline(property->isModified());
422 item->label->setFont(font);
423 item->label->setText(property->propertyName());
424 item->label->setToolTip(property->descriptionToolTip());
425 item->label->setStatusTip(property->statusTip());
426 item->label->setWhatsThis(property->whatsThis());
427 item->label->setEnabled(property->isEnabled());
428 }
429 if (item->widgetLabel) {
430 QFont font = item->widgetLabel->font();
431 font.setUnderline(false);
432 item->widgetLabel->setFont(font);
433 item->widgetLabel->setText(property->valueText());
434 item->widgetLabel->setToolTip(property->valueText());
435 item->widgetLabel->setEnabled(property->isEnabled());
436 }
437 if (item->widget) {
438 QFont font = item->widget->font();
439 font.setUnderline(false);
440 item->widget->setFont(font);
441 item->widget->setEnabled(property->isEnabled());
442 const QString valueToolTip = property->valueToolTip();
443 item->widget->setToolTip(valueToolTip.isEmpty() ? property->valueText() : valueToolTip);
444 }
445}
446
447
448
449/*!
450 \class QtButtonPropertyBrowser
451 \internal
452 \inmodule QtDesigner
453 \since 4.4
454
455 \brief The QtButtonPropertyBrowser class provides a drop down QToolButton
456 based property browser.
457
458 A property browser is a widget that enables the user to edit a
459 given set of properties. Each property is represented by a label
460 specifying the property's name, and an editing widget (e.g. a line
461 edit or a combobox) holding its value. A property can have zero or
462 more subproperties.
463
464 QtButtonPropertyBrowser provides drop down button for all nested
465 properties, i.e. subproperties are enclosed by a container associated with
466 the drop down button. The parent property's name is displayed as button text. For example:
467
468 \image qtbuttonpropertybrowser.png
469
470 Use the QtAbstractPropertyBrowser API to add, insert and remove
471 properties from an instance of the QtButtonPropertyBrowser
472 class. The properties themselves are created and managed by
473 implementations of the QtAbstractPropertyManager class.
474
475 \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser
476*/
477
478/*!
479 \fn void QtButtonPropertyBrowser::collapsed(QtBrowserItem *item)
480
481 This signal is emitted when the \a item is collapsed.
482
483 \sa expanded(), setExpanded()
484*/
485
486/*!
487 \fn void QtButtonPropertyBrowser::expanded(QtBrowserItem *item)
488
489 This signal is emitted when the \a item is expanded.
490
491 \sa collapsed(), setExpanded()
492*/
493
494/*!
495 Creates a property browser with the given \a parent.
496*/
497QtButtonPropertyBrowser::QtButtonPropertyBrowser(QWidget *parent)
498 : QtAbstractPropertyBrowser(parent), d_ptr(new QtButtonPropertyBrowserPrivate)
499{
500 d_ptr->q_ptr = this;
501
502 d_ptr->init(this);
503}
504
505/*!
506 Destroys this property browser.
507
508 Note that the properties that were inserted into this browser are
509 \e not destroyed since they may still be used in other
510 browsers. The properties are owned by the manager that created
511 them.
512
513 \sa QtProperty, QtAbstractPropertyManager
514*/
516{
517 for (auto it = d_ptr->m_itemToIndex.cbegin(), icend = d_ptr->m_itemToIndex.cend(); it != icend; ++it)
518 delete it.key();
519}
520
521/*!
522 \reimp
523*/
525{
526 d_ptr->propertyInserted(item, afterItem);
527}
528
529/*!
530 \reimp
531*/
533{
534 d_ptr->propertyRemoved(item);
535}
536
537/*!
538 \reimp
539*/
541{
542 d_ptr->propertyChanged(item);
543}
544
545/*!
546 Sets the \a item to either collapse or expanded, depending on the value of \a expanded.
547
548 \sa isExpanded(), expanded(), collapsed()
549*/
550
552{
553 QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item);
554 if (itm)
555 d_ptr->setExpanded(itm, expanded);
556}
557
558/*!
559 Returns true if the \a item is expanded; otherwise returns false.
560
561 \sa setExpanded()
562*/
563
565{
566 QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item);
567 if (itm)
568 return itm->expanded;
569 return false;
570}
571
572QT_END_NAMESPACE
573
574#include "moc_qtbuttonpropertybrowser_p.cpp"
QtAbstractPropertyBrowser provides a base class for implementing property browsers.
The QtBrowserItem class represents a property in a property browser instance.
QtProperty * property() const
Returns the property which is accosiated with this item.
void propertyChanged(QtBrowserItem *index)
QWidget * createEditor(QtProperty *property, QWidget *parent) const
void propertyRemoved(QtBrowserItem *index)
void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
The QtButtonPropertyBrowser class provides a drop down QToolButton based property browser.
bool isExpanded(QtBrowserItem *item) const
Returns true if the item is expanded; otherwise returns false.
void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) override
\reimp
void itemChanged(QtBrowserItem *item) override
\reimp
~QtButtonPropertyBrowser() override
Destroys this property browser.
void itemRemoved(QtBrowserItem *item) override
\reimp
void setExpanded(QtBrowserItem *item, bool expanded)
Sets the item to either collapse or expanded, depending on the value of expanded.
The QtProperty class encapsulates an instance of a property.
bool isEnabled() const
Returns whether the property is enabled.
bool hasValue() const
Returns whether the property has a value.
bool isModified() const
Returns whether the property is modified.
Combined button and popup list for selecting options.