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