LayoutBuilder: Update demo

Backport the learnings and simplification from the real use to
the demo case.

Change-Id: I3f501b03c760484961bfd586735c0db53ba080db
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
hjk
2024-05-22 13:49:57 +02:00
parent b36ee3c287
commit 308fb3f3fd
3 changed files with 630 additions and 551 deletions

View File

@@ -3,7 +3,6 @@
#include "lb.h"
#include <QApplication>
#include <QDebug>
#include <QFormLayout>
#include <QGridLayout>
@@ -29,19 +28,20 @@ namespace Layouting {
#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)
template <typename XInterface>
XInterface::Implementation *access(const XInterface *x)
template <typename X>
X::Implementation *access(const X *x)
{
return static_cast<XInterface::Implementation *>(x->ptr);
return static_cast<X::Implementation *>(x->ptr);
}
// Setter implementation
template <typename X>
void apply(X *x, std::initializer_list<typename X::I> ps)
{
for (auto && p : ps)
p.apply(x);
}
// These are free functions overloaded on the type of builder object
// and setter id. The function implementations are independent, but
// the base expectation is that they will forwards to the backend
// type's setter.
// FlowLayout
class FlowLayout : public QLayout
{
@@ -193,37 +193,49 @@ private:
\namespace Layouting
\inmodule QtCreator
\brief The Layouting namespace contains classes for use with layout builders.
\brief The Layouting namespace contains classes and functions to conveniently
create layouts in code.
Classes in the namespace help to create create QLayout or QWidget derived class,
instances should be used locally within a function and never stored.
\sa Layouting::Widget, Layouting::Layout
*/
/*!
\class Layouting::Layout
\inmodule QtCreator
The Layout class is a base class for more specific builder
classes to create QLayout derived objects.
*/
/*!
\class Layouting::Widget
\inmodule QtCreator
The Widget class is a base class for more specific builder
classes to create QWidget derived objects.
*/
/*!
\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.
The LayoutItem class is used for intermediate results
while creating layouts with a concept of rows and spans, such
as Form and Grid.
*/
/*!
Constructs a layout item instance representing an empty cell.
*/
LayoutItem::LayoutItem() = default;
Layout::LayoutItem::LayoutItem() = default;
LayoutItem::~LayoutItem() = default;
LayoutItem::LayoutItem(const LayoutInterface &inner)
: LayoutItem(access(&inner))
{}
LayoutItem::LayoutItem(const WidgetInterface &inner)
: LayoutItem(access(&inner))
{}
Layout::LayoutItem::~LayoutItem() = default;
Layout::LayoutItem::LayoutItem(const LayoutModifier &inner)
{
ownerModifier = inner;
}
/*!
\fn template <class T> LayoutItem(const T &t)
@@ -239,16 +251,12 @@ LayoutItem::LayoutItem(const WidgetInterface &inner)
\endlist
*/
// Helpers
// Object
Object::Object(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
static QWidget *widgetForItem(QLayoutItem *item)
@@ -273,6 +281,8 @@ static QLabel *createLabel(const QString &text)
return label;
}
using LayoutItem = Layout::LayoutItem;
static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
{
if (QWidget *w = item.widget) {
@@ -281,8 +291,6 @@ static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
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) {
@@ -300,8 +308,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
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()) {
@@ -311,31 +317,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
}
}
// 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)
// {
// Item fi;
// fi.stretch = stretch.stretch;
// builder.stack.last().pendingItems.append(fi);
// }
// void doAddLayout(LayoutBuilder &builder, QLayout *layout)
// {
// builder.stack.last().pendingItems.append(Item(layout));
// }
// void doAddWidget(LayoutBuilder &builder, QWidget *widget)
// {
// builder.stack.last().pendingItems.append(Item(widget));
// }
/*!
\class Layouting::Space
\inmodule QtCreator
@@ -350,26 +331,68 @@ static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
\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.
// Layout
Filling a layout with items happens item-by-item, row-by-row.
void Layout::span(int cols, int rows)
{
QTC_ASSERT(!pendingItems.empty(), return);
pendingItems.back().spanCols = cols;
pendingItems.back().spanRows = rows;
}
A LayoutBuilder instance is typically used locally within a function and never stored.
void Layout::noMargin()
{
customMargin({});
}
\sa addItem(), addItems()
*/
void Layout::normalMargin()
{
customMargin({9, 9, 9, 9});
}
void Layout::customMargin(const QMargins &margin)
{
access(this)->setContentsMargins(margin);
}
/*!
\internal
Destructs a layout builder.
Attaches the constructed layout to the provided QWidget \a w.
This operation can only be performed once per LayoutBuilder instance.
*/
void Layout::attachTo(QWidget *widget)
{
flush();
widget->setLayout(access(this));
}
/*!
Adds the layout item \a item as sub items.
*/
void Layout::addItem(I item)
{
item.apply(this);
}
void Layout::addItemHelper(const LayoutItem &item)
{
if (QBoxLayout *lt = asBox())
addItemToBoxLayout(lt, item);
else if (FlowLayout *lt = asFlow())
addItemToFlowLayout(lt, item);
else
pendingItems.push_back(item);
}
/*!
Adds the layout items \a items as sub items.
*/
void Layout::addItems(std::initializer_list<I> items)
{
for (const I &item : items)
item.apply(this);
}
/*!
Starts a new row containing \a items. The row can be further extended by
@@ -377,160 +400,141 @@ static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
\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);
// }
// Layout
void LayoutInterface::span(int cols, int rows)
void Layout::addRow(std::initializer_list<I> items)
{
QTC_ASSERT(!pendingItems.empty(), return);
pendingItems.back().spanCols = cols;
pendingItems.back().spanRows = rows;
for (const I &item : items)
item.apply(this);
flush();
}
void LayoutInterface::noMargin()
void Layout::setSpacing(int spacing)
{
customMargin({});
access(this)->setSpacing(spacing);
}
void LayoutInterface::normalMargin()
void Layout::setColumnStretch(int column, int stretch)
{
customMargin({9, 9, 9, 9});
if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
grid->setColumnStretch(column, stretch);
} else {
QTC_CHECK(false);
}
}
void LayoutInterface::customMargin(const QMargins &margin)
void addToWidget(Widget *widget, const Layout &layout)
{
access(this)->setContentsMargins(margin);
layout.flush_();
access(widget)->setLayout(access(&layout));
}
void LayoutInterface::addItem(const LayoutItem &item)
{
if (item.break_)
flush();
else
pendingItems.push_back(item);
}
void addNestedItem(WidgetInterface *widget, const LayoutInterface &layout)
{
widget->setLayout(layout);
}
void addNestedItem(LayoutInterface *layout, const WidgetInterface &inner)
void addToLayout(Layout *layout, const Widget &inner)
{
LayoutItem item;
item.widget = access(&inner);
layout->addItem(item);
layout->addItemHelper(item);
}
void addNestedItem(LayoutInterface *layout, const LayoutItem &inner)
{
layout->addItem(inner);
}
void addNestedItem(LayoutInterface *layout, const LayoutInterface &inner)
void addToLayout(Layout *layout, QWidget *inner)
{
LayoutItem item;
item.layout = access(&inner);
layout->addItem(item);
item.widget = inner;
layout->addItemHelper(item);
}
void addNestedItem(LayoutInterface *layout, const std::function<LayoutItem()> &inner)
void addToLayout(Layout *layout, QLayout *inner)
{
LayoutItem item = inner();
layout->addItem(item);
LayoutItem item;
item.layout = inner;
layout->addItemHelper(item);
}
void addNestedItem(LayoutInterface *layout, const QString &inner)
void addToLayout(Layout *layout, const Layout &inner)
{
inner.flush_();
LayoutItem item;
item.layout = access(&inner);
layout->addItemHelper(item);
}
void addToLayout(Layout *layout, const LayoutModifier &inner)
{
inner(layout);
}
void addToLayout(Layout *layout, const QString &inner)
{
LayoutItem item;
item.text = inner;
layout->addItem(item);
layout->addItemHelper(item);
}
LayoutItem empty()
void empty(Layout *iface)
{
LayoutItem item;
item.empty = true;
return item;
iface->addItemHelper(item);
}
LayoutItem hr()
void hr(Layout *layout)
{
LayoutItem item;
item.widget = createHr();
return item;
layout->addItemHelper(createHr());
}
LayoutItem br()
void br(Layout *iface)
{
LayoutItem item;
item.break_ = true;
return item;
iface->flush();
}
LayoutItem st()
void st(Layout *iface)
{
LayoutItem item;
item.stretch = 1;
return item;
iface->addItemHelper(item);
}
QFormLayout *LayoutInterface::asForm()
void noMargin(Layout *iface)
{
iface->noMargin();
}
void normalMargin(Layout *iface)
{
iface->normalMargin();
}
QFormLayout *Layout::asForm()
{
return qobject_cast<QFormLayout *>(access(this));
}
QGridLayout *LayoutInterface::asGrid()
QGridLayout *Layout::asGrid()
{
return qobject_cast<QGridLayout *>(access(this));
}
QBoxLayout *LayoutInterface::asBox()
QBoxLayout *Layout::asBox()
{
return qobject_cast<QBoxLayout *>(access(this));
}
void LayoutInterface::flush()
FlowLayout *Layout::asFlow()
{
return dynamic_cast<FlowLayout *>(access(this));
}
void Layout::flush()
{
if (pendingItems.empty())
return;
if (QGridLayout *lt = asGrid()) {
for (const LayoutItem &item : std::as_const(pendingItems)) {
Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment();
Qt::Alignment a;
if (currentGridColumn == 0 && useFormAlignment) {
// if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
// a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment);
}
if (item.widget)
lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
else if (item.layout)
@@ -551,11 +555,11 @@ void LayoutInterface::flush()
if (pendingItems.size() > 2) {
auto hbox = new QHBoxLayout;
hbox->setContentsMargins(0, 0, 0, 0);
for (int i = 1; i < pendingItems.size(); ++i)
for (size_t i = 1; i < pendingItems.size(); ++i)
addItemToBoxLayout(hbox, pendingItems.at(i));
while (pendingItems.size() > 1)
pendingItems.pop_back();
pendingItems.push_back(LayoutItem(hbox));
pendingItems.push_back(hbox);
}
if (pendingItems.size() == 1) { // Only one item given, so this spans both columns.
@@ -601,133 +605,138 @@ void LayoutInterface::flush()
pendingItems.clear();
return;
}
QTC_CHECK(false); // The other layouts shouldn't use flush()
}
// 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;
// }
void Layout::flush_() const
{
const_cast<Layout *>(this)->flush();
}
void withFormAlignment(Layout *iface)
{
iface->useFormAlignment = true;
}
// Flow
Flow::Flow(std::initializer_list<I> ps)
{
adopt(new FlowLayout);
for (auto && p : ps)
apply(p);
// for (const LayoutItem &item : std::as_const(pendingItems))
// addItemToFlowLayout(flowLayout, item);
ptr = new FlowLayout;
apply(this, ps);
flush();
}
// Row & Column
Row::Row(std::initializer_list<I> ps)
{
adopt(new QHBoxLayout);
for (auto && p : ps)
apply(p);
auto self = asBox();
for (const LayoutItem &item : pendingItems)
addItemToBoxLayout(self, item);
ptr = new QHBoxLayout;
apply(this, ps);
flush();
}
Column::Column(std::initializer_list<I> ps)
{
adopt(new QVBoxLayout);
for (auto && p : ps)
apply(p);
auto self = asBox();
for (const LayoutItem &item : pendingItems)
addItemToBoxLayout(self, item);
ptr = new QVBoxLayout;
apply(this, ps);
flush();
}
// Grid
Grid::Grid()
{
ptr = new QGridLayout;
}
Grid::Grid(std::initializer_list<I> ps)
{
adopt(new QGridLayout);
for (auto && p : ps)
apply(p);
ptr = new QGridLayout;
apply(this, ps);
flush();
}
// Form
Form::Form()
{
ptr = new QFormLayout;
}
Form::Form(std::initializer_list<I> ps)
{
adopt(new QFormLayout);
fieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
for (auto && p : ps)
apply(p);
auto lt = new QFormLayout;
ptr = lt;
lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
apply(this, ps);
flush();
}
void LayoutInterface::fieldGrowthPolicy(int policy)
void Layout::fieldGrowthPolicy(int policy)
{
if (auto lt = asForm())
lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
}
QWidget *Layout::emerge() const
{
const_cast<Layout *>(this)->flush();
QWidget *widget = new QWidget;
widget->setLayout(access(this));
return widget;
}
// "Widgets"
Widget::Widget(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void WidgetInterface::resize(int w, int h)
void Widget::resize(int w, int h)
{
access(this)->resize(w, h);
}
void WidgetInterface::setLayout(const LayoutInterface &layout)
void Widget::setLayout(const Layout &layout)
{
access(this)->setLayout(access(&layout));
}
void WidgetInterface::setWindowTitle(const QString &title)
void Widget::setWindowTitle(const QString &title)
{
access(this)->setWindowTitle(title);
}
void WidgetInterface::setToolTip(const QString &title)
void Widget::setToolTip(const QString &title)
{
access(this)->setToolTip(title);
}
void WidgetInterface::show()
void Widget::show()
{
access(this)->show();
}
void WidgetInterface::noMargin()
void Widget::noMargin(int)
{
customMargin({});
}
void WidgetInterface::normalMargin()
void Widget::normalMargin(int)
{
customMargin({9, 9, 9, 9});
}
void WidgetInterface::customMargin(const QMargins &margin)
void Widget::customMargin(const QMargins &margin)
{
access(this)->setContentsMargins(margin);
}
QWidget *WidgetInterface::emerge()
QWidget *Widget::emerge() const
{
return access(this);
}
@@ -736,18 +745,17 @@ QWidget *WidgetInterface::emerge()
Label::Label(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
Label::Label(const QString &text)
{
create();
ptr = new Implementation;
setText(text);
}
void LabelInterface::setText(const QString &text)
void Label::setText(const QString &text)
{
access(this)->setText(text);
}
@@ -756,32 +764,35 @@ void LabelInterface::setText(const QString &text)
Group::Group(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void GroupInterface::setTitle(const QString &title)
void Group::setTitle(const QString &title)
{
access(this)->setTitle(title);
access(this)->setObjectName(title);
}
void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
{
checker(access(this));
}
// SpinBox
SpinBox::SpinBox(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void SpinBoxInterface::setValue(int val)
void SpinBox::setValue(int val)
{
access(this)->setValue(val);
}
void SpinBoxInterface::onTextChanged(const std::function<void (QString)> &func)
void SpinBox::onTextChanged(const std::function<void (QString)> &func)
{
QObject::connect(access(this), &QSpinBox::textChanged, func);
}
@@ -790,12 +801,11 @@ void SpinBoxInterface::onTextChanged(const std::function<void (QString)> &func)
TextEdit::TextEdit(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void TextEditInterface::setText(const QString &text)
void TextEdit::setText(const QString &text)
{
access(this)->setText(text);
}
@@ -804,19 +814,18 @@ void TextEditInterface::setText(const QString &text)
PushButton::PushButton(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void PushButtonInterface::setText(const QString &text)
void PushButton::setText(const QString &text)
{
access(this)->setText(text);
}
void PushButtonInterface::onClicked(const std::function<void ()> &func)
void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
{
QObject::connect(access(this), &QAbstractButton::clicked, func);
QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
}
// Stack
@@ -826,88 +835,92 @@ void PushButtonInterface::onClicked(const std::function<void ()> &func)
// top-level widget. This can lead to the focus shifting away from the main application.
Stack::Stack(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
void addToStack(Stack *stack, const Widget &inner)
{
access(stack)->addWidget(inner.emerge());
}
void addToStack(Stack *stack, const Layout &inner)
{
inner.flush_();
access(stack)->addWidget(inner.emerge());
}
void addToStack(Stack *stack, QWidget *inner)
{
access(stack)->addWidget(inner);
}
// Splitter
Splitter::Splitter(std::initializer_list<I> ps)
{
create();
ptr = new Implementation;
access(this)->setOrientation(Qt::Vertical);
for (auto && p : ps)
apply(p);
apply(this, ps);
}
void addToSplitter(Splitter *splitter, QWidget *inner)
{
access(splitter)->addWidget(inner);
}
void addToSplitter(Splitter *splitter, const Widget &inner)
{
access(splitter)->addWidget(inner.emerge());
}
void addToSplitter(Splitter *splitter, const Layout &inner)
{
inner.flush_();
access(splitter)->addWidget(inner.emerge());
}
// ToolBar
ToolBar::ToolBar(std::initializer_list<I> ps)
{
create();
ptr = new Implementation;
apply(this, ps);
access(this)->setOrientation(Qt::Horizontal);
for (auto && p : ps)
apply(p);
}
// TabWidget
TabWidget::TabWidget(std::initializer_list<I> ps)
{
create();
for (auto && p : ps)
apply(p);
ptr = new Implementation;
apply(this, ps);
}
// // Special Tab
Tab::Tab(const QString &tabName, const Layout &inner)
: tabName(tabName), inner(inner)
{}
// 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);
// };
// }
void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
{
access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
}
// // Special If
// Special If
// If::If(bool condition, const LayoutItems &items, const LayoutItems &other)
// {
// subItems.append(condition ? items : other);
// }
If::If(bool condition,
const std::initializer_list<Layout::I> ifcase,
const std::initializer_list<Layout::I> thencase)
: used(condition ? ifcase : thencase)
{}
// "Properties"
void addToLayout(Layout *layout, const If &inner)
{
for (const Layout::I &item : inner.used)
item.apply(layout);
}
// LayoutItem spacing(int spacing)
// {
// return [spacing](QObject *target) {
// if (auto layout = qobject_cast<QLayout *>(target)) {
// layout->setSpacing(spacing);
// } 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);
// }
// };
// }
// Specials
QWidget *createHr(QWidget *parent)
{
@@ -917,10 +930,34 @@ QWidget *createHr(QWidget *parent)
return frame;
}
Span::Span(int n, const LayoutItem &item)
: LayoutItem(item)
Span::Span(int n, const Layout::I &item)
: item(item), spanCols(n)
{}
void addToLayout(Layout *layout, const Span &inner)
{
spanCols = n;
LayoutItem item;
layout->addItem(inner.item);
QTC_ASSERT(!layout->pendingItems.empty(), return);
layout->pendingItems.back().spanCols = inner.spanCols;
layout->pendingItems.back().spanRows = inner.spanRows;
}
LayoutModifier spacing(int space)
{
return [space](Layout *iface) { iface->setSpacing(space); };
}
void addToLayout(Layout *layout, const Space &inner)
{
if (auto lt = layout->asBox())
lt->addSpacing(inner.space);
}
void addToLayout(Layout *layout, const Stretch &inner)
{
if (auto lt = layout->asBox())
lt->addStretch(inner.stretch);
}
// void createItem(LayoutItem *item, QWidget *t)

View File

@@ -4,11 +4,10 @@
#pragma once
#include <QList>
#include <QMargins>
#include <QString>
#include <array>
#include <cstring>
#include <functional>
#include <initializer_list>
#if defined(UTILS_LIBRARY)
@@ -24,6 +23,7 @@ class QBoxLayout;
class QFormLayout;
class QGridLayout;
class QGroupBox;
class QHBoxLayout;
class QLabel;
class QLayout;
class QMargins;
@@ -35,129 +35,67 @@ class QStackedWidget;
class QTabWidget;
class QTextEdit;
class QToolBar;
class QVBoxLayout;
class QWidget;
QT_END_NAMESPACE
namespace Layouting {
struct LayoutItem;
// struct NestId {};
struct NestId {};
class NestId {};
template <typename T1, typename T2>
struct IdAndArg
{
IdAndArg(const T1 &id, const T2 &arg) : id(id), arg(arg) {}
T1 id;
T2 arg;
};
// The main dispatchers
void doit(auto x, auto id, auto a);
// BuilderItem
template <typename X, typename XInterface>
struct BuilderItem : XInterface
class IdAndArg
{
public:
struct I
IdAndArg(const T1 &id, const T2 &arg) : id(id), arg(arg) {}
const T1 id;
const T2 arg; // FIXME: Could be const &, but this would currently break bindTo().
};
// The main dispatcher
void doit(auto x, auto id, auto p);
template <typename X> class BuilderItem
{
public:
// Nested child object
template <typename Inner>
BuilderItem(Inner && p)
{
// Nested child object
template <typename Inner>
I(const Inner &p)
{
apply = [p](XInterface *x) { doit(x, NestId{}, p); };
}
// Property setter
template <typename Id, typename Arg1>
I(const IdAndArg<Id, Arg1> &p)
{
apply = [p](XInterface *x) { doit(x, p.id, p.arg); };
}
std::function<void(XInterface *)> apply;
};
void create()
{
XInterface::ptr = new XInterface::Implementation;
apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
}
void adopt(XInterface::Implementation *ptr)
// Property setter
template <typename Id, typename Arg>
BuilderItem(IdAndArg<Id, Arg> && idarg)
{
XInterface::ptr = ptr;
apply = [&idarg](X *x) { doit(x, idarg.id, idarg.arg); };
}
void apply(const I &init)
{
init.apply(this);
}
using Id = typename XInterface::Implementation *;
std::function<void(X *)> apply;
};
//////////////////////////////////////////////
struct LayoutInterface;
struct WidgetInterface;
struct QTCREATOR_UTILS_EXPORT LayoutItem
{
LayoutItem();
LayoutItem(QLayout *l) : layout(l), empty(!l) {}
LayoutItem(QWidget *w) : widget(w), empty(!w) {}
LayoutItem(const QString &t) : text(t) {}
LayoutItem(const LayoutInterface &inner);
LayoutItem(const WidgetInterface &inner);
~LayoutItem();
QString text;
QLayout *layout = nullptr;
QWidget *widget = nullptr;
int space = -1;
int stretch = -1;
int spanCols = 1;
int spanRows = 1;
bool empty = false;
bool break_ = false;
};
using LayoutItems = QList<LayoutItem>;
// We need two classes for each user visible Builder type.
// The actual Builder classes derive from BuilderItem with two parameters,
// one is the Builder class itself for CRTP and a one "Interface" type.
// The "Interface" types act (individually, and as a hierarchy) as interface
// members of the real QObject/QWidget/... hierarchy things. This wrapper is not
// strictly needed, the Q* hierarchy could be used directly, at the
// price of #include'ing the definitions of each participating class,
// which does not scale well.
//
// Basic
//
struct QTCREATOR_UTILS_EXPORT ThingInterface
class QTCREATOR_UTILS_EXPORT Thing
{
template <typename T>
T *access_() const { return static_cast<T *>(ptr); }
public:
void *ptr; // The product.
};
struct QTCREATOR_UTILS_EXPORT ObjectInterface : ThingInterface
class QTCREATOR_UTILS_EXPORT Object : public Thing
{
public:
using Implementation = QObject;
};
using I = BuilderItem<Object>;
struct QTCREATOR_UTILS_EXPORT Object : BuilderItem<Object, ObjectInterface>
{
Object() = default;
Object(std::initializer_list<I> ps);
};
@@ -165,235 +103,288 @@ struct QTCREATOR_UTILS_EXPORT Object : BuilderItem<Object, ObjectInterface>
// Layouts
//
struct QTCREATOR_UTILS_EXPORT LayoutInterface : ObjectInterface
class FlowLayout;
class Layout;
using LayoutModifier = std::function<void(Layout *)>;
// using LayoutModifier = void(*)(Layout *);
class QTCREATOR_UTILS_EXPORT Layout : public Object
{
public:
using Implementation = QLayout;
using I = BuilderItem<Layout>;
Layout() = default;
Layout(Implementation *w) { ptr = w; }
class LayoutItem
{
public:
~LayoutItem();
LayoutItem();
LayoutItem(QLayout *l) : layout(l) {}
LayoutItem(QWidget *w) : widget(w) {}
LayoutItem(const QString &t) : text(t) {}
LayoutItem(const LayoutModifier &inner);
QString text;
QLayout *layout = nullptr;
QWidget *widget = nullptr;
int stretch = -1;
int spanCols = 1;
int spanRows = 1;
bool empty = false;
LayoutModifier ownerModifier;
//Qt::Alignment align = {};
};
void span(int cols, int rows);
void noMargin();
void normalMargin();
void customMargin(const QMargins &margin);
void setColumnStretch(int cols, int rows);
void setSpacing(int space);
void addItem(const LayoutItem &item);
void attachTo(QWidget *);
void addItemHelper(const LayoutItem &item);
void addItem(I item);
void addItems(std::initializer_list<I> items);
void addRow(std::initializer_list<I> items);
void flush();
void flush_() const;
void fieldGrowthPolicy(int policy);
QWidget *emerge() const;
QFormLayout *asForm();
QGridLayout *asGrid();
QBoxLayout *asBox();
std::vector<LayoutItem> pendingItems;
FlowLayout *asFlow();
// Grid-only
int currentGridColumn = 0;
int currentGridRow = 0;
Qt::Alignment align = {};
//Qt::Alignment align = {};
bool useFormAlignment = false;
std::vector<LayoutItem> pendingItems;
};
struct QTCREATOR_UTILS_EXPORT Layout : BuilderItem<Layout, LayoutInterface>
class QTCREATOR_UTILS_EXPORT Column : public Layout
{
};
public:
using Implementation = QVBoxLayout;
using I = BuilderItem<Column>;
struct QTCREATOR_UTILS_EXPORT Column : BuilderItem<Column, LayoutInterface>
{
Column(std::initializer_list<I> ps);
};
struct QTCREATOR_UTILS_EXPORT Row : BuilderItem<Row, LayoutInterface>
class QTCREATOR_UTILS_EXPORT Row : public Layout
{
public:
using Implementation = QHBoxLayout;
using I = BuilderItem<Row>;
Row(std::initializer_list<I> ps);
};
struct QTCREATOR_UTILS_EXPORT Form : BuilderItem<Form, LayoutInterface>
class QTCREATOR_UTILS_EXPORT Form : public Layout
{
public:
using Implementation = QFormLayout;
using I = BuilderItem<Form>;
Form();
Form(std::initializer_list<I> ps);
};
struct QTCREATOR_UTILS_EXPORT Grid : BuilderItem<Grid, LayoutInterface>
class QTCREATOR_UTILS_EXPORT Grid : public Layout
{
public:
using Implementation = QGridLayout;
using I = BuilderItem<Grid>;
Grid();
Grid(std::initializer_list<I> ps);
};
struct QTCREATOR_UTILS_EXPORT Flow : BuilderItem<Flow, LayoutInterface>
class QTCREATOR_UTILS_EXPORT Flow : public Layout
{
public:
Flow(std::initializer_list<I> ps);
};
struct QTCREATOR_UTILS_EXPORT Stretch : LayoutItem
class QTCREATOR_UTILS_EXPORT Stretch
{
explicit Stretch(int stretch) { this->stretch = stretch; }
public:
explicit Stretch(int stretch) : stretch(stretch) {}
int stretch;
};
struct QTCREATOR_UTILS_EXPORT Space : LayoutItem
class QTCREATOR_UTILS_EXPORT Space
{
explicit Space(int space) { this->space = space; }
public:
explicit Space(int space) : space(space) {}
int space;
};
struct QTCREATOR_UTILS_EXPORT Span : LayoutItem
class QTCREATOR_UTILS_EXPORT Span
{
Span(int n, const LayoutItem &item);
public:
Span(int n, const Layout::I &item);
Layout::I item;
int spanCols = 1;
int spanRows = 1;
};
//
// Widgets
//
struct QTCREATOR_UTILS_EXPORT WidgetInterface : ObjectInterface
class QTCREATOR_UTILS_EXPORT Widget : public Object
{
public:
using Implementation = QWidget;
QWidget *emerge();
using I = BuilderItem<Widget>;
Widget() = default;
Widget(std::initializer_list<I> ps);
Widget(Implementation *w) { ptr = w; }
QWidget *emerge() const;
void show();
void resize(int, int);
void setLayout(const LayoutInterface &layout);
void setLayout(const Layout &layout);
void setWindowTitle(const QString &);
void setToolTip(const QString &);
void noMargin();
void normalMargin();
void noMargin(int = 0);
void normalMargin(int = 0);
void customMargin(const QMargins &margin);
};
struct QTCREATOR_UTILS_EXPORT Widget : BuilderItem<Widget, WidgetInterface>
{
Widget(std::initializer_list<I> ps);
};
// Label
struct QTCREATOR_UTILS_EXPORT LabelInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT Label : public Widget
{
public:
using Implementation = QLabel;
using I = BuilderItem<Label>;
Label(std::initializer_list<I> ps);
Label(const QString &text);
void setText(const QString &);
};
struct QTCREATOR_UTILS_EXPORT Label : BuilderItem<Label, LabelInterface>
{
Label(std::initializer_list<I> ps);
Label(const QString &text);
};
// Group
struct QTCREATOR_UTILS_EXPORT GroupInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT Group : public Widget
{
public:
using Implementation = QGroupBox;
using I = BuilderItem<Group>;
Group(std::initializer_list<I> ps);
void setTitle(const QString &);
void setGroupChecker(const std::function<void(QObject *)> &);
};
struct QTCREATOR_UTILS_EXPORT Group : BuilderItem<Group, GroupInterface>
{
Group(std::initializer_list<I> ps);
};
// SpinBox
struct QTCREATOR_UTILS_EXPORT SpinBoxInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
{
public:
using Implementation = QSpinBox;
using I = BuilderItem<SpinBox>;
SpinBox(std::initializer_list<I> ps);
void setValue(int);
void onTextChanged(const std::function<void(QString)> &);
};
struct QTCREATOR_UTILS_EXPORT SpinBox : BuilderItem<SpinBox, SpinBoxInterface>
{
SpinBox(std::initializer_list<I> ps);
};
// PushButton
struct QTCREATOR_UTILS_EXPORT PushButtonInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT PushButton : public Widget
{
public:
using Implementation = QPushButton;
using I = BuilderItem<PushButton>;
void setText(const QString &);
void onClicked(const std::function<void()> &);
};
struct QTCREATOR_UTILS_EXPORT PushButton : BuilderItem<PushButton, PushButtonInterface>
{
PushButton(std::initializer_list<I> ps);
void setText(const QString &);
void onClicked(const std::function<void()> &, QObject *guard);
};
// TextEdit
struct QTCREATOR_UTILS_EXPORT TextEditInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
{
public:
using Implementation = QTextEdit;
using I = BuilderItem<TextEdit>;
using Id = Implementation *;
TextEdit(std::initializer_list<I> ps);
void setText(const QString &);
};
struct QTCREATOR_UTILS_EXPORT TextEdit : BuilderItem<TextEdit, TextEditInterface>
{
TextEdit(std::initializer_list<I> ps);
};
// Splitter
struct QTCREATOR_UTILS_EXPORT SplitterInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT Splitter : public Widget
{
public:
using Implementation = QSplitter;
};
using I = BuilderItem<Splitter>;
struct QTCREATOR_UTILS_EXPORT Splitter : BuilderItem<Splitter, SplitterInterface>
{
Splitter(std::initializer_list<I> items);
};
// Stack
struct QTCREATOR_UTILS_EXPORT StackInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT Stack : public Widget
{
};
public:
using Implementation = QStackedWidget;
using I = BuilderItem<Stack>;
struct QTCREATOR_UTILS_EXPORT Stack : BuilderItem<Stack, StackInterface>
{
Stack() : Stack({}) {}
Stack(std::initializer_list<I> items);
};
// TabWidget
struct QTCREATOR_UTILS_EXPORT TabInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT Tab : public Widget
{
public:
using Implementation = QWidget;
Tab(const QString &tabName, const Layout &inner);
const QString tabName;
const Layout inner;
};
struct QTCREATOR_UTILS_EXPORT TabWidgetInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
{
public:
using Implementation = QTabWidget;
};
using I = BuilderItem<TabWidget>;
struct QTCREATOR_UTILS_EXPORT Tab : BuilderItem<Tab, TabInterface>
{
Tab(const QString &tabName, const LayoutItem &item);
};
struct QTCREATOR_UTILS_EXPORT TabWidget : BuilderItem<TabWidget, TabWidgetInterface>
{
TabWidget(std::initializer_list<I> items);
};
// ToolBar
struct QTCREATOR_UTILS_EXPORT ToolBarInterface : WidgetInterface
class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
{
public:
using Implementation = QToolBar;
};
using I = Layouting::BuilderItem<ToolBar>;
struct QTCREATOR_UTILS_EXPORT ToolBar : BuilderItem<ToolBar, ToolBarInterface>
{
ToolBar(std::initializer_list<I> items);
};
// Special
struct QTCREATOR_UTILS_EXPORT If : LayoutItem
class QTCREATOR_UTILS_EXPORT If
{
If(bool condition, const LayoutItems &item, const LayoutItems &other = {});
public:
If(bool condition,
const std::initializer_list<Layout::I> ifcase,
const std::initializer_list<Layout::I> thencase = {});
const std::initializer_list<Layout::I> used;
};
//
@@ -413,24 +404,15 @@ struct QTCREATOR_UTILS_EXPORT If : LayoutItem
// the base expectation is that they will forwards to the backend
// type's setter.
// Special dispatchers :w
// Special dispatchers
struct BindToId {};
class BindToId {};
template <typename T>
auto bindTo(T **x)
auto bindTo(T **p)
{
// FIXME: Evil hack to shut up clang-tidy which does not see that the returned tuple will
// result in an assignment to *x and complains about every use of the bound value later.
// main.cpp:129:5: Called C++ object pointer is null [clang-analyzer-core.CallAndMessage]
// 1: Calling 'bindTo<QWidget>' in /data/dev/creator/tests/manual/layoutbuilder/v2/main.cpp:73
// 2: Null pointer value stored to 'w' in /data/dev/creator/tests/manual/layoutbuilder/v2/lb.h:518
// 3: Returning from 'bindTo<QWidget>' in /data/dev/creator/tests/manual/layoutbuilder/v2/main.cpp:73
// 4: Called C++ object pointer is null in /data/dev/creator/tests/manual/layoutbuilder/v2/main.cpp:129
*x = reinterpret_cast<T*>(1);
return IdAndArg{BindToId{}, x};
return IdAndArg{BindToId{}, p};
}
template <typename Interface>
@@ -439,8 +421,8 @@ void doit(Interface *x, BindToId, auto p)
*p = static_cast<Interface::Implementation *>(x->ptr);
}
struct IdId {};
auto id(auto x) { return IdAndArg{IdId{}, x}; }
class IdId {};
auto id(auto p) { return IdAndArg{IdId{}, p}; }
template <typename Interface>
void doit(Interface *x, IdId, auto p)
@@ -450,59 +432,119 @@ void doit(Interface *x, IdId, auto p)
// Setter dispatchers
struct SizeId {};
class SizeId {};
auto size(auto w, auto h) { return IdAndArg{SizeId{}, std::pair{w, h}}; }
void doit(auto x, SizeId, auto p) { x->resize(p.first, p.second); }
struct TextId {};
auto text(auto x) { return IdAndArg{TextId{}, x}; }
void doit(auto x, TextId, auto t) { x->setText(t); }
class TextId {};
auto text(auto p) { return IdAndArg{TextId{}, p}; }
void doit(auto x, TextId, auto p) { x->setText(p); }
struct TitleId {};
auto title(auto x) { return IdAndArg{TitleId{}, x}; }
void doit(auto x, TitleId, auto t) { x->setTitle(t); }
class TitleId {};
auto title(auto p) { return IdAndArg{TitleId{}, p}; }
void doit(auto x, TitleId, auto p) { x->setTitle(p); }
struct ToolTipId {};
auto toolTip(auto x) { return IdAndArg{ToolTipId{}, x}; }
void doit(auto x, ToolTipId, auto t) { x->setToolTip(t); }
class GroupCheckerId {};
auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; }
void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); }
struct WindowTitleId {};
auto windowTitle(auto x) { return IdAndArg{WindowTitleId{}, x}; }
void doit(auto x, WindowTitleId, auto t) { x->setWindowTitle(t); }
class ToolTipId {};
auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; }
void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); }
struct OnTextChangedId {};
auto onTextChanged(auto x) { return IdAndArg{OnTextChangedId{}, x}; }
void doit(auto x, OnTextChangedId, auto func) { x->onTextChanged(func); }
class WindowTitleId {};
auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; }
void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); }
struct OnClickedId {};
auto onClicked(auto x) { return IdAndArg{OnClickedId{}, x}; }
void doit(auto x, OnClickedId, auto func) { x->onClicked(func); }
class OnTextChangedId {};
auto onTextChanged(auto p) { return IdAndArg{OnTextChangedId{}, p}; }
void doit(auto x, OnTextChangedId, auto p) { x->onTextChanged(p); }
class OnClickedId {};
auto onClicked(auto p, auto guard) { return IdAndArg{OnClickedId{}, std::pair{p, guard}}; }
void doit(auto x, OnClickedId, auto p) { x->onClicked(p.first, p.second); }
class CustomMarginId {};
inline auto customMargin(const QMargins &p) { return IdAndArg{CustomMarginId{}, p}; }
void doit(auto x, CustomMarginId, auto p) { x->customMargin(p); }
class FieldGrowthPolicyId {};
inline auto fieldGrowthPolicy(auto p) { return IdAndArg{FieldGrowthPolicyId{}, p}; }
void doit(auto x, FieldGrowthPolicyId, auto p) { x->fieldGrowthPolicy(p); }
class ColumnStretchId {};
inline auto columnStretch(int column, int stretch) { return IdAndArg{ColumnStretchId{}, std::pair{column, stretch}}; }
void doit(auto x, ColumnStretchId, auto p) { x->setColumnStretch(p.first, p.second); }
// Nesting dispatchers
QTCREATOR_UTILS_EXPORT void addNestedItem(WidgetInterface *widget, const LayoutInterface &layout);
QTCREATOR_UTILS_EXPORT void addNestedItem(LayoutInterface *layout, const LayoutItem &inner);
QTCREATOR_UTILS_EXPORT void addNestedItem(LayoutInterface *layout, const LayoutInterface &inner);
QTCREATOR_UTILS_EXPORT void addNestedItem(LayoutInterface *layout, const WidgetInterface &inner);
QTCREATOR_UTILS_EXPORT void addNestedItem(LayoutInterface *layout, const QString &inner);
QTCREATOR_UTILS_EXPORT void addNestedItem(LayoutInterface *layout, const std::function<LayoutItem()> &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Layout &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Widget &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QWidget *inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QLayout *inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const LayoutModifier &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const QString &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Space &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Stretch &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const If &inner);
QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Span &inner);
// ... can be added to anywhere later to support "user types"
void doit(auto outer, NestId, auto inner)
QTCREATOR_UTILS_EXPORT void addToWidget(Widget *widget, const Layout &layout);
QTCREATOR_UTILS_EXPORT void addToTabWidget(TabWidget *tabWidget, const Tab &inner);
QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, QWidget *inner);
QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Widget &inner);
QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Layout &inner);
QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, QWidget *inner);
QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Widget &inner);
QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Layout &inner);
template <class Inner>
void doit_nested(Layout *outer, Inner && inner)
{
addNestedItem(outer, inner);
addToLayout(outer, std::forward<Inner>(inner));
}
void doit_nested(Widget *outer, auto inner)
{
addToWidget(outer, inner);
}
void doit_nested(TabWidget *outer, auto inner)
{
addToTabWidget(outer, inner);
}
void doit_nested(Stack *outer, auto inner)
{
addToStack(outer, inner);
}
void doit_nested(Splitter *outer, auto inner)
{
addToSplitter(outer, inner);
}
template <class Inner>
void doit(auto outer, NestId, Inner && inner)
{
doit_nested(outer, std::forward<Inner>(inner));
}
// Special layout items
QTCREATOR_UTILS_EXPORT LayoutItem br();
QTCREATOR_UTILS_EXPORT LayoutItem empty();
QTCREATOR_UTILS_EXPORT LayoutItem hr();
QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
QTCREATOR_UTILS_EXPORT LayoutItem st();
QTCREATOR_UTILS_EXPORT void empty(Layout *);
QTCREATOR_UTILS_EXPORT void br(Layout *);
QTCREATOR_UTILS_EXPORT void st(Layout *);
QTCREATOR_UTILS_EXPORT void noMargin(Layout *);
QTCREATOR_UTILS_EXPORT void normalMargin(Layout *);
QTCREATOR_UTILS_EXPORT void withFormAlignment(Layout *);
QTCREATOR_UTILS_EXPORT void hr(Layout *);
QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
// Convenience

View File

@@ -68,7 +68,7 @@ int main(int argc, char *argv[])
st,
PushButton {
text("Quit"),
onClicked(QApplication::quit)
onClicked(QApplication::quit, nullptr)
}
}
}