layoutbuilder.cpp 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. // Copyright (C) 2020 The Qt Company Ltd.
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
  3. #include "layoutbuilder.h"
  4. #include <QDebug>
  5. #include <QFormLayout>
  6. #include <QGridLayout>
  7. #include <QGroupBox>
  8. #include <QLabel>
  9. #include <QPushButton>
  10. #include <QSpacerItem>
  11. #include <QSpinBox>
  12. #include <QSplitter>
  13. #include <QStackedLayout>
  14. #include <QStackedWidget>
  15. #include <QStyle>
  16. #include <QTabWidget>
  17. #include <QTextEdit>
  18. #include <QToolBar>
  19. namespace Layouting {
  20. // That's cut down qtcassert.{c,h} to avoid the dependency.
  21. #define QTC_STRINGIFY_HELPER(x) #x
  22. #define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
  23. #define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
  24. #define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
  25. #define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
  26. template <typename X>
  27. typename X::Implementation *access(const X *x)
  28. {
  29. return static_cast<typename X::Implementation *>(x->ptr);
  30. }
  31. template <typename X>
  32. void apply(X *x, std::initializer_list<typename X::I> ps)
  33. {
  34. for (auto && p : ps)
  35. p.apply(x);
  36. }
  37. // FlowLayout
  38. class FlowLayout : public QLayout
  39. {
  40. public:
  41. explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
  42. : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
  43. {
  44. setContentsMargins(margin, margin, margin, margin);
  45. }
  46. FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1)
  47. : m_hSpace(hSpacing), m_vSpace(vSpacing)
  48. {
  49. setContentsMargins(margin, margin, margin, margin);
  50. }
  51. ~FlowLayout() override
  52. {
  53. QLayoutItem *item;
  54. while ((item = takeAt(0)))
  55. delete item;
  56. }
  57. void addItem(QLayoutItem *item) override { itemList.append(item); }
  58. int horizontalSpacing() const
  59. {
  60. if (m_hSpace >= 0)
  61. return m_hSpace;
  62. else
  63. return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
  64. }
  65. int verticalSpacing() const
  66. {
  67. if (m_vSpace >= 0)
  68. return m_vSpace;
  69. else
  70. return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
  71. }
  72. Qt::Orientations expandingDirections() const override
  73. {
  74. return {};
  75. }
  76. bool hasHeightForWidth() const override { return true; }
  77. int heightForWidth(int width) const override
  78. {
  79. int height = doLayout(QRect(0, 0, width, 0), true);
  80. return height;
  81. }
  82. int count() const override { return itemList.size(); }
  83. QLayoutItem *itemAt(int index) const override
  84. {
  85. return itemList.value(index);
  86. }
  87. QSize minimumSize() const override
  88. {
  89. QSize size;
  90. for (QLayoutItem *item : itemList)
  91. size = size.expandedTo(item->minimumSize());
  92. int left, top, right, bottom;
  93. getContentsMargins(&left, &top, &right, &bottom);
  94. size += QSize(left + right, top + bottom);
  95. return size;
  96. }
  97. void setGeometry(const QRect &rect) override
  98. {
  99. QLayout::setGeometry(rect);
  100. doLayout(rect, false);
  101. }
  102. QSize sizeHint() const override
  103. {
  104. return minimumSize();
  105. }
  106. QLayoutItem *takeAt(int index) override
  107. {
  108. if (index >= 0 && index < itemList.size())
  109. return itemList.takeAt(index);
  110. else
  111. return nullptr;
  112. }
  113. private:
  114. int doLayout(const QRect &rect, bool testOnly) const
  115. {
  116. int left, top, right, bottom;
  117. getContentsMargins(&left, &top, &right, &bottom);
  118. QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
  119. int x = effectiveRect.x();
  120. int y = effectiveRect.y();
  121. int lineHeight = 0;
  122. for (QLayoutItem *item : itemList) {
  123. QWidget *wid = item->widget();
  124. int spaceX = horizontalSpacing();
  125. if (spaceX == -1)
  126. spaceX = wid->style()->layoutSpacing(
  127. QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
  128. int spaceY = verticalSpacing();
  129. if (spaceY == -1)
  130. spaceY = wid->style()->layoutSpacing(
  131. QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
  132. int nextX = x + item->sizeHint().width() + spaceX;
  133. if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
  134. x = effectiveRect.x();
  135. y = y + lineHeight + spaceY;
  136. nextX = x + item->sizeHint().width() + spaceX;
  137. lineHeight = 0;
  138. }
  139. if (!testOnly)
  140. item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
  141. x = nextX;
  142. lineHeight = qMax(lineHeight, item->sizeHint().height());
  143. }
  144. return y + lineHeight - rect.y() + bottom;
  145. }
  146. int smartSpacing(QStyle::PixelMetric pm) const
  147. {
  148. QObject *parent = this->parent();
  149. if (!parent) {
  150. return -1;
  151. } else if (parent->isWidgetType()) {
  152. auto pw = static_cast<QWidget *>(parent);
  153. return pw->style()->pixelMetric(pm, nullptr, pw);
  154. } else {
  155. return static_cast<QLayout *>(parent)->spacing();
  156. }
  157. }
  158. QList<QLayoutItem *> itemList;
  159. int m_hSpace;
  160. int m_vSpace;
  161. };
  162. /*!
  163. \namespace Layouting
  164. \inmodule QtCreator
  165. \brief The Layouting namespace contains classes and functions to conveniently
  166. create layouts in code.
  167. Classes in the namespace help to create create QLayout or QWidget derived class,
  168. instances should be used locally within a function and never stored.
  169. \sa Layouting::Widget, Layouting::Layout
  170. */
  171. /*!
  172. \class Layouting::Layout
  173. \inmodule QtCreator
  174. The Layout class is a base class for more specific builder
  175. classes to create QLayout derived objects.
  176. */
  177. /*!
  178. \class Layouting::Widget
  179. \inmodule QtCreator
  180. The Widget class is a base class for more specific builder
  181. classes to create QWidget derived objects.
  182. */
  183. /*!
  184. \class Layouting::LayoutItem
  185. \inmodule QtCreator
  186. The LayoutItem class is used for intermediate results
  187. while creating layouts with a concept of rows and spans, such
  188. as Form and Grid.
  189. */
  190. LayoutItem::LayoutItem() = default;
  191. LayoutItem::~LayoutItem() = default;
  192. LayoutItem::LayoutItem(QLayout *l)
  193. : layout(l), empty(!l)
  194. {}
  195. LayoutItem::LayoutItem(QWidget *w)
  196. : widget(w), empty(!w)
  197. {}
  198. LayoutItem::LayoutItem(const QString &t)
  199. : text(t), empty(t.isEmpty())
  200. {}
  201. /*!
  202. \fn template <class T> LayoutItem(const T &t)
  203. \internal
  204. Constructs a layout item proxy for \a t.
  205. T could be
  206. \list
  207. \li \c {QString}
  208. \li \c {QWidget *}
  209. \li \c {QLayout *}
  210. \endlist
  211. */
  212. // Object
  213. Object::Object(std::initializer_list<I> ps)
  214. {
  215. ptr = new Implementation;
  216. apply(this, ps);
  217. }
  218. static QWidget *widgetForItem(QLayoutItem *item)
  219. {
  220. if (QWidget *w = item->widget())
  221. return w;
  222. if (item->spacerItem())
  223. return nullptr;
  224. if (QLayout *l = item->layout()) {
  225. for (int i = 0, n = l->count(); i < n; ++i) {
  226. if (QWidget *w = widgetForItem(l->itemAt(i)))
  227. return w;
  228. }
  229. }
  230. return nullptr;
  231. }
  232. static QLabel *createLabel(const QString &text)
  233. {
  234. auto label = new QLabel(text);
  235. label->setTextInteractionFlags(Qt::TextSelectableByMouse);
  236. return label;
  237. }
  238. static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
  239. {
  240. if (QWidget *w = item.widget) {
  241. layout->addWidget(w);
  242. } else if (QLayout *l = item.layout) {
  243. layout->addLayout(l);
  244. } else if (item.stretch != -1) {
  245. layout->addStretch(item.stretch);
  246. } else if (!item.text.isEmpty()) {
  247. layout->addWidget(createLabel(item.text));
  248. } else if (item.empty) {
  249. // Nothing to do, but no reason to warn, either.
  250. } else {
  251. QTC_CHECK(false);
  252. }
  253. }
  254. static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
  255. {
  256. if (QWidget *w = item.widget) {
  257. layout->addWidget(w);
  258. } else if (QLayout *l = item.layout) {
  259. layout->addItem(l);
  260. // } else if (item.stretch != -1) {
  261. // layout->addStretch(item.stretch);
  262. } else if (item.empty) {
  263. // Nothing to do, but no reason to warn, either
  264. } else if (!item.text.isEmpty()) {
  265. layout->addWidget(createLabel(item.text));
  266. } else {
  267. QTC_CHECK(false);
  268. }
  269. }
  270. /*!
  271. \class Layouting::Space
  272. \inmodule QtCreator
  273. \brief The Space class represents some empty space in a layout.
  274. */
  275. /*!
  276. \class Layouting::Stretch
  277. \inmodule QtCreator
  278. \brief The Stretch class represents some stretch in a layout.
  279. */
  280. // Layout
  281. void Layout::span(int cols, int rows)
  282. {
  283. QTC_ASSERT(!pendingItems.empty(), return);
  284. pendingItems.back().spanCols = cols;
  285. pendingItems.back().spanRows = rows;
  286. }
  287. void Layout::setNoMargins()
  288. {
  289. setContentsMargins(0, 0, 0, 0);
  290. }
  291. void Layout::setNormalMargins()
  292. {
  293. setContentsMargins(9, 9, 9, 9);
  294. }
  295. void Layout::setContentsMargins(int left, int top, int right, int bottom)
  296. {
  297. access(this)->setContentsMargins(left, top, right, bottom);
  298. }
  299. /*!
  300. Attaches the constructed layout to the provided QWidget \a widget.
  301. This operation can only be performed once per LayoutBuilder instance.
  302. */
  303. void Layout::attachTo(QWidget *widget)
  304. {
  305. flush();
  306. widget->setLayout(access(this));
  307. }
  308. /*!
  309. Adds the layout item \a item as a sub item.
  310. */
  311. void Layout::addItem(I item)
  312. {
  313. item.apply(this);
  314. }
  315. void Layout::addLayoutItem(const LayoutItem &item)
  316. {
  317. if (QBoxLayout *lt = asBox())
  318. addItemToBoxLayout(lt, item);
  319. else if (FlowLayout *lt = asFlow())
  320. addItemToFlowLayout(lt, item);
  321. else
  322. pendingItems.push_back(item);
  323. }
  324. /*!
  325. Adds the layout items \a items as sub items.
  326. */
  327. void Layout::addItems(std::initializer_list<I> items)
  328. {
  329. for (const I &item : items)
  330. item.apply(this);
  331. }
  332. /*!
  333. Starts a new row containing \a items. The row can be further extended by
  334. other items using \c addItem() or \c addItems().
  335. \sa addItem(), addItems()
  336. */
  337. void Layout::addRow(std::initializer_list<I> items)
  338. {
  339. for (const I &item : items)
  340. item.apply(this);
  341. flush();
  342. }
  343. void Layout::setSpacing(int spacing)
  344. {
  345. access(this)->setSpacing(spacing);
  346. }
  347. void Layout::setColumnStretch(int column, int stretch)
  348. {
  349. if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
  350. grid->setColumnStretch(column, stretch);
  351. } else {
  352. QTC_CHECK(false);
  353. }
  354. }
  355. void addToWidget(Widget *widget, const Layout &layout)
  356. {
  357. layout.flush_();
  358. access(widget)->setLayout(access(&layout));
  359. }
  360. void addToLayout(Layout *layout, const Widget &inner)
  361. {
  362. layout->addLayoutItem(access(&inner));
  363. }
  364. void addToLayout(Layout *layout, QWidget *inner)
  365. {
  366. layout->addLayoutItem(inner);
  367. }
  368. void addToLayout(Layout *layout, QLayout *inner)
  369. {
  370. layout->addLayoutItem(inner);
  371. }
  372. void addToLayout(Layout *layout, const Layout &inner)
  373. {
  374. inner.flush_();
  375. layout->addLayoutItem(access(&inner));
  376. }
  377. void addToLayout(Layout *layout, const LayoutModifier &inner)
  378. {
  379. inner(layout);
  380. }
  381. void addToLayout(Layout *layout, const QString &inner)
  382. {
  383. layout->addLayoutItem(inner);
  384. }
  385. void empty(Layout *layout)
  386. {
  387. LayoutItem item;
  388. item.empty = true;
  389. layout->addLayoutItem(item);
  390. }
  391. void hr(Layout *layout)
  392. {
  393. layout->addLayoutItem(createHr());
  394. }
  395. void br(Layout *layout)
  396. {
  397. layout->flush();
  398. }
  399. void st(Layout *layout)
  400. {
  401. LayoutItem item;
  402. item.stretch = 1;
  403. layout->addLayoutItem(item);
  404. }
  405. void noMargin(Layout *layout)
  406. {
  407. layout->setNoMargins();
  408. }
  409. void normalMargin(Layout *layout)
  410. {
  411. layout->setNormalMargins();
  412. }
  413. QFormLayout *Layout::asForm()
  414. {
  415. return qobject_cast<QFormLayout *>(access(this));
  416. }
  417. QGridLayout *Layout::asGrid()
  418. {
  419. return qobject_cast<QGridLayout *>(access(this));
  420. }
  421. QBoxLayout *Layout::asBox()
  422. {
  423. return qobject_cast<QBoxLayout *>(access(this));
  424. }
  425. FlowLayout *Layout::asFlow()
  426. {
  427. return dynamic_cast<FlowLayout *>(access(this));
  428. }
  429. void Layout::flush()
  430. {
  431. if (pendingItems.empty())
  432. return;
  433. if (QGridLayout *lt = asGrid()) {
  434. for (const LayoutItem &item : std::as_const(pendingItems)) {
  435. Qt::Alignment a;
  436. if (currentGridColumn == 0 && useFormAlignment) {
  437. // if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
  438. // a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment);
  439. }
  440. if (item.widget)
  441. lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
  442. else if (item.layout)
  443. lt->addLayout(item.layout, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
  444. else if (!item.text.isEmpty())
  445. lt->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
  446. currentGridColumn += item.spanCols;
  447. // Intentionally not used, use 'br'/'empty' for vertical progress.
  448. // currentGridRow += item.spanRows;
  449. }
  450. ++currentGridRow;
  451. currentGridColumn = 0;
  452. pendingItems.clear();
  453. return;
  454. }
  455. if (QFormLayout *fl = asForm()) {
  456. if (pendingItems.size() > 2) {
  457. auto hbox = new QHBoxLayout;
  458. hbox->setContentsMargins(0, 0, 0, 0);
  459. for (size_t i = 1; i < pendingItems.size(); ++i)
  460. addItemToBoxLayout(hbox, pendingItems.at(i));
  461. while (pendingItems.size() > 1)
  462. pendingItems.pop_back();
  463. pendingItems.push_back(hbox);
  464. }
  465. if (pendingItems.size() == 1) { // Only one item given, so this spans both columns.
  466. const LayoutItem &f0 = pendingItems.at(0);
  467. if (auto layout = f0.layout)
  468. fl->addRow(layout);
  469. else if (auto widget = f0.widget)
  470. fl->addRow(widget);
  471. } else if (pendingItems.size() == 2) { // Normal case, both columns used.
  472. LayoutItem &f1 = pendingItems[1];
  473. const LayoutItem &f0 = pendingItems.at(0);
  474. if (!f1.widget && !f1.layout && !f1.text.isEmpty())
  475. f1.widget = createLabel(f1.text);
  476. // QFormLayout accepts only widgets or text in the first column.
  477. // FIXME: Should we be more generous?
  478. if (f0.widget) {
  479. if (f1.layout)
  480. fl->addRow(f0.widget, f1.layout);
  481. else if (f1.widget)
  482. fl->addRow(f0.widget, f1.widget);
  483. } else {
  484. if (f1.layout)
  485. fl->addRow(createLabel(f0.text), f1.layout);
  486. else if (f1.widget)
  487. fl->addRow(createLabel(f0.text), f1.widget);
  488. }
  489. } else {
  490. QTC_CHECK(false);
  491. }
  492. // Set up label as buddy if possible.
  493. const int lastRow = fl->rowCount() - 1;
  494. QLayoutItem *l = fl->itemAt(lastRow, QFormLayout::LabelRole);
  495. QLayoutItem *f = fl->itemAt(lastRow, QFormLayout::FieldRole);
  496. if (l && f) {
  497. if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
  498. if (QWidget *widget = widgetForItem(f))
  499. label->setBuddy(widget);
  500. }
  501. }
  502. pendingItems.clear();
  503. return;
  504. }
  505. QTC_CHECK(false); // The other layouts shouldn't use flush()
  506. }
  507. void Layout::flush_() const
  508. {
  509. const_cast<Layout *>(this)->flush();
  510. }
  511. void withFormAlignment(Layout *layout)
  512. {
  513. layout->useFormAlignment = true;
  514. }
  515. // Flow
  516. Flow::Flow(std::initializer_list<I> ps)
  517. {
  518. ptr = new FlowLayout;
  519. apply(this, ps);
  520. flush();
  521. }
  522. // Row & Column
  523. Row::Row(std::initializer_list<I> ps)
  524. {
  525. ptr = new QHBoxLayout;
  526. apply(this, ps);
  527. flush();
  528. }
  529. Column::Column(std::initializer_list<I> ps)
  530. {
  531. ptr = new QVBoxLayout;
  532. apply(this, ps);
  533. flush();
  534. }
  535. // Grid
  536. Grid::Grid()
  537. {
  538. ptr = new QGridLayout;
  539. }
  540. Grid::Grid(std::initializer_list<I> ps)
  541. {
  542. ptr = new QGridLayout;
  543. apply(this, ps);
  544. flush();
  545. }
  546. // Form
  547. Form::Form()
  548. {
  549. ptr = new QFormLayout;
  550. }
  551. Form::Form(std::initializer_list<I> ps)
  552. {
  553. auto lt = new QFormLayout;
  554. ptr = lt;
  555. lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
  556. apply(this, ps);
  557. flush();
  558. }
  559. void Layout::setFieldGrowthPolicy(int policy)
  560. {
  561. if (auto lt = asForm())
  562. lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
  563. }
  564. QWidget *Layout::emerge() const
  565. {
  566. const_cast<Layout *>(this)->flush();
  567. QWidget *widget = new QWidget;
  568. widget->setLayout(access(this));
  569. return widget;
  570. }
  571. void Layout::show() const
  572. {
  573. return emerge()->show();
  574. }
  575. // "Widgets"
  576. Widget::Widget(std::initializer_list<I> ps)
  577. {
  578. ptr = new Implementation;
  579. apply(this, ps);
  580. }
  581. void Widget::setSize(int w, int h)
  582. {
  583. access(this)->resize(w, h);
  584. }
  585. void Widget::setLayout(const Layout &layout)
  586. {
  587. access(this)->setLayout(access(&layout));
  588. }
  589. void Widget::setWindowTitle(const QString &title)
  590. {
  591. access(this)->setWindowTitle(title);
  592. }
  593. void Widget::setToolTip(const QString &title)
  594. {
  595. access(this)->setToolTip(title);
  596. }
  597. void Widget::show()
  598. {
  599. access(this)->show();
  600. }
  601. void Widget::setNoMargins(int)
  602. {
  603. setContentsMargins(0, 0, 0, 0);
  604. }
  605. void Widget::setNormalMargins(int)
  606. {
  607. setContentsMargins(9, 9, 9, 9);
  608. }
  609. void Widget::setContentsMargins(int left, int top, int right, int bottom)
  610. {
  611. access(this)->setContentsMargins(left, top, right, bottom);
  612. }
  613. QWidget *Widget::emerge() const
  614. {
  615. return access(this);
  616. }
  617. // Label
  618. Label::Label(std::initializer_list<I> ps)
  619. {
  620. ptr = new Implementation;
  621. apply(this, ps);
  622. }
  623. Label::Label(const QString &text)
  624. {
  625. ptr = new Implementation;
  626. setText(text);
  627. }
  628. void Label::setText(const QString &text)
  629. {
  630. access(this)->setText(text);
  631. }
  632. void Label::setTextFormat(Qt::TextFormat format)
  633. {
  634. access(this)->setTextFormat(format);
  635. }
  636. void Label::setWordWrap(bool on)
  637. {
  638. access(this)->setWordWrap(on);
  639. }
  640. void Label::setTextInteractionFlags(Qt::TextInteractionFlags flags)
  641. {
  642. access(this)->setTextInteractionFlags(flags);
  643. }
  644. void Label::setOpenExternalLinks(bool on)
  645. {
  646. access(this)->setOpenExternalLinks(on);
  647. }
  648. void Label::onLinkHovered(const std::function<void (const QString &)> &func, QObject *guard)
  649. {
  650. QObject::connect(access(this), &QLabel::linkHovered, guard, func);
  651. }
  652. // Group
  653. Group::Group(std::initializer_list<I> ps)
  654. {
  655. ptr = new Implementation;
  656. apply(this, ps);
  657. }
  658. void Group::setTitle(const QString &title)
  659. {
  660. access(this)->setTitle(title);
  661. access(this)->setObjectName(title);
  662. }
  663. void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
  664. {
  665. checker(access(this));
  666. }
  667. // SpinBox
  668. SpinBox::SpinBox(std::initializer_list<I> ps)
  669. {
  670. ptr = new Implementation;
  671. apply(this, ps);
  672. }
  673. void SpinBox::setValue(int val)
  674. {
  675. access(this)->setValue(val);
  676. }
  677. void SpinBox::onTextChanged(const std::function<void (QString)> &func)
  678. {
  679. QObject::connect(access(this), &QSpinBox::textChanged, func);
  680. }
  681. // TextEdit
  682. TextEdit::TextEdit(std::initializer_list<I> ps)
  683. {
  684. ptr = new Implementation;
  685. apply(this, ps);
  686. }
  687. void TextEdit::setText(const QString &text)
  688. {
  689. access(this)->setText(text);
  690. }
  691. // PushButton
  692. PushButton::PushButton(std::initializer_list<I> ps)
  693. {
  694. ptr = new Implementation;
  695. apply(this, ps);
  696. }
  697. void PushButton::setText(const QString &text)
  698. {
  699. access(this)->setText(text);
  700. }
  701. void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
  702. {
  703. QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
  704. }
  705. // Stack
  706. // We use a QStackedWidget instead of a QStackedLayout here because the latter will call
  707. // "setVisible()" when a child is added, which can lead to the widget being spawned as a
  708. // top-level widget. This can lead to the focus shifting away from the main application.
  709. Stack::Stack(std::initializer_list<I> ps)
  710. {
  711. ptr = new Implementation;
  712. apply(this, ps);
  713. }
  714. void addToStack(Stack *stack, const Widget &inner)
  715. {
  716. access(stack)->addWidget(inner.emerge());
  717. }
  718. void addToStack(Stack *stack, const Layout &inner)
  719. {
  720. inner.flush_();
  721. access(stack)->addWidget(inner.emerge());
  722. }
  723. void addToStack(Stack *stack, QWidget *inner)
  724. {
  725. access(stack)->addWidget(inner);
  726. }
  727. // Splitter
  728. Splitter::Splitter(std::initializer_list<I> ps)
  729. {
  730. ptr = new Implementation;
  731. access(this)->setOrientation(Qt::Vertical);
  732. apply(this, ps);
  733. }
  734. void Splitter::setOrientation(Qt::Orientation orientation)
  735. {
  736. access(this)->setOrientation(orientation);
  737. }
  738. void Splitter::setStretchFactor(int index, int stretch)
  739. {
  740. access(this)->setStretchFactor(index, stretch);
  741. }
  742. void Splitter::setChildrenCollapsible(bool collapsible)
  743. {
  744. access(this)->setChildrenCollapsible(collapsible);
  745. }
  746. void addToSplitter(Splitter *splitter, QWidget *inner)
  747. {
  748. access(splitter)->addWidget(inner);
  749. }
  750. void addToSplitter(Splitter *splitter, const Widget &inner)
  751. {
  752. access(splitter)->addWidget(inner.emerge());
  753. }
  754. void addToSplitter(Splitter *splitter, const Layout &inner)
  755. {
  756. inner.flush_();
  757. access(splitter)->addWidget(inner.emerge());
  758. }
  759. // ToolBar
  760. ToolBar::ToolBar(std::initializer_list<I> ps)
  761. {
  762. ptr = new Implementation;
  763. apply(this, ps);
  764. access(this)->setOrientation(Qt::Horizontal);
  765. }
  766. // TabWidget
  767. TabWidget::TabWidget(std::initializer_list<I> ps)
  768. {
  769. ptr = new Implementation;
  770. apply(this, ps);
  771. }
  772. Tab::Tab(const QString &tabName, const Layout &inner)
  773. : tabName(tabName), inner(inner)
  774. {}
  775. void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
  776. {
  777. access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
  778. }
  779. // Special If
  780. If::If(bool condition,
  781. const std::initializer_list<Layout::I> ifcase,
  782. const std::initializer_list<Layout::I> thencase)
  783. : used(condition ? ifcase : thencase)
  784. {}
  785. void addToLayout(Layout *layout, const If &inner)
  786. {
  787. for (const Layout::I &item : inner.used)
  788. item.apply(layout);
  789. }
  790. // Specials
  791. QWidget *createHr(QWidget *parent)
  792. {
  793. auto frame = new QFrame(parent);
  794. frame->setFrameShape(QFrame::HLine);
  795. frame->setFrameShadow(QFrame::Sunken);
  796. return frame;
  797. }
  798. Span::Span(int cols, const Layout::I &item)
  799. : item(item), spanCols(cols)
  800. {}
  801. Span::Span(int cols, int rows, const Layout::I &item)
  802. : item(item), spanCols(cols), spanRows(rows)
  803. {}
  804. void addToLayout(Layout *layout, const Span &inner)
  805. {
  806. layout->addItem(inner.item);
  807. if (layout->pendingItems.empty()) {
  808. QTC_CHECK(inner.spanCols == 1 && inner.spanRows == 1);
  809. return;
  810. }
  811. layout->pendingItems.back().spanCols = inner.spanCols;
  812. layout->pendingItems.back().spanRows = inner.spanRows;
  813. }
  814. LayoutModifier spacing(int space)
  815. {
  816. return [space](Layout *layout) { layout->setSpacing(space); };
  817. }
  818. void addToLayout(Layout *layout, const Space &inner)
  819. {
  820. if (auto lt = layout->asBox())
  821. lt->addSpacing(inner.space);
  822. }
  823. void addToLayout(Layout *layout, const Stretch &inner)
  824. {
  825. if (auto lt = layout->asBox())
  826. lt->addStretch(inner.stretch);
  827. }
  828. // void createItem(LayoutItem *item, QWidget *t)
  829. // {
  830. // if (auto l = qobject_cast<QLabel *>(t))
  831. // l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
  832. // item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
  833. // }
  834. } // Layouting