Use LayoutBuilder V2

This puts the implementation introduced in acf1ecb47f into use, after
significant simplifications in the class hierarchy. CRTP is not used
anymore, and the new tag based dispatch is also used for Layout::addItem,
effectively reducing the number of different code paths.

The Lua based settings access is disabled for now.

Change-Id: Idb6d1a25675378757c5267bdb630bcd4c1f52d34
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
hjk
2024-05-14 10:33:01 +02:00
parent e9b002ff7e
commit 971938421c
102 changed files with 1599 additions and 1244 deletions

View File

@@ -3,12 +3,12 @@
#pragma once
#include <QFormLayout>
#include <QList>
#include <QMargins>
#include <QString>
#include <QtGlobal>
#include <optional>
#include <functional>
#include <initializer_list>
#include <vector>
#if defined(UTILS_LIBRARY)
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
@@ -19,251 +19,536 @@
#endif
QT_BEGIN_NAMESPACE
class QBoxLayout;
class QFormLayout;
class QGridLayout;
class QGroupBox;
class QHBoxLayout;
class QLabel;
class QLayout;
class QMargins;
class QObject;
class QPushButton;
class QSpinBox;
class QSplitter;
class QStackedWidget;
class QTabWidget;
class QTextEdit;
class QToolBar;
class QVBoxLayout;
class QWidget;
template <class T> T qobject_cast(QObject *object);
QT_END_NAMESPACE
namespace Layouting {
// LayoutItem
class NestId {};
class LayoutBuilder;
class LayoutItem;
using LayoutItems = QList<LayoutItem>;
template <typename T1, typename T2>
class IdAndArg
{
public:
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)
{
apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
}
// Property setter
template <typename Id, typename Arg>
BuilderItem(IdAndArg<Id, Arg> && idarg)
{
apply = [&idarg](X *x) { doit(x, idarg.id, idarg.arg); };
}
std::function<void(X *)> apply;
};
//////////////////////////////////////////////
//
// Basic
//
class QTCREATOR_UTILS_EXPORT Thing
{
public:
void *ptr; // The product.
};
class QTCREATOR_UTILS_EXPORT Object : public Thing
{
public:
using Implementation = QObject;
using I = BuilderItem<Object>;
Object() = default;
Object(std::initializer_list<I> ps);
};
//
// Layouts
//
class FlowLayout;
class Layout;
using LayoutModifier = std::function<void(Layout *)>;
// using LayoutModifier = void(*)(Layout *);
class QTCREATOR_UTILS_EXPORT LayoutItem
{
public:
using Setter = std::function<void(QObject *target)>;
LayoutItem();
~LayoutItem();
LayoutItem();
LayoutItem(QLayout *l) : layout(l) {}
LayoutItem(QWidget *w) : widget(w) {}
LayoutItem(const QString &t) : text(t) {}
LayoutItem(const LayoutModifier &inner);
LayoutItem(const LayoutItem &t);
LayoutItem &operator=(const LayoutItem &t) = default;
template <class T> LayoutItem(const T &t)
{
if constexpr (std::is_base_of_v<LayoutItem, T>)
LayoutItem::operator=(t);
else
createItem(this, t);
}
void attachTo(QWidget *w) const;
QWidget *emerge();
void addItem(const LayoutItem &item);
void addItems(const LayoutItems &items);
void addRow(const LayoutItems &items);
std::function<void(LayoutBuilder &)> onAdd;
std::function<void(LayoutBuilder &)> onExit;
std::function<void(QObject *target)> setter;
LayoutItems subItems;
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 = {};
};
// Special items
class QTCREATOR_UTILS_EXPORT Space
class QTCREATOR_UTILS_EXPORT Layout : public Object
{
public:
explicit Space(int space) : space(space) {}
const int space;
using Implementation = QLayout;
using I = BuilderItem<Layout>;
Layout() = default;
Layout(Implementation *w) { ptr = w; }
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 attachTo(QWidget *);
void addItem(I item);
void addItems(std::initializer_list<I> items);
void addRow(std::initializer_list<I> items);
void addLayoutItem(const LayoutItem &item);
void flush();
void flush_() const;
void fieldGrowthPolicy(int policy);
QWidget *emerge() const;
QFormLayout *asForm();
QGridLayout *asGrid();
QBoxLayout *asBox();
FlowLayout *asFlow();
// Grid-only
int currentGridColumn = 0;
int currentGridRow = 0;
//Qt::Alignment align = {};
bool useFormAlignment = false;
std::vector<LayoutItem> pendingItems;
};
class QTCREATOR_UTILS_EXPORT Column : public Layout
{
public:
using Implementation = QVBoxLayout;
using I = BuilderItem<Column>;
Column(std::initializer_list<I> ps);
};
class QTCREATOR_UTILS_EXPORT Row : public Layout
{
public:
using Implementation = QHBoxLayout;
using I = BuilderItem<Row>;
Row(std::initializer_list<I> ps);
};
class QTCREATOR_UTILS_EXPORT Form : public Layout
{
public:
using Implementation = QFormLayout;
using I = BuilderItem<Form>;
Form();
Form(std::initializer_list<I> ps);
};
class QTCREATOR_UTILS_EXPORT Grid : public Layout
{
public:
using Implementation = QGridLayout;
using I = BuilderItem<Grid>;
Grid();
Grid(std::initializer_list<I> ps);
};
class QTCREATOR_UTILS_EXPORT Flow : public Layout
{
public:
Flow(std::initializer_list<I> ps);
};
class QTCREATOR_UTILS_EXPORT Stretch
{
public:
explicit Stretch(int stretch = 1) : stretch(stretch) {}
const int stretch;
explicit Stretch(int stretch) : stretch(stretch) {}
int stretch;
};
class QTCREATOR_UTILS_EXPORT Space
{
public:
explicit Space(int space) : space(space) {}
int space;
};
class QTCREATOR_UTILS_EXPORT Span
{
public:
Span(int span, const LayoutItem &item) : span(span), item(item) {}
const int span;
LayoutItem item;
Span(int cols, const Layout::I &item);
Span(int cols, int rows, const Layout::I &item);
Layout::I item;
int spanCols = 1;
int spanRows = 1;
};
class QTCREATOR_UTILS_EXPORT Column : public LayoutItem
//
// Widgets
//
class QTCREATOR_UTILS_EXPORT Widget : public Object
{
public:
Column(std::initializer_list<LayoutItem> items);
using Implementation = QWidget;
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 Layout &layout);
void setWindowTitle(const QString &);
void setToolTip(const QString &);
void noMargin(int = 0);
void normalMargin(int = 0);
void customMargin(const QMargins &margin);
};
class QTCREATOR_UTILS_EXPORT Row : public LayoutItem
class QTCREATOR_UTILS_EXPORT Label : public Widget
{
public:
Row(std::initializer_list<LayoutItem> items);
using Implementation = QLabel;
using I = BuilderItem<Label>;
Label(std::initializer_list<I> ps);
Label(const QString &text);
void setText(const QString &);
};
class QTCREATOR_UTILS_EXPORT Flow : public LayoutItem
class QTCREATOR_UTILS_EXPORT Group : public Widget
{
public:
Flow(std::initializer_list<LayoutItem> items);
using Implementation = QGroupBox;
using I = BuilderItem<Group>;
Group(std::initializer_list<I> ps);
void setTitle(const QString &);
void setGroupChecker(const std::function<void(QObject *)> &);
};
class QTCREATOR_UTILS_EXPORT Grid : public LayoutItem
class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
{
public:
Grid() : Grid({}) {}
Grid(std::initializer_list<LayoutItem> items);
using Implementation = QSpinBox;
using I = BuilderItem<SpinBox>;
SpinBox(std::initializer_list<I> ps);
void setValue(int);
void onTextChanged(const std::function<void(QString)> &);
};
class QTCREATOR_UTILS_EXPORT Form : public LayoutItem
class QTCREATOR_UTILS_EXPORT PushButton : public Widget
{
public:
Form() : Form({}) {}
Form(std::initializer_list<LayoutItem> items);
using Implementation = QPushButton;
using I = BuilderItem<PushButton>;
PushButton(std::initializer_list<I> ps);
void setText(const QString &);
void onClicked(const std::function<void()> &, QObject *guard);
};
class QTCREATOR_UTILS_EXPORT Widget : public LayoutItem
class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
{
public:
Widget(std::initializer_list<LayoutItem> items);
using Implementation = QTextEdit;
using I = BuilderItem<TextEdit>;
using Id = Implementation *;
TextEdit(std::initializer_list<I> ps);
void setText(const QString &);
};
class QTCREATOR_UTILS_EXPORT Stack : public LayoutItem
class QTCREATOR_UTILS_EXPORT Splitter : public Widget
{
public:
using Implementation = QSplitter;
using I = BuilderItem<Splitter>;
Splitter(std::initializer_list<I> items);
};
class QTCREATOR_UTILS_EXPORT Stack : public Widget
{
public:
using Implementation = QStackedWidget;
using I = BuilderItem<Stack>;
Stack() : Stack({}) {}
Stack(std::initializer_list<LayoutItem> items);
Stack(std::initializer_list<I> items);
};
class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
class QTCREATOR_UTILS_EXPORT Tab : public Widget
{
public:
Tab(const QString &tabName, const LayoutItem &item);
using Implementation = QWidget;
Tab(const QString &tabName, const Layout &inner);
const QString tabName;
const Layout inner;
};
class QTCREATOR_UTILS_EXPORT If : public LayoutItem
class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
{
public:
If(bool condition, const LayoutItems &item, const LayoutItems &other = {});
using Implementation = QTabWidget;
using I = BuilderItem<TabWidget>;
TabWidget(std::initializer_list<I> items);
};
class QTCREATOR_UTILS_EXPORT Group : public LayoutItem
class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
{
public:
Group(std::initializer_list<LayoutItem> items);
using Implementation = QToolBar;
using I = Layouting::BuilderItem<ToolBar>;
ToolBar(std::initializer_list<I> items);
};
class QTCREATOR_UTILS_EXPORT TextEdit : public LayoutItem
// Special
class QTCREATOR_UTILS_EXPORT If
{
public:
TextEdit(std::initializer_list<LayoutItem> items);
If(bool condition,
const std::initializer_list<Layout::I> ifcase,
const std::initializer_list<Layout::I> thencase = {});
const std::initializer_list<Layout::I> used;
};
class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
//
// Dispatchers
//
// We need one 'Id' (and a corresponding function wrapping arguments into a
// tuple marked by this id) per 'name' of "backend" setter member function,
// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText.
// The name of the Id does not have to match the backend names as it
// is mapped per-backend-type in the respective setter implementation
// but we assume that it generally makes sense to stay close to the
// wrapped API name-wise.
// 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.
// Special dispatchers
class BindToId {};
template <typename T>
auto bindTo(T **p)
{
public:
PushButton(std::initializer_list<LayoutItem> items);
};
return IdAndArg{BindToId{}, p};
}
class QTCREATOR_UTILS_EXPORT SpinBox : public LayoutItem
template <typename Interface>
void doit(Interface *x, BindToId, auto p)
{
public:
SpinBox(std::initializer_list<LayoutItem> items);
};
*p = static_cast<typename Interface::Implementation *>(x->ptr);
}
class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
class IdId {};
auto id(auto p) { return IdAndArg{IdId{}, p}; }
template <typename Interface>
void doit(Interface *x, IdId, auto p)
{
public:
Splitter(std::initializer_list<LayoutItem> items);
};
**p = static_cast<typename Interface::Implementation *>(x->ptr);
}
class QTCREATOR_UTILS_EXPORT ToolBar : public LayoutItem
// Setter dispatchers
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); }
class TextId {};
auto text(auto p) { return IdAndArg{TextId{}, p}; }
void doit(auto x, TextId, auto p) { x->setText(p); }
class TitleId {};
auto title(auto p) { return IdAndArg{TitleId{}, p}; }
void doit(auto x, TitleId, auto p) { x->setTitle(p); }
class GroupCheckerId {};
auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; }
void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); }
class ToolTipId {};
auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; }
void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); }
class WindowTitleId {};
auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; }
void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); }
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 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"
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)
{
public:
ToolBar(std::initializer_list<LayoutItem> items);
};
addToLayout(outer, std::forward<Inner>(inner));
}
class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
void doit_nested(Widget *outer, auto inner)
{
public:
TabWidget(std::initializer_list<LayoutItem> items);
};
addToWidget(outer, inner);
}
class QTCREATOR_UTILS_EXPORT Application : public LayoutItem
void doit_nested(TabWidget *outer, auto inner)
{
public:
Application(std::initializer_list<LayoutItem> items);
addToTabWidget(outer, inner);
}
int exec(int &argc, char *argv[]);
};
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const std::function<void(QObject *target)> &t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QWidget *t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QLayout *t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, LayoutItem(*t)());
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const QString &t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Span &t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Space &t);
void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Stretch &t);
// "Singletons"
QTCREATOR_UTILS_EXPORT LayoutItem br();
QTCREATOR_UTILS_EXPORT LayoutItem st();
QTCREATOR_UTILS_EXPORT LayoutItem empty();
QTCREATOR_UTILS_EXPORT LayoutItem hr();
QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
// "Setters"
QTCREATOR_UTILS_EXPORT LayoutItem title(const QString &title);
QTCREATOR_UTILS_EXPORT LayoutItem text(const QString &text);
QTCREATOR_UTILS_EXPORT LayoutItem tooltip(const QString &toolTip);
QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int);
QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch);
QTCREATOR_UTILS_EXPORT LayoutItem spacing(int);
QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle);
QTCREATOR_UTILS_EXPORT LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy);
// "Getters"
class ID
void doit_nested(Stack *outer, auto inner)
{
public:
QObject *ob = nullptr;
};
addToStack(outer, inner);
}
QTCREATOR_UTILS_EXPORT LayoutItem id(ID &out);
void doit_nested(Splitter *outer, auto inner)
{
addToSplitter(outer, inner);
}
QTCREATOR_UTILS_EXPORT void setText(ID id, const QString &text);
template <class Inner>
void doit(auto outer, NestId, Inner && inner)
{
doit_nested(outer, std::forward<Inner>(inner));
}
// Special layout items
// "Signals"
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 LayoutItem onClicked(const std::function<void()> &,
QObject *guard = nullptr);
QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(const std::function<void(const QString &)> &,
QObject *guard = nullptr);
QTCREATOR_UTILS_EXPORT LayoutItem onValueChanged(const std::function<void(int)> &,
QObject *guard = nullptr);
QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(ID &id, QVariant(*sig)(QObject *));
QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
// Convenience
QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
template <class T>
LayoutItem bindTo(T **out)
{
return [out](QObject *target) { *out = qobject_cast<T *>(target); };
}
} // Layouting