File indexing completed on 2024-05-12 04:35:08
0001 /**************************************************************************** 0002 ** 0003 ** Copyright (C) 2016 The Qt Company Ltd. 0004 ** Contact: https://www.qt.io/licensing/ 0005 ** 0006 ** This file is part of the tools applications of the Qt Toolkit. 0007 ** 0008 ** $QT_BEGIN_LICENSE:LGPL$ 0009 ** Commercial License Usage 0010 ** Licensees holding valid commercial Qt licenses may use this file in 0011 ** accordance with the commercial license agreement provided with the 0012 ** Software or, alternatively, in accordance with the terms contained in 0013 ** a written agreement between you and The Qt Company. For licensing terms 0014 ** and conditions see https://www.qt.io/terms-conditions. For further 0015 ** information use the contact form at https://www.qt.io/contact-us. 0016 ** 0017 ** GNU Lesser General Public License Usage 0018 ** Alternatively, this file may be used under the terms of the GNU Lesser 0019 ** General Public License version 3 as published by the Free Software 0020 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 0021 ** packaging of this file. Please review the following information to 0022 ** ensure the GNU Lesser General Public License version 3 requirements 0023 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 0024 ** 0025 ** GNU General Public License Usage 0026 ** Alternatively, this file may be used under the terms of the GNU 0027 ** General Public License version 2.0 or (at your option) the GNU General 0028 ** Public license version 3 or any later version approved by the KDE Free 0029 ** Qt Foundation. The licenses are as published by the Free Software 0030 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 0031 ** included in the packaging of this file. Please review the following 0032 ** information to ensure the GNU General Public License requirements will 0033 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 0034 ** https://www.gnu.org/licenses/gpl-3.0.html. 0035 ** 0036 ** $QT_END_LICENSE$ 0037 ** 0038 ****************************************************************************/ 0039 0040 #include "qtbuttonpropertybrowser.h" 0041 #include <QtCore/QSet> 0042 #include <QtWidgets/QGridLayout> 0043 #include <QtWidgets/QLabel> 0044 #include <QtCore/QTimer> 0045 #include <QtCore/QMap> 0046 #include <QtWidgets/QToolButton> 0047 #include <QtWidgets/QStyle> 0048 0049 QT_BEGIN_NAMESPACE 0050 0051 class QtButtonPropertyBrowserPrivate 0052 { 0053 QtButtonPropertyBrowser *q_ptr; 0054 Q_DECLARE_PUBLIC(QtButtonPropertyBrowser) 0055 public: 0056 0057 void init(QWidget *parent); 0058 0059 void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); 0060 void propertyRemoved(QtBrowserItem *index); 0061 void propertyChanged(QtBrowserItem *index); 0062 QWidget *createEditor(QtProperty *property, QWidget *parent) const 0063 { return q_ptr->createEditor(property, parent); } 0064 0065 void slotEditorDestroyed(); 0066 void slotUpdate(); 0067 void slotToggled(bool checked); 0068 0069 struct WidgetItem 0070 { 0071 QWidget *widget{nullptr}; // can be null 0072 QLabel *label{nullptr}; // main label with property name 0073 QLabel *widgetLabel{nullptr}; // label substitute showing the current value if there is no widget 0074 QToolButton *button{nullptr}; // expandable button for items with children 0075 QWidget *container{nullptr}; // container which is expanded when the button is clicked 0076 QGridLayout *layout{nullptr}; // layout in container 0077 WidgetItem *parent{nullptr}; 0078 QList<WidgetItem *> children; 0079 bool expanded{false}; 0080 }; 0081 private: 0082 void updateLater(); 0083 void updateItem(WidgetItem *item); 0084 void insertRow(QGridLayout *layout, int row) const; 0085 void removeRow(QGridLayout *layout, int row) const; 0086 int gridRow(WidgetItem *item) const; 0087 int gridSpan(WidgetItem *item) const; 0088 void setExpanded(WidgetItem *item, bool expanded); 0089 QToolButton *createButton(QWidget *panret = 0) const; 0090 0091 QMap<QtBrowserItem *, WidgetItem *> m_indexToItem; 0092 QMap<WidgetItem *, QtBrowserItem *> m_itemToIndex; 0093 QMap<QWidget *, WidgetItem *> m_widgetToItem; 0094 QMap<QObject *, WidgetItem *> m_buttonToItem; 0095 QGridLayout *m_mainLayout; 0096 QList<WidgetItem *> m_children; 0097 QList<WidgetItem *> m_recreateQueue; 0098 }; 0099 0100 QToolButton *QtButtonPropertyBrowserPrivate::createButton(QWidget *parent) const 0101 { 0102 QToolButton *button = new QToolButton(parent); 0103 button->setCheckable(true); 0104 button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); 0105 button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); 0106 button->setArrowType(Qt::DownArrow); 0107 button->setIconSize(QSize(3, 16)); 0108 /* 0109 QIcon icon; 0110 icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowDown), QIcon::Normal, QIcon::Off); 0111 icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowUp), QIcon::Normal, QIcon::On); 0112 button->setIcon(icon); 0113 */ 0114 return button; 0115 } 0116 0117 int QtButtonPropertyBrowserPrivate::gridRow(WidgetItem *item) const 0118 { 0119 QList<WidgetItem *> siblings; 0120 if (item->parent) 0121 siblings = item->parent->children; 0122 else 0123 siblings = m_children; 0124 0125 int row = 0; 0126 for (WidgetItem *sibling : qAsConst(siblings)) { 0127 if (sibling == item) 0128 return row; 0129 row += gridSpan(sibling); 0130 } 0131 return -1; 0132 } 0133 0134 int QtButtonPropertyBrowserPrivate::gridSpan(WidgetItem *item) const 0135 { 0136 if (item->container && item->expanded) 0137 return 2; 0138 return 1; 0139 } 0140 0141 void QtButtonPropertyBrowserPrivate::init(QWidget *parent) 0142 { 0143 m_mainLayout = new QGridLayout(); 0144 parent->setLayout(m_mainLayout); 0145 QLayoutItem *item = new QSpacerItem(0, 0, 0146 QSizePolicy::Fixed, QSizePolicy::Expanding); 0147 m_mainLayout->addItem(item, 0, 0); 0148 } 0149 0150 void QtButtonPropertyBrowserPrivate::slotEditorDestroyed() 0151 { 0152 QWidget *editor = qobject_cast<QWidget *>(q_ptr->sender()); 0153 if (!editor) 0154 return; 0155 if (!m_widgetToItem.contains(editor)) 0156 return; 0157 m_widgetToItem[editor]->widget = 0; 0158 m_widgetToItem.remove(editor); 0159 } 0160 0161 void QtButtonPropertyBrowserPrivate::slotUpdate() 0162 { 0163 for (WidgetItem *item : qAsConst(m_recreateQueue)) { 0164 WidgetItem *parent = item->parent; 0165 QWidget *w = 0; 0166 QGridLayout *l = 0; 0167 const int oldRow = gridRow(item); 0168 if (parent) { 0169 w = parent->container; 0170 l = parent->layout; 0171 } else { 0172 w = q_ptr; 0173 l = m_mainLayout; 0174 } 0175 0176 int span = 1; 0177 if (!item->widget && !item->widgetLabel) 0178 span = 2; 0179 item->label = new QLabel(w); 0180 item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); 0181 l->addWidget(item->label, oldRow, 0, 1, span); 0182 0183 updateItem(item); 0184 } 0185 m_recreateQueue.clear(); 0186 } 0187 0188 void QtButtonPropertyBrowserPrivate::setExpanded(WidgetItem *item, bool expanded) 0189 { 0190 if (item->expanded == expanded) 0191 return; 0192 0193 if (!item->container) 0194 return; 0195 0196 item->expanded = expanded; 0197 const int row = gridRow(item); 0198 WidgetItem *parent = item->parent; 0199 QGridLayout *l = 0; 0200 if (parent) 0201 l = parent->layout; 0202 else 0203 l = m_mainLayout; 0204 0205 if (expanded) { 0206 insertRow(l, row + 1); 0207 l->addWidget(item->container, row + 1, 0, 1, 2); 0208 item->container->show(); 0209 } else { 0210 l->removeWidget(item->container); 0211 item->container->hide(); 0212 removeRow(l, row + 1); 0213 } 0214 0215 item->button->setChecked(expanded); 0216 item->button->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow); 0217 } 0218 0219 void QtButtonPropertyBrowserPrivate::slotToggled(bool checked) 0220 { 0221 WidgetItem *item = m_buttonToItem.value(q_ptr->sender()); 0222 if (!item) 0223 return; 0224 0225 setExpanded(item, checked); 0226 0227 if (checked) 0228 Q_EMIT q_ptr->expanded(m_itemToIndex.value(item)); 0229 else 0230 Q_EMIT q_ptr->collapsed(m_itemToIndex.value(item)); 0231 } 0232 0233 void QtButtonPropertyBrowserPrivate::updateLater() 0234 { 0235 QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); 0236 } 0237 0238 void QtButtonPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) 0239 { 0240 WidgetItem *afterItem = m_indexToItem.value(afterIndex); 0241 WidgetItem *parentItem = m_indexToItem.value(index->parent()); 0242 0243 WidgetItem *newItem = new WidgetItem(); 0244 newItem->parent = parentItem; 0245 0246 QGridLayout *layout = 0; 0247 QWidget *parentWidget = 0; 0248 int row = -1; 0249 if (!afterItem) { 0250 row = 0; 0251 if (parentItem) 0252 parentItem->children.insert(0, newItem); 0253 else 0254 m_children.insert(0, newItem); 0255 } else { 0256 row = gridRow(afterItem) + gridSpan(afterItem); 0257 if (parentItem) 0258 parentItem->children.insert(parentItem->children.indexOf(afterItem) + 1, newItem); 0259 else 0260 m_children.insert(m_children.indexOf(afterItem) + 1, newItem); 0261 } 0262 0263 if (!parentItem) { 0264 layout = m_mainLayout; 0265 parentWidget = q_ptr; 0266 } else { 0267 if (!parentItem->container) { 0268 m_recreateQueue.removeAll(parentItem); 0269 WidgetItem *grandParent = parentItem->parent; 0270 QGridLayout *l = 0; 0271 const int oldRow = gridRow(parentItem); 0272 if (grandParent) { 0273 l = grandParent->layout; 0274 } else { 0275 l = m_mainLayout; 0276 } 0277 QFrame *container = new QFrame(); 0278 container->setFrameShape(QFrame::Panel); 0279 container->setFrameShadow(QFrame::Raised); 0280 parentItem->container = container; 0281 parentItem->button = createButton(); 0282 m_buttonToItem[parentItem->button] = parentItem; 0283 q_ptr->connect(parentItem->button, SIGNAL(toggled(bool)), q_ptr, SLOT(slotToggled(bool))); 0284 parentItem->layout = new QGridLayout(); 0285 container->setLayout(parentItem->layout); 0286 if (parentItem->label) { 0287 l->removeWidget(parentItem->label); 0288 delete parentItem->label; 0289 parentItem->label = 0; 0290 } 0291 int span = 1; 0292 if (!parentItem->widget && !parentItem->widgetLabel) 0293 span = 2; 0294 l->addWidget(parentItem->button, oldRow, 0, 1, span); 0295 updateItem(parentItem); 0296 } 0297 layout = parentItem->layout; 0298 parentWidget = parentItem->container; 0299 } 0300 0301 newItem->label = new QLabel(parentWidget); 0302 newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); 0303 newItem->widget = createEditor(index->property(), parentWidget); 0304 if (newItem->widget) { 0305 QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); 0306 m_widgetToItem[newItem->widget] = newItem; 0307 } else if (index->property()->hasValue()) { 0308 newItem->widgetLabel = new QLabel(parentWidget); 0309 newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); 0310 } 0311 0312 insertRow(layout, row); 0313 int span = 1; 0314 if (newItem->widget) 0315 layout->addWidget(newItem->widget, row, 1); 0316 else if (newItem->widgetLabel) 0317 layout->addWidget(newItem->widgetLabel, row, 1); 0318 else 0319 span = 2; 0320 layout->addWidget(newItem->label, row, 0, span, 1); 0321 0322 m_itemToIndex[newItem] = index; 0323 m_indexToItem[index] = newItem; 0324 0325 updateItem(newItem); 0326 } 0327 0328 void QtButtonPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) 0329 { 0330 WidgetItem *item = m_indexToItem.value(index); 0331 0332 m_indexToItem.remove(index); 0333 m_itemToIndex.remove(item); 0334 0335 WidgetItem *parentItem = item->parent; 0336 0337 const int row = gridRow(item); 0338 0339 if (parentItem) 0340 parentItem->children.removeAt(parentItem->children.indexOf(item)); 0341 else 0342 m_children.removeAt(m_children.indexOf(item)); 0343 0344 const int colSpan = gridSpan(item); 0345 0346 m_buttonToItem.remove(item->button); 0347 0348 if (item->widget) 0349 delete item->widget; 0350 if (item->label) 0351 delete item->label; 0352 if (item->widgetLabel) 0353 delete item->widgetLabel; 0354 if (item->button) 0355 delete item->button; 0356 if (item->container) 0357 delete item->container; 0358 0359 if (!parentItem) { 0360 removeRow(m_mainLayout, row); 0361 if (colSpan > 1) 0362 removeRow(m_mainLayout, row); 0363 } else if (parentItem->children.count() != 0) { 0364 removeRow(parentItem->layout, row); 0365 if (colSpan > 1) 0366 removeRow(parentItem->layout, row); 0367 } else { 0368 const WidgetItem *grandParent = parentItem->parent; 0369 QGridLayout *l = 0; 0370 if (grandParent) { 0371 l = grandParent->layout; 0372 } else { 0373 l = m_mainLayout; 0374 } 0375 0376 const int parentRow = gridRow(parentItem); 0377 const int parentSpan = gridSpan(parentItem); 0378 0379 l->removeWidget(parentItem->button); 0380 l->removeWidget(parentItem->container); 0381 delete parentItem->button; 0382 delete parentItem->container; 0383 parentItem->button = 0; 0384 parentItem->container = 0; 0385 parentItem->layout = 0; 0386 if (!m_recreateQueue.contains(parentItem)) 0387 m_recreateQueue.append(parentItem); 0388 if (parentSpan > 1) 0389 removeRow(l, parentRow + 1); 0390 0391 updateLater(); 0392 } 0393 m_recreateQueue.removeAll(item); 0394 0395 delete item; 0396 } 0397 0398 void QtButtonPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const 0399 { 0400 QMap<QLayoutItem *, QRect> itemToPos; 0401 int idx = 0; 0402 while (idx < layout->count()) { 0403 int r, c, rs, cs; 0404 layout->getItemPosition(idx, &r, &c, &rs, &cs); 0405 if (r >= row) { 0406 itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); 0407 } else { 0408 idx++; 0409 } 0410 } 0411 0412 for (auto it = itemToPos.constBegin(), icend = itemToPos.constEnd(); it != icend; ++it) { 0413 const QRect r = it.value(); 0414 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); 0415 } 0416 } 0417 0418 void QtButtonPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const 0419 { 0420 QMap<QLayoutItem *, QRect> itemToPos; 0421 int idx = 0; 0422 while (idx < layout->count()) { 0423 int r, c, rs, cs; 0424 layout->getItemPosition(idx, &r, &c, &rs, &cs); 0425 if (r > row) { 0426 itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); 0427 } else { 0428 idx++; 0429 } 0430 } 0431 0432 for (auto it = itemToPos.constBegin(), icend = itemToPos.constEnd(); it != icend; ++it) { 0433 const QRect r = it.value(); 0434 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); 0435 } 0436 } 0437 0438 void QtButtonPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) 0439 { 0440 WidgetItem *item = m_indexToItem.value(index); 0441 0442 updateItem(item); 0443 } 0444 0445 void QtButtonPropertyBrowserPrivate::updateItem(WidgetItem *item) 0446 { 0447 QtProperty *property = m_itemToIndex[item]->property(); 0448 if (item->button) { 0449 QFont font = item->button->font(); 0450 font.setUnderline(property->isModified()); 0451 item->button->setFont(font); 0452 item->button->setText(property->propertyName()); 0453 item->button->setToolTip(property->descriptionToolTip()); 0454 item->button->setStatusTip(property->statusTip()); 0455 item->button->setWhatsThis(property->whatsThis()); 0456 item->button->setEnabled(property->isEnabled()); 0457 } 0458 if (item->label) { 0459 QFont font = item->label->font(); 0460 font.setUnderline(property->isModified()); 0461 item->label->setFont(font); 0462 item->label->setText(property->propertyName()); 0463 item->label->setToolTip(property->descriptionToolTip()); 0464 item->label->setStatusTip(property->statusTip()); 0465 item->label->setWhatsThis(property->whatsThis()); 0466 item->label->setEnabled(property->isEnabled()); 0467 } 0468 if (item->widgetLabel) { 0469 QFont font = item->widgetLabel->font(); 0470 font.setUnderline(false); 0471 item->widgetLabel->setFont(font); 0472 item->widgetLabel->setText(property->valueText()); 0473 item->widgetLabel->setToolTip(property->valueText()); 0474 item->widgetLabel->setEnabled(property->isEnabled()); 0475 } 0476 if (item->widget) { 0477 QFont font = item->widget->font(); 0478 font.setUnderline(false); 0479 item->widget->setFont(font); 0480 item->widget->setEnabled(property->isEnabled()); 0481 const QString valueToolTip = property->valueToolTip(); 0482 item->widget->setToolTip(valueToolTip.isEmpty() ? property->valueText() : valueToolTip); 0483 } 0484 } 0485 0486 0487 0488 /*! 0489 \class QtButtonPropertyBrowser 0490 \internal 0491 \inmodule QtDesigner 0492 \since 4.4 0493 0494 \brief The QtButtonPropertyBrowser class provides a drop down QToolButton 0495 based property browser. 0496 0497 A property browser is a widget that enables the user to edit a 0498 given set of properties. Each property is represented by a label 0499 specifying the property's name, and an editing widget (e.g. a line 0500 edit or a combobox) holding its value. A property can have zero or 0501 more subproperties. 0502 0503 QtButtonPropertyBrowser provides drop down button for all nested 0504 properties, i.e. subproperties are enclosed by a container associated with 0505 the drop down button. The parent property's name is displayed as button text. For example: 0506 0507 \image qtbuttonpropertybrowser.png 0508 0509 Use the QtAbstractPropertyBrowser API to add, insert and remove 0510 properties from an instance of the QtButtonPropertyBrowser 0511 class. The properties themselves are created and managed by 0512 implementations of the QtAbstractPropertyManager class. 0513 0514 \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser 0515 */ 0516 0517 /*! 0518 \fn void QtButtonPropertyBrowser::collapsed(QtBrowserItem *item) 0519 0520 This signal is emitted when the \a item is collapsed. 0521 0522 \sa expanded(), setExpanded() 0523 */ 0524 0525 /*! 0526 \fn void QtButtonPropertyBrowser::expanded(QtBrowserItem *item) 0527 0528 This signal is emitted when the \a item is expanded. 0529 0530 \sa collapsed(), setExpanded() 0531 */ 0532 0533 /*! 0534 Creates a property browser with the given \a parent. 0535 */ 0536 QtButtonPropertyBrowser::QtButtonPropertyBrowser(QWidget *parent) 0537 : QtAbstractPropertyBrowser(parent), d_ptr(new QtButtonPropertyBrowserPrivate) 0538 { 0539 d_ptr->q_ptr = this; 0540 0541 d_ptr->init(this); 0542 } 0543 0544 /*! 0545 Destroys this property browser. 0546 0547 Note that the properties that were inserted into this browser are 0548 \e not destroyed since they may still be used in other 0549 browsers. The properties are owned by the manager that created 0550 them. 0551 0552 \sa QtProperty, QtAbstractPropertyManager 0553 */ 0554 QtButtonPropertyBrowser::~QtButtonPropertyBrowser() 0555 { 0556 const QMap<QtButtonPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); 0557 for (QMap<QtButtonPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) 0558 delete it.key(); 0559 } 0560 0561 /*! 0562 \reimp 0563 */ 0564 void QtButtonPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) 0565 { 0566 d_ptr->propertyInserted(item, afterItem); 0567 } 0568 0569 /*! 0570 \reimp 0571 */ 0572 void QtButtonPropertyBrowser::itemRemoved(QtBrowserItem *item) 0573 { 0574 d_ptr->propertyRemoved(item); 0575 } 0576 0577 /*! 0578 \reimp 0579 */ 0580 void QtButtonPropertyBrowser::itemChanged(QtBrowserItem *item) 0581 { 0582 d_ptr->propertyChanged(item); 0583 } 0584 0585 /*! 0586 Sets the \a item to either collapse or expanded, depending on the value of \a expanded. 0587 0588 \sa isExpanded(), expanded(), collapsed() 0589 */ 0590 0591 void QtButtonPropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) 0592 { 0593 QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); 0594 if (itm) 0595 d_ptr->setExpanded(itm, expanded); 0596 } 0597 0598 /*! 0599 Returns true if the \a item is expanded; otherwise returns false. 0600 0601 \sa setExpanded() 0602 */ 0603 0604 bool QtButtonPropertyBrowser::isExpanded(QtBrowserItem *item) const 0605 { 0606 QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); 0607 if (itm) 0608 return itm->expanded; 0609 return false; 0610 } 0611 0612 QT_END_NAMESPACE 0613 0614 #include "moc_qtbuttonpropertybrowser.cpp"