| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100 |
- // Copyright (C) 2020 The Qt Company Ltd.
- // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
- #include "layoutbuilder.h"
- #include "qglobal.h"
- #include <QApplication>
- #include <QDebug>
- #include <QFormLayout>
- #include <QGridLayout>
- #include <QGroupBox>
- #include <QLabel>
- #include <QPushButton>
- #include <QSpacerItem>
- #include <QSpinBox>
- #include <QSplitter>
- #include <QStackedLayout>
- #include <QStackedWidget>
- #include <QStyle>
- #include <QTabWidget>
- #include <QTextEdit>
- #include <QToolBar>
- namespace Layouting {
- // That's cut down qtcassert.{c,h} to avoid the dependency.
- #define QTC_STRINGIFY_HELPER(x) #x
- #define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
- #define QTC_STRING(cond) \
- qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
- #define QTC_ASSERT(cond, action) \
- if (Q_LIKELY(cond)) { \
- } else { \
- QTC_STRING(#cond); \
- action; \
- } \
- do { \
- } while (0)
- #define QTC_CHECK(cond) \
- if (cond) { \
- } else { \
- QTC_STRING(#cond); \
- } \
- do { \
- } while (0)
- class FlowLayout final : public QLayout
- {
- Q_OBJECT
- public:
- explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
- : QLayout(parent)
- , m_hSpace(hSpacing)
- , m_vSpace(vSpacing)
- {
- setContentsMargins(margin, margin, margin, margin);
- }
- FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1)
- : m_hSpace(hSpacing)
- , m_vSpace(vSpacing)
- {
- setContentsMargins(margin, margin, margin, margin);
- }
- ~FlowLayout() override
- {
- QLayoutItem *item;
- while ((item = takeAt(0)))
- delete item;
- }
- void addItem(QLayoutItem *item) override { itemList.append(item); }
- int horizontalSpacing() const
- {
- if (m_hSpace >= 0)
- return m_hSpace;
- else
- return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
- }
- int verticalSpacing() const
- {
- if (m_vSpace >= 0)
- return m_vSpace;
- else
- return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
- }
- Qt::Orientations expandingDirections() const override { return {}; }
- bool hasHeightForWidth() const override { return true; }
- int heightForWidth(int width) const override
- {
- int height = doLayout(QRect(0, 0, width, 0), true);
- return height;
- }
- int count() const override { return itemList.size(); }
- QLayoutItem *itemAt(int index) const override { return itemList.value(index); }
- QSize minimumSize() const override
- {
- QSize size;
- for (QLayoutItem *item : itemList)
- size = size.expandedTo(item->minimumSize());
- int left, top, right, bottom;
- getContentsMargins(&left, &top, &right, &bottom);
- size += QSize(left + right, top + bottom);
- return size;
- }
- void setGeometry(const QRect &rect) override
- {
- QLayout::setGeometry(rect);
- doLayout(rect, false);
- }
- QSize sizeHint() const override { return minimumSize(); }
- QLayoutItem *takeAt(int index) override
- {
- if (index >= 0 && index < itemList.size())
- return itemList.takeAt(index);
- else
- return nullptr;
- }
- private:
- int doLayout(const QRect &rect, bool testOnly) const
- {
- int left, top, right, bottom;
- getContentsMargins(&left, &top, &right, &bottom);
- QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
- int x = effectiveRect.x();
- int y = effectiveRect.y();
- int lineHeight = 0;
- for (QLayoutItem *item : itemList) {
- QWidget *wid = item->widget();
- int spaceX = horizontalSpacing();
- if (spaceX == -1)
- spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton,
- QSizePolicy::PushButton,
- Qt::Horizontal);
- int spaceY = verticalSpacing();
- if (spaceY == -1)
- spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton,
- QSizePolicy::PushButton,
- Qt::Vertical);
- int nextX = x + item->sizeHint().width() + spaceX;
- if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
- x = effectiveRect.x();
- y = y + lineHeight + spaceY;
- nextX = x + item->sizeHint().width() + spaceX;
- lineHeight = 0;
- }
- if (!testOnly)
- item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
- x = nextX;
- lineHeight = qMax(lineHeight, item->sizeHint().height());
- }
- return y + lineHeight - rect.y() + bottom;
- }
- int smartSpacing(QStyle::PixelMetric pm) const
- {
- QObject *parent = this->parent();
- if (!parent) {
- return -1;
- } else if (parent->isWidgetType()) {
- auto pw = static_cast<QWidget *>(parent);
- return pw->style()->pixelMetric(pm, nullptr, pw);
- } else {
- return static_cast<QLayout *>(parent)->spacing();
- }
- }
- QList<QLayoutItem *> itemList;
- int m_hSpace;
- int m_vSpace;
- };
- /*!
- \namespace Layouting
- \inmodule QtCreator
- \brief The Layouting namespace contains classes for use with layout builders.
- */
- /*!
- \class Layouting::LayoutItem
- \inmodule QtCreator
- \brief The LayoutItem class represents widgets, layouts, and aggregate
- items for use in conjunction with layout builders.
- Layout items are typically implicitly constructed when adding items to a
- \c LayoutBuilder instance using \c LayoutBuilder::addItem() or
- \c LayoutBuilder::addItems() and never stored in user code.
- */
- /*!
- Constructs a layout item instance representing an empty cell.
- */
- LayoutItem::LayoutItem() = default;
- LayoutItem::~LayoutItem() = default;
- /*!
- \fn template <class T> LayoutItem(const T &t)
- \internal
- Constructs a layout item proxy for \a t.
- T could be
- \list
- \li \c {QString}
- \li \c {QWidget *}
- \li \c {QLayout *}
- \endlist
- */
- struct ResultItem
- {
- ResultItem() = default;
- explicit ResultItem(QLayout *l)
- : layout(l)
- , empty(!l)
- {}
- explicit ResultItem(QWidget *w)
- : widget(w)
- , empty(!w)
- {}
- QString text;
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
- int space = -1;
- int stretch = -1;
- int span = 1;
- bool empty = false;
- };
- struct Slice
- {
- Slice() = default;
- Slice(QLayout *l)
- : layout(l)
- {}
- Slice(QWidget *w, bool isLayouting = false)
- : widget(w)
- , isLayouting(isLayouting)
- {}
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
- void flush();
- // Grid-specific
- int currentGridColumn = 0;
- int currentGridRow = 0;
- bool isFormAlignment = false;
- bool isLayouting = false;
- Qt::Alignment align = {}; // Can be changed to
- // Grid or Form
- QList<ResultItem> pendingItems;
- };
- static QWidget *widgetForItem(QLayoutItem *item)
- {
- if (QWidget *w = item->widget())
- return w;
- if (item->spacerItem())
- return nullptr;
- QLayout *l = item->layout();
- if (!l)
- return nullptr;
- for (int i = 0, n = l->count(); i < n; ++i) {
- if (QWidget *w = widgetForItem(l->itemAt(i)))
- return w;
- }
- return nullptr;
- }
- static QLabel *createLabel(const QString &text)
- {
- auto label = new QLabel(text);
- label->setTextInteractionFlags(Qt::TextSelectableByMouse);
- return label;
- }
- static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
- {
- if (QWidget *w = item.widget) {
- layout->addWidget(w);
- } else if (QLayout *l = item.layout) {
- layout->addLayout(l);
- } else if (item.stretch != -1) {
- layout->addStretch(item.stretch);
- } else if (item.space != -1) {
- layout->addSpacing(item.space);
- } else if (!item.text.isEmpty()) {
- layout->addWidget(createLabel(item.text));
- } else if (item.empty) {
- // Nothing to do, but no reason to warn, either.
- } else {
- QTC_CHECK(false);
- }
- }
- static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
- {
- if (QWidget *w = item.widget) {
- layout->addWidget(w);
- } else if (QLayout *l = item.layout) {
- layout->addItem(l);
- // } else if (item.stretch != -1) {
- // layout->addStretch(item.stretch);
- // } else if (item.space != -1) {
- // layout->addSpacing(item.space);
- } else if (item.empty) {
- // Nothing to do, but no reason to warn, either
- } else if (!item.text.isEmpty()) {
- layout->addWidget(createLabel(item.text));
- } else {
- QTC_CHECK(false);
- }
- }
- void Slice::flush()
- {
- if (pendingItems.empty())
- return;
- if (auto formLayout = qobject_cast<QFormLayout *>(layout)) {
- // If there are more than two items, we cram the last ones in one hbox.
- if (pendingItems.size() > 2) {
- auto hbox = new QHBoxLayout;
- hbox->setContentsMargins(0, 0, 0, 0);
- for (int i = 1; i < pendingItems.size(); ++i)
- addItemToBoxLayout(hbox, pendingItems.at(i));
- while (pendingItems.size() > 1)
- pendingItems.pop_back();
- pendingItems.append(ResultItem(hbox));
- }
- if (pendingItems.size() == 1) { // One one item given, so this spans both columns.
- const ResultItem &f0 = pendingItems.at(0);
- if (auto layout = f0.layout)
- formLayout->addRow(layout);
- else if (auto widget = f0.widget)
- formLayout->addRow(widget);
- } else if (pendingItems.size() == 2) { // Normal case, both columns used.
- ResultItem &f1 = pendingItems[1];
- const ResultItem &f0 = pendingItems.at(0);
- if (!f1.widget && !f1.layout && !f1.text.isEmpty())
- f1.widget = createLabel(f1.text);
- if (f0.widget) {
- if (f1.layout)
- formLayout->addRow(f0.widget, f1.layout);
- else if (f1.widget)
- formLayout->addRow(f0.widget, f1.widget);
- } else {
- if (f1.layout)
- formLayout->addRow(createLabel(f0.text), f1.layout);
- else if (f1.widget)
- formLayout->addRow(createLabel(f0.text), f1.widget);
- }
- } else {
- QTC_CHECK(false);
- }
- // Set up label as buddy if possible.
- const int lastRow = formLayout->rowCount() - 1;
- QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
- QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
- if (l && f) {
- if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
- if (QWidget *widget = widgetForItem(f))
- label->setBuddy(widget);
- }
- }
- } else if (auto gridLayout = qobject_cast<QGridLayout *>(layout)) {
- for (const ResultItem &item : std::as_const(pendingItems)) {
- Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment();
- if (item.widget)
- gridLayout
- ->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (item.layout)
- gridLayout
- ->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (!item.text.isEmpty())
- gridLayout
- ->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, a);
- currentGridColumn += item.span;
- }
- ++currentGridRow;
- currentGridColumn = 0;
- } else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToBoxLayout(boxLayout, item);
- } else if (auto flowLayout = qobject_cast<FlowLayout *>(layout)) {
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToFlowLayout(flowLayout, item);
- } else {
- QTC_CHECK(false);
- }
- pendingItems.clear();
- }
- // LayoutBuilder
- class LayoutBuilder
- {
- Q_DISABLE_COPY_MOVE(LayoutBuilder)
- public:
- LayoutBuilder();
- ~LayoutBuilder();
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
- QList<Slice> stack;
- };
- static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
- {
- if (item.onAdd)
- item.onAdd(builder);
- if (item.setter) {
- if (QWidget *widget = builder.stack.last().widget)
- item.setter(widget);
- else if (QLayout *layout = builder.stack.last().layout)
- item.setter(layout);
- else
- QTC_CHECK(false);
- }
- for (const LayoutItem &subItem : item.subItems)
- addItemHelper(builder, subItem);
- if (item.onExit)
- item.onExit(builder);
- }
- void doAddText(LayoutBuilder &builder, const QString &text)
- {
- ResultItem fi;
- fi.text = text;
- builder.stack.last().pendingItems.append(fi);
- }
- void doAddSpace(LayoutBuilder &builder, const Space &space)
- {
- ResultItem fi;
- fi.space = space.space;
- builder.stack.last().pendingItems.append(fi);
- }
- void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
- {
- ResultItem fi;
- fi.stretch = stretch.stretch;
- builder.stack.last().pendingItems.append(fi);
- }
- void doAddLayout(LayoutBuilder &builder, QLayout *layout)
- {
- builder.stack.last().pendingItems.append(ResultItem(layout));
- }
- void doAddWidget(LayoutBuilder &builder, QWidget *widget)
- {
- builder.stack.last().pendingItems.append(ResultItem(widget));
- }
- /*!
- \class Layouting::Space
- \inmodule QtCreator
- \brief The Space class represents some empty space in a layout.
- */
- /*!
- \class Layouting::Stretch
- \inmodule QtCreator
- \brief The Stretch class represents some stretch in a layout.
- */
- /*!
- \class Layouting::LayoutBuilder
- \internal
- \inmodule QtCreator
- \brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
- and \c QGridLayouts with contents.
- Filling a layout with items happens item-by-item, row-by-row.
- A LayoutBuilder instance is typically used locally within a function and never stored.
- \sa addItem(), addItems()
- */
- LayoutBuilder::LayoutBuilder() = default;
- /*!
- \internal
- Destructs a layout builder.
- */
- LayoutBuilder::~LayoutBuilder() = default;
- void LayoutBuilder::addItem(const LayoutItem &item)
- {
- addItemHelper(*this, item);
- }
- void LayoutBuilder::addItems(const LayoutItems &items)
- {
- for (const LayoutItem &item : items)
- addItemHelper(*this, item);
- }
- /*!
- Starts a new row containing \a items. The row can be further extended by
- other items using \c addItem() or \c addItems().
- \sa addItem(), addItems()
- */
- void LayoutItem::addRow(const LayoutItems &items)
- {
- addItem(br);
- addItems(items);
- }
- /*!
- Adds the layout item \a item as sub items.
- */
- void LayoutItem::addItem(const LayoutItem &item)
- {
- subItems.append(item);
- }
- /*!
- Adds the layout items \a items as sub items.
- */
- void LayoutItem::addItems(const LayoutItems &items)
- {
- subItems.append(items);
- }
- /*!
- Attaches the constructed layout to the provided QWidget \a w.
- This operation can only be performed once per LayoutBuilder instance.
- */
- void LayoutItem::attachTo(QWidget *w) const
- {
- LayoutBuilder builder;
- builder.stack.append(w);
- addItemHelper(builder, *this);
- }
- QWidget *LayoutItem::emerge()
- {
- LayoutBuilder builder;
- builder.stack.append(Slice());
- addItemHelper(builder, *this);
- if (builder.stack.empty())
- return nullptr;
- QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr);
- ResultItem ri = builder.stack.last().pendingItems.takeFirst();
- QTC_ASSERT(ri.layout || ri.widget, return nullptr);
- if (ri.layout) {
- auto w = new QWidget;
- w->setLayout(ri.layout);
- return w;
- }
- return ri.widget;
- }
- static void layoutExit(LayoutBuilder &builder)
- {
- builder.stack.last().flush();
- QLayout *layout = builder.stack.last().layout;
- builder.stack.pop_back();
- if (builder.stack.last().isLayouting) {
- builder.stack.last().pendingItems.append(ResultItem(layout));
- } else if (QWidget *widget = builder.stack.last().widget) {
- widget->setLayout(layout);
- } else
- builder.stack.last().pendingItems.append(ResultItem(layout));
- }
- template<class T>
- static void layoutingWidgetExit(LayoutBuilder &builder)
- {
- const Slice slice = builder.stack.last();
- T *w = qobject_cast<T *>(slice.widget);
- for (const ResultItem &ri : slice.pendingItems) {
- if (ri.widget) {
- w->addWidget(ri.widget);
- } else if (ri.layout) {
- auto child = new QWidget;
- child->setLayout(ri.layout);
- w->addWidget(child);
- }
- }
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(w));
- }
- static void widgetExit(LayoutBuilder &builder)
- {
- QWidget *widget = builder.stack.last().widget;
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(widget));
- }
- Column::Column(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
- onExit = layoutExit;
- }
- Row::Row(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
- onExit = layoutExit;
- }
- Flow::Flow(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); };
- onExit = layoutExit;
- }
- Grid::Grid(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
- onExit = layoutExit;
- }
- static QFormLayout *newFormLayout()
- {
- auto formLayout = new QFormLayout;
- formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- return formLayout;
- }
- Form::Form(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
- onExit = layoutExit;
- }
- LayoutItem br()
- {
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { builder.stack.last().flush(); };
- return item;
- }
- LayoutItem empty()
- {
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- ResultItem ri;
- ri.empty = true;
- builder.stack.last().pendingItems.append(ri);
- };
- return item;
- }
- LayoutItem hr()
- {
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
- return item;
- }
- LayoutItem st()
- {
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
- return item;
- }
- LayoutItem noMargin()
- {
- return customMargin({});
- }
- LayoutItem normalMargin()
- {
- return customMargin({9, 9, 9, 9});
- }
- LayoutItem customMargin(const QMargins &margin)
- {
- LayoutItem item;
- item.onAdd = [margin](LayoutBuilder &builder) {
- if (auto layout = builder.stack.last().layout)
- layout->setContentsMargins(margin);
- else if (auto widget = builder.stack.last().widget)
- widget->setContentsMargins(margin);
- };
- return item;
- }
- LayoutItem withFormAlignment()
- {
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- if (builder.stack.size() >= 2) {
- if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
- const Qt::Alignment align(
- widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
- builder.stack.last().align = align;
- }
- }
- };
- return item;
- }
- // "Widgets"
- template<class T>
- void setupWidget(LayoutItem *item)
- {
- item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
- item->onExit = widgetExit;
- };
- Widget::Widget(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QWidget>(this);
- }
- Group::Group(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QGroupBox>(this);
- }
- Stack::Stack(std::initializer_list<LayoutItem> items)
- {
- // We use a QStackedWidget instead of a QStackedLayout here because the latter will call
- // "setVisible()" when a child is added, which can lead to the widget being spawned as a
- // top-level widget. This can lead to the focus shifting away from the main application.
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(Slice(new QStackedWidget, true)); };
- onExit = layoutingWidgetExit<QStackedWidget>;
- }
- PushButton::PushButton(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QPushButton>(this);
- }
- SpinBox::SpinBox(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QSpinBox>(this);
- }
- TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QTextEdit>(this);
- }
- Splitter::Splitter(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto splitter = new QSplitter;
- splitter->setOrientation(Qt::Vertical);
- builder.stack.append(Slice(splitter, true));
- };
- onExit = layoutingWidgetExit<QSplitter>;
- }
- ToolBar::ToolBar(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto toolbar = new QToolBar;
- toolbar->setOrientation(Qt::Horizontal);
- builder.stack.append(Slice(toolbar, true));
- };
- onExit = layoutingWidgetExit<QToolBar>;
- }
- TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
- {
- this->subItems = items;
- setupWidget<QTabWidget>(this);
- }
- // Special Tab
- Tab::Tab(const QString &tabName, const LayoutItem &item)
- {
- onAdd = [item](LayoutBuilder &builder) {
- auto tab = new QWidget;
- builder.stack.append(tab);
- item.attachTo(tab);
- };
- onExit = [tabName](LayoutBuilder &builder) {
- QWidget *inner = builder.stack.last().widget;
- builder.stack.pop_back();
- auto tabWidget = qobject_cast<QTabWidget *>(builder.stack.last().widget);
- QTC_ASSERT(tabWidget, return);
- tabWidget->addTab(inner, tabName);
- };
- }
- // Special If
- If::If(bool condition, const LayoutItems &items, const LayoutItems &other)
- {
- subItems.append(condition ? items : other);
- }
- // Special Application
- Application::Application(std::initializer_list<LayoutItem> items)
- {
- subItems = items;
- setupWidget<QWidget>(this);
- onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
- }
- int Application::exec(int &argc, char *argv[])
- {
- QApplication app(argc, argv);
- LayoutBuilder builder;
- addItemHelper(builder, *this);
- if (QWidget *widget = builder.stack.last().widget)
- widget->show();
- return app.exec();
- }
- // "Properties"
- LayoutItem title(const QString &title)
- {
- return [title](QObject *target) {
- if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
- groupBox->setTitle(title);
- groupBox->setObjectName(title);
- } else if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(title);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem windowTitle(const QString &windowTitle)
- {
- return [windowTitle](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(windowTitle);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem text(const QString &text)
- {
- return [text](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- button->setText(text);
- } else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
- textEdit->setText(text);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem tooltip(const QString &toolTip)
- {
- return [toolTip](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setToolTip(toolTip);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem spacing(int spacing)
- {
- return [spacing](QObject *target) {
- if (auto layout = qobject_cast<QLayout *>(target)) {
- layout->setSpacing(spacing);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem resize(int w, int h)
- {
- return [w, h](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->resize(w, h);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem columnStretch(int column, int stretch)
- {
- return [column, stretch](QObject *target) {
- if (auto grid = qobject_cast<QGridLayout *>(target)) {
- grid->setColumnStretch(column, stretch);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem rowStretch(int row, int stretch)
- {
- return [row, stretch](QObject *target) {
- if (auto grid = qobject_cast<QGridLayout *>(target)) {
- grid->setRowStretch(row, stretch);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy)
- {
- return [policy](QObject *target) {
- if (auto form = qobject_cast<QFormLayout *>(target)) {
- form->setFieldGrowthPolicy(policy);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- // Id based setters
- LayoutItem id(ID &out)
- {
- return [&out](QObject *target) { out.ob = target; };
- }
- void setText(ID id, const QString &text)
- {
- if (auto textEdit = qobject_cast<QTextEdit *>(id.ob))
- textEdit->setText(text);
- }
- // Signals
- LayoutItem onClicked(const std::function<void()> &func, QObject *guard)
- {
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem onTextChanged(const std::function<void(const QString &)> &func, QObject *guard)
- {
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- LayoutItem onValueChanged(const std::function<void(int)> &func, QObject *guard)
- {
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button,
- QOverload<int>::of(&QSpinBox::valueChanged),
- guard ? guard : target,
- func);
- } else {
- QTC_CHECK(false);
- }
- };
- }
- // Convenience
- QWidget *createHr(QWidget *parent)
- {
- auto frame = new QFrame(parent);
- frame->setFrameShape(QFrame::HLine);
- frame->setFrameShadow(QFrame::Sunken);
- return frame;
- }
- // Singletons.
- LayoutItem::LayoutItem(const LayoutItem &t)
- {
- operator=(t);
- }
- void createItem(LayoutItem *item, LayoutItem (*t)())
- {
- *item = t();
- }
- void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
- {
- item->setter = t;
- }
- void createItem(LayoutItem *item, QWidget *t)
- {
- if (auto l = qobject_cast<QLabel *>(t))
- l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
- item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
- }
- void createItem(LayoutItem *item, QLayout *t)
- {
- item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
- }
- void createItem(LayoutItem *item, const QString &t)
- {
- item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); };
- }
- void createItem(LayoutItem *item, const Space &t)
- {
- item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
- }
- void createItem(LayoutItem *item, const Stretch &t)
- {
- item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
- }
- void createItem(LayoutItem *item, const Span &t)
- {
- item->onAdd = [t](LayoutBuilder &builder) {
- addItemHelper(builder, t.item);
- builder.stack.last().pendingItems.last().span = t.span;
- };
- }
- } // namespace Layouting
- #include "layoutbuilder.moc"
|