Files
qt-creator/src/libs/utils/layoutbuilder.cpp
Marcus Tillmanns 83111bb3f6 Utils: Make validation async
Changes FancyLineEdit to accept two types of validation functions:

AsyncValidationFunction, which returns a QFuture and takes the new text,
or SynchronousValidationFunction.

Especially PathChooser is changed to use async validation function to
improve snappyness of settings pages that do heavy validation,
for instance the Debugger page.

Change-Id: I1677e7d8acc29e36c69a867850304b7913e6ae7e
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Reviewed-by: hjk <hjk@qt.io>
2023-06-29 13:25:03 +00:00

1017 lines
26 KiB
C++

// 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 <QDebug>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QPushButton>
#include <QStackedLayout>
#include <QSpacerItem>
#include <QSpinBox>
#include <QSplitter>
#include <QStyle>
#include <QTabWidget>
#include <QTextEdit>
#include <QApplication>
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) {}
explicit ResultItem(QWidget *w) : widget(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) : widget(w) {}
QLayout *layout = nullptr;
QWidget *widget = nullptr;
void flush();
// Grid-specific
int currentGridColumn = 0;
int currentGridRow = 0;
bool isFormAlignment = 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.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 if (auto stackLayout = qobject_cast<QStackedLayout *>(layout)) {
for (const ResultItem &item : std::as_const(pendingItems)) {
if (item.widget)
stackLayout->addWidget(item.widget);
else
QTC_CHECK(false);
}
} 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()
{
auto w = new QWidget;
attachTo(w);
return w;
}
static void layoutExit(LayoutBuilder &builder)
{
builder.stack.last().flush();
QLayout *layout = builder.stack.last().layout;
builder.stack.pop_back();
if (QWidget *widget = builder.stack.last().widget)
widget->setLayout(layout);
else
builder.stack.last().pendingItems.append(ResultItem(layout));
}
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;
}
Stack::Stack(std::initializer_list<LayoutItem> items)
{
subItems = items;
onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QStackedLayout); };
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);
}
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(splitter);
};
onExit = [](LayoutBuilder &builder) {
const Slice slice = builder.stack.last();
QSplitter *splitter = qobject_cast<QSplitter *>(slice.widget);
for (const ResultItem &ri : slice.pendingItems) {
if (ri.widget)
splitter->addWidget(ri.widget);
}
builder.stack.pop_back();
builder.stack.last().pendingItems.append(ResultItem(splitter));
};
}
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 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 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 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, &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;
};
}
} // Layouting
#include "layoutbuilder.moc"