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