qtbuttonpropertybrowser.cpp 19 KB

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