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"