From 308fb3f3fd4e4fa82c09da71b4209daa5d3d4592 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 22 May 2024 13:49:57 +0200 Subject: [PATCH] LayoutBuilder: Update demo Backport the learnings and simplification from the real use to the demo case. Change-Id: I3f501b03c760484961bfd586735c0db53ba080db Reviewed-by: Marcus Tillmanns --- tests/manual/layoutbuilder/v2/lb.cpp | 647 +++++++++++++------------ tests/manual/layoutbuilder/v2/lb.h | 532 ++++++++++---------- tests/manual/layoutbuilder/v2/main.cpp | 2 +- 3 files changed, 630 insertions(+), 551 deletions(-) diff --git a/tests/manual/layoutbuilder/v2/lb.cpp b/tests/manual/layoutbuilder/v2/lb.cpp index 0529909dacd..de8f674177d 100644 --- a/tests/manual/layoutbuilder/v2/lb.cpp +++ b/tests/manual/layoutbuilder/v2/lb.cpp @@ -3,7 +3,6 @@ #include "lb.h" -#include #include #include #include @@ -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 -XInterface::Implementation *access(const XInterface *x) +template +X::Implementation *access(const X *x) { - return static_cast(x->ptr); + return static_cast(x->ptr); } -// Setter implementation +template +void apply(X *x, std::initializer_list 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 LayoutItem(const T &t) @@ -239,16 +251,12 @@ LayoutItem::LayoutItem(const WidgetInterface &inner) \endlist */ -// Helpers - - // Object Object::Object(std::initializer_list 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 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 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(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 &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(access(this)); } -QGridLayout *LayoutInterface::asGrid() +QGridLayout *Layout::asGrid() { return qobject_cast(access(this)); } -QBoxLayout *LayoutInterface::asBox() +QBoxLayout *Layout::asBox() { return qobject_cast(access(this)); } -void LayoutInterface::flush() +FlowLayout *Layout::asFlow() { + return dynamic_cast(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(this)->flush(); +} + +void withFormAlignment(Layout *iface) +{ + iface->useFormAlignment = true; +} // Flow Flow::Flow(std::initializer_list 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 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 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 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 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(this)->flush(); + QWidget *widget = new QWidget; + widget->setLayout(access(this)); + return widget; +} + // "Widgets" Widget::Widget(std::initializer_list 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 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 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 &checker) +{ + checker(access(this)); +} + // SpinBox SpinBox::SpinBox(std::initializer_list 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 &func) +void SpinBox::onTextChanged(const std::function &func) { QObject::connect(access(this), &QSpinBox::textChanged, func); } @@ -790,12 +801,11 @@ void SpinBoxInterface::onTextChanged(const std::function &func) TextEdit::TextEdit(std::initializer_list 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 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 &func) +void PushButton::onClicked(const std::function &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 &func) // top-level widget. This can lead to the focus shifting away from the main application. Stack::Stack(std::initializer_list 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 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 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 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(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 ifcase, + const std::initializer_list 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(target)) { -// layout->setSpacing(spacing); -// } else { -// QTC_CHECK(false); -// } -// }; -// } - -// LayoutItem columnStretch(int column, int stretch) -// { -// return [column, stretch](QObject *target) { -// if (auto grid = qobject_cast(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) diff --git a/tests/manual/layoutbuilder/v2/lb.h b/tests/manual/layoutbuilder/v2/lb.h index 463a0e2b33f..99c87af564b 100644 --- a/tests/manual/layoutbuilder/v2/lb.h +++ b/tests/manual/layoutbuilder/v2/lb.h @@ -4,11 +4,10 @@ #pragma once -#include +#include #include -#include -#include +#include #include #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 -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 -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 class BuilderItem +{ +public: + // Nested child object + template + BuilderItem(Inner && p) { - // Nested child object - template - I(const Inner &p) - { - apply = [p](XInterface *x) { doit(x, NestId{}, p); }; - } - - // Property setter - template - I(const IdAndArg &p) - { - apply = [p](XInterface *x) { doit(x, p.id, p.arg); }; - } - - std::function apply; - }; - - void create() - { - XInterface::ptr = new XInterface::Implementation; + apply = [&p](X *x) { doit(x, NestId{}, std::forward(p)); }; } - void adopt(XInterface::Implementation *ptr) + // Property setter + template + BuilderItem(IdAndArg && 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 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; - - -// 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 - T *access_() const { return static_cast(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; -struct QTCREATOR_UTILS_EXPORT Object : BuilderItem -{ + Object() = default; Object(std::initializer_list ps); }; @@ -165,235 +103,288 @@ struct QTCREATOR_UTILS_EXPORT Object : BuilderItem // Layouts // -struct QTCREATOR_UTILS_EXPORT LayoutInterface : ObjectInterface +class FlowLayout; +class Layout; +using LayoutModifier = std::function; +// using LayoutModifier = void(*)(Layout *); + +class QTCREATOR_UTILS_EXPORT Layout : public Object { +public: using Implementation = QLayout; + using I = BuilderItem; + + 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 items); + void addRow(std::initializer_list items); void flush(); + void flush_() const; void fieldGrowthPolicy(int policy); + QWidget *emerge() const; + QFormLayout *asForm(); QGridLayout *asGrid(); QBoxLayout *asBox(); - - std::vector pendingItems; + FlowLayout *asFlow(); // Grid-only int currentGridColumn = 0; int currentGridRow = 0; - Qt::Alignment align = {}; + //Qt::Alignment align = {}; + bool useFormAlignment = false; + + std::vector pendingItems; }; -struct QTCREATOR_UTILS_EXPORT Layout : BuilderItem +class QTCREATOR_UTILS_EXPORT Column : public Layout { -}; +public: + using Implementation = QVBoxLayout; + using I = BuilderItem; -struct QTCREATOR_UTILS_EXPORT Column : BuilderItem -{ Column(std::initializer_list ps); }; -struct QTCREATOR_UTILS_EXPORT Row : BuilderItem +class QTCREATOR_UTILS_EXPORT Row : public Layout { +public: + using Implementation = QHBoxLayout; + using I = BuilderItem; + Row(std::initializer_list ps); }; -struct QTCREATOR_UTILS_EXPORT Form : BuilderItem +class QTCREATOR_UTILS_EXPORT Form : public Layout { +public: + using Implementation = QFormLayout; + using I = BuilderItem
; + + Form(); Form(std::initializer_list ps); }; -struct QTCREATOR_UTILS_EXPORT Grid : BuilderItem +class QTCREATOR_UTILS_EXPORT Grid : public Layout { +public: + using Implementation = QGridLayout; + using I = BuilderItem; + + Grid(); Grid(std::initializer_list ps); }; -struct QTCREATOR_UTILS_EXPORT Flow : BuilderItem +class QTCREATOR_UTILS_EXPORT Flow : public Layout { +public: Flow(std::initializer_list 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() = default; + Widget(std::initializer_list 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(std::initializer_list ps); -}; - -// Label - -struct QTCREATOR_UTILS_EXPORT LabelInterface : WidgetInterface +class QTCREATOR_UTILS_EXPORT Label : public Widget { +public: using Implementation = QLabel; + using I = BuilderItem